在剛剛結束的一個專案中用到了微信支付,從接入微信支付到工具類的封裝,在本文中做個積累,方便日後使用。
1.開始接入微信支付的準備工作
首先你需要去微信開放平臺註冊賬號,在這裡要吐槽一下,不知道這個微信平臺的賬號密碼驗證機制,忘了以前的密碼,重新登入,各種找回密碼都不行,實在沒轍,去註冊個Gmail?,登入成功後,可以看到如下介面。
點選右上角的建立移動應用,一步一步填寫必要填入的資訊,在這裡就不做多餘的贅述了。 需要注意的是這裡填入的 Bundle ID需要和專案的一一對應
接下來就等一個星期左右,等待微信稽核通過你就可以將微信支付SDK整合到專案中,詳情可以看官方給出的文件. pod 整合方法pod 'WechatOpenSDK'
複製程式碼
在Xcode中,選擇你的工程設定項,選中“TARGETS”一欄,在“info”標籤欄的“URL type“新增“URL scheme”為你所註冊的應用程式id(如下圖所示)。
完成之後可以獲取到appid(微信開放平臺為應用生成的唯一識別碼)、商戶id、商戶secretKey。對於app端來說只用到appid,商戶id最好通過介面從server獲取,商戶secretKey是用來簽名的,一般只有server能用到。
2.支付流程
剛開始看這個流程圖可能會覺得很複雜,所以總結了我們比較關係的流程是:
- app客戶端向伺服器傳送支付請求
- 伺服器在收到客戶端請求之後向微信後臺呼叫統一下單API,獲得預付單資訊
- 服務端生成帶簽名的客戶端支付資訊給app
- app客戶端使用者確認支付,app喚醒微信客戶端進行支付
- app獲得支付結果後向服務端查詢最終的結果並顯示
app端的工作:
- 接入微信支付SDK
- 向伺服器傳送支付請求
- 支付資訊喚醒微信app,然後進行支付
- 收到微信支付回撥後向伺服器確認支付結果
- 根據查詢結果展示結果頁面告知使用者支付結果
伺服器端的工作:
- 收到app客戶端支付請求後向微信後臺請求預支付訂單
- 伺服器端簽名並返回資訊給app客戶端
- 接收微信後臺返回的支付結果,用來app端查詢
伺服器端返回的欄位說明:
- appId:返回的appid
- partnerId: 父級id
- prepayId: 支付id
- packages: 包名(微信預設的為“Sign=WXPay”)
- nonceStr: 生成的隨機字串
- timesTamp: 時間戳
- sign: 簽名
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
4.注意點及問題
注意點:
- 設定好scheme,否則應用無法跳轉到微信客戶端
- 支付簽名時的key值全部是小寫的
- 如果支付顯示驗證簽名失敗 的時候,可以將packages設為預設值(Sign=WXPay)試試
問題:
系統版本大於等於iOS9的,調起微信客戶端之後,可以直接點選狀態列左側按鈕返回,這時是不走回撥方法的。
解決方案: 在AppDelegate.m的applicationWillEnterForeground方法中,呼叫查詢支付結果介面然後重新整理當然頁面。需要設定bool變數作為標誌,否則每次應用進入前臺都去查詢,就不符合業務要求了。
進入微信支付頁面之後,不做操作,切換到自己應用中,退出當前支付頁面,然後再進入微信客戶端點選支付或者取消,此時自己的應用會崩潰閃退
原因:退出頁面後頁面已經出棧被銷燬,但wx回撥時還是去呼叫其中的代理方法,就會出現野指標。
解決方案:在頁面的viewWillDisappear
方法中加入
[WXPayService sharedManager].delegate = nil;
複製程式碼
5.結束語
微信支付簽名建議和服務端協商做二次簽名,以保證支付的安全性。 ps: 如有不對的地方,歡迎批評指正,更多文章請點選這裡
如有需要,請關注公眾號JackerooChu,瞭解更多文章