46

iOS微信支付接入以及工具类封装

 5 years ago
source link: http://www.cocoachina.com/ios/20180727/24339.html?amp%3Butm_medium=referral
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

在刚刚结束的一个项目中用到了微信支付,从接入微信支付到工具类的封装,在本文中做个积累,方便日后使用。

1.开始接入微信支付的准备工作

i2YRviY.png!web

首先你需要去 微信开放平台 注册账号,在这里要吐槽一下,不知道这个微信平台的账号密码验证机制,忘了以前的密码,重新登录,各种找回密码都不行,实在没辙,去注册个Gmail,登录成功后,可以看到如下界面。

EZ7VB3V.jpg!web

点击右上角的创建移动应用,一步一步填写必要填入的信息,在这里就不做多余的赘述了。

需要注意的是这里填入的

Bundle ID需要和项目的一一对应

R3UjYr2.jpg!web

接下来就等一个星期左右,等待微信审核通过你就可以将微信支付SDK集成到项目中,详情可以看 官方给出的文档 .

pod 集成方法

pod 'WechatOpenSDK'

在Xcode中,选择你的工程设置项,选中“TARGETS”一栏,在“info”标签栏的“URL type“添加“URL scheme”为你所注册的应用程序id(如下图所示)。

q2YnueE.jpg!web

完成之后可以获取到appid(微信开放平台为应用生成的唯一识别码)、商户id、商户secretKey。对于app端来说只用到appid,商户id最好通过接口从server获取,商户secretKey是用来签名的,一般只有server能用到。

2.支付流程

nIV7VfU.png!web

刚开始看这个流程图可能会觉得很复杂,所以总结了我们比较关系的流程是:

  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: 签名

FRJZRvi.jpg!web

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 
#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

z2uu6zE.jpg!web

4.注意点及问题

注意点:

  • 设置好scheme,否则应用无法跳转到微信客户端

  • 支付签名时的key值全部是小写的

  • 如果支付显示验证签名失败的时候,可以将packages设为默认值(Sign=WXPay)试试

问题:

系统版本大于等于iOS9的,调起微信客户端之后,可以直接点击状态栏左侧按钮返回,这时是不走回调方法的。

解决方案:

在AppDelegate.m的applicationWillEnterForeground方法中,调用查询支付结果接口然后刷新当然页面。需要设置bool变量作为标志,否则每次应用进入前台都去查询,就不符合业务要求了。

进入微信支付页面之后,不做操作,切换到自己应用中,退出当前支付页面,然后再进入微信客户端点击支付或者取消,此时自己的应用会崩溃闪退

原因:退出页面后页面已经出栈被销毁,但wx回调时还是去调用其中的代理方法,就会出现野指针。

解决方案:在页面的viewWillDisappear方法中加入

[WXPayService sharedManager].delegate = nil;

5.结束语

微信支付签名建议和服务端协商做二次签名,以保证支付的安全性。

ps: 如有不对的地方,欢迎批评指正,更多文章请 点击这里

作者:JackerooChu

链接:https://www.jianshu.com/p/9afae0cf6c60


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK