iOS WKWebView H5微信支付跳轉

小東邪發表於2018-11-06

需求:iOS客戶端實現嵌入H5進行微信支付跳轉到微信客戶端,支付完成後再跳轉回我們的APP,解決WKWebView無法跳轉回APP的BUG.


閱讀前提:

  • 瞭解WKWebView基本初始化及使用
  • 瞭解如何利用URL Schemes進行應用間跳轉
  • 公司或個人已經在微信後臺註冊了一級域名

GitHub地址(附程式碼) : iOS WKWebView H5微信支付跳轉

簡書地址 : iOS WKWebView H5微信支付跳轉

部落格地址 : iOS WKWebView H5微信支付跳轉

掘金地址 : iOS WKWebView H5微信支付跳轉


更新說明

2019.1.3

  • 首先對所有解析的URL進行decode,因為伺服器端可能進行encode導致帶有redirect url部分無法被正確的載入
  • 修改替換redirect url引數方式,因為redirect url可能含有&等特殊字元,導致原先方法識別錯誤,當然前提是必須把redirect url放到url的最後
  • 對一些伺服器載入的特殊Url(非http/https開頭)進行處理,防止進入到微信跳回後重定向地址的邏輯中.

注意:如對WKWebView基本的初始化代理方法不瞭解可自行百度谷歌,解釋眾多,本文只針對瞭解基礎後實現。

1.在專案的Info.plist中新增一個URL Schemes(用於跳回我們專案),如下圖所示

URL Schemes

其中我們只要關心URL Schemes中item的名字例如 xdx.xiaodongxie.cn

  • xdx 為字首,可任意填寫
  • xiaodongxie.cn為你們公司在微信後臺註冊的一級域名

注意:xiaodongxie.cn一級域名不可隨意填寫,否則網頁將報錯:商家引數格式錯誤,請聯絡商家解決.

2.新增WKNavigationDelegate代理,並實現重定向代理方法

3.通過攔截微信連結以實現跳轉

3-1. 攔截微信支付地址

字首https://wx.tenpay.com/cgi-bin/mmpayweb-bin/checkmweb的網址,該網址即為進行微信支付

3-2. 解決完成微信支付後無法跳轉回APP

攔截後我們需要首先關注在原始地址中是否含有redirect_url=字串,如果含有該字串則說明你們後臺人員是利用該字串在微信支付完成後跳轉到支付完成的介面.而我們也需要利用該欄位以實現支付完成後跳轉回我們的APP.

  • 如果包含redirect_url=欄位,我們需要先記住後臺重定向的地址,然後將其替換成我們配置好的URL schemes以實現跳轉回我們的APP.然後在跳轉回我們APP之後我們會手動再載入一次原先重定向的URL地址。
  • 如果不包含redirect_url=欄位,我們只需要新增該欄位到原始URL最後面即可

3-3. 使用[[UIApplication sharedApplication] openURL:request.URL];即可開啟微信客戶端

注意

  • 我們需要判斷微信地址的同時判斷“redirect_url=xdx.%@://”,因為我們在第一次檢測到後會重新載入該地址,該地址中也含有 https://wx.tenpay.com/cgi-bin/mmpayweb-bin/checkmweb,會形成迴圈衝突,所以判斷條件如下
  • 如我們專案中有支付完成的重定向地址,我們可以在跳轉前即載入該地址,否則我們在回到APP後會有明顯的載入過程,介面不友好
  • 一般跳轉到其他APP時,地址不是以http或https開頭,所以我們在最後的判斷中以此為依據,但注意如果伺服器端載入錯誤的地址可能會走入此邏輯,所以必須對特定的scheme進行處理
- (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler {
    
    NSURLRequest *request        = navigationAction.request;
    NSString     *scheme         = [request.URL scheme];
    // decode for all URL to avoid url contains some special character so that it was not load.
    NSString     *absoluteString = [navigationAction.request.URL.absoluteString stringByRemovingPercentEncoding];
    NSLog(@"Current URL is %@",absoluteString);
    
    static NSString *endPayRedirectURL = nil;
    
    // Wechat Pay, Note : modify redirect_url to resolve we could not return our app from wechat client.
    if ([absoluteString hasPrefix:@"https://wx.tenpay.com/cgi-bin/mmpayweb-bin/checkmweb"] && ![absoluteString hasSuffix:[NSString stringWithFormat:@"redirect_url=xdx.%@://",CompanyFirstDomainByWeChatRegister]]) {
        decisionHandler(WKNavigationActionPolicyCancel);
        
#warning Note : The string "xiaodongxie.cn://" must be configured by wechat background. It must be your company first domin. You also should configure "URL types" in the Info.plist file.
        
        // 1. If the url contain "redirect_url" : We need to remember it to use our scheme replace it.
        // 2. If the url not contain "redirect_url" , We should add it so that we will could jump to our app.
        //  Note : 2. if the redirect_url is not last string, you should use correct strategy, because the redirect_url value may contain some "&" special character so that my cut method may be incorrect.
        NSString *redirectUrl = nil;
        if ([absoluteString containsString:@"redirect_url="]) {
            NSRange redirectRange = [absoluteString rangeOfString:@"redirect_url"];
            endPayRedirectURL =  [absoluteString substringFromIndex:redirectRange.location+redirectRange.length+1];
            redirectUrl = [[absoluteString substringToIndex:redirectRange.location] stringByAppendingString:[NSString stringWithFormat:@"redirect_url=xdx.%@://",CompanyFirstDomainByWeChatRegister]];
        }else {
            redirectUrl = [absoluteString stringByAppendingString:[NSString stringWithFormat:@"&redirect_url=xdx.%@://",CompanyFirstDomainByWeChatRegister]];
        }
        
        NSMutableURLRequest *newRequest = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:redirectUrl] cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:XDX_URL_TIMEOUT];
        newRequest.allHTTPHeaderFields = request.allHTTPHeaderFields;
        newRequest.URL = [NSURL URLWithString:redirectUrl];
        [webView loadRequest:newRequest];
        return;
    }
    
    // Judge is whether to jump to other app.
    if (![scheme isEqualToString:@"https"] && ![scheme isEqualToString:@"http"]) {
        decisionHandler(WKNavigationActionPolicyCancel);
        if ([scheme isEqualToString:@"weixin"]) {
            // The var endPayRedirectURL was our saved origin url redirect address. We need to load it when we return from wechat client.
            if (endPayRedirectURL) {
                [webView loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:endPayRedirectURL] cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:XDX_URL_TIMEOUT]];
            }
        }else if ([scheme isEqualToString:[NSString stringWithFormat:@"xdx.%@",CompanyFirstDomainByWeChatRegister]]) {
            
        }
        
        BOOL canOpen = [[UIApplication sharedApplication] canOpenURL:request.URL];
        if (canOpen) {
            [[UIApplication sharedApplication] openURL:request.URL];
        }
        return;
    }
    
    decisionHandler(WKNavigationActionPolicyAllow);
}
複製程式碼

相關文章