iOS開發微信支付
現在基本所有的App都會接入支付寶支付以及微信支付,也有很多第三方提供給你
SDK幫你接入,但是這種涉及到支付的東西還是自己服務器搞來的好一些,其實搞懂了
邏輯非常的簡單,下面直接給大家說說下基本流程和接入需要註意的東西。
支付寶詳細爬坑接入指南傳送門
前期準備(這個東西一般來講我們不需要來操心,但是還是稍微介紹下)
1.到微信開放平臺註冊賬號點擊打開鏈接
2.進入管理中心------移動應用------創建移動應用----根據頁面完善應用資料
3.審核過後,通過應用詳情頁面,查看應用詳情,查看AppID和AppSecret相關信息
4.創建這些是沒有支付能力的,需要額外申請,還是根據提示一步步填寫,填寫完之後會發一封郵件到您的預留的郵箱,然後到商戶平臺點擊打開鏈接填寫資料,最主要的是驗證下開戶收款賬號,會收到一波幾分錢的巨額財產,那麽這個時候如果你填寫的是你的開戶賬號,直接跑路吧,這些錢夠你在深圳買房了。。。。。。如果你是個好人,那麽找你們財務驗證下是否有收到,就代表通過了,愉快的代碼時間來了
開擼代碼之前先看下基本流程
商戶系統和微信支付系統主要交互說明:
步驟1:用戶在商戶APP中選擇商品,提交訂單,選擇微信支付。
步驟2:商戶後臺收到用戶支付單,調用微信支付統一下單接口。點擊打開鏈接
步驟3:統一下單接口返回正常的prepay_id,再按簽名規範重新生成簽名後,將數據傳輸給APP。參與簽名的字段名為appId,partnerId,prepayId,nonceStr,timeStamp,package。註意:package的值格式為Sign=WXPay
步驟4:商戶APP調起微信支付。點擊打開鏈接
步驟5:商戶後臺接收支付通知。點擊打開鏈接
步驟6:商戶後臺查詢支付結果。點擊打開鏈接
看完流程,來看看咱們客戶端要做什麽準備
1.SDK接入
2.依賴庫導入(貌似還差個libc++.dylib,也一並加入)
3.iOS 9 配置白名單
4.配置下Scheme(這填寫的是申請回來的ID)
終於可以愉快的寫代碼了
1.向微信註冊你的AppID
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { // Override point for customization after application launch. //註冊APP,這裏的字符串就是Wechat URL Scheme裏面對應的ID 也是申請回來的ID,必須一致 [WXApi registerApp:@"這裏填寫申請回來的ID"]; return YES; }
2.請求服務器的參數,拉起微信支付App(超級關鍵,註意聽)
#pragma mark - 微信支付 - (void)wechatPay { // 把生成的訂單信息組裝起來傳給服務器,如何組裝就和服務器約定好 [[TWTShoppingCartLogic sharedData] goToWechatEasyPay:self.orderStr way:@"2" complete:^(NSError *error, id data) { NSMutableString *stamp = [data objectForKey:@"timestamp"]; //調起微信支付 PayReq* req = [[PayReq alloc] init]; req.partnerId = [data objectForKey:@"partnerid"]; req.prepayId = [data objectForKey:@"prepayid"]; req.nonceStr = [data objectForKey:@"noncestr"]; req.timeStamp = stamp.intValue; req.package = [data objectForKey:@"package"]; req.sign = [data objectForKey:@"sign"]; [WXApi sendReq:req]; }]; }
這裏請求的方法和步驟就不寫了,無非就是post信息給服務器,咱們看看需要的數據格式(假數據)
{
"appid" : "wxb4b",微信開放平臺審核通過的AppID
"noncestr" : "171127dd056d05e423c8b9e",隨機字符串
"package" : "Sign=WXPay", 固定值
"partnerid" : "130", 微信支付分配的商戶ID
"prepayid" : "wx201609291601", 預支付交易會話ID
"sign" : "684371081C049B6017641", 簽名,除了sign,剩下6個組合的再次簽名字符串
"timestamp" : 147513 當前時間
}
註意啦!!!!!!
第一種:老司機後臺類型
其實當你把訂單傳給後臺的時候,後臺事先會把訂單通過微信的生成預支付訂單生成
prepayID點擊打開鏈接,那麽對於老司機來說,怎麽可能把這種返回的數據返回給你?
他們會把接受的prepayID根據上面的結構組裝起來,那麽預支付訂單生成的時候也會返
回sign字段,老司機不會直接用,後臺會把這個字段,也就是剩下6個字段再次md5簽
名生成簽名算法新的sign字段組裝完畢返回給你,這種情況下直接在App上配置模型,
拉起微信支付,非常舒暢,一氣呵成!!!
第二種:無法理解類型後臺(讓你自己簽名)
當你把訂單傳給他的時候,同樣他會生成個預訂單prepayID,那麽這種司機開車特別
猛,直接把返回的參數根據格式組裝後彈回給你,sign字段也是預訂單生成後的,沒有
經過二次md5簽名,他也沒有告訴你,那麽你也特別猛,沒問他,直接用他的字段,組
裝完畢,拉起微信,我擦,你會直接懵逼了,那麽你將會只會看到這個。
問題不大,就是自己簽名了,自己寫個本地的md5玩玩(假的千萬別用,網上
找來的分享下)
//創建package簽名 -(NSString*) createMd5Sign:(NSMutableDictionary*)dict { NSMutableString *contentString =[NSMutableString string]; NSArray *keys = [dict allKeys]; //按字母順序排序 NSArray *sortedArray = [keys sortedArrayUsingComparator:^NSComparisonResult(id obj1, id obj2) { return [obj1 compare:obj2 options:NSNumericSearch]; }]; //拼接字符串 for (NSString *categoryId in sortedArray) { if ( ![[dict objectForKey:categoryId] isEqualToString:@""] && ![categoryId isEqualToString:@"sign"] && ![categoryId isEqualToString:@"key"] ) { [contentString appendFormat:@"%@=%@&", categoryId, [dict objectForKey:categoryId]]; } } //添加key字段 [contentString appendFormat:@"key=%@", self.spKey]; //得到MD5 sign簽名 NSString *md5Sign =[contentString MD5]; return md5Sign; } - (NSMutableDictionary*)payWithprePayid:(NSString*)prePayid { if(prePayid == nil) { NSLog(@"prePayid 為空"); return nil; } //獲取到prepayid後進行第二次簽名 NSString *package, *time_stamp, *nonce_str; //設置支付參數 time_t now; time(&now); time_stamp = [NSString stringWithFormat:@"%ld", now]; nonce_str = [time_stamp MD5]; //重新按提交格式組包,微信客戶端暫只支持package=Sign=WXPay格式,須考慮升級後支持攜帶package具體參數的情況 //package = [NSString stringWithFormat:@"Sign=%@",package]; package = @"Sign=WXPay"; //第二次簽名參數列表 NSMutableDictionary *signParams = [NSMutableDictionary dictionary]; NSLog(@"%@",signParams); [signParams setObject: self.appId forKey:@"appid"]; [signParams setObject: self.mchId forKey:@"partnerid"]; [signParams setObject: nonce_str forKey:@"noncestr"]; [signParams setObject: package forKey:@"package"]; [signParams setObject: time_stamp forKey:@"timestamp"]; [signParams setObject: prePayid forKey:@"prepayid"]; //生成簽名 NSString *sign = [self createMd5Sign:signParams]; //添加簽名 [signParams setObject: sign forKey:@"sign"]; //返回參數列表 return signParams; }
如果真的要在App端二次簽名的話,那加密的時候還要加入申請的密鑰,但是真的不好
這樣做,其一:服務器已經做過一次簽名了,第二次做了返回給你就好了,沒必要再給
App。其二:不安全,全放在App上,這種東西一定要放到服務器
小技巧:其實出現上面那種情況有幾種可能
1.sign沒有二次簽名
2.noncerStr是服務器返回的,不要自己生成
3.package是寫死的,不要寫錯了
4.timeStamp是10位數
5.自己簽名的sign一定要全部大寫
6.為了避免上面的情況,交給服務器管理,我們負責組裝拉起微信支付就好了
3.處理回調信息
Appdelegate
- (BOOL)application:(UIApplication *)application handleOpenURL:(NSURL *)url { // 跳轉到URL scheme中配置的地址 //NSLog(@"跳轉到URL scheme中配置的地址-->%@",url); return [WXApi handleOpenURL:url delegate:[WXApiManager sharedManager]]; } //支付成功時調用,回到第三方應用中 - (BOOL)application:(UIApplication *)application openURL:(NSURL *)url sourceApplication:(NSString *)sourceApplication annotation:(id)annotation { if ([url.scheme isEqualToString:WECHAT_APPKEY])//微信調用結束 { return [WXApi handleOpenURL:url delegate:[WXApiManager sharedManager]]; } }
這裏的處理是根據微信官網提供的方法,代理到專門處理的單利當中去統一處理WXApiManager
註意點:有些人用NSNotificationCenter來通知到發出請求的界面去,然後在發起的界面處理回調的邏輯,但是這裏
你要考慮一種非人類的交互,TMD有人在拉起微信支付的時候把自己的App給推出了或者App自己掛了,那麽當回調
生效的時候,原先拉起微信支付App的界面已經消失了,你發的通知他收不到了,這種情況我是存到本地的
[[NSUserDefaultsstandardUserDefaults]setValue:self.orderStrforKey:@"WECHAT_PAY_ORDER_TRADEID"];
[[NSUserDefaultsstandardUserDefaults]synchronize];
處理回調的時候直接從本地讀取
最終處理邏輯的地方(這裏不能直接用他的返回接過,要二次確認)
//微信回調,有支付結果的時候會回調這個方法 - (void)onResp:(BaseResp *)resp { if([resp isKindOfClass:[PayResp class]]){ //支付返回結果,實際支付結果需要去微信服務器端查詢 NSString *strMsg,*strTitle = [NSString stringWithFormat:@"支付結果"]; switch (resp.errCode) { case WXSuccess: strMsg = @"支付結果:成功!"; NSLog(@"支付成功-PaySuccess,retcode = %d", resp.errCode); // 這裏別用返回的狀態來確定是否正真支付成功了,這樣是不對的,我們必須拿著存到本地的traderID去服務器再次check,這樣和服務器收到的異步回調結果匹配之後才能確認是否真的已經支付成功了 [[TWTShoppingCartLogic sharedData] gotoCheckWeChatOrder:tradeID compelete:^(NSError *error, id data) { // 二次確認 }]; break; default: strMsg = [NSString stringWithFormat:@"支付結果:失敗!retcode = %d, retstr = %@", resp.errCode,resp.errStr]; NSLog(@"錯誤,retcode = %d, retstr = %@", resp.errCode,resp.errStr); break; } } }
還是總結下重要的地方吧
1.App Scheme一定要配置正確
2.千萬不能用生成預訂單返回的Sign,要重新生成(和後臺溝通)
3.要考慮拉起App支付的時候自己程序被退出或者自殺了
4.一定不能用異步返回給App的參數進行判斷成功與否,需要和後臺進行二次確認,異步返回給後臺的數據才是最終的
看官方給的說法
差不多介紹到這裏了,自己微信遇到的坑沒有接入支付寶的時候多,接過支付寶再接入
微信,真的太簡單了,有空再寫個支付寶支付,覺得有幫到大家的記得給個贊哦~~~
遇到其他問題了再補充
12.6日更新:同一訂單支付兩次的問題,商戶保證支付平臺大姨媽的情況下去重
遇到了這麽個場景,當你支付完的時候支付寶或者微信沒有及時回調,用戶已經支付,但是平臺還在處理中,也沒有異步通知商戶後臺,例如第一張圖的支付寶同步狀態碼8000 or 6004,第二張圖的-1,那麽這個時候我們App做完一系列操作之後非人類用戶以為沒支付,但是其實已經支付了,支付寶和微信大姨媽了,土豪買家又點了支付,這個時候是和9000支付成功不同的,成功的時候支付寶是會去重的,不會讓你重復支付的,微信暫時不清楚,這個時候後臺還沒收到任何回調,又拉起了支付,竟然還能支付,神奇的兩個訂單產生了,雖然最終最會成功一個訂單,另一個訂單會支付失敗,這個情況遇到了還是很懵逼的,記錄下,有不同意見的可以留言分享下。
我的做法就是:
對於同步回調的狀態碼,讓後臺再開一個接口post給他,他根據這個狀態碼避免同一個已支付的訂單,但是在處理中的時候重復簽名,重復去支付,這樣就又能愉快的玩耍了,雖然是比較罕見的操作,但是也得稍微留意下
這裏就沒有Demo了,有個官方的已經很詳細了
點擊打開微信官方Demo鏈接
iOS開發微信支付