[iOS]微信支付接入詳解

文章涉及的demo在Github LQThirdParty, 歡迎Star | Fork

微信支付,在許多需要支付的APP中,幾乎是必須要集成的,使用微信支付,也漸漸成為用戶的習(xí)慣,筆者花了一些時(shí)間整理了一下微信支付的流程,在這里分享一下,也是對(duì)自己學(xué)習(xí)的總結(jié).

一. 前期準(zhǔn)備

  1. 到微信的開(kāi)放平臺(tái)注冊(cè)賬號(hào):https://open.weixin.qq.com;
  2. 進(jìn)入管理中心-->移動(dòng)應(yīng)用-->創(chuàng)建移動(dòng)應(yīng)用;根據(jù)頁(yè)面提示完善應(yīng)用資料;
  3. 審核通過(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

配置Info.plist文件

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è)屬性:
NSIncludesSubdomainsNSExceptionRequiresForwardSecrecy驾中、NSExceptionAllowsInsecureHTTPLoads唉堪。
均為Boolean類型模聋,值分別為YES、NO唠亚、YES链方。

設(shè)置域
各平臺(tái)域

方式二設(shè)置的需要把所有不支持HTTPS的接口都設(shè)置一遍,如果項(xiàng)目中都是HTTP協(xié)議的接口,可使用方式一,直接回退到HTTP,就不用單個(gè)設(shè)置了.

三. 導(dǎo)入微信SDK

到微信開(kāi)放平臺(tái)下載最新的SDK,下載之后導(dǎo)入文件:
libWeChatSDK.aWXApi.h灶搜,WXApiObject.h到你的工程中

添加依賴庫(kù):

SystemConfiguration.framework,liz.tbd,libsqlite3.0.tbd,libc++.tbd

添加系統(tǒng)依賴庫(kù)

設(shè)置URL scheme

設(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

(完)

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末割卖,一起剝皮案震驚了整個(gè)濱河市前酿,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌鹏溯,老刑警劉巖罢维,帶你破解...
    沈念sama閱讀 221,695評(píng)論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異丙挽,居然都是意外死亡肺孵,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,569評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門(mén)取试,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)悬槽,“玉大人怀吻,你說(shuō)我怎么就攤上這事瞬浓。” “怎么了蓬坡?”我有些...
    開(kāi)封第一講書(shū)人閱讀 168,130評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵猿棉,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我屑咳,道長(zhǎng)萨赁,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 59,648評(píng)論 1 297
  • 正文 為了忘掉前任兆龙,我火速辦了婚禮杖爽,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘紫皇。我一直安慰自己慰安,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,655評(píng)論 6 397
  • 文/花漫 我一把揭開(kāi)白布聪铺。 她就那樣靜靜地躺著化焕,像睡著了一般。 火紅的嫁衣襯著肌膚如雪铃剔。 梳的紋絲不亂的頭發(fā)上撒桨,一...
    開(kāi)封第一講書(shū)人閱讀 52,268評(píng)論 1 309
  • 那天查刻,我揣著相機(jī)與錄音,去河邊找鬼凤类。 笑死穗泵,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的踱蠢。 我是一名探鬼主播火欧,決...
    沈念sama閱讀 40,835評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼茎截!你這毒婦竟也來(lái)了苇侵?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 39,740評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤企锌,失蹤者是張志新(化名)和其女友劉穎榆浓,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體撕攒,經(jīng)...
    沈念sama閱讀 46,286評(píng)論 1 318
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡陡鹃,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,375評(píng)論 3 340
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了抖坪。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片萍鲸。...
    茶點(diǎn)故事閱讀 40,505評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖擦俐,靈堂內(nèi)的尸體忽然破棺而出脊阴,到底是詐尸還是另有隱情,我是刑警寧澤蚯瞧,帶...
    沈念sama閱讀 36,185評(píng)論 5 350
  • 正文 年R本政府宣布嘿期,位于F島的核電站,受9級(jí)特大地震影響埋合,放射性物質(zhì)發(fā)生泄漏备徐。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,873評(píng)論 3 333
  • 文/蒙蒙 一甚颂、第九天 我趴在偏房一處隱蔽的房頂上張望蜜猾。 院中可真熱鬧,春花似錦振诬、人聲如沸蹭睡。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 32,357評(píng)論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)棠笑。三九已至,卻和暖如春禽绪,著一層夾襖步出監(jiān)牢的瞬間蓖救,已是汗流浹背洪规。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,466評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留循捺,地道東北人斩例。 一個(gè)月前我還...
    沈念sama閱讀 48,921評(píng)論 3 376
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像从橘,于是被迫代替她去往敵國(guó)和親念赶。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,515評(píng)論 2 359