剛入職不久,接到一個項目需求是在WebView上進行微信支付和支付寶支付。原本認為只要將WebView加載一下,剩下的交給網頁處理就行了,實踐告訴我并不是那么簡單。
在處理WebView的時候,我仿佛遇到了WebView加載的支付的所有問題,總結遇到的問題如下:
- 支付寶回調APP問題
- 微信回調APP問題
- AbsoluteURL加載為NULL問題
- 支付寶回調回來,頁面不穩定的問題
接下來,我會一一解釋一下問題和解決過程。
一、回調問題
在我們支付過程中,微信支付的接口比較少,只有兩個:
一個是https://wx.tenpay.com/cgi-bin/mmpayweb-bin/checkmweb打頭的,一個是weixin://wap/pay打頭的。
支付寶因為他們自己做了H5支付,故支付的接口相對多一些。
我使用過UIWebView和WKWebView兩種方式進行加載,前提是不做任何處理使用WebView加載支付,
使用UIWebView調用微信支付的時候,可以直接跳轉到App,支付成功或失敗返回到Safari里面,不會回調到APP中;使用WKWebView調用微信支付的時候,則是定格在了一個空白的頁面;使用UIWebView和WKWebView加載支付寶支付的時候,都會自動加載他們支付寶自己做的H5原生頁面,但兩者都不會自動跳轉。
由于,WKWebView有著天然的優勢,蘋果官方也推薦,最后選擇WKWebView進行處理。
在處理回調之前需要進行一步————配置Scheme
配置Scheme一定要注意,一般情況下是公司的一個域名www.xxx.com,這個是存在于微信支付平臺配置中的,如果不清楚的問后臺。
1.微信回調APP
在處理回調的過程主要是要在WebView中的URL的攔截處理,首先我們看看微信支付的兩個URL的形式:
第一個
redirect_url后面的https://xxx.xxx.com/xxx是公司的返回H5頁面
第二個
weixin://wap/pay?prepayid=xxxxxx&package=xxxxx&noncestr=xxxxx&sign=27fbd4e19dfcd5773887a3867981d732
處理思路:
我們需要對這兩個URL進行攔截加載,對第一個URL進行攔截,攔截到之后,獲取并修改redirect_url的內容。redirect_url這個是控制回調的定位標識,原來是個Https的公司鏈接,故返回的時候,他會自動調用Safari。我們只要把這個地方修改成我們的Scheme://即可返回。為什么是這種形式呢?我們可以做個簡單的嘗試就會明白,把我們的APP的Scheme://打到Safari中,你會發現他會自動調用我們的APP,同理,我們如果在Safari中輸入Alipay://和Wetchat://他會分別調用支付寶和微信APP。
另外,可能還有些小伙伴會出現這樣的情況,APP跳回來之后,發現是空白頁。這里推薦做這個處理,將redirect_url后面的本地保存一份兒,當回調回來之后,加載公司自己的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當前的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]) {
//把將要回調的字符串進行本地存儲
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]];
//字符串進行替換,讓回調之后返回自己的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.支付寶回調APP
雖然支付寶跳轉的URL較多,但是我們只需要攔截的是下面這個接口即可,故相對微信略微簡單些。
處理思路:
很明顯,URL的參數是個字典。同微信,這里需要處理的返回標識是字典中的"fromAppUrlScheme"。我們需要做的操作是,將參數后面的部分轉化成字典,然后,將字典fromAppUrlScheme參數換成scheme://再拼接加載新的URL即可。
值得一說的是,支付寶因為有自己的H5,當我們跳轉回來之后,不會出現白頁情況。所以這里不需要再本地存儲回調回來的公司H5頁面信息了。
- (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler {
//absoluteURL
NSString *absoluteUrl = navigationAction.request.URL.absoluteString;
absoluteUrl = [absoluteUrl URLDecodedString];
LWLog(@"\n當前的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]; //轉成 dictionary
if (![json isKindOfClass:[NSDictionary class]]) {
decisionHandler(WKNavigationActionPolicyAllow);
return;
}
NSMutableDictionary *dicM = [NSMutableDictionary dictionaryWithDictionary:json];
dicM[@"fromAppUrlScheme"] = [NSString stringWithFormat:@"%@://", comScheme];
NSString *jsonStr = [LWTools convertToJsonData: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];
param replaceCharactersInRange:NSMakeRange(range.location, param.length - range.location) withString:encodedString];
[[UIApplication sharedApplication] openURL:[NSURL URLWithString:param]];
}
decisionHandler(WKNavigationActionPolicyCancel);
}
decisionHandler(WKNavigationActionPolicyAllow);
}
到此,支付寶和微信的回調問題就處理完了。
再次感謝這兩位iOSer的分享
H5回調參考
微信h5支付無法直接返回APP的參考
二、AbsoluteURL加載為NULL問題
我再嘮叨兩個小問題,這兩個問題可以大家也會忽視。
這個問題是:在我用webView加載的時候,獲取了absoluteURL之后,新建一個Request之后再次重新加載會出現NULL的情況。
發生的根源是:
NSString *absoluteUrl = navigationAction.request.URL.absoluteString;
//*******萬惡之源
absoluteUrl = [absoluteUrl URLDecodedString];
當時,因為加載absoluteUrl是那種URL編碼的形式,像一些冒號、分號等在日志中都是%數字的形式,完全沒法操作,所以進行了URL編碼的處理。那么問題來了,看似一個非常有血統的URL,重新在NewRequest一下,下一次的加載為什么是空呢?然而,事實證明,我們只要對URL做編碼處理之后,一旦加載absoluteUrl一定要再對URL反編譯回去,實測有的URL可以,有的URL不可以。
//這句話千萬不要省,要不可能出錯就很難找原因了!
newUrl = [newUrl stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet URLQueryAllowedCharacterSet]];
三、支付寶回調回來,頁面不穩定的問題
項目測試的時候,出現了一個問題,支付寶有一個頁面是選擇頁面:可以選擇已完成支付和繼續支付的兩個按鈕頁面。假設這個是頁面A,然后再假設自己公司的支付結果返回頁面是頁面B。
測試多次這邊會發現會出現三種情況,
-回調回來會停在頁面A上;
-回調回來會在A頁面停留1、2秒跳轉B,在B中進行操作;
-回調回來A頁面短暫停留到B頁面,B頁面短暫停留,pop出了當前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中遇到的比較麻煩的問題,根據項目的區別大家遇到的問題或多或少也會有差別,歡迎大家在留言區評論、交流。