一、业务场景发生

最近在跟一些合作公司作业务对接,在对方的APP中接入我们的H5支付,包括微信和支付宝。

那就开搞,进展顺利,貌似一切都在掌握之中,给到对方一个链接即可调起支付。形如:

https://www.batsing.com/api/h5pay?appid=1234567001&userid=10&money=0.01&paytype=weixin

通过QQ或者浏览器打开即可付款发货,丝滑完美。

然而。。。接入到他们APP中却报错了,赶紧去微信开发文档看看

微信官方给的方案很简单,让APP开发的去修改webview,但这,合理吗?

对于自家做APP的是可以。但对于像我们这种做第三方的,这显然不是一个优秀的解决途径,那怎么办呢?

二、 问题本质

出现这个问题的本质是什么呢?是APP中页面跳转到MWEB_URL的请求中没有带上Referer。

(APP中的webview把一些http请求头给吃掉了,referer不会像普通浏览器那样自动识别转发出去)

既然前端的解决方案不友好,那就换成后端来解决,强大的curl可以模拟一切http请求。

三、Hack跟踪微信H5支付过程

在微信下单接口解析xml得到 MWEB_URL,然后对照着微信开发文档,经过一番参数调试,伪造了请求IP和referer,请求IP要与调用下单接口时传递的客户端IP一致,referer要与微信商户后台上填写的授权域名一致。

然后捕捉到了正常的微信支付页面代码。

这是curl发送模拟请求的代码

  1. <?php
  2. $mweb_url = 'https://wx.tenpay.com/cgi-bin/mmpayweb-bin/checkmweb?prepay_id=wx20161110163838f231619da20804912345&package=1037687096';
  3. $headers = [
  4. "CLIENT-IP:" . $_SERVER['REMOTE_ADDR'],
  5. "X-FORWARDED-FOR:" . $_SERVER['REMOTE_ADDR'],
  6. ];
  7. $result = Common_Http::curl_get_content($mweb_url, [
  8. 'CURLOPT_HTTPHEADER' => $headers,
  9. 'CURLOPT_REFERER' => 'https://www.batsing.com'
  10. ]);
  11. echo'<pre>';print_r(htmlspecialchars($result));echo'</pre>';

