在工作中对于 h5 支付这个,遇到的问题很多,所以想记录一下,以后说不定就忘记了,没对接过的,看一下也可以避免很多坑。
背景 因需要更灵活的运营场景,在 app 内需要接入第三方(微信、支付宝)h5 支付
基本信息文档 微信 h5 支付开发及常见问题:https://pay.weixin.qq.com/wiki/doc/api/H5.php?chapter=15_4
项目 基本的接入方式不做过多的说明,下面以项目的维度简单说明一下在使用 h5 支付的过程中碰到的问题,以及处理方式;
星座牌小游戏 概述: 星座小游戏是一个卡片类抽金币的小游戏,用户通过选择对应的牌来抽取大于支付金额等值的金币;
基本流程:
关键逻辑及处理方式 一、星座牌小游戏调起微信 h5 支付
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 let payParams = JSON .parse (ret.data .payParams );let openWxUrl = payParams.payUrl + "&redirect_url=" + encodeURIComponent (redirectUrl); let hideFrame = document .createElement ("iframe" );hideFrame.setAttribute ("src" , openWxUrl); hideFrame.setAttribute ( "sandbox" , "allow-scripts allow-top-navigation allow-same-origin" ); document .body .appendChild (hideFrame);hideFrame.onload = function ( ) { setTimeout (function ( ) { hideFrame.parentNode .removeChild (hideFrame); }, 100 ); };
上面的代码,有几个处逻辑处理,对应的,通过下面的几个问题解释;
为什么通过 iframe 的形式加载 payUrl? 由星座牌小游戏的交互决定,因为用户在支付完之后,还需要在当前页面开奖,因此,通过 iframe 的形式处理,还能解决微信 h5 支付对于 payUrl 加载的 referer 验证问题;
iframe 为什么要设置 sandbox?,为什么要设定 setTimeout 去移除 iframe?
在 ios13 发布的时候,ios 出现了问题,微信 h5 支付没有调起成功;
app 在安卓的某个版本之后也出现了同样的问题,对应那个版本的 app,app 打包的安卓 sdk 版本做了更新
对应的异常 log 如下
1 2 message:Uncaught SecurityError: Failed to set the 'href' property on 'Location': The current window does not have permission to navigate the target frame to 'weixin://wap/pay?prepayid%3Dwx241530497040689dd1394a4f1296524900&package=3510274145&noncestr=1563953450&sign=7867a2dd7feb974db9285725cb2822d0'. 从log看到的代码error,其实不是业务本身的代码,而是payUrl加载后,支付页里面的"top.location.href"异常,关键代码如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 var is_postmsg = "" ;if (is_postmsg == "1" ) { parent.postMessage ( JSON .stringify ({ action : "send_deeplink" , data : { deeplink : "weixin://wap/pay?prepayid%3Dwx28141344091494d0c83d3c3cd472e50000&package=3738825662&noncestr=1609136025&sign=02d138bef13beb9222479014a4a4ea85" , }, }), "" ); } else { var url = "weixin://wap/pay?prepayid%3Dwx28141344091494d0c83d3c3cd472e50000&package=3738825662&noncestr=1609136025&sign=02d138bef13beb9222479014a4a4ea85" ; var redirect_url = "https://ulink.com/ulink/lucky/index.html" ; top.location .href = url; if (redirect_url) { setTimeout (function ( ) { top.location .href = redirect_url; }, 5000 ); } else { setTimeout (function ( ) { window .history .back (); }, 5000 ); } }
从上面的代码块可以看到 payUrl 调起微信的逻辑
调起微信走的 else 的逻辑(这里的 postmsg 的逻辑暂时未从微信官方文档内找到对于的配置方式),通过 top.loaction.href= url 的形式 加载 scheme 调起;
如果有 redirect_url,会设置个定时器, 5 秒后重定向到 redirect_url — 这也是为什么星座牌小游戏需要添加一个 remove iframe 的逻辑的原因,
整体梳理下来,问题的原因就是 pp 在用新的 android sdk 打包之后,webview 的内核版本的提升,对应的内容安全策略(csp)调整(默认设置调整),阻止了 payUrl 通过 top 的方式直接访问父页面的 api;那么这个问题是通过 iframe 设置 sandbox 属性解决:
1 2 3 4 5 6 7 8 9 10 11 12 hideFrame.setAttribute ( "sandbox" , "allow-scripts allow-top-navigation allow-same-origin" );
3、redirectUrl 是回调地址,微信是怎么跳回指定的地址的?redirectUrl 为什么配置 ulink 的地址? 微信处理 redirectUrl 的逻辑
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 if (redirect_url){ setTimeout ( function ( ){ top.location .href =redirect_url; }, 5000 ); } else { setTimeout ( function ( ){ window .history .back (); }, 5000 ); } redirectUrl为什么配置ulink的地址?
android 在跳到微信支付完成后,点击支付成功页面的”完成”按钮,会回到调起微信支付的 app,ios 点击完成不会做这部分操作,在有配置 redirectUrl 的情况下会用 safari 打开 redirectUrl;且 redirectUrl 也有域名的验证;
配置成 ulink 的地址,是为了解决 ios 在支付完成后回到 app 内的处理方式;
注:ios 也有不能直接回到 app 的情况,而是通过 safari 打开了 ulink 的页面,这种情况,经过跟客户端同事的一同排查的结果是因为用户在安装 app 的时候,没有下载到 apple-app-site-association 这个文件导致;
app 充值(内嵌、外部) 概述: 内嵌页 h5 充值页面,提供方便用户充值的页面,并且支持微信支付,支付宝支付,对应 app 内的充值;
基本逻辑: 同星座牌小游戏类似,不同的地方是开奖变成了用户余额查询;
一、微信 h5 支付 避免了星座牌小游戏上喷到的问题之后,暂时(这个在子 app 的充值弹窗上又遇到了新的问题)没其他的问题出现; 加载 payUrl 后交互时序图(非官方):
二、支付宝 h5 支付(支付宝 h5 支付有两种方式:1. 纯 h5 页面支付;2.h5 调起支付宝 app 支付),以下为 h5 调起支付宝 app 支付的流程 支付宝 h5 支付,返回的不是 payUrl,而是一段 formDomString;
提交 payForm 后交互时序图(非官方):
1 2 <!-- 支付宝h5支付,接口返回的form信息 --> <form name="punchout_form" method="post" action="https://openapi.alipay.com/gateway.do?charset=UTF-8&method=alipay.trade.wap.pay&sign=HfcRgFeT%2FSVj1soSQrBQYCV%2BaoQzrBVupczUmmjM0sQ2FqXlFHMqOti4EexmhSh3Ap%2FRAAG8MXlo%2FTbzVquR59bXe3deuTXc30S5cgsV9l00jaKPOKXdSfJah2r%2FR5onafKys9caXLaaQmVwtrSrWr5hMFz%2FmtfZvZWwch%2FFvJuVS0wlGT128GBG0KSiUue0g2Bs%2BVg%2B3WKhIiQLCBMKB7BuuyFCnvwnpjeLiGafjIYr6CNBn83uzac1QX9OBuzp91EVLGbBSwAFyyxALhporUh4pDe27SqJbwg15kQd6tDp2f7423M6AoQGkEDMdzaBWRTu2UrMenzaqDOpFpilHA%3D%3D&return_url=https%3A%2F%2Fapp.test.com%2Fstatic%2Fh5Conversion%2Findex.html¬ify_url=https%3A%2F%2Fapp.com%2Fcallback%2Fppywforkylin%2Falipay%2Falipay%2F5103092247759423283&version=1.0&app_id=2021001145660238&sign_type=RSA2×tamp=2020-12-28+15%3A28%3A39&alipay_sdk=alipay-sdk-java-3.4.49.ALL&format=json">\n<input type="hidden" name="biz_content" value="{"body":"10金币","out_trade_no":"1231231233123123123","product_code":"QUICK_WAP_WAY","subject":"10金币","timeout_express":"2m","total_amount":"1"}">\n<inp
1、以上 form 代码段直接通过 innerHtml 插入页面还是不行的,script 内的 submit 并不会被执行 HTML 5 中指定不执行由 innerHTML 插入的