iOS實現H5支付(微信、支付寶)原生封裝
前言
支付分APP支付、H5支付、掃碼支付等。app支付一般在app中使用,並且需要整合相應的支付SDK,H5支付多用於網頁。如果你的APP不想整合支付SDK,又想實現支付功能,你可以在專案中使用H5支付。本文主要講述如何將H5支付封裝成一個原生可呼叫的元件。
1.H5支付流程
注:以下為網頁H5支付流程,原生呼叫需要修改部分流程
1.1 微信支付
- 統一下單,獲取微信中間頁地址mweb_url
- 頁面重定向到微信中間頁
- 微信中間頁發起支付請求
- safari瀏覽器攔截支付請求開啟微信APP開始支付(如果在app中,需要在shouldStartLoadWithRequest:方法裡面攔截支付請求,並開啟微信)
微信中間頁重新向到redirect_url
1.2 支付寶支付
- 發起網頁支付請求,H5為一個form表單提交。
- 頁面重定向到支付寶收銀臺頁面
- 發起APP支付請求,並且開始倒計時,如果開啟支付寶超時頁面跳轉到網頁支付介面,如果喚起支付寶,倒計時結束。
- 支付完畢頁面跳轉到return_url頁面,需使用者手動觸發。
2.原生封裝思路
新開一個webView載入支付中間頁,攔截中間頁支付請求並喚起支付,然後關閉webView流程結束。
webView需要加到window(或者當前控制器的view上),並設定一個大小(肉眼不可見就行)。因為使用wkwebview時,webView不顯示的情況下,H5請求會被掛起,會導致支付寶頁面不能喚起支付請求。
3.程式碼實現
具體步驟見程式碼註釋
@interface HJH5WebPayManager()<UIWebViewDelegate> @property (nonatomic,strong) UIWebView *payWebview; @property (nonatomic,strong) void(^sendPayResult)(HJH5SendWebPayResult); @end @implementation HJH5WebPayManager +(instancetype)sharedInstance{ static dispatch_once_t once ; static HJH5WebPayManager *_instace = nil; dispatch_once(&once,^{ _instace = [[self alloc] init]; }); return _instace; } -(void)loadWebPayTransitionPage:(NSString *)html handleBlock:(void (^)(HJH5SendWebPayResult))handle{ NSMutableURLRequest *request = nil; if ([html hasPrefix:@"https://wx.tenpay.com"]) { //微信安全域名 NSString *wxScheme = @""; NSString *referer = [NSString stringWithFormat:@"%@://",wxScheme]; //將redirect_url替換成scheme,微信支付完畢才能跳回APP,否則會開啟Safari瀏覽器(因為redirect_url一般為一個HTTP的地址) NSRange range = [html rangeOfString:@"redirect_url="]; NSString *reqUrl; if (range.length>0) { reqUrl = [html substringToIndex:range.location+range.length]; reqUrl = [reqUrl stringByAppendingString:referer]; }else{ reqUrl = [html stringByAppendingString:[NSString stringWithFormat:@"&redirect_url=%@",referer]]; } request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:reqUrl] cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:60.0]; //設定授權域名,偽造Referer頭,因為微信中間頁會檢驗Referer頭,並且Referer對應的值需要包含安全域名 [request setValue:referer forHTTPHeaderField:@"Referer"]; if (self.payWebview) { [self.payWebview removeFromSuperview]; self.payWebview = nil; } self.payWebview = [[UIWebView alloc] initWithFrame:CGRectMake(0,0.1,0.1)]; self.sendPayResult = handle; [[UIApplication sharedApplication].keyWindow addSubview:self.payWebview]; self.payWebview.delegate = self; [self.payWebview loadRequest:request]; }else if ([html hasPrefix:@"<form"]){ //如果是支付寶,html對應的應該是一段form表單提交指令碼,需要呼叫loadString方法載入 if (self.payWebview) { [self.payWebview removeFromSuperview]; self.payWebview = nil; } self.payWebview = [[UIWebView alloc] initWithFrame:CGRectMake(0,0.1)]; self.sendPayResult = handle; [[UIApplication sharedApplication].keyWindow addSubview:self.payWebview]; self.payWebview.delegate = self; NSString *payStr = html; NSString *htmlString = [NSString stringWithFormat:@"htmlString:<html> \n" "<head> \n" "<meta name=\"viewport\" content=\"initial-scale=1.0,maximum-scale=1.0,user-scalable=no\" /> \n" "<style type=\"text/css\"> \n" "body {font-size:16px;}\n" "</style> \n" "</head> \n" "<body>" "%@" "</body>" "</html>",payStr]; [self.payWebview loadHTMLString:htmlString baseURL:nil]; }else{ //非法html,返回錯誤 handle(HJH5SendWebPayResultOther); return; } //容錯處理,20秒沒喚起支付,當錯誤處理。 __weak typeof(self) weakSelf = self; dispatch_after(dispatch_time(DISPATCH_TIME_NOW,(int64_t)(20 * NSEC_PER_SEC)),dispatch_get_main_queue(),^{ if (weakSelf.sendPayResult) { weakSelf.sendPayResult(HJH5SendWebPayResultOther); } [weakSelf endPayment]; }); } - (void)webView:(UIWebView *)webView didFailLoadWithError:(NSError *)error{ //頁面載入失敗,返回錯誤 if (self.sendPayResult) { self.sendPayResult(HJH5SendWebPayResultLoadFail); } [self endPayment]; } - (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType{ NSURL *url = request.URL; NSString *newUrl = url.absoluteString; //攔截微信支付請求,並開啟微信 if([newUrl rangeOfString:@"weixin://wap/pay"].location != NSNotFound){ //判斷是否能開啟微信 if ([[UIApplication sharedApplication] canOpenURL:url]) { if (@available(iOS 10.0,*)){ [[UIApplication sharedApplication] openURL:url options:@{} completionHandler:nil]; }else{ [[UIApplication sharedApplication] openURL:url]; } if (self.sendPayResult) { self.sendPayResult(HJH5SendWebPayResultSuccess); } [self endPayment]; }else{ if (self.sendPayResult) { self.sendPayResult(HJH5SendWebPayResultSendFail); } [self endPayment]; } return NO; }else if([newUrl rangeOfString:@"alipay://alipayclient/?"].location != NSNotFound){ //攔截支付寶支付請求,並且替換fromAppUrlScheme引數為當前APP的scheme,實現支付完畢返回APP功能。 NSString *aliScheme = @"支付寶支付scheme,支付完畢可通過scheme返回到當前APP"; newUrl = [HJStringHelper decodeURL:newUrl]; NSString *parameterString = [newUrl stringByReplacingOccurrencesOfString:@"alipay://alipayclient/?" withString:@""]; NSError *error = nil; id dict = [NSJSONSerialization JSONObjectWithData:[parameterString dataUsingEncoding:NSUTF8StringEncoding] options:NSJSONReadingMutableContainers error:&error]; if (!error) { if ([dict isKindOfClass:[NSMutableDictionary class]]) { dict[@"fromAppUrlScheme"] = aliScheme; NSData *jsonData = [NSJSONSerialization dataWithJSONObject:dict options:NSJSONWritingPrettyPrinted error:&error]; if (!error) { parameterString = [HJStringHelper escapeURL:[[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding]]; NSString *payUrl = [NSString stringWithFormat:@"alipay://alipayclient/?%@",parameterString]; dispatch_async(dispatch_get_main_queue(),^{ //判斷是否能開啟支付寶 if ([[UIApplication sharedApplication] canOpenURL:[NSURL URLWithString:payUrl]]) { if (@available(iOS 10.0,*)){ [[UIApplication sharedApplication] openURL:[NSURL URLWithString:payUrl] options:@{} completionHandler:nil]; }else{ [[UIApplication sharedApplication] openURL:[NSURL URLWithString:payUrl]]; } if (self.sendPayResult) { self.sendPayResult(HJH5SendWebPayResultSuccess); } [self endPayment]; }else{ if (self.sendPayResult) { self.sendPayResult(HJH5SendWebPayResultSendFail); } [self endPayment]; } }); } } } return NO; }else{ return YES; } } -(void)endPayment{ self.sendPayResult = nil; [self.payWebview removeFromSuperview]; self.payWebview = nil; } @end
3.1入參說明
呼叫該方法喚起支付-(void)loadWebPayTransitionPage:(NSString *)html handleBlock:(void (^)(HJH5SendWebPayResult))handle.
其中html為微信中間頁地址和支付寶form表單指令碼。如:
微信:https://wx.tenpay.com ?xxxx
支付寶:<form id="alipaysubmit " name="alipaysubmit" action=xxxx></form><script>document.forms['alipaysubmit '].submit();</script>
見1.H5支付流程,微信下單之後可以獲取中間頁地址,支付則需要form表單提交載入中間頁。
3.2錯誤處理
typedef NS_ENUM(NSUInteger,HJH5SendWebPayResult) { HJH5SendWebPayResultSuccess = 0,//喚起登入成功 HJH5SendWebPayResultLoadFail,//支付頁面載入失敗 HJH5SendWebPayResultSendFail,//調起支付失敗,可能是沒新增未安裝微信或者支付寶 HJH5SendWebPayResultOther //其他 };
支付請求傳送成功則表示這次H5支付發起完成,具體支付結果需要查詢後臺獲得。所以需要對一些異常情況進行處理,比如頁面載入失敗,微信或支付寶未安裝等異常進行處理。
4.說明
這種方案可以統一微信和支付寶H5支付的流程,並且隱式地顯示支付中間頁,不會影響H5單頁面應用的路由。APP不需要整合支付SDK,可以繞過蘋果掃描程式碼。
由於支付寶支付流程改成和微信一樣,所以支付寶網頁支付功能被砍掉,只能通過開啟支付寶APP去支付。這也是這種方案的不足之處。
iOS-APP實現微信H5支付總結
到此這篇關於iOS實現H5支付(微信、支付寶)原生封裝的文章就介紹到這了,更多相關iOS H5支付內容請搜尋我們以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援我們!