iOS微信支付接入以及工具類封裝

JackerooChu發表於2018-04-03

在剛剛結束的一個專案中用到了微信支付,從接入微信支付到工具類的封裝,在本文中做個積累,方便日後使用。

1.開始接入微信支付的準備工作

接入流程.png

首先你需要去微信開放平臺註冊賬號,在這裡要吐槽一下,不知道這個微信平臺的賬號密碼驗證機制,忘了以前的密碼,重新登入,各種找回密碼都不行,實在沒轍,去註冊個Gmail?,登入成功後,可以看到如下介面。

1.png

點選右上角的建立移動應用,一步一步填寫必要填入的資訊,在這裡就不做多餘的贅述了。 需要注意的是這裡填入的 Bundle ID需要和專案的一一對應

2.png
接下來就等一個星期左右,等待微信稽核通過你就可以將微信支付SDK整合到專案中,詳情可以看官方給出的文件. pod 整合方法

pod 'WechatOpenSDK'
複製程式碼

在Xcode中,選擇你的工程設定項,選中“TARGETS”一欄,在“info”標籤欄的“URL type“新增“URL scheme”為你所註冊的應用程式id(如下圖所示)。

3.png

完成之後可以獲取到appid(微信開放平臺為應用生成的唯一識別碼)、商戶id、商戶secretKey。對於app端來說只用到appid,商戶id最好通過介面從server獲取,商戶secretKey是用來簽名的,一般只有server能用到。

2.支付流程

支付流程.png

剛開始看這個流程圖可能會覺得很複雜,所以總結了我們比較關係的流程是:

  1. app客戶端向伺服器傳送支付請求
  2. 伺服器在收到客戶端請求之後向微信後臺呼叫統一下單API,獲得預付單資訊
  3. 服務端生成帶簽名的客戶端支付資訊給app
  4. app客戶端使用者確認支付,app喚醒微信客戶端進行支付
  5. app獲得支付結果後向服務端查詢最終的結果並顯示
app端的工作:
  • 接入微信支付SDK
  • 向伺服器傳送支付請求
  • 支付資訊喚醒微信app,然後進行支付
  • 收到微信支付回撥後向伺服器確認支付結果
  • 根據查詢結果展示結果頁面告知使用者支付結果
伺服器端的工作:
  • 收到app客戶端支付請求後向微信後臺請求預支付訂單
  • 伺服器端簽名並返回資訊給app客戶端
  • 接收微信後臺返回的支付結果,用來app端查詢

伺服器端返回的欄位說明:

  • appId:返回的appid
  • partnerId: 父級id
  • prepayId: 支付id
  • packages: 包名(微信預設的為“Sign=WXPay”)
  • nonceStr: 生成的隨機字串
  • timesTamp: 時間戳
  • sign: 簽名
    4.png

3.iOS端使用

AppDelegate.m,匯入微信SDK標頭檔案WXApi.h

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
[WXApi registerApp:@"註冊獲得的appid"];//註冊appid
    return YES;
}

 //支援所有iOS系統回撥
- (BOOL)application:(UIApplication *)application openURL:(NSURL *)url sourceApplication:(NSString *)sourceApplication annotation:(id)annotation
{
//    [self handleOpenURL:url];
    BOOL result = [[UMSocialManager defaultManager] handleOpenURL:url sourceApplication:sourceApplication annotation:annotation];
    if (!result) {
        // 其他如支付等SDK的回撥
        [self handleOpenURL:url];
    }
    return YES;
}
- (void)handleOpenURL:(NSURL*)url {
    if ([url.host isEqualToString:@"pay"]) { // -- 微信支付
        [WXApi handleOpenURL:url delegate:[WXPayService sharedInstance]];
    }
}

複製程式碼

此處的WXPayService就是自己單獨抽出來寫的一個類,遵循WXApiManagerDelegate協議

WXPayService.h
#import <Foundation/Foundation.h>
#import "WXApi.h"
@interface WXPayService : NSObject <WXApiDelegate>
///單例來接收微信請求的回撥
+ (instancetype)sharedInstance;
// -- 根據介面返回的預支付資訊,構造支付請求
+ (PayReq *)getPayRequest:(NSDictionary *)prepayData;

///處理非支付請求的回撥
- (void)onRespCallBack:(void(^)(BaseResp * resp))callback;
///從伺服器端獲取到微信返回的支付請求用到的引數來發起支付請求
- (void)startPayWithReq:(PayReq *)req callback:(void(^)(BaseResp * resp))callback; 
@end
複製程式碼
WXPayService.m

