iOS解決H5支付跳轉(zhuǎn)到支付App及返回原App問題

標(biāo)題如此拗口, 我也是無可奈何??

本文會(huì)涉及到兩個(gè)方面:

  1. H5 支付時(shí)調(diào)起微信或支付寶 App
  2. 調(diào)起微信或支付寶 App 完成支付操作后章咧,返回到自己的原來的 App惨险。

公司業(yè)務(wù)需求食呻,需客戶端嵌套一個(gè)完整的 H5 開發(fā)的網(wǎng)頁,其中帶有 H5 的微信支付和支付寶支付嘶摊。微信支付一直無法打開頁面邑跪,無法支付族铆;支付寶支付可以打開支付寶的網(wǎng)頁,如下圖

支付寶支付.jpg

最初討論的解決辦法是:走到支付時(shí)摘昌,通過 js 橋來調(diào)起原生支付斥杜。如果 H5 頁面是同一公司同事開發(fā)的虱颗,這倒是個(gè)簡(jiǎn)單快捷的方法。但是結(jié)合公司情況蔗喂,考慮到后期可能會(huì)接入其他公司的 H5 頁面忘渔,聯(lián)調(diào)起來會(huì)很麻煩,所以還是決定不通過橋解決缰儿。

1. H5 支付時(shí)調(diào)起微信或支付寶 App

H5 支付調(diào)起微信或支付寶 App 的原理都一樣畦粮,以 WK 為例,都是在 decidePolicyForNavigationAction 代理方法里面攔截 URL乖阵,再用 [[UIApplication sharedApplication] openURL:navigationAction.request.URL]; 調(diào)起宣赔。
不同的只是攔截的字段有區(qū)分,微信需攔截的字符串為 @"weixin://wap/pay" , 支付寶攔截的字符串為 @"alipay://alipayclient"瞪浸。簡(jiǎn)單代碼如下:

- (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler{
    // 在發(fā)送請(qǐng)求之前儒将,決定是否跳轉(zhuǎn)
    NSString *url = navigationAction.request.URL.absoluteString;
    if ([url containsString:@"weixin://wap/pay"] || [url containsString:@"alipay://alipayclient"]) {
        
        [[UIApplication sharedApplication] openURL:navigationAction.request.URL];
        decisionHandler(WKNavigationActionPolicyCancel);
        
        return;
    }
    decisionHandler(WKNavigationActionPolicyAllow);
}

至此,H5 支付可以成功調(diào)起微信或支付寶的 App 進(jìn)行支付了默终。

支付寶還有另一種調(diào)起的方式, 在支付寶的開發(fā)文檔中有提到, 這里也貼一下地址, 有興趣的可以去試下: 支付寶手機(jī)網(wǎng)站支付轉(zhuǎn)App支付

但是椅棺,如果代碼寫到這里就完的話犁罩,可能會(huì)出現(xiàn)兩種情況:

  1. 使用微信支付, 操作完成, 仍停留在微信, 不會(huì)像原生調(diào)起支付那樣返回自己的APP;
  2. 支付寶支持,操作完后两疚,調(diào)起了 Safari床估,打開的網(wǎng)頁就是之前在 WK 里打開的頁面

ps:查資料時(shí),有網(wǎng)友微信支付完成時(shí)诱渤,也會(huì)調(diào)起 Safari丐巫,但我調(diào)試過程中未出現(xiàn)這種情況

接下來解決第二個(gè)問題,完成支付操作后勺美,返回自己的 App

2. 調(diào)起微信或支付寶 App 完成支付操作后递胧,返回到自己的 App

老規(guī)矩,先貼上參考的鏈接
微信返回參考 http://www.reibang.com/p/90db7dfb075c
支付寶返回參考 http://www.reibang.com/p/0d8dd04fe94e
以上兩篇文章里, 非常詳細(xì)的描述了解決的整個(gè)過程, 包括解決過程中遇到哪些問題, 從哪些方面思考得到靈感, 最終如何一步步找到解決辦法, 非常建議去看看, 了解下. 這里就不照搬了, 直接講解決步驟了...

這部分內(nèi)容得再拆分成兩部分, 一個(gè)支付寶的, 一個(gè)微信的

支付寶支付返回到 App

  1. 攔截到支付寶支付的 URL (就是 URL 里包含 @"alipay://alipayclient") 時(shí), 對(duì) URL 進(jìn)行 URL 解碼;

解碼后的 URL 如下:
alipay://alipayclient/?{"requestType":"SafePay","fromAppUrlScheme":"alipays","dataString":"XXX"}

  1. 解碼后得到一字符串, 字符串里包含了一個(gè)json 串. 把 json 部分截取出來, 再轉(zhuǎn)成 dictionary, dictionary 里面將會(huì)有一個(gè) key 為 fromAppUrlScheme 的鍵值對(duì), 把 fromAppUrlScheme 的值改成自己 App 的 scheme;
  2. 把第二步得到 dictionary 再轉(zhuǎn)成 json, 再對(duì)已經(jīng)改了 fromAppUrlScheme 值的 json 進(jìn)行 URL 編碼;
  3. 把第三步編碼好的字符串, 替換掉第一步攔截的 URL 的 json 部分... 注意!!! 這里替換的只是 URL 的 json 部分!!! 只是 URL 的 json 部分!!! 替換后得到一個(gè)新的的 URL;
  4. 拿第四步得到的帶有自己 APP 的 scheme 的 URL, 去調(diào)起支付寶 App(就是文章第一部分說的調(diào)起 App 那樣的調(diào)起)

需要注意的一個(gè)地方是, 進(jìn)行 URL 編碼時(shí), 不是 URL 整體進(jìn)行編碼, 只有 json 那部分需要編碼, 如果對(duì)完整的 URL 進(jìn)行編碼, 那么我們用來識(shí)別支付寶的字符串的那部分 (@"alipay://alipayclient") 也會(huì)被編碼, 從而導(dǎo)致無法調(diào)起支付寶

基本實(shí)現(xiàn)如下:

- (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler{
    // 在發(fā)送請(qǐng)求之前赡茸,決定是否跳轉(zhuǎn)
    NSString *url = navigationAction.request.URL.absoluteString;
    
    // 支付寶
    if ([url containsString:@"alipay://alipayclient"]) {
        
        NSMutableString *param = [NSMutableString stringWithFormat:@"%@", (__bridge_transfer NSString *)CFURLCreateStringByReplacingPercentEscapesUsingEncoding(NULL, (__bridge CFStringRef)url, CFSTR(""), CFStringConvertNSStringEncodingToEncoding(NSUTF8StringEncoding))];
        
        NSRange range = [param rangeOfString:@"{"];
        // 截取 json 部分
        NSString *param1 = [param substringFromIndex:range.location];
        if ([param1 rangeOfString:@"\"fromAppUrlScheme\":"].length > 0) {
            id json = jsonToClass(param1); // 這里為偽代碼, 自行轉(zhuǎn)成 dictionary
            if (![json isKindOfClass:[NSDictionary class]]) {
                decisionHandler(WKNavigationActionPolicyAllow);
                return;
            }
            
            NSMutableDictionary *dicM = [NSMutableDictionary dictionaryWithDictionary:json];
            dicM[@"fromAppUrlScheme"] = 自己App的scheme;
            
            NSString *jsonStr = classToJson(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];
            
            [[UIApplication sharedApplication] openURL:[NSURL URLWithString:param]];
        }
        
        decisionHandler(WKNavigationActionPolicyCancel);
    }
    
    decisionHandler(WKNavigationActionPolicyAllow);
}

微信支付返回到 App

微信的問題比支付寶的稍微麻煩些. 首先麻煩的就是配置問題, 需要在微信開發(fā)者平臺(tái)上進(jìn)行了相應(yīng)的配置. 如果配置不對(duì), 請(qǐng)參照 微信支付開發(fā)步驟&常見問題.

提醒下, 微信開發(fā)者平臺(tái)上的配置的有次數(shù)限制的, 每個(gè)月只能修改多少多少次, 所以配置時(shí)盡量把能想到的需要用到的都一起配置了, 省得開發(fā)的時(shí)候被這些細(xì)節(jié)問題浪費(fèi)時(shí)間

  1. 攔截到微信支付的 URL @"https://wx.tenpay.com/cgi-bin/mmpayweb-bin/checkmweb?". (這里需要攔截的 URL 與調(diào)起微信的 URL 不是同一個(gè))
  2. H5 調(diào)起微信支付時(shí), 需要設(shè)置 Referer 請(qǐng)求頭, 所以直接拿請(qǐng)求頭 newRequest.allHTTPHeaderFields = navigationAction.request.allHTTPHeaderFields;
  3. 給 Referer 賦值 [newRequest setValue:@"www.xxx.com://" forHTTPHeaderField: @"Referer"];, 自己的 App 也要設(shè)置一個(gè) www.xxx.com 的 scheme. 并且取消此次加載.

解釋下 www.xxx.com , 其實(shí)就是公司的一個(gè)域名, 可以是 H5 支付的域名, 也可以是公司其他域名, 但必須確保這個(gè)域名存在于公司的微信開發(fā)者平臺(tái)的配置中.
那么問題來了, 這既然是公司的一個(gè)域名, 又要把這個(gè)域名設(shè)置成 scheme, 那有可能出現(xiàn)這么一個(gè)問題: 同公司的其他 App 也可能配置了同樣的 scheme. 所以, 這里的域名和 scheme 要保證唯一性. 至于怎么保證, 跟后臺(tái)哥們商量下吧. 記得配置到微信開發(fā)者平臺(tái)上!!!

  1. 重新加載修改了 Referer 的請(qǐng)求.
  2. 攔截包含 @"weixin://wap/pay" 的 URL, 調(diào)起微信.

針對(duì)微信這部分, 劃幾個(gè)重點(diǎn):

  1. scheme 必須唯一, 不唯一的話隨機(jī)打開一個(gè) (公司有個(gè) App 不知在什么情況下配了個(gè)跟某支付一毛一樣的 scheme, 導(dǎo)致裝有那個(gè) App 的用戶都不能用某支付來支付, 后來被發(fā)律師函了... )
  2. @"https://wx.tenpay.com/cgi-bin/mmpayweb-bin/checkmweb?" 會(huì)攔截兩次. 攔截第一次時(shí), 需要修改 Referer, 取消此次加載, 重新加載修改了請(qǐng)求頭的請(qǐng)求; 雖然請(qǐng)求頭修改了, 可是 URL 并沒有修改, 所以, 重新加載之后攔截到的還是 @"https://wx.tenpay.com/cgi-bin/mmpayweb-bin/checkmweb?". 在這一步需做處理, 如不處理, 這一步就死循環(huán).
  3. 再次強(qiáng)調(diào)微信開發(fā)者平臺(tái)的配置問題. 誰配誰知道!

基本實(shí)現(xiàn)如下:

- (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler{
    // 在發(fā)送請(qǐng)求之前缎脾,決定是否跳轉(zhuǎn)
    //self.load  用來控制對(duì) @"https://wx.tenpay.com/cgi-bin/mmpayweb-bin/checkmweb?" 的攔截
    NSString *url = navigationAction.request.URL.absoluteString;
    if ([url containsString:@"weixin://wap/pay"]) {
        self.load = NO; 
        [[UIApplication sharedApplication] openURL:navigationAction.request.URL];
        decisionHandler(WKNavigationActionPolicyCancel);
    }
    else if ([url containsString:@"https://wx.tenpay.com/cgi-bin/mmpayweb-bin/checkmweb?"] && !self.isLoad) {
        NSURLRequest *request = navigationAction.request;
        NSMutableURLRequest *newRequest = [[NSMutableURLRequest alloc] init];
        newRequest.allHTTPHeaderFields = request.allHTTPHeaderFields;
#warning scheme 要改
        [newRequest setValue:@"www.xxx.cn://" forHTTPHeaderField: @"Referer"];
        newRequest.URL = request.URL;
        [webView loadRequest:newRequest];
        self.load = YES;
        decisionHandler(WKNavigationActionPolicyCancel);
    }
    else if ([url containsString:@"https://wx.tenpay.com/cgi-bin/mmpayweb-bin/checkmweb?"]) {
        self.load = NO;
        decisionHandler(WKNavigationActionPolicyAllow);
    }
    
    decisionHandler(WKNavigationActionPolicyAllow);
}

至此, 已經(jīng)完成文章開頭所說的兩個(gè)部分, 能調(diào)起也能返回了.

2019/8/23 更新

不愿意修改 header ? 反正就是不能改 header, 不接受上面微信的解決方案, 怎么辦呢? 去翻了下 微信支付開發(fā)步驟&常見問題 , 還真的有新發(fā)現(xiàn), 不知道是之前沒注意還是新加的... 拼接 redirect_url 可以指定回調(diào)頁面.

回調(diào)頁面.png

拿之前的 demo 簡(jiǎn)單的改改, 試了一下, 確實(shí)可以 ??

- (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler{
    // 在發(fā)送請(qǐng)求之前,決定是否跳轉(zhuǎn)
    //self.load  用來控制對(duì) @"https://wx.tenpay.com/cgi-bin/mmpayweb-bin/checkmweb?" 的攔截
    NSString *url = navigationAction.request.URL.absoluteString;
    if ([url containsString:@"weixin://wap/pay"]) {
        self.load = NO; 
        [[UIApplication sharedApplication] openURL:navigationAction.request.URL];
        decisionHandler(WKNavigationActionPolicyCancel);
    }
    else if ([url containsString:@"https://wx.tenpay.com/cgi-bin/mmpayweb-bin/checkmweb?"] && !self.isLoad) {
        NSURLRequest *request = navigationAction.request;
        NSMutableURLRequest *newRequest = [[NSMutableURLRequest alloc] init];
//        newRequest.allHTTPHeaderFields = request.allHTTPHeaderFields;
//        [newRequest setValue:@"www.xxx.cn://" forHTTPHeaderField: @"Referer"];
//        newRequest.URL = request.URL;
        
        
        // 這里 redirect_url 要傳的值, 就是上面 Referer 的值
        NSString *urlStr = [NSString stringWithFormat:@"%@&redirect_url=www.xxx.cn://", [request.URL absoluteString]];
        
        newRequest.URL = [NSURL URLWithString:urlStr];
        [webView loadRequest:newRequest];
        
        self.load = YES;
        decisionHandler(WKNavigationActionPolicyCancel);
    }
    else if ([url containsString:@"https://wx.tenpay.com/cgi-bin/mmpayweb-bin/checkmweb?"]) {
        self.load = NO;
        decisionHandler(WKNavigationActionPolicyAllow);
    }
    
    decisionHandler(WKNavigationActionPolicyAllow);
}

簡(jiǎn)單點(diǎn)說就是不設(shè)置 Referer, 把之前需要設(shè)置的 Referer 值, 直接作為 redirect_url 的值, 拼在 URL 后面. 但返回自有 App 兩者顯示的頁面效果是不太相同的, 以支付失敗為例(沒有1分錢的支付就不存支付成功的?? ??)

Referer 回到原有 App, 會(huì)停留在跳轉(zhuǎn)前的頁面(仿佛時(shí)間靜止了)

redirect_url 回到原因 App, 打開了一個(gè)微信頁面, 白屏... 手動(dòng)返回會(huì)回到跳轉(zhuǎn)前的頁面

不過 微信支付開發(fā)步驟&常見問題 也有提到, 設(shè)置 redirect_url 后, 可能需要用戶手動(dòng)觸發(fā)查單操作, 可能還需要 H5 那邊做點(diǎn)啥子操作吧...... (看到這里, 我基本確定了, 這個(gè)是新加上去的!!!!!)

redirect_url 注意事項(xiàng).png
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末占卧,一起剝皮案震驚了整個(gè)濱河市遗菠,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌华蜒,老刑警劉巖辙纬,帶你破解...
    沈念sama閱讀 206,968評(píng)論 6 482
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異叭喜,居然都是意外死亡贺拣,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,601評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門捂蕴,熙熙樓的掌柜王于貴愁眉苦臉地迎上來譬涡,“玉大人,你說我怎么就攤上這事启绰“喝澹” “怎么了?”我有些...
    開封第一講書人閱讀 153,220評(píng)論 0 344
  • 文/不壞的土叔 我叫張陵委可,是天一觀的道長渊跋。 經(jīng)常有香客問我,道長着倾,這世上最難降的妖魔是什么拾酝? 我笑而不...
    開封第一講書人閱讀 55,416評(píng)論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮卡者,結(jié)果婚禮上蒿囤,老公的妹妹穿的比我還像新娘。我一直安慰自己崇决,他們只是感情好材诽,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,425評(píng)論 5 374
  • 文/花漫 我一把揭開白布底挫。 她就那樣靜靜地躺著,像睡著了一般脸侥。 火紅的嫁衣襯著肌膚如雪建邓。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,144評(píng)論 1 285
  • 那天睁枕,我揣著相機(jī)與錄音官边,去河邊找鬼。 笑死外遇,一個(gè)胖子當(dāng)著我的面吹牛注簿,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播跳仿,決...
    沈念sama閱讀 38,432評(píng)論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼诡渴,長吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了塔嬉?” 一聲冷哼從身側(cè)響起玩徊,我...
    開封第一講書人閱讀 37,088評(píng)論 0 261
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎谨究,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體泣棋,經(jīng)...
    沈念sama閱讀 43,586評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡胶哲,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,028評(píng)論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了潭辈。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片鸯屿。...
    茶點(diǎn)故事閱讀 38,137評(píng)論 1 334
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖把敢,靈堂內(nèi)的尸體忽然破棺而出寄摆,到底是詐尸還是另有隱情,我是刑警寧澤修赞,帶...
    沈念sama閱讀 33,783評(píng)論 4 324
  • 正文 年R本政府宣布婶恼,位于F島的核電站,受9級(jí)特大地震影響柏副,放射性物質(zhì)發(fā)生泄漏勾邦。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,343評(píng)論 3 307
  • 文/蒙蒙 一割择、第九天 我趴在偏房一處隱蔽的房頂上張望眷篇。 院中可真熱鬧,春花似錦荔泳、人聲如沸蕉饼。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,333評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽昧港。三九已至厦幅,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間慨飘,已是汗流浹背确憨。 一陣腳步聲響...
    開封第一講書人閱讀 31,559評(píng)論 1 262
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留瓤的,地道東北人休弃。 一個(gè)月前我還...
    沈念sama閱讀 45,595評(píng)論 2 355
  • 正文 我出身青樓,卻偏偏與公主長得像圈膏,于是被迫代替她去往敵國和親塔猾。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,901評(píng)論 2 345

推薦閱讀更多精彩內(nèi)容