美文网首页pay
iOS 解决微信h5支付,支付宝h5支付无法直接返回APP的问题

iOS 解决微信h5支付,支付宝h5支付无法直接返回APP的问题

作者: 大宝来巡山 | 来源:发表于2018-07-04 20:04 被阅读73次

    由于公司的业务需要,为了节省申请开通微信支付的时间和人力,公司决定使用微信h5支付。这样即节省了时间,同时以后所有的APP都能使用h5支付,既方便又快捷。

    但是真正做的时候问题来了,当你支付成功之后或者是取消支付的时候会跳转到Safari浏览器,这就很尴尬了,完全无用户体验啊。当时看着Android可以直接返回到APP,心里瞬间不爽了。开始埋怨苹果公司了,但是由于公司的业务必须要做h5,没办法开始找办法解决。

    首先你在Xcode中,选择你的工程设置项,选中“TARGETS”一栏,在“info”标签栏的“LSApplicationQueriesSchemes“添加weixin

    然后你通过统一下单后台会从微信拿到这么个链接https://wx.tenpay.com/cgi-bin/mmpayweb-bin/checkmweb?prepay_id=wx20180115115052bedf091fba0369993002&package=2975002856给你

    进入微信查了一下,发现需要设置Referer这个请求头的参数,当然微信也给出了例子微信h5支付其他常见错误。针对于这个问题在网上搜索了一下发现有专门针对于微信h5支付设置Referer的文章iOSwebView设置Referer,在这里我给大家贴一下代码吧!

    #pragma mark - MajordomoWKWebViewDelegate

    -(void)webView:(WKWebView*)webView decidePolicyForNavigationAction:(WKNavigationAction*)navigationAction decisionHandler:(void(^)(WKNavigationActionPolicy))decisionHandler

    {

        NSString *url = navigationAction.request.URL.absoluteString;

        if ([url containsString:@"https://wx.tenpay.com/cgi-bin/mmpayweb-bin/checkmweb?"] || [url rangeOfString:@"https://wx.tenpay.com"].location != NSNotFound) {

    #warning 链接不要拼接redirect_url,如果拼接了还是会返回到浏览器的

            //这里把webView设置成一个像素点,主要是不影响操作和界面,主要的作用是设置referer和调起微信

            WebChatPayH5VIew*h5View = [[WebChatPayH5VIewalloc]initWithFrame:CGRectMake(0,0,1,1)];

            //url是没有拼接redirect_url微信h5支付链接

            [h5ViewloadingURL:url withIsWebChatURL:NO];

            [self.viewaddSubview:h5View];

            decisionHandler(WKNavigationActionPolicyCancel);

            return;

        }

    #warning todo -处理支付宝h5支付返回问题

        NSString *urlStr = [navigationAction.request.URL.absoluteString stringByRemovingPercentEncoding];

        DLOG(@"urlStr == %@",urlStr);

        NSLog(@"当前加载的webView  decidePolicyForNavigationAction:\n%@",webView.URL.absoluteString);

        if([urlStrhasPrefix:@"alipayqr://"] || [urlStrisStartWithString:@"alipay"] || [urlStrhasPrefix:@"alipay://"]) {

           NSMutableString* tmpUrlStr = urlStr.mutableCopy;

            if([urlStrcontainsString:@"fromAppUrlScheme"]) {

                NSDictionary* tmpDic = [selfdictionaryWithUrlString:tmpUrlStr];

                DLOG(@"tmpDic == %@",tmpDic);

                NSString* tmpValue = [tmpDicvalueForKey:@"fromAppUrlScheme"];

                tmpUrlStr = [[tmpUrlStrstringByReplacingOccurrencesOfString:tmpValue withString:@"panda"] mutableCopy];

                DLOG(@"tmpUrlStr == %@",tmpUrlStr);

                tmpUrlStr = [[tmpUrlStrstringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding] mutableCopy];

            }

            NSURL * newURl = [NSURL URLWithString:tmpUrlStr];//navigationAction.request.URL;//[NSURL URLWithString:urlStr];//

            [[UIApplication sharedApplication]openURL:newURl];

        }

        //如果是跳转一个新页面

        if(navigationAction.targetFrame==nil) {

            [webViewloadRequest:navigationAction.request];

        }

        decisionHandler(WKNavigationActionPolicyAllow);

        self.loadUrl= webView.URL.absoluteString;

    }

    -(NSDictionary*)dictionaryWithUrlString:(NSString*)urlStr

    {

        if(urlStr && urlStr.length&& [urlStrrangeOfString:@"?"].length==1) {

            NSArray *array = [urlStr componentsSeparatedByString:@"?"];

            if(array && array.count==2) {

                NSString*paramsStr = array[1];

                if(paramsStr.length) {

                    //paramsStr解析出来的数据:{"soeNote":"电压高超标,当前值:390.78V,大于上上限值:350.0V","name":"电压"}

                    NSData *jsonData = [paramsStr dataUsingEncoding:NSUTF8StringEncoding];

                    NSDictionary *responseDic = [NSJSONSerialization JSONObjectWithData:jsonData options:NSJSONReadingMutableLeaves  error:nil];

                    DLOG(@"responseDic == %@",responseDic);

                    returnresponseDic;

                }else{

                    returnnil;

                }

            }else{

                returnnil;

            }

        }else{

            returnnil;

        }

    }

    ###############################

     WebChatPayH5VIew 

    #pragma mark - UIWebViewDelegate

    - (BOOL)webView:(UIWebView*)webView shouldStartLoadWithRequest:(NSURLRequest*)request navigationType:(UIWebViewNavigationType)navigationType {

        NSURL*url = [requestURL];

        NSString *newUrl = url.absoluteString;

        if(!self.isLoading) {

            if ([newUrl rangeOfString:@"weixin://wap/pay"].location != NSNotFound) {

                NSMutableURLRequest* request = [NSMutableURLRequest requestWithURL:url cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:60.0];

                [self.myWebViewloadRequest:request];

                self.isLoading=YES;

                returnNO;

            }

        }else{

            if ([newUrl rangeOfString:@"weixin://wap/pay"].location != NSNotFound) {

                self.myWebView=nil;

                UIWebView *web = [[UIWebView alloc] initWithFrame:CGRectMake(0, 0, 1, 1)];

                [selfaddSubview:web];

                NSMutableURLRequest* request = [NSMutableURLRequest requestWithURL:url cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:60.0];

                [webloadRequest:request];

    //            [[self getCurrentVC] showhide];

                returnYES;

            }

        }

        NSDictionary *headers = [request allHTTPHeaderFields];

        BOOLhasReferer = [headersobjectForKey:@"Referer"] !=nil;

        if(hasReferer) {

            returnYES;

        }else{

            // relaunch with a modified request

            dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{

                dispatch_async(dispatch_get_main_queue(), ^{

                    NSURL*url = [requestURL];

                    NSMutableURLRequest* request = [NSMutableURLRequest requestWithURL:url cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:60.0];

                    //设置授权域名 就是你们商户申请H5时提交的授权域名.

                    [requestsetValue:@"channel.vk7201.com://" forHTTPHeaderField:@"Referer"];//www.aglhz.com://

                    [self.myWebView loadRequest:request];//http://channel.vk7201.com/front/register/index.html://

                });

            });

            returnNO;

        }

    }

    走到这一步,你就可以加载webView进行支付了,但是问题来了,不论是支付成功还是取消支付之后他都会跳转到Safari浏览器,而且打开的内容是就是你设置Referer时的授权域名http://www.xxx.com,这个家伙就是我们的公司的主页。

    那么,怎么办呢,最后我们经过协商,只要能支付,不管支付成功或者取消支付,只要停留在微信界面就可以了,然后让用户点击右上角手动返回APP,这个是没有办法的办法了。然后Android那个哥们给我生成了一个weixin://wap/pay?prepayid%3Dwx201801151450335872c8f41a0452242290&package=122735683&noncestr=1515999038&sign=0b3590852e847b336e6f0187a0f56ab1这样的链接,这个链接是通过拦截webView访问获取的,然后我试了一下,可以支付,也停留在了微信,于是我高高兴兴的去尝试了一下。

    - (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType

    通过webView的代理方法来获取到这个链接,然后进行支付,发现还是一样的会跳转到Safari浏览器,当时瞬间懵逼了,怎么会这个样子呢,不信邪的我又让Android那个哥们拦截一个链接给我,尝试一下不会跳转浏览器,我又试了一下自己的拦截的链接还是会跳转到Safari浏览器。我突然明白了这个是我设置了Referer,不管是用https://wx.tenpay.com/cgi-bin/mmpayweb-bin/checkmweb?prepay_id=wx20180115115052bedf091fba0369993002&package=2975002856还是拦截的weixin://wap/pay?prepayid%3Dwx201801151450335872c8f41a0452242290&package=122735683&noncestr=1515999038&sign=0b3590852e847b336e6f0187a0f56ab1进行支付都是不行的,他都会跳转到Safari。

    我当时想要不用TFHpple解析HTML内容吧,但是仔细一想不行的,因为头部没有Referer,无法解析。最后实在没办法了,都准备放弃了,突然想到了设置scheme,既然我可以Safari可以打开APP,那么我这肯定也能做的。于是我又信心满满的开始我的实验了。

    首先要设置scheme,假如scheme设置xxxx,那么你在Safari输入xxxx://就可以打开APP了。做到这就全部明白了,只要设置好Referer和我的scheme对应就行了,然后我开始实验:

    Referer设置:http://www.xxx.com  scheme设置:http://www.xxx.com发现不行

    我突然间发现是不是只要是带有h5的授权的域名就可以呢,于是我把公司的一个链接www.aglhz.com/sub_property_ysq/m/html/introduction.html设置成Referer之后我发现我还是能支付的,我擦,突然之间发现了一个新的大陆啊,太激动了,然后为了确保准确性进行了各种尝试,发现只要带有域名的都可以,然后我又进行了一个尝试:就是把http://去掉直接把Referer设置成:www.xxx.com,然后也是能支付的。这样就全部OK了,我把Referer设置成:www.xxx.com://这个样式的然后把scheme设置成:www.xxx.com这样的话支付成功或者取消支付都可以直接返回到APP了。

    所以h5支付最终返回APP的解决方案是:把Referer设置成:www.xxx.com:// scheme设置成:www.xxx.com就可以直接返回APP了。

    当然目前还没有解决多个APP同时使用同一个授权域名时,怎么返回APP的问题,多个APP使用h5支付的话会导致返回错乱的问题,如果哪位大神知道解决办法的话可以告诉我一下,谢谢!

    同样你如果有多个APP被同一家客户使用的话,客户也同意用户点击左上角手动返回的话你可以这样设置Referer:  www.xxx.com/test://,这样的话你支付成功会直接停留在微信,不会造成返回APP错乱的问题。

    由于大家都在问微信h5支付返回的问题,所以抽空写了一个demo,针对的是webVIew和WKWebVIewdemo地址。喜欢的朋友给个star。

    如果有多个APP运用到h5支付的话,返回的Scheme设置是一个问题,在这里我给大家提供一篇文章,是解决多个APP返回的问题iOS实现微信外部H5支付完成后返回原APP(多APP也可实现)

    微信审核通过需要3至5个工作日。

    我们假设支付域名填写的  aaa.cn,注意是顶级域名,不是二级或三级的子域名。

    服务器端通过统一下单接口从微信拿到支付跳转链接(https://wx.tenpay.com/cgi-bin/mmpayweb-bin/checkmweb?prepay_id=wx20180115115052bedf091fba0369993002&package=2975002856),称为mweb_url,有效期为5分钟。

    由于这里是APP调起支付,不是网页H5发起支付,所以这里mweb_url值不需要再拼接上redirect_url参数。

    然后移动端进行请求mweb_url,就会调起微信APP支付。

    可能会出现以下问题:

    “出现商家参数格式有误,请联系商家解决”

    说明在请求mweb_url时referer为空导致,设置上就可以,这里我们设置最先在商户后台注册的aaa.cn的一个多级子域名,比如:b.aaa.cn。

    如果不是设置商户后台注册的顶级域名的多级子域名,比如:b.ccc.cn。

    就会出现“商家存在未配置的参数,请联系商家解决”异常。

    那就把ccc.cn注册到商户后台就可以解决了。

    到目前为止,你就可能正常支付了,但支付成功后,并不能正常跳回原APP,严重影响了用户体验。

    解决方案:把referer设置成:b.aaa.cn://,scheme设置成:b.aaa.cn就可以直接返回APP了。

    如是你只有一款APP使用此支付方式,那就可以到此为止了。

    如果你有两款以上APP(比如X1,X2,X3),并都把referer设置成:b.aaa.cn://,scheme设置成:b.aaa.cn。

    用户只安装了其中一款APP(X1),此时支付能够成功,并能转回原APP,一点问题没有。如果同时安装了(X1,X2),就会发现支付能够成功,但成功后页面静止,不能跳回原APP了,也严重影响了用户体验。

    想到的第一个解决方案就是更改referer与scheme。

    APPX1X2X3

    refererb.aaa.cn/x1://b.aaa.cn/x2://b.aaa.cn/x3://

    schemeb.aaa.cn/x1b.aaa.cn/x2b.aaa.cn/x3

    通过测试发现不起作用。在网上搜索了很久也没有找到解决方案,感觉只能妥协了,牺牲点用户体验,让用户点击左上角返回原APP。

    突然灵光一闪,微信商户后台即然让我们注册顶级域名,那是不是只要把referer与scheme设置成注册的顶级域名的多级子域名就可以了呢,如下:

    APPX1X2X3

    refererb1.aaa.cn://b2.aaa.cn://b3.aaa.cn://

    schemeb1.aaa.cnb2.aaa.cnb3.aaa.cn

    通过测试,完美运行。理论上这个解决方案可以支持无数个APP,也同时解决了微信开放平台APP支付的限制个数。

    //支付宝h5支付返回页面

    schemeStr,这个关键字让我眼前一亮。

    手机网站支付转Native支付:支付宝的意思是,我们提供了一个SDK,你接了之后就可以很方便的实现H5收银台(即在web上输入支付宝账号密码支付)到支付宝APP收银台的过渡。

    如果你是一个"正规"APP开发者,至此已经可以解决你的问题了。按照支付宝文档接支付宝SDK即可,就能实现H5支付回调APP了。

    下载了支付宝的Demo,试了下确实可以跳回APP。

    楼主琢磨着,既然SDK能实现这个功能,说明支付宝还是支持的H5支付完成后返回APP功能的,并不是向文档上说的,"在iOS系统中,唤起支付宝App支付完成后,不会自动回到浏览器或商户APP。用户可手工切回到浏览器或商户APP",只是支付宝不愿意公开而已。

    /** * 支付接口

     * *@paramorderStr 订单信息 

    *@paramschemeStr  调用支付的app注册在info.plist中的scheme 

    *@paramcompletionBlock 支付结果回调Block,用于wap支付结果回调(非跳转钱包支付) */

    - (void)payOrder:(NSString *)orderStr fromScheme:(NSString *)schemeStr callback:(CompletionBlock)completionBlock;

    最终打开支付宝APP都会走 [UIApplicationsharedApplication] openURL:]

    #import

    #import

    @implementationUIApplication(TrackTimer)

    + (void)load{staticdispatch_once_toneToken;

    dispatch_once(&oneToken, ^{ 

     SEL mySelector =NSSelectorFromString(@"my_openURL:");

     SEL orginalSelector =NSSelectorFromString(@"openURL:"); 

     Method myMethod = class_getInstanceMethod([selfclass], mySelector);

     Method orginalMethod = class_getInstanceMethod([selfclass], orginalSelector);

    BOOLdidAddMethod = class_addMethod([selfclass], orginalSelector, method_getImplementation(myMethod), method_getTypeEncoding(myMethod));

    If(didAddMethod) {

     class_replaceMethod([selfclass], mySelector, method_getImplementation(orginalMethod), method_getTypeEncoding(orginalMethod));

     }else{ 

     method_exchangeImplementations(myMethod, orginalMethod); 

     }

     });

    }

    - (void)my_openURL:(NSURL*)url{

    NSLog(@"%@",url);

     [selfmy_openURL:url];

    }

    打印结果

    alipaymatrixbwf0cml3://alipayclient/?%7B%0A%20%20%22fromAppUrlScheme%22%20%3A%20%22alisdkdemo%22%2C%0A%20%20%22requestType%22%20%3A%20%22SafePay%22%2C%0A%20%20%22dataString%22%20%3A%20%22trade_no%3D%5C%222018041921001001270586523089%5C%22%26pay_phase_id%3D%5C%22%5C%22%26biz_type%3D%5C%22trade%5C%22%26biz_sub_type%3D%5C%22TRADE%5C%22%26app_name%3D%5C%22tb%5C%22%26extern_token%3D%5C%221fc77c67c70ef70d58e2bf7c513d91a5%5C%22%26appenv%3D%5C%22%5C%22%26pay_channel_id%3D%5C%22alipay_sdk%5C%22%26bizcontext%3D%5C%22%7B%5C%22av%5C%22%3A%5C%221%5C%22%2C%5C%22sc%5C%22%3A%5C%22h5tonative%5C%22%2C%5C%22ty%5C%22%3A%5C%22ios_lite%5C%22%2C%5C%22appkey%5C%22%3A%5C%222014052600006128%5C%22%2C%5C%22sv%5C%22%3A%5C%22h.a.3.5.3%5C%22%2C%5C%22an%5C%22%3A%5C%22com.antfin.AliSDKDemo%5C%22%7D%5C%22%22%0A%7D

    URLDecode

    alipaymatrixbwf0cml3://alipayclient/?{

    "fromAppUrlScheme" : "alisdkdemo",

    "requestType" : "SafePay",

    "dataString" : "trade_no="2018041921001001270586523089"&pay_phase_id=""&biz_type="trade"&biz_sub_type="TRADE"&app_name="tb"&extern_token="1fc77c67c70ef70d58e2bf7c513d91a5"&appenv=""&pay_channel_id="alipay_sdk"&bizcontext="{"av":"1","sc":"h5tonative","ty":"ios_lite","appkey":"2014052600006128","sv":"h.a.3.5.3","an":"com.antfin.AliSDKDemo"}""

    }

    为什么前面会有一点乱码,alipaymatrixbwf0cml3,不管它了。fromAppUrlScheme,是不是很惊喜。

    URLDecode以后(部分字段我做了屏蔽)

    alipay://alipayclient/?{"dataString":"h5_route_token="xxxxxxxxxxxxx"&is_h5_route="true"","requestType":"SafePay","fromAppUrlScheme":"alipays"}

    也有fromAppUrlScheme这个参数,而且默认是支付宝APP的URLScheme:alipays

    对比一下发现,SDK中传入的schemeStr参数对应就是alipay:// 中的 fromAppUrlScheme字段。

    由此楼主猜测,在 alipay:// 打开支付宝时,传入 fromAppUrlScheme,支付结束后就会跳到对应的APP。

    最终,楼主在自己的项目中验证了上述猜测。

    总结下吧

    最终解决方案

    方案一

    接支付宝提供的SDK,schemeStr参数传入自己APP的URLScheme即可。 具体的手机网站支付转Native支付

    方案二

    不需要接SDK,不需要添加支付宝白名单,webView拦截alipay://alipayclient请求,追加或修改参数 fromAppUrlScheme为你自己的URLScheme值(这一步可没那么简单,自己动手吧,偷笑),生成新的NSURL,然后用 [[UIApplication sharedApplication] openURL:] 打开即可。

    2018.5.24更新(上面看懂了下面这段可以不用看)

    总有人私信我问我怎么追加fromAppUrlScheme。可能是我说的"webView拦截",让初学者不明白吧,简单提一下。

    在webView"发起请求的代理方法"里面拦截请求的URL,即如果请求是alipay://alipayclient开头的,说明这个请求就跳转支付宝的请求。

    替换里面fromAppUrlScheme的值为你自己的scheme值。(当然如果更严谨的话,应该是判断fromAppUrlScheme这个key是否存在,存在则替换其值,不存在则追加)。用替换好的字符串生成一个NSURL,用[[UIApplication sharedApplication] openURL:newURL]跳转支付宝。代理方法返回NO,即终止这次请求。

    应该是判断fromAppUrlScheme这个key是否存在,存在则替换其值,不存在则追加

    代码如下:

     NSString *urlStr = [navigationAction.request.URL.absoluteString stringByRemovingPercentEncoding];

        DLOG(@"urlStr == %@",urlStr);

        NSLog(@"当前加载的webView  decidePolicyForNavigationAction:\n%@",webView.URL.absoluteString);

        if([urlStrhasPrefix:@"alipayqr://"] || [urlStrisStartWithString:@"alipay"] || [urlStrhasPrefix:@"alipay://"]) {

           NSMutableString* tmpUrlStr = urlStr.mutableCopy;

            if([urlStrcontainsString:@"fromAppUrlScheme"]) {

                NSDictionary* tmpDic = [selfdictionaryWithUrlString:tmpUrlStr];

                DLOG(@"tmpDic == %@",tmpDic);

                NSString* tmpValue = [tmpDicvalueForKey:@"fromAppUrlScheme"];

                tmpUrlStr = [[tmpUrlStrstringByReplacingOccurrencesOfString:tmpValue withString:@"panda"] mutableCopy];

                DLOG(@"tmpUrlStr == %@",tmpUrlStr);

                tmpUrlStr = [[tmpUrlStrstringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding] mutableCopy];

            }

            NSURL * newURl = [NSURL URLWithString:tmpUrlStr];//navigationAction.request.URL;//[NSURL URLWithString:urlStr];//

            [[UIApplication sharedApplication]openURL:newURl];

        }

    -(NSDictionary*)dictionaryWithUrlString:(NSString*)urlStr

    {

        if(urlStr && urlStr.length&& [urlStrrangeOfString:@"?"].length==1) {

            NSArray *array = [urlStr componentsSeparatedByString:@"?"];

            if(array && array.count==2) {

                NSString*paramsStr = array[1];

                if(paramsStr.length) {

                    //paramsStr解析出来的数据:{"soeNote":"电压高超标,当前值:390.78V,大于上上限值:350.0V","name":"电压"}

                    NSData *jsonData = [paramsStr dataUsingEncoding:NSUTF8StringEncoding];

                    NSDictionary *responseDic = [NSJSONSerialization JSONObjectWithData:jsonData options:NSJSONReadingMutableLeaves  error:nil];

                    DLOG(@"responseDic == %@",responseDic);

                    returnresponseDic;

                }else{

                    returnnil;

                }

            }else{

                returnnil;

            }

        }else{

            returnnil;

        }

    }

    作者:CocoaKier

    链接:https://www.jianshu.com/p/0d8dd04fe94e

    相关文章

      网友评论

        本文标题:iOS 解决微信h5支付,支付宝h5支付无法直接返回APP的问题

        本文链接:https://www.haomeiwen.com/subject/rrryuftx.html