微信h5支付测试时,发现使用Safari iframe的方式无法呼起微信app进行支付。本文首先讲述了微信支付的流程、随后进行了两个测试与网上的搜索等进行了一系列的探索,得到了可以证实的结论。
微信h5支付流程如下图所示:
1、用户在商户侧完成下单,使用微信支付进行支付;
2、由商户后台向微信支付发起下单请求(调用统一下单接口);
3、统一下单接口返回支付相关参数给商户后台,包括:支付跳转url(参数名“mweb_url”);
4. 商户重定向到mweb_url,调起微信支付中间页,客户进行支付,点击支付完成回调到商户的买单页 or 商户指定的redirect_url,至此,客户端流程结束。
5. 其他流程,在此不解释。普通商户微信h5支付官方地址
【过程4解析】:当用户重定向到mweb_url后,微信会返回一个呼起微信app的scheme串,形如如下代码描述:
mweb_url串 : https://wx.tenpay.com/cgi-bin/mmpayweb-bin/checkmweb?prepay_id=wx06203454877852b3d995061c1369865961&package=2470898687
scheme串:weixin://wap/pay?prepayid%3Dwx06203454877852b3d995061c1369865961&package=2470898687&noncestr=1533558901&sign=06c9a12b39e341a2dfe9d68e470edbfa
在做微信的h5支付时,发现在Safari中使用iframe无法呼起微信客户端的问题。
现象A: h5支付,不使用iframe,直接redirect到预下单返回的mweb_url,火狐、chrome、Safari均可以呼起微信支付。
现象B: h5支付,使用iframe,将返回的mweb_url设置到iframe中的src,火狐、chrome均可以呼起微信支付,唯独Safari中无法呼起(ios的其他浏览器也做过尝试,是可以呼起的)。
现象A前端代码:
<html>
<head>
<meta charset="utf-8">
<title>spy test h5 paytitle>
head>
<body>
<h1>h5 pay testh1>
<p>测试商品 0.01元p>
<form id="payFrom" name="payform" action="https://您的后台处理地址" method="post">
<input type="text" name="operFlag" value="h5pay">
<input type="button" style="background-image:url(wechaticon.png);" value="微信支付">
form>
body>
html>
现象A后端代码:
//与微信交互获取到mweb_url,在此省略
response.sendRedirect(mweb_url );//重定向
现象B前端代码:
<html>
<head>
<meta charset="utf-8">
<title>spy test h5 paytitle>
<script src="/script/jquery-3.3.1.min.js">script>
<script src="/script/jQuery.form.js">script>
<script type="text/javascript">
function submitform(){
$('#payForm').ajaxSubmit( //ajax方式提交表单
{
url: '您的地址',
type: 'post',
dataType: 'json',
success: function (data) {
$('#payIframe').attr({"src":data.url});//src 是赋值成功的
}
})
}
script>
head>
<body>
<h1>h5 pay testh1>
<p>测试商品 0.01元p>
<iframe id="payIframe" name="payIframe" style="width:200px;height:200px" sandbox="allow-same-origin allow-top-navigation allow-forms allow-scripts">iframe>
<form id="payForm" name="payForm" action="#" method="post" target="payIframe">
<input type="text" name="operFlag" value="h5pay1">
form>
<input type="button" style="background-image:url(wechaticon.png);" onclick="submitform()" value="微信支付">
body>
html>
现象B后端代码:
//与微信交互获取到mweb_url,在此省略
results = "{\"url\":\""+ mweb_url +"\"}";
response.setContentType("application/json;charset=UTF-8");
response.setHeader("catch-control", "no-catch");
PrintWriter out = response.getWriter();
out.write(results);
out.flush();
out.close();
return;
可以看到现象B的form表单中,target是iframe,针对无法呼起来的原因,博主做了如下探索。
在调试阶段,当我们使用mweb_url请求微信时,可以看到微信的返回中的一段代码:下图中url是呼起app的scheme串,赋值给了top.location.href,因为我们是从iframe发出去的,当response回来后,存在于iframe中,那么top.location 便指向了我们前端自己的jsp,即form所在的jsp中。随后,如图箭头所示,浏览器根据scheme协议,跳转到微信app,这个逻辑是没有任何问题的(至少其他浏览器总是可以呼起app的)。
为了证明我的推理,由于微信的返回我们无法控制,于是拷贝了内容到h5wxjs.jsp,并alert top.location.href对象,如下是h5wxjs.jsp.
<script type="text/javascript">
window.onload=function()
{
var url="weixin://wap/pay?prepayid%3Dwx06203454877852b3d995061c1369865961&package=2470898687&noncestr=1533558901&sign=06c9a12b39e341a2dfe9d68e470edbfa";
var redirect_url="";
alert(top.location.href);//弹出2
top.location.href=url;
}
script>
在测试的jsp中,引入h5wxjs.jsp:
var tmp_url = "https://你的地址/h5wxjs.jsp";
alert(location.href); //弹出1
$("#payIframe").attr("src",tmp_url);//src 是赋值成功的
测试发现,依然是其他浏览器均可以呼起app,而Safari无法呼起,同本文前序中的测试现象B,并且两次弹出都是同一个对象(注释的弹出1和弹出2)。
我们抛弃h5wxjs.jsp,直接在测试jsp中做如下测试:
function submitform(){
var tmp_url ="weixin://wap/pay?prepayid%3Dwx06203454877852b3d995061c1369865961&package=2470898687&noncestr=1533558901&sign=06c9a12b39e341a2dfe9d68e470edbfa";
location.href = tmp_url; //ok
//top.location.href=tmp_url; //ok
//window.location.href= tmp_url; //ok
// window.top.location.href = tmp_url;//ok
return;
}
实现证明,如上的4句赋值,包括Safari在内的所有浏览器均可以呼起微信app,同本文前序的测试现象A。
Safari做了安全控制,从iframe发出去的请求得到的response中,无法使用scheme协议呼起app。 此博文的博主也遇到了同样的问题,只不过是无法呼起支付宝app。参考:Safari iframe 无法呼起支付宝app
【备注:简书中 cls 的答复,其实并没有解决本质问题】
为了证明这点,我又使用苹果真机,Safari调试,可以看到请求微信后收到了response,但是并没有呼起微信app的过程。调试方法参考:苹果真机调试Safari
在此特别感谢我师父陈立全、孙彦杰、白长昊同事,北京零度在线科技有限公司朋友的帮助。