iOS集成第三方H5 调起微信支付和支付宝支付

今天公司产品过来交了个需求,App内部加载第三方的H5页面需要调起微信和支付宝进行支付,但是不是调起原生中集成的,而是H5内部调起。
现在遇到问题是可以打开微信,但是支付完成之后不会回到app,并且取消之后之后会跳转到Safari浏览器加载一个第三方的支付完成的网址。
这并不符合预期,期待的效果是加载H5页面调起微信或者支付宝App进行支付,取消或者支付成功之后跳转 回到原来的App,并且加载第三方的支付完成的网址。

无法调起微信或者调起微信之后无法返回

无法调起微信的原因是没有正确的设置正确的Referer或者没有含有一级域名的redirect_url

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

正常流程用户支付完成后会返回至发起支付的页面,如需返回至指定页面,则可以在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。
2.这里的company.com请和你微信下单时的一级域名保持一致,即与微信中间页https://wx.tenpay.com请求头中的Referer字段对应的值一致。因为在微信中间页会去校验Referer和redirect_url的值是否在微信后台注册过。
3.在APP工程配置中设置URL Scheme,比如 A.company.com(A你可以随便写,后面的域名得和1.中一致)
在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);
    }
}

4.微信支付结束后,你的webView会发起重定向到redirect_url的请求,即会请求"company.com://",拦截这个请求关闭微信中间页。(后文会细说)

微信支付返回后白屏问题

白屏问题我以前也没搞清楚原因,只知道怎么规避,这次整理这篇博客时又好好研究了一下,终于弄清除原因了。
现象:
首先,不使用这篇文章中的方法时,微信支付结束后,手动回到App,不会有白屏的问题。
其次,如果使用了这篇文章中的方法,有些读者反映,虽然微信支付结束后能够自动跳回App,但是App会整个白屏,无法继续操作。
白屏原因总结:
发起微信支付时,会把页面重定向到中间页,也可以理解为校验页,有错误提示错误原因(如下图),没错误就是纯白色页面同时拉起微信客户端支付,中间页的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网站对比差异。

下面贴出来部分代码

#pragma mark - ToolFunction
- (NSString *)WK_URLDecodedString:(NSString *)urlString {
    NSString *string = urlString;
    NSString *decodedString=(__bridge_transfer NSString *)CFURLCreateStringByReplacingPercentEscapesUsingEncoding(NULL, (__bridge CFStringRef)string, CFSTR(""), CFStringConvertNSStringEncodingToEncoding(NSUTF8StringEncoding));
    return decodedString;
}

- (NSString *)WK_URLEncodedString:(NSString *)urlString {
    NSString *string = urlString;
    NSString *encodedString = (NSString *) CFBridgingRelease(CFURLCreateStringByAddingPercentEscapes(kCFAllocatorDefault, (CFStringRef)string, NULL, (CFStringRef)@"!*'();:@&=+$,/?%#[]", kCFStringEncodingUTF8));
    return encodedString;
}
//防止被苹果抓到pay字段 需要下面的编码
- (NSString *)encode:(NSString *)str
{
    NSData *data = [str dataUsingEncoding:NSUTF8StringEncoding];
    NSString *string = [data base64EncodedStringWithOptions:NSDataBase64EncodingEndLineWithLineFeed];
    return string;
}

- (NSString *)decode:(NSString *)str
{
    NSData *data = [[NSData alloc] initWithBase64EncodedString:str options:NSDataBase64DecodingIgnoreUnknownCharacters];
    NSString *string = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
    return string;
}

- (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler方法中拦截支付

if ([requestString hasPrefix:[self decode:@"YWxpcGF5czovLw=="]] || [requestString hasPrefix:[self decode:@"YWxpcGF5Oi8v"]]) {支付宝需要添加返回的schme
        NSString *urlString = [[self WK_URLDecodedString:requestString] stringByReplacingOccurrencesOfString:[self decode:@"YWxpcGF5cw=="] withString:@"your schme"];
        NSString *prefixString = @"YWxpcGF5Oi8vYWxpcGF5Y2xpZW50Lz8=";
        NSURL *openedURL = navigationAction.request.URL;
        if ([urlString hasPrefix:[self decode:prefixString]]) {
            NSRange rang = [urlString rangeOfString:[self decode:prefixString]];
            NSString *subString = [urlString substringFromIndex:rang.length];
            NSString *encodedString = [[self decode:prefixString] stringByAppendingString:[self WK_URLEncodedString:subString]];
            openedURL = [NSURL URLWithString:encodedString];
        }
        if (![[UIApplication sharedApplication] openURL:openedURL]) {
            [CCHUDTool showTipWithText:[self decode:@"5pyq5a6J6KOF5pSv5LuY5a6d5a6i5oi356uv"]];
        }
         allowJumpToUrl = NO;
    }
    if ([requestString hasPrefix:[self decode:@"aHR0cHM6Ly93eC50ZW5wYXkuY29tL2NnaS1iaW4vbW1wYXl3ZWItYmluL2NoZWNrbXdlYg=="]] &&(![[self WK_URLDecodedString:requestString] containsString:@"redirect_url=your schme"] && ![[self WK_URLDecodedString:requestString] containsString:@"redirect_url=your another schme"] &&![[self WK_URLDecodedString:requestString] containsString:@"redirect_url=your another schme2"])) {
        NSMutableURLRequest *newRequest = [[NSMutableURLRequest alloc] init];
        newRequest.allHTTPHeaderFields = navigationAction.request.allHTTPHeaderFields;
        newRequest.URL = [NSURL URLWithString:[self handleRedirectUrl:requestString]];
        [webView loadRequest:newRequest];
        allowJumpToUrl = NO;
    }else if ([requestString isEqualToString:@"your schme://"] || [requestString isEqualToString:@"your schme://"]|| [requestString isEqualToString:@"your schme://"]) {
        allowJumpToUrl = NO;  
        [webView goBack];
        NSURL *url = [NSURL URLWithString:_redirect_url];
        NSMutableURLRequest* request = [NSMutableURLRequest requestWithURL:url cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:60.0];
        [webView loadRequest:request];
    }

支付宝更简单了,只需要替换链接中的alipays字段为你的schme就可以了,上面的代码有.

参考地址 :https://bbm.loovee.com/pay/weixin
https://www.jianshu.com/p/6d4cccba1288

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 218,204评论 6 506
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,091评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,548评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,657评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,689评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,554评论 1 305
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,302评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,216评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,661评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,851评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,977评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,697评论 5 347
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,306评论 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,898评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,019评论 1 270
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,138评论 3 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,927评论 2 355

推荐阅读更多精彩内容