阅读 2959

iOS微信H5支付无法返回APP解决方案

本文只讨论已成功调起微信支付后,无法返回自己的APP的问题,iOS微信H5支付不在讨论范围内。提供下列参考:
微信H5支付官方文档
微信H5支付官方Demo

本文实现的效果:你的App->微信客户端->支付或取消->你的App
此方案支持多App,不同的App添加处理后不会出现返回混乱的情况。

另附,iOS支付宝H5支付无法返回APP解决方案

解决思路

微信官方文档中关于“回调页面”有这么一段话:

正常流程用户支付完成后会返回至发起支付的页面,如需返回至指定页面,则可以在MWEB_URL后拼接上redirect_url参数,来指定回调页面。 如,您希望用户支付完成后跳转至https://www.wechatpay.com.cn,则可以做如下处理:
假设您通过统一下单接口获到的
MWEB_URL= https://wx.tenpay.com/cgi-bin/mmpayweb-bin/checkmweb?prepay_id=wx20161110163838f231619da20804912345&package=1037687096
则拼接后的地址为
MWEB_URL= https://wx.tenpay.com/cgi-bin/mmpayweb-bin/checkmweb?prepay_id=wx20161110163838f231619da20804912345&package=1037687096&redirect_url=https%3A%2F%2Fwww.wechatpay.com.cn

微信H5支付,在中间页面的URL上提供了一个redirect_url参数,用于指定支付结束后的回调页面(默认是没有redirect_url参数的,微信会取请求头中referer参数的值作为回调页)。微信客户端会通过[[UIApplication sharedApplication] openURL:url]方法来返回回调页。如果我们把redirect_ur设置成我们App的URLSchemes是不是就可以返回我们的App了?

解决方案

  1. 在微信商户后台(微信商户平台-产品中心-开发配置-H5支付(最下面那个))注册一级域名,比如 company.com。
    这里的company.com请和你微信下单时的一级域名保持一致,即与微信中间页https://wx.tenpay.com请求头中的Referer字段对应的值一致。因为在微信中间页会去校验Referer和redirect_url的值是否在微信后台注册过。
  2. 在APP工程配置中设置URL Scheme,比如 A.company.com(A你可以随便写,后面的域名得和1.中一致)
  3. 在webView代理方法中拦截微信中间页请求(注意是请求不是返回结果),在这个请求的基础上新建一个请求,追加参数redirect_url=URLEncode(A.company.com://),cancel掉原来的请求,webView重新加载这个新的请求。
//以WKWebView为例(下面的代码可能不严谨,只是表达一个思路,请根据自己实际情况调整)
- (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler {
    NSURLRequest *request = navigationAction.request;
    //下面这个这个字符串不要直接写在代码中,否则会被苹果机审扫描到pay字段,导致被拒绝。可以自行加密处理或让后台返回
    NSString *wxPre = @"https://wx.tenpay.com/cgi-bin/mmpayweb-bin/checkmweb";
    if ([request.URL.absoluteString hasPrefix:wxPre] && [request.URL.absoluteString rangeOfString:@"redirect_url"].length==0) {
        //开始微信支付会走这里
        //将要请求微信中间页,且中间页没有追加过redirect_url参数,追加redirect_url
        NSMutableURLRequest *newRequest = [[NSMutableURLRequest alloc] init];
        newRequest.allHTTPHeaderFields = request.allHTTPHeaderFields;
        NSString *newURLStr = nil;
        //TODO: 对newURLStr追加参数redirect_url=URLEncode(@"A.company.com://")
        newRequest.URL = [NSURL URLWithString:newURLStr];
        [webView loadRequest:newRequest];
        decisionHandler(WKNavigationActionPolicyCancel);
    }
    else if ([request.URL.scheme rangeOfString:@"company.com"].length!=0) {
        //微信支付结束(完成\取消\超时)后会走这里
        //TODO: 关闭微信中间页,比如dismiss webViewController,或[webView goBack]
    }
    else {
      decisionHandler(WKNavigationActionPolicyAllow);
    }
}
复制代码
  1. 微信支付结束后,你的webView会发起重定向到redirect_url的请求,即会请求"company.com://",拦截这个请求关闭微信中间页。(后文会细说)

需要注意的问题

  1. Referer头和redirect_url中的域名,都必须在微信后台注册过。否则微信中间页会报错“商家存在未配置的参数,请联系商家解决”。常见错误类型
  2. redirect_url需要连同冒号反斜杠一起URLEncode处理。
  3. 微信支付完成回调redirect_url的时机并不可靠,可能微信支付还没结束就回调了。

微信官方文档对redirect_url的描述:
由于设置redirect_url后,回跳指定页面的操作可能发生在:1,微信支付中间页调起微信收银台后超过5秒 2,用户点击“取消支付“或支付完成后点“完成”按钮。因此无法保证页面回跳时,支付流程已结束,所以商户设置的redirect_url地址不能自动执行查单操作,应让用户去点击按钮触发查单操作

  1. 拦截微信中间页请求的前缀字符串不要直接写在代码里,否则会被机审扫描到"pay"字段导致被拒,请自行加密处理或后台返回。
  2. 微信后台注册一级域名的原因是,这样可以支持"多App",你的第一个App的URLScheme叫A.company.com,第二个App的URLScheme叫B.company.com,而不用再次去微信后台注册域名了。

微信支付返回后白屏问题

白屏问题我以前也没搞清楚原因,只知道怎么规避,这次整理这篇博客时又好好研究了一下,终于弄清除原因了。

现象:
首先,不使用这篇文章中的方法时,微信支付结束后,手动回到App,不会有白屏的问题。
其次,如果使用了这篇文章中的方法,有些读者反映,虽然微信支付结束后能够自动跳回App,但是App会整个白屏,无法继续操作。

添加redirect_url后白屏的原因

微信中间页https://wx.tenpay.com/cgi-bin/mmpayweb-bin/checkmweb代码中有这么一段:

window.onload = function() {
...省略
    var url = "weixin://wap/pay?prepayid%3Dwx18xxxxxxxxx&package=156xxxxxx&noncestr=157xxxxx&sign=719bxxxxxxxxxxx";
    var redirect_url = "A.company.com://";
    top.location.href = url;

    if (redirect_url) {
        setTimeout(function() {
            top.location.href = redirect_url;
        },
        5000);
    } else {
        setTimeout(function() {
            window.history.back();
        },
        5000);
    }
}
复制代码

看完是不是就豁然开朗了。

白屏原因总结:
发起微信支付时,会把页面重定向到中间页,也可以理解为校验页,有错误提示错误原因(如下图),没错误就是纯白色页面同时拉起微信客户端支付,中间页的js代码里面会判断是否有redirect_url的值,如果没有则5s后返回上一页window.history.back(),也就返回支付前的H5页面了;如果redirect_url有值,则重定向到redirect_url页面,而redirect_url=“A.company.com://“,重定向会失败,就停留在微信中间页了,这就是白屏的原因。

微信中间页

白屏问题如何解决

在支付结束后关闭微信中间页即可解决白屏问题。
有两个点,一个是支付结束的时机,一个是关闭微信中间页。

支付结束的时机,我上文提到过

- (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler {
    NSURLRequest *request = navigationAction.request;
    if ([request.URL.scheme rangeOfString:@"company.com"].length!=0) {
        //微信支付结束(完成\取消\超时)后会走这里
        //TODO: 关闭微信中间页,比如dismiss webViewController,或[webView goBack]
    }
    ...
}
复制代码

关闭微信中间页,我这里提供两种思路:

  1. 新开一个原生的webViewController来加载你的收银台页面(选择微信、支付宝那个页面),在支付结束后dismiss这个webViewController。
  2. 借鉴微信的思路,在支付结束后,[webView goBack]来返回原来的页面。

常见问题自查

1.按文章步骤操作后,App跳转微信后,支付或取消没有返回App而是跳转到Safari

  1. 检查redirect_url中的域名(有冒号反斜杠)、App URLScheme(没有冒号反斜杠),是否填写正确,是否一致,微信商户后台是否注册了对应的域名。
  2. 检查是否是URLEncode(redirect_url)的过程出现了问题,如果你URLEncode后仍然有 :// ,这是不对的,你可以拿你URLEncode后的值和在线URLEncode网站对比差异。

如果觉得这篇文章对你有帮助,请点个赞吧。如果有疑问可以关注我的公众号给我留言。
转载请注明出处,谢谢!

参考链接:
iOS 解决微信h5支付无法直接返回APP的问题
iOS实现微信外部H5支付完成后返回原APP(多APP也可实现)