iOS App中微信H5支付打开safari,无法返回App终极解决方案

7,044 阅读4分钟

废话不多说,直接说重点

微信官方提醒:H5支付不建议在APP端使用,如需要在APP中使用微信支付,请接APP支付,文档详见微信支付开发文档

场景:公司业务需要,在移动端App中引入了微信H5支付逻辑,支付流程Android没问题,但是在iOS端出现了支付成功/失败之后打开了Safari,并没有直接返回App的问题。

以下为解决方案:

首先在网页中H5调起微信时,可以获取到H5发出的支付链接为https://wx.tenpay.com/cgi-bin/mmpayweb-bin/checkmweb?prepay_id=************&package=*********&redirect_url=http://pay.*********.com/phonepay/wxsuccess.jsp此链接会发起支付,然后打开微信App支付页面,支付之后返回App,这个流程在Android中是没有问题的,但是在iOS端支付之后确莫名其妙的打开了Safari,Safari显示的界面为redirect_url参数对应的http://pay.*********.com/phonepay/wxsuccess.jsp链接界面。

注意:此处链接中的redirect_url是微信支付之后的结果界面,如果在此链接中拼接了redirect_url,那么Safari是一定会被打开的,如果前端拼接了此地址,那么可以让前端来对iOS端区别操作,不拼接redirect_url

如果前端处理起来比较麻烦,或者不愿意做处理,那么只能移动端(iOS)来手动将这部分参数截取

最终,iOS端发起支付的H5链接为https://wx.tenpay.com/cgi-bin/mmpayweb-bin/checkmweb?prepay_id=************&package=*********,仅仅只是这样还不行,这个时候链接打开会报错

删除redirect_url之后

此时,还需要一步操作:

在项目中配置Schemes为pay.***.com

配置Schemes

注意:此处的pay.***.com,为微信H5支付注册时配置的授权域名

然后在UIWebView的代理-(BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType中添加以下代码:

    /* 
    此处省略了从
    https://wx.tenpay.com/cgi-bin/mmpayweb-bin/checkmweb?prepay_id=************&package=*********&redirect_url=http://pay.*********.com/phonepay/wxsuccess.jsp
    链接中删除redirect_url的代码  
    */
    
    /*
    将删除了redirect_url的支付链接,头部添加Referer信息,以便微信支付之后可以返回app
    此处做链接的前缀判断是为了防止在其他链接中加入Referer信息
    */
    NSDictionary *headers = [request allHTTPHeaderFields];
    NSString *preStr = @"https://wx.tenpay.com/cgi-bin/mmpayweb-bin/checkmweb";
    if (![[headers objectForKey:@"Referer"] isEqualToString:@"pay.***.com://"] && [request.URL.absoluteString hasPrefix:preStr]) {
        dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
            dispatch_async(dispatch_get_main_queue(), ^{
                NSURL *url = [request URL];
                NSMutableURLRequest *req = [NSMutableURLRequest requestWithURL:url cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:60.0];
                [req setHTTPMethod:@"GET"];
                [req setValue:@"pay.***.com://" forHTTPHeaderField:@"Referer"];
                [self->webView loadRequest:req];
            });
        });
        
        return YES;
    }

解释: 如果链接中拼接了redirect_url,那么系统会用Safari打开此链接,将此链接相关配置删除之后,在请求头部添加Referer之后,那么将会打开Referer对应的链接。由于我们这里给Referer配置的pay.***.com://协议,之前已经在App中注册了Schemes,那么在Safari打开此协议时将会直接打开App,并且没有中间Safari跳转的过程(关于此处请自行搜索iOS 如何使用Safari打开App).

到此,支付逻辑可以返回App了,那么可能还会有疑问,我的redirect_url对应的支付结果界面如何展示呢。

关于redirect_url,微信支付开发文档有如下介绍

redirect_url

总的来说:使用redirect_url返回支付结果界面的时机本就无法精确掌控。所以根据微信此处介绍,可以有两种方案解决:

  • 调起微信支付,五秒之后刷新UIWebView,跳转支付结果界面redirect_url
  • 支付之后,返回App后,弹框进行查单操作(如上图)。

由于,我们支付成功失败之后都是同一个界面,所以采用了第一种方案,五秒之后刷新

最终代码如下:

-(BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType {
    /* 
    此处省略了从
    https://wx.tenpay.com/cgi-bin/mmpayweb-bin/checkmweb?prepay_id=************&package=*********&redirect_url=http://pay.*********.com/phonepay/wxsuccess.jsp
    链接中删除redirect_url的代码  
    */
    
    /*
    将删除了redirect_url的支付链接,头部添加Referer信息,以便微信支付之后可以返回app
    此处做链接的前缀判断是为了防止在其他链接中加入Referer信息
    */
    NSDictionary *headers = [request allHTTPHeaderFields];
    NSString *preStr = @"https://wx.tenpay.com/cgi-bin/mmpayweb-bin/checkmweb";
    if (![[headers objectForKey:@"Referer"] isEqualToString:@"pay.***.com://"] && [request.URL.absoluteString hasPrefix:preStr]) {
        dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
            dispatch_async(dispatch_get_main_queue(), ^{
                NSURL *url = [request URL];
                NSMutableURLRequest *req = [NSMutableURLRequest requestWithURL:url cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:60.0];
                [req setHTTPMethod:@"GET"];
                [req setValue:@"pay.***.com://" forHTTPHeaderField:@"Referer"];
                [self->webView loadRequest:req];
            });
        });
        
        return YES;
    }
    
    /*
    从打开微信支付开始,五秒之后刷新webView界面(redirect_url本就不靠谱,无法精确)
    weixin://wap/pay 为最终调起微信支付时的协议开头
    */
    if ([request.URL.absoluteString hasPrefix:@"weixin://wap/pay"]) {
        dispatch_time_t delayTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(5.0 * NSEC_PER_SEC));
        dispatch_after(delayTime, dispatch_get_main_queue(), ^{
            [self->webView loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:@"http://pay.***.com/phonepay/wxsuccess.jsp"]]];
        });
        return YES;
    }
}