剛?cè)肼毑痪茫拥揭粋€(gè)項(xiàng)目需求是在WebView上進(jìn)行微信支付和支付寶支付始绍。原本認(rèn)為只要將WebView加載一下拔恰,剩下的交給網(wǎng)頁處理就行了,實(shí)踐告訴我并不是那么簡單闹获。
在處理WebView的時(shí)候期犬,我仿佛遇到了WebView加載的支付的所有問題,總結(jié)遇到的問題如下:
- 支付寶回調(diào)APP問題
- 微信回調(diào)APP問題
- AbsoluteURL加載為NULL問題
- 支付寶回調(diào)回來避诽,頁面不穩(wěn)定的問題
接下來龟虎,我會(huì)一一解釋一下問題和解決過程。
一茎用、回調(diào)問題
在我們支付過程中遣总,微信支付的接口比較少,只有兩個(gè):
一個(gè)是https://wx.tenpay.com/cgi-bin/mmpayweb-bin/checkmweb打頭的轨功,一個(gè)是weixin://wap/pay打頭的。
支付寶因?yàn)樗麄冏约鹤隽薍5支付容达,故支付的接口相對(duì)多一些古涧。
我使用過UIWebView和WKWebView兩種方式進(jìn)行加載,前提是不做任何處理使用WebView加載支付花盐,
使用UIWebView調(diào)用微信支付的時(shí)候羡滑,可以直接跳轉(zhuǎn)到App,支付成功或失敗返回到Safari里面算芯,不會(huì)回調(diào)到APP中柒昏;使用WKWebView調(diào)用微信支付的時(shí)候,則是定格在了一個(gè)空白的頁面熙揍;使用UIWebView和WKWebView加載支付寶支付的時(shí)候职祷,都會(huì)自動(dòng)加載他們支付寶自己做的H5原生頁面,但兩者都不會(huì)自動(dòng)跳轉(zhuǎn)。
由于有梆,WKWebView有著天然的優(yōu)勢是尖,蘋果官方也推薦,最后選擇WKWebView進(jìn)行處理泥耀。
在處理回調(diào)之前需要進(jìn)行一步————配置Scheme
配置Scheme一定要注意饺汹,一般情況下是公司的一個(gè)域名www.xxx.com,這個(gè)是存在于微信支付平臺(tái)配置中的痰催,如果不清楚的問后臺(tái)兜辞。
1.微信回調(diào)APP
在處理回調(diào)的過程主要是要在WebView中的URL的攔截處理,首先我們看看微信支付的兩個(gè)URL的形式:
第一個(gè)
redirect_url后面的https://xxx.xxx.com/xxx是公司的返回H5頁面
第二個(gè)
weixin://wap/pay?prepayid=xxxxxx&package=xxxxx&noncestr=xxxxx&sign=27fbd4e19dfcd5773887a3867981d732
處理思路:
我們需要對(duì)這兩個(gè)URL進(jìn)行攔截加載夸溶,對(duì)第一個(gè)URL進(jìn)行攔截逸吵,攔截到之后,獲取并修改redirect_url的內(nèi)容蜘醋。redirect_url這個(gè)是控制回調(diào)的定位標(biāo)識(shí)胁塞,原來是個(gè)Https的公司鏈接,故返回的時(shí)候压语,他會(huì)自動(dòng)調(diào)用Safari啸罢。我們只要把這個(gè)地方修改成我們的Scheme://即可返回。為什么是這種形式呢胎食?我們可以做個(gè)簡單的嘗試就會(huì)明白扰才,把我們的APP的Scheme://打到Safari中,你會(huì)發(fā)現(xiàn)他會(huì)自動(dòng)調(diào)用我們的APP厕怜,同理衩匣,我們?nèi)绻赟afari中輸入Alipay://和Wetchat://他會(huì)分別調(diào)用支付寶和微信APP。
另外粥航,可能還有些小伙伴會(huì)出現(xiàn)這樣的情況琅捏,APP跳回來之后,發(fā)現(xiàn)是空白頁递雀。這里推薦做這個(gè)處理柄延,將redirect_url后面的本地保存一份兒,當(dāng)回調(diào)回來之后缀程,加載公司自己的H5頁面搜吧。
最后,微信支付還需要在Header中杨凑,把Scheme://加到Referer里面滤奈,具體看代碼
//WKWebView的處理攔截的Delegate
- (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler {
//absoluteURL
NSString *absoluteUrl = navigationAction.request.URL.absoluteString;
absoluteUrl = [absoluteUrl URLDecodedString];
LWLog(@"\n當(dāng)前的absoluteURL:------\n\n\n %@\n\n\n", absoluteUrl);
#pragma mark - 微信支付
if ([absoluteUrl containsString:@"weixin://wap/pay"]) {
//打開APP
[[UIApplication sharedApplication] openURL:navigationAction.request.URL];
self.isWXLoad = NO;
decisionHandler(WKNavigationActionPolicyCancel);
} else if ([absoluteUrl containsString:@"https://wx.tenpay.com/cgi-bin/mmpayweb-bin/checkmweb?"] && self.isWXLoad == NO) {
self.isWXLoad = YES;
NSString *redirect_url = @"redirect_url=https://pay.xyyl.com/app/payreturn.html";
if ([absoluteUrl containsString:redirect_url]) {
//把將要回調(diào)的字符串進(jìn)行本地存儲(chǔ)
NSRange range = [absoluteUrl rangeOfString:redirect_url];
self.backURL = [absoluteUrl substringFromIndex:range.location+13];
//替換
NSString *newUrl = [absoluteUrl stringByReplacingOccurrencesOfString:redirect_url withString:[NSString stringWithFormat:@"&redirect_url=%@://", comScheme]];
//字符串進(jìn)行替換,讓回調(diào)之后返回自己的app
newUrl = [newUrl stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet URLQueryAllowedCharacterSet]];
NSMutableURLRequest *newRequest = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:newUrl]];
//header中添加Referer
newRequest.allHTTPHeaderFields = navigationAction.request.allHTTPHeaderFields;
[newRequest setValue:[NSString stringWithFormat:@"%@://", comScheme] forHTTPHeaderField: @"Referer"];
[webView loadRequest:newRequest];
} else {
NSURLRequest *request = navigationAction.request;
NSMutableURLRequest *newRequest = [[NSMutableURLRequest alloc] init];
newRequest.allHTTPHeaderFields = request.allHTTPHeaderFields;
[newRequest setValue:[NSString stringWithFormat:@"%@://", comScheme] forHTTPHeaderField: @"Referer"];
newRequest.URL = request.URL;
[webView loadRequest:newRequest];
}
decisionHandler(WKNavigationActionPolicyCancel);
} else if ([absoluteUrl containsString:@"https://wx.tenpay.com/cgi-bin/mmpayweb-bin/checkmweb?"]) {
self.isWXLoad = NO;
decisionHandler(WKNavigationActionPolicyAllow);
}
2.支付寶回調(diào)APP
雖然支付寶跳轉(zhuǎn)的URL較多撩满,但是我們只需要攔截的是下面這個(gè)接口即可蜒程,故相對(duì)微信略微簡單些绅你。
處理思路:
很明顯,URL的參數(shù)是個(gè)字典搞糕。同微信勇吊,這里需要處理的返回標(biāo)識(shí)是字典中的"fromAppUrlScheme"。我們需要做的操作是窍仰,將參數(shù)后面的部分轉(zhuǎn)化成字典汉规,然后,將字典fromAppUrlScheme參數(shù)換成scheme://再拼接加載新的URL即可驹吮。
值得一說的是针史,支付寶因?yàn)橛凶约旱腍5,當(dāng)我們跳轉(zhuǎn)回來之后碟狞,不會(huì)出現(xiàn)白頁情況啄枕。所以這里不需要再本地存儲(chǔ)回調(diào)回來的公司H5頁面信息了。
- (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler {
//absoluteURL
NSString *absoluteUrl = navigationAction.request.URL.absoluteString;
absoluteUrl = [absoluteUrl URLDecodedString];
LWLog(@"\n當(dāng)前的absoluteURL:------\n\n\n %@\n\n\n", absoluteUrl);
if ([absoluteUrl containsString:@"alipay://alipayclient"]) {
NSMutableString *param = [NSMutableString stringWithFormat:@"%@", (__bridge_transfer NSString *)CFURLCreateStringByReplacingPercentEscapesUsingEncoding(NULL, (__bridge CFStringRef)absoluteUrl, CFSTR(""), CFStringConvertNSStringEncodingToEncoding(NSUTF8StringEncoding))];
NSRange range = [param rangeOfString:@"{"];
// 截取 json 部分
NSString *param1 = [param substringFromIndex:range.location];
if ([param1 rangeOfString:@"\"fromAppUrlScheme\":"].length > 0) {
id json = [LWTools dictionaryWithJsonString:param1]; //轉(zhuǎn)成 dictionary
if (![json isKindOfClass:[NSDictionary class]]) {
decisionHandler(WKNavigationActionPolicyAllow);
return;
}
NSMutableDictionary *dicM = [NSMutableDictionary dictionaryWithDictionary:json];
dicM[@"fromAppUrlScheme"] = [NSString stringWithFormat:@"%@://", comScheme];
NSString *jsonStr = [LWTools convertToJsonData:dicM]; //轉(zhuǎn)成json
NSString *encodedString = (NSString*) CFBridgingRelease(CFURLCreateStringByAddingPercentEscapes(kCFAllocatorDefault, (CFStringRef)jsonStr, NULL, (CFStringRef)@"!*'();:@&=+$,/?%#[]", kCFStringEncodingUTF8));
// 只替換 json 部分
[param replaceCharactersInRange:NSMakeRange(range.location, param.length - range.location) withString:encodedString];
param replaceCharactersInRange:NSMakeRange(range.location, param.length - range.location) withString:encodedString];
[[UIApplication sharedApplication] openURL:[NSURL URLWithString:param]];
}
decisionHandler(WKNavigationActionPolicyCancel);
}
decisionHandler(WKNavigationActionPolicyAllow);
}
到此族沃,支付寶和微信的回調(diào)問題就處理完了频祝。
再次感謝這兩位iOSer的分享
H5回調(diào)參考
微信h5支付無法直接返回APP的參考
二、AbsoluteURL加載為NULL問題
我再嘮叨兩個(gè)小問題脆淹,這兩個(gè)問題可以大家也會(huì)忽視常空。
這個(gè)問題是:在我用webView加載的時(shí)候,獲取了absoluteURL之后盖溺,新建一個(gè)Request之后再次重新加載會(huì)出現(xiàn)NULL的情況漓糙。
發(fā)生的根源是:
NSString *absoluteUrl = navigationAction.request.URL.absoluteString;
//*******萬惡之源
absoluteUrl = [absoluteUrl URLDecodedString];
當(dāng)時(shí),因?yàn)榧虞dabsoluteUrl是那種URL編碼的形式烘嘱,像一些冒號(hào)昆禽、分號(hào)等在日志中都是%數(shù)字的形式,完全沒法操作蝇庭,所以進(jìn)行了URL編碼的處理醉鳖。那么問題來了,看似一個(gè)非常有血統(tǒng)的URL哮内,重新在NewRequest一下辐棒,下一次的加載為什么是空呢?然而牍蜂,事實(shí)證明,我們只要對(duì)URL做編碼處理之后泰涂,一旦加載absoluteUrl一定要再對(duì)URL反編譯回去鲫竞,實(shí)測有的URL可以,有的URL不可以逼蒙。
//這句話千萬不要省从绘,要不可能出錯(cuò)就很難找原因了!
newUrl = [newUrl stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet URLQueryAllowedCharacterSet]];
三、支付寶回調(diào)回來僵井,頁面不穩(wěn)定的問題
項(xiàng)目測試的時(shí)候陕截,出現(xiàn)了一個(gè)問題,支付寶有一個(gè)頁面是選擇頁面:可以選擇已完成支付和繼續(xù)支付的兩個(gè)按鈕頁面批什。假設(shè)這個(gè)是頁面A农曲,然后再假設(shè)自己公司的支付結(jié)果返回頁面是頁面B。
測試多次這邊會(huì)發(fā)現(xiàn)會(huì)出現(xiàn)三種情況驻债,
-回調(diào)回來會(huì)停在頁面A上乳规;
-回調(diào)回來會(huì)在A頁面停留1、2秒跳轉(zhuǎn)B合呐,在B中進(jìn)行操作暮的;
-回調(diào)回來A頁面短暫停留到B頁面,B頁面短暫停留淌实,pop出了當(dāng)前VC冻辩。
問題主要是WKWebView的重定向問題,只要添加下面的代理方法即可:
-(WKWebView *)webView:(WKWebView *)webView createWebViewWithConfiguration:(WKWebViewConfiguration *)configuration forNavigationAction:(WKNavigationAction *)navigationAction windowFeatures:(WKWindowFeatures *)windowFeatures
{
WKFrameInfo *frameInfo = navigationAction.targetFrame;
if (![frameInfo isMainFrame]) {
[webView loadRequest:navigationAction.request];
}
//
return nil;
}
以上是我在webView中遇到的比較麻煩的問題拆祈,根據(jù)項(xiàng)目的區(qū)別大家遇到的問題或多或少也會(huì)有差別恨闪,歡迎大家在留言區(qū)評(píng)論、交流缘屹。