#import "WXPayService.h"
@interface WXPayService ()
@property (nonatomic,copy) void(^RespCallBack)(BaseResp *);
@end
static WXPayService *sharedInstance;

@implementation WXPayService
+ (instancetype)allocWithZone:(struct _NSZone *)zone{
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        sharedInstance = [super allocWithZone:zone];
    });
    return sharedInstance;
}
///單例來接收微信請求的回撥
+ (instancetype)sharedInstance {
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        sharedInstance = [[self alloc] init];
    });
    return sharedInstance;
}

// -- 根據介面返回的預支付資訊,構造支付請求
+ (PayReq *)getPayRequest:(NSDictionary *)prepayData {
    if (prepayData) {
//  此處Tools是自己的另一個工具類,用來判斷字典的
        PayReq *req = [[PayReq alloc] init];
        if ([Tools dicContain:prepayData withKey:@"partnerid"]) {
            req.partnerId = prepayData[@"partnerid"];
        }
        if ([Tools dicContain:prepayData withKey:@"prepayid"]) {
            req.prepayId = prepayData[@"prepayid"];
        }
        if ([Tools dicContain:prepayData withKey:@"noncestr"]) {
            req.nonceStr = prepayData[@"noncestr"];
        }
        if ([Tools dicContain:prepayData withKey:@"timestamp"]) {
            req.timeStamp = [prepayData[@"timestamp"] intValue];
        }

        req.package =@"Sign=WXPay";
        req.sign =  @"null";
        //日誌輸出
        NSLog(@"appid=%@/npartid=%@/nprepayid=%@/nnoncestr=%@/ntimestamp=%ld/npackage=%@/nsign=%@",[prepayData objectForKey:@"appid"],req.partnerId,req.prepayId,req.nonceStr,(long)req.timeStamp,req.package,req.sign);
        return req;
    }
    return nil;
}

///處理非支付請求的回撥
- (void)onRespCallBack:(void(^)(BaseResp * resp))callback {
    self.RespCallBack = callback;
}
///從伺服器端獲取到微信返回的支付請求用到的引數來發起支付請求
- (void)startPayWithReq:(PayReq *)req callback:(void(^)(BaseResp * resp))callback {
    NSAssert(req !=nil , @"未成功建立微信支付請求");
    self.RespCallBack = callback;
    if ([WXApi isWXAppInstalled]) { // -- 判斷是否安裝微信應用
        //發起微信支付,設定引數
        [WXApi sendReq:req];
    }else {
        self.RespCallBack(nil);
    }
}

#pragma mark WXApiDelegate
- (void)onResp:(BaseResp *)resp {
    if ([resp isKindOfClass:[PayResp class]]) { // -- 判斷是否為支付的回撥
        self.RespCallBack(resp);
    }
}
@end
複製程式碼

在需要支付的ViewController中匯入工具類WXPayService

5.png

4.注意點及問題

注意點:

  • 設定好scheme,否則應用無法跳轉到微信客戶端
  • 支付簽名時的key值全部是小寫的
  • 如果支付顯示驗證簽名失敗 的時候,可以將packages設為預設值(Sign=WXPay)試試

問題:

系統版本大於等於iOS9的,調起微信客戶端之後,可以直接點選狀態列左側按鈕返回,這時是不走回撥方法的。

解決方案: 在AppDelegate.m的applicationWillEnterForeground方法中,呼叫查詢支付結果介面然後重新整理當然頁面。需要設定bool變數作為標誌,否則每次應用進入前臺都去查詢,就不符合業務要求了。

進入微信支付頁面之後,不做操作,切換到自己應用中,退出當前支付頁面,然後再進入微信客戶端點選支付或者取消,此時自己的應用會崩潰閃退

原因:退出頁面後頁面已經出棧被銷燬,但wx回撥時還是去呼叫其中的代理方法,就會出現野指標。 解決方案:在頁面的viewWillDisappear方法中加入

[WXPayService sharedManager].delegate = nil;
複製程式碼

5.結束語

微信支付簽名建議和服務端協商做二次簽名,以保證支付的安全性。 ps: 如有不對的地方,歡迎批評指正,更多文章請點選這裡

如有需要,請關注公眾號JackerooChu,瞭解更多文章

公眾號

相關文章