近期筆者開發的項目中,需要用到支付寶支付和微信支付。大概一個月前,支付寶就已經集成完畢並可以正常使用。但在集成坑爹的微信支付SDK時,遇到了諸多問題,搞了將近三個星期。期間不斷的跟後臺同事覈對代碼(簽名、下單),支付流程,其中的血淚艱辛,不言而喻。現筆者把集成過程中遇到的一些問題記錄下來,供自己和大家參考。如果有什麼不對的地方,也請大家多多指正:
吐槽完了,下面出正文。
補充說明:第一準備階段不需要開發者負責操作,如果你是iOS開發人員,只想找到調用微信支付的代碼,可直接跳過 第一準備階段。
一、準備階段
(1)筆者公司用戶是微信開放平臺下開通的微信支付功能,網址:https://open.weixin.qq.com/?qq-pf-to=pcqq.c2c。網上大部分資料講的都是微信公衆平臺下的微信支付集成,其實原理和代碼實現上大同小異。
(2)到微信開放平臺的管理中心創建移動應用,待移動應用審覈通過以後,申請獲得微信支付能,這個流程走下來,大概需要 2 - 3 周。筆者建議提前到開放平臺審覈爲好。網址:https://open.weixin.qq.com/cgi-bin/applist?t=manage/list&lang=zh_CN&token=478d1d01575659fb7b53ee40d1f37c5cfc8689e5
移動應用審覈通過後,獲取APP_ID和AppSecret,附上截圖:
(3)等到移動應用審覈通過,獲得微信支付能力之後,進入微信商戶商戶平臺。商戶平臺的賬號和密碼,在公司賬號開通微信支付功能以後,財付通就會下發。微信商戶平臺網址:https://pay.weixin.qq.com/index.php/home/login?return_url=/
進入微信商戶平臺後,獲取商戶號和商戶API密鑰。附上截圖:
二、獲取微信支付IOS頭文件和庫下載及APP支付示例和解讀
(1)進入網址https://pay.weixin.qq.com/wiki/doc/api/app.php?chapter=11_1,下載微信支付APP支付示例。在APP支付示例中,已經包含了頭文件和庫,所以不需要在下載頭文件和庫。
(2)解壓文件,會得到文件名爲:wechat_sdk_sample_ios_v3_pay的文件夾。從名字可以知道,這是一份v3版本的代碼,目前v2版本的微信支付幾乎已經不用,新添加的微信支付SDK幾乎都是v3版本的。集成的時候,要明白這點,很重要。
(3)文件目錄及作用
readme.txt:首次集成微信支付SDK時,讀讀沒壞處。
設置appid.jpg:直接運行demo是無法調用微信客戶端的,需要按鈕圖片的內容,設置 URL Schemes 和 APP_ID 才能運行。
wechat_sdk_sample_ios_payV3:
~ lib:配置文件,配置了微信支付所要設置的相關內容
~ SDKExport:微信支付頭文件和庫文件
~ SDKSample:項目功能實現代碼
(4)代碼準備工作
Xcode打開工程代碼,點擊AppDelegate.m文件,可以查看到微信支付的實現代碼。
~[WXApi registerApp:APP_ID withDescription:@"demo 2.0"];向微信註冊,必寫代碼。
~ -(void) onReq:(BaseReq*)req:發送信息給微信客戶端,請查看微信支付demo具體代碼實現
~ -(void) onResp:(BaseResp*)resp:收到微信客戶端信息的代理方法,請查看微信支付demo具體代碼實現
(5)調起微信支付
重點來了!!!通過調試,我們可以知道,"微信支付測試簽名"、“微信支付demo”兩個按鈕,分別對應的是
- (void)sendPay 和 - (void)sendPay_demo 兩個方法,那麼這兩個方法都做了什麼呢?什麼是簽名呢?
在解釋上面的問題前,有必要先解釋一下微信支付的簽名流程。
微信支付demo和微信支付開發文檔多處提到以下幾點:
~ 後臺服務器做簽名、下單請求,從微信服務器獲取到 預支付ID 和 sign 簽名等多個參數;
~ APP通過獲取後臺返回的參數(openID、partnerID商戶號、prepayID預支付ID、nonceStr隨機字符串、timeStamp時間戳、package包、sign簽名),調用微信支付接口,就可以調用手機微信客戶端進行支付
~ 調用微信支付的代碼如下(核心代碼哦,我們所有的操作都是爲了這段核心的參數做準備):
//調起微信支付 PayReq* req = [[[PayReq alloc] init]autorelease]; req.openID = [dict objectForKey:@"appid"]; req.partnerId = [dict objectForKey:@"partnerid"]; req.prepayId = [dict objectForKey:@"prepayid"]; req.nonceStr = [dict objectForKey:@"noncestr"]; req.timeStamp = stamp.intValue; req.package = [dict objectForKey:@"package"]; req.sign = [dict objectForKey:@"sign"]; [WXApi sendReq:req];
根據上面的說明,童鞋們不知道明白了沒。其實所有的代碼,都是爲了上面的核心作準備的,目的地就是爲了獲取上面核心代碼的參數,已調用微信支付的接口。我們回到最初點:
~ - (void)sendPay:模擬簽名的過程。爲了更好的安全性,關於微信支付的下單和簽名的過程,一般都是在後臺完成,後臺再把這些參數傳回到APP,APP再調用。這個方法卻是在APP端完成下單和簽名的操作,這其實只是一個演示的過程“//本實例只是演示簽名過程, 請將該過程在商戶服務器上實現”
~ - (void)sendPay_demo:使用微信後臺提供的參數,做支付演示。調用這個方法,控制檯會打印出這樣一串東西:url:http://wxpay.weixin.qq.com/pub_v2/app/app_pay.php?plat=ios&order_no=1444376336&product_name=Ios%B7%FE%CE%F1%C6%F7%B6%CB%C7%A9%C3%FB%D6%A7%B8%B6%20%B2%E2%CA%D4&order_price=0.01。這串字符串中,包含了調用微信支付接口所需的所有的參數。核心代碼通過使用這些參數,就可以成功調用微信客戶端完成支付。
微信支付提供的官方文檔:
統一下單,獲取預支付ID:https://pay.weixin.qq.com/wiki/doc/api/app.php?chapter=9_1
調起支付接口,調起手機微信客戶端:https://pay.weixin.qq.com/wiki/doc/api/app.php?chapter=9_12&index=2
通過上面的描述,童鞋們明白了簽名、下單、調用微信支付調用的原理和代碼實現了沒?
三、代碼實現微信支付
通過上面的描述,我們可以得知一下幾點:
(0)在- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions 函數中添加以下代碼://向微信註冊 [WXApi registerApp:APP_ID withDescription:@"demo 2.0"];
(1)調用微信支付前,需要下單、簽名等操作,以便獲取微信支付所必要的參數。爲了提高安全性,下單、簽名操作一般是在後臺完成
參數包括:appid、partid(商戶號)、prepayid(預支付訂單ID)、noncestr(參與簽名的隨機字符串)、timestamp(參與簽名的時間戳)、sign(簽名字符串)
(2)APP端獲取後臺的參數數據,調用一下的核心代碼,調起微信支付
//調起微信支付 PayReq* req = [[[PayReq alloc] init]autorelease]; req.openID = [dict objectForKey:@"appid"]; req.partnerId = [dict objectForKey:@"partnerid"]; req.prepayId = [dict objectForKey:@"prepayid"]; req.nonceStr = [dict objectForKey:@"noncestr"]; req.timeStamp = stamp.intValue; req.package = [dict objectForKey:@"package"]; req.sign = [dict objectForKey:@"sign"]; [WXApi sendReq:req];
(3)筆者提供一段親測成功的核心代碼,演示如何後臺獲取參數,調起微信支付:
//請求網絡數據 [[TDNetworkingHelper sharedInstane] postRequestWithPath:[NSString stringWithFormat:@"%@%@", BASE_URL, orders_pay_wxpay_sign] parameter:paramDic whenSuccessed:^(id json) { if ([[json objectForKey:@"code"] integerValue]==1) //數據獲取成功 { //調用微信支付 [self sendWeChatReqWithOrderData:[json objectForKey:@"data"]]; } else //獲取失敗 { ALERT([json objectForKey:@"msg"]); } [self hideHub]; self.view.userInteractionEnabled = true; } whenFailed:^(NSError *error) { [self hideHub]; self.view.userInteractionEnabled = true; //判斷網絡狀態 [self judgeNetworkStaus]; }]; -(void)sendWeChatReqWithOrderData:(NSDictionary*)orderData { NSLog(@"提交的參數:%@", orderData); //調起微信支付 PayReq* req = [[PayReq alloc] init]; req.openID = [orderData objectForKey:@"appid"]; req.partnerId = [orderData objectForKey:@"partnerid"]; req.prepayId = [orderData objectForKey:@"prepayid"]; req.nonceStr = [orderData objectForKey:@"noncestr"]; req.timeStamp = (UInt32)[[orderData objectForKey:@"timestamp"] integerValue]; req.package = [orderData objectForKey:@"package"]; req.sign = [orderData objectForKey:@"sign"]; [WXApi sendReq:req]; }
五、無法完成微信支付的各種異常狀態分與處理
筆者在開發過程中,也遇到過不少的異常狀態,現記錄如下:
(1)無法調起手機微信客戶端、無法調起微信客戶端頁面,一閃而過
原因分析:第一種如果您的APP集成了友盟或者shareSDK的第三方分享SDK,會導致WechaSDK包的重複衝突;
處理方法:刪除友盟或者shareSDK裏面的WechaSDK包;在調用微信支付接口的文件裏,後綴改爲 .mm
(2)中間只有一個確定按鈕,點擊按鈕,返回APP,提示 “支付結果:失敗!retcode=-2,retstr=nil ”
原因分析:到這裏,說明代碼沒有問題,是傳遞的參數有問題,任何一個參數出錯都可能導致這樣的問題。
處理方法:與後臺完成簽名、下單的同事溝通,檢查後臺的代碼。
備註:我就是卡在這裏兩個多星期,後來檢查發現是後臺同事在簽名的時候出現了問題。微信支付要求有兩次簽名,第二次需要timestamp(時間戳)參與簽名。我們只有一次簽名,一直沒法調起支付界面
希望這篇可以幫到你,讓你少走彎路。
對以上內容有任何疑問的童鞋,請留言指教!