[iOS]微信支付接入详解

作者: 流火绯瞳 | 来源:发表于2016-05-18 23:15 被阅读2124次

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

    微信支付,在许多需要支付的APP中,几乎是必须要集成的,使用微信支付,也渐渐成为用户的习惯,笔者花了一些时间整理了一下微信支付的流程,在这里分享一下,也是对自己学习的总结.

    一. 前期准备

    1. 到微信的开放平台注册账号:https://open.weixin.qq.com;
    2. 进入管理中心-->移动应用-->创建移动应用;根据页面提示完善应用资料;
    3. 审核通过后,进入应用详情页,查看应用详情,这里可以查看AppID和AppSecret以及一些接口信息;

    应用创建时,是没有支付能力的,需要额外申请,具体的申请过程,根据网页提示,一步步完善资料,具体资料就向你公司的相关人员索取吧,提交审核;审核通过后,微信平台会给你填写审核资料时预留邮箱发送一个邮件,邮件中包含了与支付能力相关的微信商户号的信息,然后到微信的商户平台:https://pay.weixin.qq.com,填写相关资料,最主要的是验证开户行,微信会向你填写的开户银行账户汇一笔钱(一般是几分钱),让你们的财务查一下,然后验证一下即可,通过后即获取了支付能力.下面就开始集成到APP中去吧...

    二. 适配iOS9

    在iOS9下,默认使用的是HTTPS协议,系统会拦截对HTTP协议接口的访问,因此无法获取HTTP协议接口的数据,控制台会输出如下信息:

    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. 暂时全部回退到HTTP协议

    具体方法:
    在项目的Info.plist中添加一个Key:NSAppTransportSecurity,类型为字典类型。
    然后给它添加一个Key:NSAllowsArbitraryLoads,类型为Boolean类型,值为YES

    配置Info.plist文件

    2.设置域,把不支持HTTPS协议的接口设置成HTTP的接口

    具体方法:

    1.在项目的info.plist中添加一个Key:NSAppTransportSecurity,类型为字典类型。
    2.然后给它添加一个NSExceptionDomains,类型为字典类型;
    3.把需要的支持的域添加給NSExceptionDomains。其中域作为Key,类型为字典类型。
    4.每个域下面需要设置3个属性:
    NSIncludesSubdomainsNSExceptionRequiresForwardSecrecyNSExceptionAllowsInsecureHTTPLoads
    均为Boolean类型,值分别为YES、NO、YES。

    设置域 各平台域

    方式二设置的需要把所有不支持HTTPS的接口都设置一遍,如果项目中都是HTTP协议的接口,可使用方式一,直接回退到HTTP,就不用单个设置了.

    三. 导入微信SDK

    到微信开放平台下载最新的SDK,下载之后导入文件:
    libWeChatSDK.aWXApi.hWXApiObject.h到你的工程中

    添加依赖库:

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

    添加系统依赖库

    设置URL scheme

    设置URL scheme

    编译运行,如果报以下错误:

    Undefined symbols for architecture armv7:  
      "_OBJC_CLASS_$_CTTelephonyNetworkInfo", referenced from:  
          objc-class-ref in libWeChatSDK.a(MTAHelper.o)  
    

    这是因为还需要添加库文件:CoreTelephony.framework

    PS:如果项目中使用了ShareSDK,可以直接使用ShareSDK导入的微信SDK,ShareSDK是支持cocoa pods的,只需在Podfile文件中加入:

    pod 'ShareSDK3'  
    pod 'ShareSDK3/ShareSDKPlatforms/WeChat'  
    

    使用cocoa pods的好处就不用说了...

    四. 使用微信的API

    在需要使用微信支付的地方导入:

    #import "WXApi.h"  
    
    • 在AppDelegate中的didFinishLaunchingWithOptions方法中添加以下代码向微信注册APP:
    - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {  
        // Override point for customization after application launch.  
          
        //注册APP,  
        [WXApi registerApp:@"wxb4ba3c02aa476ea1"];  
        return YES;  
    }  
    
    • 然后,重写以下两个方法:
    - (BOOL)application:(UIApplication *)app openURL:(NSURL *)url options:(NSDictionary<NSString *,id> *)options  
    {  
        // 跳转到URL scheme中配置的地址  
        //NSLog(@"跳转到URL scheme中配置的地址-->%@",url);  
        return [WXApi handleOpenURL:url delegate:(id<WXApiDelegate>)self];  
    }  
    //支付成功时调用,回到第三方应用中  
    - (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;  
    }  
    
    • 添加微信的支付结果回调方法:
    //微信回调,有支付结果的时候会回调这个方法  
    - (void)onResp:(BaseResp *)resp  
    {    
        //    支付结果回调  
        if([resp isKindOfClass:[PayResp class]]){  
              
            switch (resp.errCode) {  
                case WXSuccess:{  
                      
                    //支付返回结果,实际支付结果需要去自己的服务器端查询  
                    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;  
                }  
            }  
        }  
    }  
    

    这里微信的支付结果需要向自己的服务器查询,成功后通过发送通知的方式告诉吊起微信的控制器...

    • 吊起微信

    吊起微信所需的参数配置可参考官方提供的一个连接:
    http://wxpay.weixin.qq.com/pub_v2/app/app_pay.php,用于调试支付接口的参数设置;
    这里所获取到的参数可以全部由服务器提供,即在服务器返回prepayId的时候,一并将吊起微信所需的参数返回:

    [Networking getWithUrl:@"http://wxpay.weixin.qq.com/pub_v2/app/app_pay.php" params:nil success:^(id response) {  
            NSLog(@"%@",response);  
              
            //配置调起微信支付所需要的参数  
              
            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"];  
              
            //调起微信支付  
            if ([WXApi sendReq:req]) {  
                NSLog(@"吊起成功");  
            }  
      
              
        } fail:^(NSError *error) {  
            NSLog(@"%@",error);  
        }];  
    
    • 注册通知

    在吊起微信的地方注册通知(用于接收支付结果)的时候,最好先判断一下用户是否安装了微信:

    //判断是否安装微信   
    if([WXApi isWXAppInstalled]) {  
            // 监听一个通知  
            [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(getOrderPayResult:) name:@"ORDER_PAY_NOTIFICATION" object:nil];  
      }  
    

    然后实现通知方法,在这里获取支付结果,处理相关逻辑:

    #pragma mark - 收到支付成功的消息后作相应的处理  
    - (void)getOrderPayResult:(NSNotification *)notification  
    {  
        if ([notification.object isEqualToString:@"success"]) {  
            NSLog(@"支付成功");  
        } else {  
            NSLog(@"支付失败");  
        }  
      
    }  
    

    到此,一个完整的支付流程就完成了...

    附加

    如果,吊起微信后,页面只显示一个确定按钮,如图所示:

    吊起微信异常
    引起这个问题的主要原因就是吊起微信时,参数设置的不正确,而且多半是因为签名的问题,就是参数中的sign值的问题,在后台向微信后台请求账单的时候有过一次签名,而客户端吊起支付的签名和那个签名是不同的,多半是后台直接把那个签名发送给客户端,并没有进行二次签名,可以和后台协商,再签一次,因为两次签名的规则都一样,没必要再让客户端写一遍签名规则了.
    当然,客户端签名也不是不可以,Demo中也有二次签名的方法,需要自己去配置参数:
    //有的服务器没有对sign字段进行二次签名,需要客户端进行,下面这些是对吊起支付时的sign字段进行二次签名的,这些操作可以和服务器协商全让服务器做了,因为签名算法都是一样的,后台已经进行了第一次的签名,第二次只是多了prePayid,算法都是一样的没必要客户端再写一次算法
    
    //注意:下面的方法不能直接使用,这里只是给出了算法和参数配置,相应的填充数据就行
    //创建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

    (完)

    相关文章

      网友评论

        本文标题:[iOS]微信支付接入详解

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