項目開發(fā)遇到web頁調(diào)起支付的功能,參考了網(wǎng)上很多資料蛀恩,在此做個筆記疫铜,便于記憶,同時也希望幫助到遇到相同問題的小伙伴双谆。
參考鏈接:
http://www.reibang.com/p/8ac06ffd090f
http://www.reibang.com/p/28483a16c4d5
http://www.reibang.com/p/66d2c0ff51c6
微信支付
問題:web頁點擊支付跳轉(zhuǎn)微信支付頁后點擊左上角的取消按鈕或完成支付之后壳咕,微信APP跳轉(zhuǎn)的是Safari瀏覽器,而不是跳轉(zhuǎn)回原APP顽馋。
解決方案:
1.設(shè)置自己項目中Schemes
Info->URL Types
格式為:www.xxxx.com谓厘,注意:www.xxxx.com 此域名是H5授權(quán)的域名,如果是二級域名寸谜,可以寫成:aaa.xxxx.com(aaa隨便寫)竟稳。可以直接問申請微信支付的人要熊痴。
2.修改redirect_url
目的是保證redirect_url這個回調(diào)鏈接能跟設(shè)置的URL Schemes一致他爸。
攔截后我們需要首先關(guān)注在原始地址中是否含有redirect_url=字符串,如果含有該字符串則說明你們后臺人員是利用該字符串在微信支付完成后跳轉(zhuǎn)到支付完成的界面.而我們也需要利用該字段以實現(xiàn)支付完成后跳轉(zhuǎn)回我們的APP.
- 如果包含redirect_url=字段果善,我們需要先記住后臺重定向的地址诊笤,然后將其替換成我們配置好的URL schemes以實現(xiàn)跳轉(zhuǎn)回我們的APP.然后在跳轉(zhuǎn)回我們APP之后我們會手動再加載一次原先重定向的URL地址。
- 如果不包含redirect_url=字段巾陕,我們只需要添加該字段到原始URL最后面即可
3.設(shè)置webView請求header中的Referer字段
微信支付結(jié)束后默認(rèn)回調(diào)Referer字段中地址讨跟。
Referer設(shè)置為www.xxxx.com://,這樣的格式鄙煤。
4.添加WKNavigationDelegate代理晾匠,實現(xiàn)重定向代理方法
注意
- 我們需要判斷微信地址的同時判斷“redirect_url=www.xxxx.com://”,因為我們在第一次檢測到后會重新加載該地址,該地址中也含有 https://wx.tenpay.com/cgi-bin/mmpayweb-bin/checkmweb梯刚,會形成循環(huán)沖突凉馆,所以判斷條件如下
- 如我們項目中有支付完成的重定向地址,我們可以在跳轉(zhuǎn)前即加載該地址亡资,否則我們在回到APP后會有明顯的加載過程句喜,界面不友好
- 一般跳轉(zhuǎn)到其他APP時,地址不是以http或https開頭沟于,所以我們在最后的判斷中以此為依據(jù),但注意如果服務(wù)器端加載錯誤的地址可能會走入此邏輯咳胃,所以必須對特定的scheme進(jìn)行處理
支付寶支付
問題:web頁點擊支付跳轉(zhuǎn)支付寶支付頁后點擊左上角的取消按鈕或完成支付之后,停留在支付寶旷太,而不是跳轉(zhuǎn)回原APP展懈。
解決方案:
1.添加 URL Scheme
2.實現(xiàn)代理方法攔截鏈接并跳轉(zhuǎn)支付寶
WKNavigationDelegate代碼
#define XDX_URL_TIMEOUT 30
static const NSString *CompanyFirstDomainByWeChatRegister = @"www.xxxx.com";
- (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 wasn't load.
NSString *absoluteString = [navigationAction.request.URL.absoluteString stringByRemovingPercentEncoding];
NSLog(@"Current URL is %@",absoluteString);
static NSString *endPayRedirectURL = nil;
// 解決跳轉(zhuǎn)到本地支付寶App不返回的問題
if ([absoluteString hasPrefix:@"alipays://"] || [absoluteString hasPrefix:@"alipay://"])
{
NSURL *openedURL = navigationAction.request.URL;
NSString *prefixString = @"alipay://alipayclient/?";
//替換里面的默認(rèn)Scheme為自己的Scheme
NSString *urlString = [[self xh_URLDecodedString:absoluteString] stringByReplacingOccurrencesOfString:@"alipays" withString:[NSString stringWithFormat:@"%@",CompanyFirstDomainByWeChatRegister]];
if ([urlString hasPrefix:prefixString]) {
NSRange rang = [urlString rangeOfString:prefixString];
NSString *subString = [urlString substringFromIndex:rang.length];
NSString *encodedString = [prefixString stringByAppendingString:[self xh_URLEncodedString:subString]];
openedURL = [NSURL URLWithString:encodedString];
}
BOOL isSucc = [[UIApplication sharedApplication] openURL:openedURL];
if (!isSucc) {
NSLog(@"未安裝某寶客戶端");
}
decisionHandler(WKNavigationActionPolicyCancel);
return;
}
//解決微信支付后為返回當(dāng)前應(yīng)用的問題
// Wechat Pay, Note : modify redirect_url to resolve we couldn't return our app from wechat client.
if ([absoluteString hasPrefix:@"https://wx.tenpay.com/cgi-bin/mmpayweb-bin/checkmweb"] && ![absoluteString hasSuffix:[NSString stringWithFormat:@"redirect_url=%@://",CompanyFirstDomainByWeChatRegister]]) {
decisionHandler(WKNavigationActionPolicyCancel);
// 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's 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=%@://",CompanyFirstDomainByWeChatRegister]];
}else {
redirectUrl = [absoluteString stringByAppendingString:[NSString stringWithFormat:@"&redirect_url=%@://",CompanyFirstDomainByWeChatRegister]];
}
NSMutableURLRequest *newRequest = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:redirectUrl] cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:XDX_URL_TIMEOUT];
newRequest.allHTTPHeaderFields = request.allHTTPHeaderFields;
[newRequest setValue:[NSString stringWithFormat:@"%@",CompanyFirstDomainByWeChatRegister] forHTTPHeaderField:@"Referer"];
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's 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:@"%@",CompanyFirstDomainByWeChatRegister]]) {
}
// BOOL canOpen = [[UIApplication sharedApplication] canOpenURL:request.URL];
// if (canOpen) {
// [[UIApplication sharedApplication] openURL:request.URL];
// }
if ([navigationAction.request.URL.absoluteString hasPrefix:@"weixin://"]) {
[[UIApplication sharedApplication] openURL:navigationAction.request.URL];
}
return;
}
decisionHandler(WKNavigationActionPolicyAllow);
}
- (NSString *)xh_URLDecodedString:(NSString *)urlString {
NSString *string = urlString;
NSString *decodedString=(__bridge_transfer NSString *)CFURLCreateStringByReplacingPercentEscapesUsingEncoding(NULL, (__bridge CFStringRef)string, CFSTR(""), CFStringConvertNSStringEncodingToEncoding(NSUTF8StringEncoding));
return decodedString;
}
- (NSString *)xh_URLEncodedString:(NSString *)urlString {
NSString *string = urlString;
NSString *encodedString = (NSString *) CFBridgingRelease(CFURLCreateStringByAddingPercentEscapes(kCFAllocatorDefault,
(CFStringRef)string,
NULL,
(CFStringRef)@"!*'();:@&=+$,/?%#[]",
kCFStringEncodingUTF8));
return encodedString;
}
跳轉(zhuǎn)返回通知
跳轉(zhuǎn)回原APP后,在Appdelegate.m文件中會有回調(diào)方法供璧。在方法里存崖,可以寫一些其他的邏輯代碼。
// 僅支持iOS9以上系統(tǒng)睡毒,iOS8及以下系統(tǒng)不會回調(diào)
- (BOOL)application:(UIApplication *)app openURL:(NSURL *)url options:(NSDictionary<UIApplicationOpenURLOptionsKey, id> *)options {
//6.3的新的API調(diào)用来惧,是為了兼容國外平臺的調(diào)用[如果用6.2的api調(diào)用會沒有回調(diào)],對國內(nèi)平臺沒有影響
BOOL result = [[UMSocialManager defaultManager] handleOpenURL:url options:options];
if (!result) {
// 其他如支付等SDK的回調(diào)
// H5微信支付的回調(diào)
if ([url.scheme isEqualToString:@"www.xxxx.com"]) {
// 發(fā)送通知加載頁面
[[NSNotificationCenter defaultCenter] postNotificationName:kWeChatComeBackKey object:nil userInfo:@{@"url" : url.absoluteString}];
}
}
return result;
}