微信、支付宝支付技术iOS 开发

[iOS]微信支付接入详解

2016-05-18  本文已影响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"  
- (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

(完)

上一篇下一篇

猜你喜欢

热点阅读