文章涉及的demo在Github LQThirdParty, 歡迎Star | Fork
微信支付,在許多需要支付的APP中,幾乎是必須要集成的,使用微信支付,也漸漸成為用戶的習(xí)慣,筆者花了一些時(shí)間整理了一下微信支付的流程,在這里分享一下,也是對(duì)自己學(xué)習(xí)的總結(jié).
一. 前期準(zhǔn)備
- 到微信的開(kāi)放平臺(tái)注冊(cè)賬號(hào):https://open.weixin.qq.com;
- 進(jìn)入管理中心-->移動(dòng)應(yīng)用-->創(chuàng)建移動(dòng)應(yīng)用;根據(jù)頁(yè)面提示完善應(yīng)用資料;
- 審核通過(guò)后,進(jìn)入應(yīng)用詳情頁(yè),查看應(yīng)用詳情,這里可以查看AppID和AppSecret以及一些接口信息;
應(yīng)用創(chuàng)建時(shí),是沒(méi)有支付能力的,需要額外申請(qǐng),具體的申請(qǐng)過(guò)程,根據(jù)網(wǎng)頁(yè)提示,一步步完善資料,具體資料就向你公司的相關(guān)人員索取吧,提交審核;審核通過(guò)后,微信平臺(tái)會(huì)給你填寫(xiě)審核資料時(shí)預(yù)留郵箱發(fā)送一個(gè)郵件,郵件中包含了與支付能力相關(guān)的微信商戶號(hào)的信息,然后到微信的商戶平臺(tái):https://pay.weixin.qq.com,填寫(xiě)相關(guān)資料,最主要的是驗(yàn)證開(kāi)戶行,微信會(huì)向你填寫(xiě)的開(kāi)戶銀行賬戶匯一筆錢(一般是幾分錢),讓你們的財(cái)務(wù)查一下,然后驗(yàn)證一下即可,通過(guò)后即獲取了支付能力.下面就開(kāi)始集成到APP中去吧...
二. 適配iOS9
在iOS9下,默認(rèn)使用的是HTTPS協(xié)議,系統(tǒng)會(huì)攔截對(duì)HTTP協(xié)議接口的訪問(wèn),因此無(wú)法獲取HTTP協(xié)議接口的數(shù)據(jù),控制臺(tái)會(huì)輸出如下信息:
2016-02-29 15:42:35.765 LQQWeChatDemo[3228:740276] App Transport Security has blocked a cleartext HTTP (http://) resource load since it is insecure. Temporary exceptions can be configured via your app's Info.plist file.
2016-02-29 15:42:35.769 LQQWeChatDemo[3228:740247] Error Domain=NSURLErrorDomain Code=-1022 "The resource could not be loaded because the App Transport Security policy requires the use of a secure connection." UserInfo={NSErrorFailingURLStringKey=http://wxpay.weixin.qq.com/pub_v2/app/app_pay.php, NSErrorFailingURLKey=http://wxpay.weixin.qq.com/pub_v2/app/app_pay.php, NSLocalizedDescription=The resource could not be loaded because the App Transport Security policy requires the use of a secure connection., NSUnderlyingError=0x145826d0 {Error Domain=kCFErrorDomainCFNetwork Code=-1022 "The resource could not be loaded because the App Transport Security policy requires the use of a secure connection." UserInfo={NSErrorFailingURLKey=http://wxpay.weixin.qq.com/pub_v2/app/app_pay.php, NSErrorFailingURLStringKey=http://wxpay.weixin.qq.com/pub_v2/app/app_pay.php, NSLocalizedDescription=The resource could not be loaded because the App Transport Security policy requires the use of a secure connection.}}}
解決方案(以下方法2選1)
1. 暫時(shí)全部回退到HTTP協(xié)議
具體方法:
在項(xiàng)目的Info.plist中添加一個(gè)Key:NSAppTransportSecurity
,類型為字典類型。
然后給它添加一個(gè)Key:NSAllowsArbitraryLoads
拍埠,類型為Boolean
類型纳像,值為YES
;
2.設(shè)置域,把不支持HTTPS協(xié)議的接口設(shè)置成HTTP的接口
具體方法:
1.在項(xiàng)目的info.plist中添加一個(gè)Key:
NSAppTransportSecurity
,類型為字典類型。
2.然后給它添加一個(gè)NSExceptionDomains
,類型為字典類型大猛;
3.把需要的支持的域添加給NSExceptionDomains
。其中域作為Key淀零,類型為字典類型挽绩。
4.每個(gè)域下面需要設(shè)置3個(gè)屬性:
NSIncludesSubdomains
、NSExceptionRequiresForwardSecrecy
驾中、NSExceptionAllowsInsecureHTTPLoads
唉堪。
均為Boolean類型模聋,值分別為YES、NO唠亚、YES链方。
方式二設(shè)置的需要把所有不支持HTTPS的接口都設(shè)置一遍,如果項(xiàng)目中都是HTTP協(xié)議的接口,可使用方式一,直接回退到HTTP,就不用單個(gè)設(shè)置了.
三. 導(dǎo)入微信SDK
到微信開(kāi)放平臺(tái)下載最新的SDK,下載之后導(dǎo)入文件:
libWeChatSDK.a
,WXApi.h
灶搜,WXApiObject.h
到你的工程中
添加依賴庫(kù):
SystemConfiguration.framework
,liz.tbd
,libsqlite3.0.tbd
,libc++.tbd
設(shè)置URL scheme
編譯運(yùn)行,如果報(bào)以下錯(cuò)誤:
Undefined symbols for architecture armv7:
"_OBJC_CLASS_$_CTTelephonyNetworkInfo", referenced from:
objc-class-ref in libWeChatSDK.a(MTAHelper.o)
這是因?yàn)檫€需要添加庫(kù)文件:CoreTelephony.framework
PS:如果項(xiàng)目中使用了ShareSDK,可以直接使用ShareSDK導(dǎo)入的微信SDK,ShareSDK是支持cocoa pods的,只需在Podfile文件中加入:
pod 'ShareSDK3'
pod 'ShareSDK3/ShareSDKPlatforms/WeChat'
使用cocoa pods的好處就不用說(shuō)了...
四. 使用微信的API
在需要使用微信支付的地方導(dǎo)入:
#import "WXApi.h"
- 在AppDelegate中的didFinishLaunchingWithOptions方法中添加以下代碼向微信注冊(cè)APP:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// Override point for customization after application launch.
//注冊(cè)APP,
[WXApi registerApp:@"wxb4ba3c02aa476ea1"];
return YES;
}
- 然后,重寫(xiě)以下兩個(gè)方法:
- (BOOL)application:(UIApplication *)app openURL:(NSURL *)url options:(NSDictionary<NSString *,id> *)options
{
// 跳轉(zhuǎn)到URL scheme中配置的地址
//NSLog(@"跳轉(zhuǎn)到URL scheme中配置的地址-->%@",url);
return [WXApi handleOpenURL:url delegate:(id<WXApiDelegate>)self];
}
//支付成功時(shí)調(diào)用祟蚀,回到第三方應(yīng)用中
- (BOOL)application:(UIApplication *)application openURL:(NSURL *)url sourceApplication:(NSString *)sourceApplication annotation:(id)annotation
{
// NSLog(@"****************url.host -- %@",url.host);
if ([url.scheme isEqualToString:@"wx23a1f7f291ef4b3d"])
{
return [WXApi handleOpenURL:url delegate:(id<WXApiDelegate>)self];
}
return YES;
}
- 添加微信的支付結(jié)果回調(diào)方法:
//微信回調(diào),有支付結(jié)果的時(shí)候會(huì)回調(diào)這個(gè)方法
- (void)onResp:(BaseResp *)resp
{
// 支付結(jié)果回調(diào)
if([resp isKindOfClass:[PayResp class]]){
switch (resp.errCode) {
case WXSuccess:{
//支付返回結(jié)果,實(shí)際支付結(jié)果需要去自己的服務(wù)器端查詢
NSNotification *notification = [NSNotification notificationWithName:@"ORDER_PAY_NOTIFICATION" object:@"success"];
[[NSNotificationCenter defaultCenter] postNotification:notification];
break;
}
default:{
NSNotification *notification = [NSNotification notificationWithName:@"ORDER_PAY_NOTIFICATION"object:@"fail"];
[[NSNotificationCenter defaultCenter] postNotification:notification];
break;
}
}
}
}
這里微信的支付結(jié)果需要向自己的服務(wù)器查詢,成功后通過(guò)發(fā)送通知的方式告訴吊起微信的控制器...
- 吊起微信
吊起微信所需的參數(shù)配置可參考官方提供的一個(gè)連接:
http://wxpay.weixin.qq.com/pub_v2/app/app_pay.php,用于調(diào)試支付接口的參數(shù)設(shè)置;
這里所獲取到的參數(shù)可以全部由服務(wù)器提供,即在服務(wù)器返回prepayId的時(shí)候,一并將吊起微信所需的參數(shù)返回:
[Networking getWithUrl:@"http://wxpay.weixin.qq.com/pub_v2/app/app_pay.php" params:nil success:^(id response) {
NSLog(@"%@",response);
//配置調(diào)起微信支付所需要的參數(shù)
PayReq *req = [[PayReq alloc] init];
req.partnerId = [response objectForKey:@"partnerid"];
req.prepayId = [response objectForKey:@"prepayid"];
req.package = [response objectForKey:@"package"];
req.nonceStr = [response objectForKey:@"noncestr"];
req.timeStamp = [[response objectForKey:@"timestamp"]intValue];
req.sign = [response objectForKey:@"sign"];
//調(diào)起微信支付
if ([WXApi sendReq:req]) {
NSLog(@"吊起成功");
}
} fail:^(NSError *error) {
NSLog(@"%@",error);
}];
- 注冊(cè)通知
在吊起微信的地方注冊(cè)通知(用于接收支付結(jié)果)的時(shí)候,最好先判斷一下用戶是否安裝了微信:
//判斷是否安裝微信
if([WXApi isWXAppInstalled]) {
// 監(jiān)聽(tīng)一個(gè)通知
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(getOrderPayResult:) name:@"ORDER_PAY_NOTIFICATION" object:nil];
}
然后實(shí)現(xiàn)通知方法,在這里獲取支付結(jié)果,處理相關(guān)邏輯:
#pragma mark - 收到支付成功的消息后作相應(yīng)的處理
- (void)getOrderPayResult:(NSNotification *)notification
{
if ([notification.object isEqualToString:@"success"]) {
NSLog(@"支付成功");
} else {
NSLog(@"支付失敗");
}
}
到此,一個(gè)完整的支付流程就完成了...
附加
如果,吊起微信后,頁(yè)面只顯示一個(gè)確定按鈕,如圖所示:
引起這個(gè)問(wèn)題的主要原因就是吊起微信時(shí),參數(shù)設(shè)置的不正確,而且多半是因?yàn)楹灻膯?wèn)題,就是參數(shù)中的
sign
值的問(wèn)題,在后臺(tái)向微信后臺(tái)請(qǐng)求賬單的時(shí)候有過(guò)一次簽名,而客戶端吊起支付的簽名和那個(gè)簽名是不同的,多半是后臺(tái)直接把那個(gè)簽名發(fā)送給客戶端,并沒(méi)有進(jìn)行二次簽名,可以和后臺(tái)協(xié)商,再簽一次,因?yàn)閮纱魏灻囊?guī)則都一樣,沒(méi)必要再讓客戶端寫(xiě)一遍簽名規(guī)則了.當(dāng)然,客戶端簽名也不是不可以,Demo中也有二次簽名的方法,需要自己去配置參數(shù):
//有的服務(wù)器沒(méi)有對(duì)sign字段進(jìn)行二次簽名,需要客戶端進(jìn)行,下面這些是對(duì)吊起支付時(shí)的sign字段進(jìn)行二次簽名的,這些操作可以和服務(wù)器協(xié)商全讓服務(wù)器做了,因?yàn)楹灻惴ǘ际且粯拥?后臺(tái)已經(jīng)進(jìn)行了第一次的簽名,第二次只是多了prePayid,算法都是一樣的沒(méi)必要客戶端再寫(xiě)一次算法
//注意:下面的方法不能直接使用,這里只是給出了算法和參數(shù)配置,相應(yīng)的填充數(shù)據(jù)就行
//創(chuàng)建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;
}
文章涉及的demo在Github LQThirdParty, 歡迎Star | Fork