iOS H5微信和支付寶支付的那些坑

剛入職不久,接到一個項目需求是在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的形式:

第一個

https://wx.tenpay.com/cgi-bin/mmpayweb-bin/checkmweb?prepay_id=wxxxxxxxx&package=xxxxxx&redirect_url=https://xxx.xxx.com?oc=ten&sn=xxxxx&userid=xxxxx&token=LWj4G5WPnHQsggPRmTqXa7/aMm1hGbsvlaCR68YTfAaQaxz7gOfN6TD7zTzWgfatJnkIS5BwiNyiK37S1mQ==

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較多,但是我們只需要攔截的是下面這個接口即可,故相對微信略微簡單些。


image.png

處理思路:

很明顯,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中遇到的比較麻煩的問題,根據項目的區別大家遇到的問題或多或少也會有差別,歡迎大家在留言區評論、交流。

?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容