近期筆者開發的項目中,需要用到支付寶支付和微信支付。大概一個月前,支付寶就已經內建完畢并可以正常使用。但在內建坑爹的微信支付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(時間戳)參與簽名。我們隻有一次簽名,一直沒法調起支付界面
希望這篇可以幫到你,讓你少走彎路。
對以上内容有任何疑問的童鞋,請留言指教!