iOS解决H5支付跳转到支付App及返回原App问题

您所在的位置:网站首页 网页总是自动打开支付宝 iOS解决H5支付跳转到支付App及返回原App问题

iOS解决H5支付跳转到支付App及返回原App问题

2024-07-09 14:07| 来源: 网络整理| 查看: 265

标题如此拗口, 我也是无可奈何🤣

本文会涉及到两个方面:

H5 支付时调起微信或支付宝 App; 调起微信或支付宝 App 完成支付操作后,返回到自己的原来的 App。

公司业务需求,需客户端嵌套一个完整的 H5 开发的网页,其中带有 H5 的微信支付和支付宝支付。微信支付一直无法打开页面,无法支付;支付宝支付可以打开支付宝的网页,如下图

支付宝支付.jpg

最初讨论的解决办法是:走到支付时,通过 js 桥来调起原生支付。如果 H5 页面是同一公司同事开发的,这倒是个简单快捷的方法。但是结合公司情况,考虑到后期可能会接入其他公司的 H5 页面,联调起来会很麻烦,所以还是决定不通过桥解决。

1. H5 支付时调起微信或支付宝 App

H5 支付调起微信或支付宝 App 的原理都一样,以 WK 为例,都是在 decidePolicyForNavigationAction 代理方法里面拦截 URL,再用 [[UIApplication sharedApplication] openURL:navigationAction.request.URL]; 调起。 不同的只是拦截的字段有区分,微信需拦截的字符串为 @"weixin://wap/pay" , 支付宝拦截的字符串为 @"alipay://alipayclient"。简单代码如下:

- (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler{ // 在发送请求之前,决定是否跳转 NSString *url = navigationAction.request.URL.absoluteString; if ([url containsString:@"weixin://wap/pay"] || [url containsString:@"alipay://alipayclient"]) { [[UIApplication sharedApplication] openURL:navigationAction.request.URL]; decisionHandler(WKNavigationActionPolicyCancel); return; } decisionHandler(WKNavigationActionPolicyAllow); }

至此,H5 支付可以成功调起微信或支付宝的 App 进行支付了。

支付宝还有另一种调起的方式, 在支付宝的开发文档中有提到, 这里也贴一下地址, 有兴趣的可以去试下: 支付宝手机网站支付转App支付

但是,如果代码写到这里就完的话,可能会出现两种情况:

使用微信支付, 操作完成, 仍停留在微信, 不会像原生调起支付那样返回自己的APP; 支付宝支持,操作完后,调起了 Safari,打开的网页就是之前在 WK 里打开的页面

ps:查资料时,有网友微信支付完成时,也会调起 Safari,但我调试过程中未出现这种情况

接下来解决第二个问题,完成支付操作后,返回自己的 App

2. 调起微信或支付宝 App 完成支付操作后,返回到自己的 App

老规矩,先贴上参考的链接 微信返回参考 https://www.jianshu.com/p/90db7dfb075c 支付宝返回参考 https://www.jianshu.com/p/0d8dd04fe94e 以上两篇文章里, 非常详细的描述了解决的整个过程, 包括解决过程中遇到哪些问题, 从哪些方面思考得到灵感, 最终如何一步步找到解决办法, 非常建议去看看, 了解下. 这里就不照搬了, 直接讲解决步骤了...

这部分内容得再拆分成两部分, 一个支付宝的, 一个微信的

支付宝支付返回到 App 拦截到支付宝支付的 URL (就是 URL 里包含 @"alipay://alipayclient") 时, 对 URL 进行 URL 解码;

解码后的 URL 如下: alipay://alipayclient/?{"requestType":"SafePay","fromAppUrlScheme":"alipays","dataString":"XXX"}

解码后得到一字符串, 字符串里包含了一个json 串. 把 json 部分截取出来, 再转成 dictionary, dictionary 里面将会有一个 key 为 fromAppUrlScheme 的键值对, 把 fromAppUrlScheme 的值改成自己 App 的 scheme; 把第二步得到 dictionary 再转成 json, 再对已经改了 fromAppUrlScheme 值的 json 进行 URL 编码; 把第三步编码好的字符串, 替换掉第一步拦截的 URL 的 json 部分... 注意!!! 这里替换的只是 URL 的 json 部分!!! 只是 URL 的 json 部分!!! 替换后得到一个新的的 URL; 拿第四步得到的带有自己 APP 的 scheme 的 URL, 去调起支付宝 App(就是文章第一部分说的调起 App 那样的调起)

需要注意的一个地方是, 进行 URL 编码时, 不是 URL 整体进行编码, 只有 json 那部分需要编码, 如果对完整的 URL 进行编码, 那么我们用来识别支付宝的字符串的那部分 (@"alipay://alipayclient") 也会被编码, 从而导致无法调起支付宝

基本实现如下:

- (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler{ // 在发送请求之前,决定是否跳转 NSString *url = navigationAction.request.URL.absoluteString; // 支付宝 if ([url containsString:@"alipay://alipayclient"]) { NSMutableString *param = [NSMutableString stringWithFormat:@"%@", (__bridge_transfer NSString *)CFURLCreateStringByReplacingPercentEscapesUsingEncoding(NULL, (__bridge CFStringRef)url, CFSTR(""), CFStringConvertNSStringEncodingToEncoding(NSUTF8StringEncoding))]; NSRange range = [param rangeOfString:@"{"]; // 截取 json 部分 NSString *param1 = [param substringFromIndex:range.location]; if ([param1 rangeOfString:@"\"fromAppUrlScheme\":"].length > 0) { id json = jsonToClass(param1); // 这里为伪代码, 自行转成 dictionary if (![json isKindOfClass:[NSDictionary class]]) { decisionHandler(WKNavigationActionPolicyAllow); return; } NSMutableDictionary *dicM = [NSMutableDictionary dictionaryWithDictionary:json]; dicM[@"fromAppUrlScheme"] = 自己App的scheme; NSString *jsonStr = classToJson(dicM); // 这里为伪代码, 自行转成json NSString *encodedString = (NSString*) CFBridgingRelease(CFURLCreateStringByAddingPercentEscapes(kCFAllocatorDefault, (CFStringRef)jsonStr, NULL, (CFStringRef)@"!*'();:@&=+$,/?%#[]", kCFStringEncodingUTF8)); // 只替换 json 部分 [param replaceCharactersInRange:NSMakeRange(range.location, param.length - range.location) withString:encodedString]; [[UIApplication sharedApplication] openURL:[NSURL URLWithString:param]]; } decisionHandler(WKNavigationActionPolicyCancel); } decisionHandler(WKNavigationActionPolicyAllow); } 微信支付返回到 App

微信的问题比支付宝的稍微麻烦些. 首先麻烦的就是配置问题, 需要在微信开发者平台上进行了相应的配置. 如果配置不对, 请参照 微信支付开发步骤&常见问题.

提醒下, 微信开发者平台上的配置的有次数限制的, 每个月只能修改多少多少次, 所以配置时尽量把能想到的需要用到的都一起配置了, 省得开发的时候被这些细节问题浪费时间

拦截到微信支付的 URL @"https://wx.tenpay.com/cgi-bin/mmpayweb-bin/checkmweb?". (这里需要拦截的 URL 与调起微信的 URL 不是同一个) H5 调起微信支付时, 需要设置 Referer 请求头, 所以直接拿请求头 newRequest.allHTTPHeaderFields = navigationAction.request.allHTTPHeaderFields; 给 Referer 赋值 [newRequest setValue:@"www.xxx.com://" forHTTPHeaderField: @"Referer"];, 自己的 App 也要设置一个 www.xxx.com 的 scheme. 并且取消此次加载.

解释下 www.xxx.com , 其实就是公司的一个域名, 可以是 H5 支付的域名, 也可以是公司其他域名, 但必须确保这个域名存在于公司的微信开发者平台的配置中. 那么问题来了, 这既然是公司的一个域名, 又要把这个域名设置成 scheme, 那有可能出现这么一个问题: 同公司的其他 App 也可能配置了同样的 scheme. 所以, 这里的域名和 scheme 要保证唯一性. 至于怎么保证, 跟后台哥们商量下吧. 记得配置到微信开发者平台上!!!

重新加载修改了 Referer 的请求. 拦截包含 @"weixin://wap/pay" 的 URL, 调起微信.

针对微信这部分, 划几个重点:

scheme 必须唯一, 不唯一的话随机打开一个 (公司有个 App 不知在什么情况下配了个跟某支付一毛一样的 scheme, 导致装有那个 App 的用户都不能用某支付来支付, 后来被发律师函了... ) @"https://wx.tenpay.com/cgi-bin/mmpayweb-bin/checkmweb?" 会拦截两次. 拦截第一次时, 需要修改 Referer, 取消此次加载, 重新加载修改了请求头的请求; 虽然请求头修改了, 可是 URL 并没有修改, 所以, 重新加载之后拦截到的还是 @"https://wx.tenpay.com/cgi-bin/mmpayweb-bin/checkmweb?". 在这一步需做处理, 如不处理, 这一步就死循环. 再次强调微信开发者平台的配置问题. 谁配谁知道!

基本实现如下:

- (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler{ // 在发送请求之前,决定是否跳转 //self.load 用来控制对 @"https://wx.tenpay.com/cgi-bin/mmpayweb-bin/checkmweb?" 的拦截 NSString *url = navigationAction.request.URL.absoluteString; if ([url containsString:@"weixin://wap/pay"]) { self.load = NO; [[UIApplication sharedApplication] openURL:navigationAction.request.URL]; decisionHandler(WKNavigationActionPolicyCancel); } else if ([url containsString:@"https://wx.tenpay.com/cgi-bin/mmpayweb-bin/checkmweb?"] && !self.isLoad) { NSURLRequest *request = navigationAction.request; NSMutableURLRequest *newRequest = [[NSMutableURLRequest alloc] init]; newRequest.allHTTPHeaderFields = request.allHTTPHeaderFields; #warning scheme 要改 [newRequest setValue:@"www.xxx.cn://" forHTTPHeaderField: @"Referer"]; newRequest.URL = request.URL; [webView loadRequest:newRequest]; self.load = YES; decisionHandler(WKNavigationActionPolicyCancel); } else if ([url containsString:@"https://wx.tenpay.com/cgi-bin/mmpayweb-bin/checkmweb?"]) { self.load = NO; decisionHandler(WKNavigationActionPolicyAllow); } decisionHandler(WKNavigationActionPolicyAllow); }

至此, 已经完成文章开头所说的两个部分, 能调起也能返回了.

2019/8/23 更新

不愿意修改 header ? 反正就是不能改 header, 不接受上面微信的解决方案, 怎么办呢? 去翻了下 微信支付开发步骤&常见问题 , 还真的有新发现, 不知道是之前没注意还是新加的... 拼接 redirect_url 可以指定回调页面.

回调页面.png

拿之前的 demo 简单的改改, 试了一下, 确实可以 👍

- (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler{ // 在发送请求之前,决定是否跳转 //self.load 用来控制对 @"https://wx.tenpay.com/cgi-bin/mmpayweb-bin/checkmweb?" 的拦截 NSString *url = navigationAction.request.URL.absoluteString; if ([url containsString:@"weixin://wap/pay"]) { self.load = NO; [[UIApplication sharedApplication] openURL:navigationAction.request.URL]; decisionHandler(WKNavigationActionPolicyCancel); } else if ([url containsString:@"https://wx.tenpay.com/cgi-bin/mmpayweb-bin/checkmweb?"] && !self.isLoad) { NSURLRequest *request = navigationAction.request; NSMutableURLRequest *newRequest = [[NSMutableURLRequest alloc] init]; // newRequest.allHTTPHeaderFields = request.allHTTPHeaderFields; // [newRequest setValue:@"www.xxx.cn://" forHTTPHeaderField: @"Referer"]; // newRequest.URL = request.URL; // 这里 redirect_url 要传的值, 就是上面 Referer 的值 NSString *urlStr = [NSString stringWithFormat:@"%@&redirect_url=www.xxx.cn://", [request.URL absoluteString]]; newRequest.URL = [NSURL URLWithString:urlStr]; [webView loadRequest:newRequest]; self.load = YES; decisionHandler(WKNavigationActionPolicyCancel); } else if ([url containsString:@"https://wx.tenpay.com/cgi-bin/mmpayweb-bin/checkmweb?"]) { self.load = NO; decisionHandler(WKNavigationActionPolicyAllow); } decisionHandler(WKNavigationActionPolicyAllow); }

简单点说就是不设置 Referer, 把之前需要设置的 Referer 值, 直接作为 redirect_url 的值, 拼在 URL 后面. 但返回自有 App 两者显示的页面效果是不太相同的, 以支付失败为例(没有1分钱的支付就不存支付成功的🌰 🤣)

Referer 回到原有 App, 会停留在跳转前的页面(仿佛时间静止了)

redirect_url 回到原因 App, 打开了一个微信页面, 白屏... 手动返回会回到跳转前的页面

不过 微信支付开发步骤&常见问题 也有提到, 设置 redirect_url 后, 可能需要用户手动触发查单操作, 可能还需要 H5 那边做点啥子操作吧...... (看到这里, 我基本确定了, 这个是新加上去的!!!!!)

redirect_url 注意事项.png


【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3