打印出来得到的内容如下,我们只看 window.onload 部分就可以了。

  1. <!DOCTYPE html>
  2. <html>
  3. <head lang="en">
  4. <meta http-equiv=Content-Type content="text/html;charset=utf-8">
  5. <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0">
  6. <meta name="apple-mobile-web-app-capable" content="yes">
  7. <meta name="apple-mobile-web-app-status-bar-style" content="black">
  8. <meta name="format-detection" content="telephone=no">
  9. <title>weixin</title>
  10. <style>.f10{font-size:10px}.f11{font-size:11px}.f12{font-size:12px}.f13{font-size:13px}.f14{font-size:14px}.f15{font-size:15px}.f16{font-size:16px}.f17{font-size:17px}.f18{font-size:18px}.f19{font-size:19px}.f20{font-size:20px}body{font-size:14px}h1,h2,h3,h4,h5{font-weight:400;font-style:normal}h1,.h1{font-size:20px}h2,.h2{font-size:18px}h3,.h3{font-size:16px}h4,.h4{font-size:14px}h5,.h5{font-size:12px}a,a:visited{color:#007aff}.text_color{color:#888}.title_color{color:#000}.desc{color:#b2b2b2}.warn{color:#b71414}.nickname{color:#576b95}.tips{font-size:13px;color:#b2b2b2}body{background-color:#fff}body.msg_dark{background-color:#2e3132;color:#fff}.page_msg{padding:75px 15px 0;text-align:center}.icon_area{margin-bottom:19px}.text_area{margin-bottom:25px}.text_area .title{margin-bottom:12px}.opr_area{margin-bottom:25px}.extra_area{margin-bottom:20px}@media screen and (min-height:416px){.extra_area{position:fixed;left:0;bottom:0;width:100%}}.btn{display:block;margin-left:auto;margin-right:auto;padding-left:14px;padding-right:14px;font-size:16px;text-align:center;text-decoration:none;overflow:visible;height:40px;border-radius:5px;-moz-border-radius:5px;-webkit-border-radius:5px;box-sizing:border-box;-moz-box-sizing:border-box;-webkit-box-sizing:border-box;color:#fff;line-height:40px;-webkit-tap-highlight-color:rgba(255,255,255,0)}.btn.btn_inline{display:inline-block}.btn_default{background-color:#d1d1d1}.btn_default:not(.btn_disabled):visited{color:#fff}.btn_default:not(.btn_disabled):active{color:rgba(255,255,255,.4);background-color:#a7a7a7}.btn_primary{background-color:#04be02}.btn_primary:not(.btn_disabled):visited{color:#fff}.btn_primary:not(.btn_disabled):active{color:rgba(255,255,255,.4);background-color:#039702}.btn_warn{background-color:#ef4f4f}.btn_warn:not(.btn_disabled):visited{color:#fff}.btn_warn:not(.btn_disabled):active{color:rgba(255,255,255,.4);background-color:#c13e3e}.btn.btn_mini{height:25px;line-height:25px;font-size:14px}button.btn,input.btn{width:100%;border:0;outline:0;-webkit-appearance:none}button.btn:focus,input.btn:focus{outline:0}button.btn_inline,input.btn_inline{width:auto}.btn_disabled{color:rgba(255,255,255,.6)}.btn+.btn{margin-top:10px}.btn.btn_inline+.btn.btn_inline{margin-top:auto;margin-left:10px}.btn_area{margin-left:-5px;margin-right:-5px;font-size:0}.btn_area.btn_area_inline{margin-left:auto;margin-right:auto;display:-webkit-box;display:-webkit-flex;display:-moz-box;display:-ms-flexbox;display:flex}.btn_area.btn_area_inline .btn{margin-top:auto;margin-right:10px;width:100%;-webkit-box-flex:1;-webkit-flex:1;-moz-box-flex:1;-ms-flex:1;box-flex:1;flex:1;display:inline-block \9;width:48% \9;margin-left:1% \9;margin-right:1% \9}.btn_area.btn_area_inline .btn:last-child{margin-right:0}span.btn button{display:block;width:100%;height:100%;background-color:transparent;border:0;outline:0;color:#fff}span.btn button:active{color:rgba(255,255,255,.4)}span.btn.btn_loading button,span.btn.btn_disabled button{color:#fff}.icon_msg{width:100px;height:100px;vertical-align:middle;display:inline-block;border-radius:50%;-moz-border-radius:50%;-webkit-border-radius:50%}.icon_msg.warn{background-color:#f86161;color:#fff;font-size:60px;font-style:normal}html{-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%}body{font:14px/1.5em "Helvetica Neue",Helvetica,Arial,sans-serif;background-color:#efeff4;line-height:1.6}body,h1,h2,h3,h4,h5,p,ul,ol,dl,dd,fieldset,textarea{margin:0}fieldset,legend,textarea,input,button{padding:0}button,input,select,textarea{font-family:inherit;font-size:100%;margin:0;*font-family:"Helvetica Neue",Helvetica,Arial,sans-serif}ul,ol{padding-left:0;list-style-type:none}a img,fieldset{border:0}a{text-decoration:none}</style>
  11. </head>
  12. <body>
  13. <div class="body">
  14. <div id="errpage" class="page_msg">
  15. </div>
  16. </div>
  17. <script src="//wx.gtimg.com/wxpay_h5/fingerprint2.min.1.4.1.js"></script>
  18. <script type="text/javascript">
  19.  
  20. var is_postmsg="";
  21. if( 0!==0 && is_postmsg=="1" )
  22. {
  23. parent.postMessage(JSON.stringify({
  24. action : "send_deeplink_fail",
  25. data : {
  26. deeplink : ""
  27. },
  28. error : {
  29. error_code : "0",
  30. error_msg : "ok"
  31. }
  32. }), "");
  33. }
  34. if( 0===0)
  35. {
  36. window.onload=function()
  37. {
  38. // var fp=new Fingerprint2();
  39. // fp.get(function(result)
  40. {
  41. // var fingerprint="";
  42. /* if(fingerprint!=result && fingerprint)
  43. {
  44. document.getElementById("errpage").innerHTML='<div class="icon_area"><i class="icon_msg warn">!</i></div> \
  45. <div class="text_area"> \
  46. <h2 id="111" class="title"> '+result+'网络环境未能通过安全验证,请稍后再试</h2> \
  47. </div>';
  48. return;
  49. }*/
  50. var is_postmsg="";
  51. if(is_postmsg=="1")
  52. {
  53. parent.postMessage(JSON.stringify({
  54. action : "send_deeplink",
  55. data : {
  56. deeplink : "weixin://wap/pay?prepayid%3Dwx0917024062319287307475501524736300&package=2450968634&noncestr=1591693360&sign=07b94cb3986c14bab48ec240f805f9a5"
  57. }
  58. }), "");
  59. }
  60. else
  61. {
  62. var url="weixin://wap/pay?prepayid%3Dwx0917024062319287307475501524736300&package=2450968634&noncestr=1591693360&sign=07b94cb3986c14bab48ec240f805f9a5";
  63. var redirect_url="";
  64. top.location.href=url;
  65.  
  66. if(redirect_url)
  67. {
  68. setTimeout(
  69. function(){
  70. top.location.href=redirect_url;
  71. },
  72. 5000
  73. );
  74. }
  75. else
  76. {
  77. setTimeout(
  78. function(){
  79. window.history.back();
  80. },
  81. 5000);
  82. }
  83. }
  84. }
  85. // );
  86. }
  87.  
  88. }
  89. </script>
  90. </body>
  91. </html>

一开始还以为一直被判断“网络环境未能通过安全验证”,然后才发现那段是被注释掉的,被虚晃了一枪。

关键部分,页面里有两段一样的 weixin://wap/pay? 开头的网址,很明显这个网址就是要拉起手机里的微信应用发起支付的。

其他的js代码逻辑也很简单,先从顶层跳转到weixin:// 即是拉起支付,然后5秒钟后跳转到重定向地址(若无重定向地址则返回上一页)。

我们尝试用手机直接访问这个 weixin:// 网址,确实可以拉起手机支付,支付也可以顺利完成到账。

那可以确定,IP地址和referer校验只是在 https://wx.tenpay.com/ 这里有做,到了 weixin://wap/pay 这一步他就不再校验这些设备信息了。

四、制作新的H5支付页面

最后,我们参照微信H5支付页面,拼出一个类似的H5界面,实现跳转支付以及5秒后跳转到支付完成页;

这样就可以实现在webview中也能顺利发起微信H5支付,并实现同样的跳转逻辑了。

“商家参数格式有误”应用切微信H5支付完美解决方案的更多相关文章

  1. 微信h5支付出现“商家参数格式有误,请联系商家解决”

    在浏览器进行微信h5支付时出现:

  2. 非内置浏览器WebView 调起H5支付,提示商家参数格式有误

    微信H5 支付开发官方文档参考资料: https://pay.weixin.qq.com/wiki/doc/api/H5.php?chapter=15_4 0. 场景描述:在APP 中使用webVie ...

  3. 黄聪:微信h5支付demo微信H5支付demo非微信浏览器支付demo微信wap支付

    一.首先先确定H5支付权限已经申请! 二.开发流程 1.用户在商户侧完成下单,使用微信支付进行支付 2.由商户后台向微信支付发起下单请求(调用统一下单接口)注:交易类型trade_type=MWEB ...

  4. 微信H5支付 遇到坑的一些解决方法

    解决办法 1. 商家参数格式有误,请联系商家解决 a.对于前后端分离的开发模式 前端发起请求 服务端请求微信h5支付统一下单接口 返回参数mweb_url 给前端 然后前端调起微信h5支付 b.注意的 ...

  5. 微信h5支付demo微信H5支付demo非微信浏览器支付demo微信wap支付

    服务项目 新手技术咨询 企业技术咨询 定制开发 服务说明 QQ有问必答 QQ.微信.电话 微信开发.php开发,网站开发,系统定制,小程序开发 价格说明 200元/月 1000/月 商议       ...

  6. 微信H5支付常见问题汇总

    常见问题 一.回调页面 正常流程用户支付完成后会返回至发起支付的页面,如需返回至指定页面,则可以在MWEB_URL后拼接上redirect_url参数,来指定回调页面. 如,您希望用户支付完成后跳转至 ...

  7. C# 微信h5支付

    相关文档  https://pay.weixin.qq.com/wiki/doc/api/H5.php?chapter=9_20&index=1 需要准备 公众号ID.商户号.商家私钥 1.登 ...

  8. 【第二十篇】C#微信H5支付 非微信内浏览器H5支付 浏览器微信支付

    微信开发者文档 微信H5支付官方文档   请阅读清楚  最起码把所有参数看一遍 这个地方也可以看看 微信案例 http://wxpay.wxutil.com/mch/pay/h5.v2.php,请在微 ...

  9. 微信公众号支付|微信H5支付|微信扫码支付|小程序支付|APP微信支付解决方案总结

    最近负责的一些项目开发,都用到了微信支付(微信公众号支付.微信H5支付.微信扫码支付.APP微信支付).在开发的过程中,在调试支付的过程中,或多或少都遇到了一些问题,今天总结下,分享,留存. 先说注意 ...

随机推荐

  1. Redis四大模式之主从配置

    Redis工作模式主要有单机模式.主从模式(slave).哨兵模式(sentinel).集群模式(cluster)这四种,本文主要讲解一下主从模式的部署方式. 我是windows单机进行的这套搭建操作 ...

  2. UVA 11383 Golden Tiger Claw 题解

    题目 --> 题解 其实就是一个KM的板子 KM算法在进行中, 需要满足两个点的顶标值之和大于等于两点之间的边权, 所以进行一次KM即可. KM之后, 顶标之和就是最小的.因为如果不是最小的,就 ...

  3. MobileNetV1/V2/V3简述 | 轻量级网络

    MobileNet系列很重要的轻量级网络家族,出自谷歌,MobileNetV1使用深度可分离卷积来构建轻量级网络,MobileNetV2提出创新的inverted residual with line ...

  4. java 基本语法(八) 数组(一) 数组的概述

    * 1.数组的理解:数组(Array),是多个相同类型数据一定顺序排列的集合,并使用一个名字命名, * 并通过编号的方式对这些数据进行统一管理. * * 2.数组相关的概念: * >数组名 * ...

  5. 前端03 /css简绍/css选择器

    前端03 /css简绍/css选择器 目录 前端03 /css简绍/css选择器 昨日内容回顾 html标签 常用标签 table标签:表格标签 input标签 select下拉框 textarea多 ...

  6. 【RPA Starter第一课】 Uipath RPA Starter Course

    今天开始学习Uipath学院上面的课程,准备考下高级开发认证. 官网全部都是英文,然后自己一步一步的翻译,解读.开始第一步. 考纲里有写这需要学习哪些课程.自己按着上面来, 第一门课: RPA Sta ...

  7. python学习03-使用动态ua

    在写爬虫的时候要使用到浏览器ua 分享一下今天学到的如何使用动态ua的进行爬取 1.简单的爬取网页信息 from urllib.request import urlopen #目标地址 url = & ...

  8. Oracle RACDB 增加、删除 在线重做日志组

    Oracle RACDB 增加.删除 在线重做日志组 select * from v$log;select * from v$logfile ; ----删除日志组:alter database dr ...

  9. [CISCN2019 华东南赛区]Double Secret

    0x01 进入页面如下 提示我们寻找secret,再加上题目的提示,猜测这里有secret页面,我们尝试访问,结果如下 根据它这个话的意思,是让我们传参,然后它会给你加密,我们试一下 发现输入的1变成 ...

  10. Python基础点记录1

    1 变量:一个变量就是一个单词,只有一个单一的值 1 Python里面的数据类型 interage , floats , booleans , String等 2 Python是一个区分大小写的语言 ...