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

作者: JackerooChu | 来源:发表于2018-04-03 15:08 被阅读328次

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

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

    接入流程.png

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

    0.jpeg

    点击右上角的创建移动应用,一步一步填写必要填入的信息,在这里就不做多余的赘述了。
    需要注意的是这里填入的
    Bundle ID需要和项目的一一对应


    22.jpeg

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

    pod 'WechatOpenSDK'
    

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


    2.jpeg

    完成之后可以获取到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: 签名


    3.jpeg

    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.jpeg

    4.注意点及问题

    注意点:

    • 设置好scheme,否则应用无法跳转到微信客户端
    • 支付签名时的key值全部是小写的
    • 如果支付显示验证签名失败
      的时候,可以将packages设为默认值(Sign=WXPay)试试

    问题:

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

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

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

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

    [WXPayService sharedManager].delegate = nil;
    

    5.结束语

    微信支付签名建议和服务端协商做二次签名,以保证支付的安全性。
    ps: 如有不对的地方,欢迎批评指正,更多文章请点击这里

    相关文章

      网友评论

      • 炎成:scheme不是用来跳回APP的吗:flushed:
        JackerooChu:@炎成 嗯呢,是这样
        炎成:@JackerooChu URL Scheme是用来跳转别的应用后跳回当前应用所需要的,如果不设置这个,跳转微信后,就不能跳转回来

      本文标题:iOS微信支付接入以及工具类封装

      本文链接:https://www.haomeiwen.com/subject/krqicftx.html