一、介紹:
現在的消費越來越方便,直接帶個手機用各種三方的支付平台進行支付就行,例如微信、支付寶。現在正好我所做的項目中用到了微信支付,今天就來整理一下。
二、准備:
1、去微信官方開發者平台注冊開發者賬號:https://open.weixin.qq.com
2、然后登陸開發平台:

3、給項目對應的Bundle ID創建應用程序(默認有登陸和分享功能,當然需要花300塊錢申請支付功能,一般公司會購買,完成這些操作就是等待審核了,一般一到兩個星期就搞定了)
4、審核通過,可以看到如下顯示:會生成AppID(很重要,開發時會用到,用來注冊微信支付時使用的)



5、選擇APP支付方式,參看文檔進行集成

6、下載資源包:(一般都是最新的版本,需要在Xocde8.0上編譯,我在后面使用的是1.7.1版,在Xcode7.3.1上編譯)


7、參考APP端開發步驟,配置屬性
(1)我下載的是1.7.1版本,最好手動把SDK拖入到項目中,很簡單,主要有四個文件:libWeChatSDK.a靜態包、WechatAuthSDK.h、WXApi.h、WXApiObject.h

(2)設置plist網絡請求字段(iOS9.0以上需要設置)

(3)添加依賴庫后編譯,Build success

(4)項目設置APPID.
商戶在微信開放平台申請開發APP應用后,微信開放平台會生成APP的唯一標識APPID。在Xcode中打開項目,設置項目屬性中的URL Schemes為您的APPID。如圖:

(5)進入項目,寫代碼,注冊APPID
商戶APP工程中引入微信lib庫和頭文件,調用API前,需要先向微信注冊您的APPID,代碼如下:
[WXApi registerApp:@"wxd930xxxxxxxxx" withDescription:@"wxchatpay"]; //注冊微信的AppID
(6)商戶服務器生成支付訂單,先調用【統一下單API】生成預付單,獲取到prepay_id后將參數再次簽名傳輸給APP發起支付,給出的參數很多,就不全部截圖了。

(7)隨機字符串和簽名都必須按照微信所給出的算法生成,而且參數都是以xml格式放入到請求正文里

a.隨機字符串算法:我們推薦生成隨機數算法如下:調用隨機數函數生成,將得到的值轉換為字符串。
#pragma mark - 產生隨機字符串 //生成隨機數算法 ,隨機字符串,不長於32位 //微信支付API接口協議中包含字段nonce_str,主要保證簽名不可預測。 //我們推薦生成隨機數算法如下:調用隨機數函數生成,將得到的值轉換為字符串。 + (NSString *)generateTradeNO { static int kNumber = 15; NSString *sourceStr = @"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"; NSMutableString *resultStr = [[NSMutableString alloc] init]; // srand函數是初始化隨機數的種子,為接下來的rand函數調用做准備。 // time(0)函數返回某一特定時間的小數值。 // 這條語句的意思就是初始化隨機數種子,time函數是為了提高隨機的質量(也就是減少重復)而使用的。 // srand(time(0)) 就是給這個算法一個啟動種子,也就是算法的隨機種子數,有這個數以后才可以產生隨機數,用1970.1.1至今的秒數,初始化隨機數種子。 // Srand是種下隨機種子數,你每回種下的種子不一樣,用Rand得到的隨機數就不一樣。為了每回種下一個不一樣的種子,所以就選用Time(0),Time(0)是得到當前時時間值(因為每時每刻時間是不一樣的了)。 srand((unsigned int)time(0)); for (int i = 0; i < kNumber; i++) { unsigned index = rand() % [sourceStr length]; NSString *oneStr = [sourceStr substringWithRange:NSMakeRange(index, 1)]; [resultStr appendString:oneStr]; } return resultStr; }
b.簽名生成算法查看鏈接:https://pay.weixin.qq.com/wiki/doc/api/app/app.php?chapter=4_3
也可以模擬簽名生成接口,作為一次性測試使用:https://pay.weixin.qq.com/wiki/tools/signverify/,需要填寫對應的字段參數,點擊生成即可:如圖

c、填寫參數,使用AFN,進行統一下單,最終的所有的參數統一為xml,不是json,格式如下:

d、返回發送下單請求后的反饋結果:

統一下單代碼:
// 交易類型 #define TRADE_TYPE @"APP" // 交易結果通知網站此處用於測試,隨意填寫,正式使用時填寫正確網站 #define NOTIFY_URL @"http://wxpay.weixin.qq.com/pub_v2/pay/notify.v2.php" // 交易價格1表示0.01元,10表示0.1元 #define PRICE @"1" #pragma mark - 客戶端操作/ 實際操作由服務端操作 // 隨機字符串變量 這里最好使用和安卓端一致的生成邏輯 NSString *tradeNO = [self generateTradeNO]; // 設備IP地址,請在wifi環境下測試,否則獲取的ip地址為error,正確格式應該是8.8.8.8 //NSString *addressIP = [self fetchIPAddress]; NSString *addressIP = [[IPToolManager sharedManager] currentIpAddress]; // 隨機產生訂單號用於測試,正式使用請換成你從自己服務器獲取的訂單號 NSString *orderno = [NSString stringWithFormat:@"%ld",time(0)]; // 獲取SIGN簽名 DataMD5 *data = [[DataMD5 alloc] initWithAppid:WX_APPID mch_id:MCH_ID nonce_str:tradeNO partner_id:WX_PartnerKey body:@"充值" out_trade_no:orderno total_fee:PRICE spbill_create_ip:addressIP notify_url:NOTIFY_URL trade_type:TRADE_TYPE]; // 轉換成XML字符串,這里知識形似XML,實際並不是正確的XML格式,需要使用AF方法進行轉義 NSString *string = [[data dic] XMLString]; AFHTTPSessionManager *session = [AFHTTPSessionManager manager]; // 這里傳入的XML字符串只是形似XML,但不是正確是XML格式,需要使用AF方法進行轉義 session.responseSerializer = [[AFHTTPResponseSerializer alloc] init]; [session.requestSerializer setValue:@"text/xml; charset=utf-8" forHTTPHeaderField:@"Content-Type"]; [session.requestSerializer setValue:WXUNIFIEDORDERURL forHTTPHeaderField:@"SOAPAction"]; [session.requestSerializer setQueryStringSerializationWithBlock:^NSString *(NSURLRequest *request, NSDictionary *parameters, NSError *__autoreleasing *error) { return string; }]; [session POST:WXUNIFIEDORDERURL parameters:string progress:nil success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) { // 輸出XML數據 NSString *responseString = [[NSString alloc] initWithData:responseObject encoding:NSUTF8StringEncoding] ; // 將微信返回的xml數據解析轉義成字典 NSDictionary *dic = [NSDictionary dictionaryWithXMLString:responseString]; // 判斷返回的許可 if ([[dic objectForKey:@"result_code"] isEqualToString:@"SUCCESS"] &&[[dic objectForKey:@"return_code"] isEqualToString:@"SUCCESS"] ) { //這里面調起支付 (就是下面的第8步) //............pay code....... } } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) { NSLog(@"%@",error); }];
(8)下單成功后,調起支付
// 發起微信支付,設置參數 PayReq *request = [[PayReq alloc] init]; request.openID = [dic objectForKey:WXAPPID]; request.partnerId = [dic objectForKey:WXMCHID]; request.prepayId= [dic objectForKey:WXPREPAYID]; request.package = @"Sign=WXPay"; request.nonceStr= [dic objectForKey:WXNONCESTR]; // 將當前時間轉化成時間戳 NSDate *datenow = [NSDate date]; NSString *timeSp = [NSString stringWithFormat:@"%ld", (long)[datenow timeIntervalSince1970]]; UInt32 timeStamp =[timeSp intValue]; request.timeStamp= timeStamp; // 簽名加密 DataMD5 *md5 = [[DataMD5 alloc] init]; request.sign=[md5 createMD5SingForPay:request.openID partnerid:request.partnerId prepayid:request.prepayId package:request.package noncestr:request.nonceStr timestamp:request.timeStamp]; // 調用微信 [WXApi sendReq:request];
支付的接口參數和返回結果截圖如下:


(9)設置支付代理,可以設置APPDelegate為代理,也可以自己創下創建單例工具類作為代理,處理支付回調結果。照微信SDK Sample,在類實現onResp函數,支付完成后,微信APP會返回到商戶APP並回調onResp函數,開發者需要在該函數中接收通知,判斷返回錯誤碼,如果支付成功則去后台查詢支付結果再展示用戶實際支付結果。注意 一定不能以客戶端返回作為用戶支付的結果,應以服務器端的接收的支付通知或查詢API返回的結果為准。代碼示例如下:
-(void)onResp:(BaseResp*)resp{ if ([respisKindOfClass:[PayRespclass]]){ PayResp*response=(PayResp*)resp; switch(response.errCode){ caseWXSuccess: //服務器端查詢支付通知或查詢API返回的結果再提示成功 NSlog(@"支付成功"); break; default: NSlog(@"支付失敗,retcode=%d",resp.errCode); break; } } }
好了,大致差不多就可以了,下面是我用真機測試的結果,測試宏定義設置的一分錢:點擊綠色的微信支付按鈕



(10)demo
本人demo地址如下(demo中需要在pch文件和info.plist文件設置appID等屬性):https://github.com/xiayuanquan/WXChatPay
另外ip地址的獲取也很重要,本人demo地址:https://github.com/xiayuanquan/IP_Test
借鑒的支付demo(demo中需要在pch文件和info.plist文件設置appID等屬性):https://github.com/lyoniOS/WxPayDemo
參考博客地址:
