美文网首页支付iOS开发
浅谈三方支付之微信支付集成

浅谈三方支付之微信支付集成

作者: 可能是含钙最高的钙钙 | 来源:发表于2016-06-10 03:42 被阅读231次

    下面所写的微信支付SDK和之前写的支付宝的SDK支付,都是我在项目集成时的步骤,在写文章时,我又创建了一个小的demo进行截图说明的,有什么不恰当的地方,还请各位大神指出,共同学习进步,该文章只是作为集成参考,每个公司的需求不一样,服务器返回的数据不一样,具体还是请参考官方的文档.

    微信支付的前期工作和支付宝差不多,注册认证,填写申请资料,这些一般是产品做好的工作.我们关心的就是appID,apiKey和商户账号了. SDK的下载地址:https://pay.weixin.qq.com/wiki/doc/api/app/app.php?chapter=11_1

    • 下载了SDK后开始一步步集成微信支付了.集成SDK所需要的.
    Paste_Image.png

    添加到项目中后直接运行下,发现没有报错.官方文档里也没看见让添加依赖文件,我的项目中因为集成了别的SDK,添加的依赖文件挺多的,也没有报错.但是在我把工具类写好后编译下就开始狂报错了,
    一般需要的依赖库,请根据具体情况添加
    SystemConfiguration.framework
    libz.tbd
    libsqlite3.0.tbd
    CoreTelephony.framework
    libc++.tbd
    添加依赖库的截图

    Paste_Image.png
    • 完事后可以设置项目的APPID,请看截图
    Paste_Image.png
    • 设置白名单
    // 把info.plist文件 source code打开,把下面这段话直接复制进去就可以了
    // 说明, 如果在项目中没有做是否能打开微信的判断,不加白名单也是可以的,但是这样不够严谨.
    <key>LSApplicationQueriesSchemes</key>
     <array>
      <string>weixin</string>
      <string>wechat</string>
     </array>
    
    // 不加白名单做微信是否能够打开的判断在控制台会报警告
     -canOpenURL: failed for URL: "weixin://app/wxfc88431fb68d2797/" - error: "This app is not allowed to query for scheme weixin"
    
    
    Paste_Image.png
    • 基本工作已经准备完毕,现在开始集成代码.
    • 1.在AppDelegate里注册
    //导入头文件:
    #import "WXApi.h"
    
    - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    // 注册
     [WXApi registerApp:@"你们公司的appID"];
    
        return YES;
    }
    
    // 回调处理
    
    - (BOOL)application:(UIApplication *)application handleOpenURL:(NSURL *)url{
    
    // 此处设置了代理,需要遵守协议: WXApiDelegate,并实现 - (void)onResp:(BaseResp *)resp; 方法
        return [WXApi handleOpenURL:url delegate:self];
    }
    
    - (BOOL)application:(UIApplication *)application openURL:(NSURL *)url sourceApplication:(NSString *)sourceApplication annotation:(id)annotation{
       // 此处设置了代理,需要遵守协议: WXApiDelegate 并实现 - (void)onResp:(BaseResp *)resp; 方法
      [WXApi handleOpenURL:url delegate:self];
        
        return YES;
    }
    
    • 2.支付代码:
      每个公司服务器返回的数据不一样,可能在集成的时候回有差异,先看下官方demo中的代码.官方代码中集成的东西比较多,查找起来不是特别的方便,请看截图
    Paste_Image.png

    下面是官方demo中的代码,里面会用到时间戳,随机字符串和加密后的签名,这些东西有些公司是服务器直接做好返回给你的,有些公司不是,比如,我们公司

    
    + (NSString *)jumpToBizPay {
    
        //============================================================
        // V3&V4支付流程实现
        // 注意:参数配置请查看服务器端Demo
        // 更新时间:2015年11月20日
        //============================================================
        NSString *urlString   = @"http://wxpay.weixin.qq.com/pub_v2/app/app_pay.php?plat=ios";
            //解析服务端返回json数据
            NSError *error;
            //加载一个NSURL对象
            NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:urlString]];
            //将请求的url数据放到NSData对象中
            NSData *response = [NSURLConnection sendSynchronousRequest:request returningResponse:nil error:nil];
        
        
            if ( response != nil) {
                NSMutableDictionary *dict = NULL;
                //IOS5自带解析类NSJSONSerialization从response中解析出数据放到字典中
                dict = [NSJSONSerialization JSONObjectWithData:response options:NSJSONReadingMutableLeaves error:&error];
                
                NSLog(@"url:%@",urlString);
                if(dict != nil){
                    NSMutableString *retcode = [dict objectForKey:@"retcode"];
                    if (retcode.intValue == 0){
                        NSMutableString *stamp  = [dict objectForKey:@"timestamp"];
                        
                        //调起微信支付
                        PayReq* req             = [[[PayReq alloc] init]autorelease];
                        
                        req.partnerId           = [dict objectForKey:@"partnerid"];   // 商户账生成号
                        req.prepayId            = [dict objectForKey:@"prepayid"];   // 支付订单,有公司服务器生成
                        req.nonceStr            = [dict objectForKey:@"noncestr"]; // 32位的随机字符创
                        req.timeStamp           = stamp.intValue; // 时间戳
                        req.package             = [dict objectForKey:@"package"];  //  固定写法 @"Sign=WXPay"
                        req.sign                = [dict objectForKey:@"sign"];  // 签名
                        
                        [WXApi sendReq:req];
                        //日志输出
                        NSLog(@"appid=%@\npartid=%@\nprepayid=%@\nnoncestr=%@\ntimestamp=%ld\npackage=%@\nsign=%@",[dict objectForKey:@"appid"],req.partnerId,req.prepayId,req.nonceStr,(long)req.timeStamp,req.package,req.sign );
                        return @"";
                    }else{
                        return [dict objectForKey:@"retmsg"];
                    }
                }else{
                    return @"服务器返回错误,未获取到json对象";
                }
            }else{
                return @"服务器返回错误";
            }
    }
    

    代理 WXApiDelegate

    #pragma mark - WXApiDelegate
    
    - (void)onResp:(BaseResp *)resp{
        
        // 支付成功后,返回你的app会调用该方法,可以在这里做相应的处理
        
        if([resp isKindOfClass:[PayResp class]]){
            //支付返回结果,实际支付结果需要去微信服务器端查询
            NSString *strMsg = nil;
            
            switch (resp.errCode) {
                case WXSuccess:
                    strMsg = @"支付结果:成功!";
                    NSLog(@"支付成功-PaySuccess,retcode = %d", resp.errCode);
                    break;
                    
                default:
                    strMsg = [NSString stringWithFormat:@"支付结果:失败!retcode = %d, retstr = %@", resp.errCode,resp.errStr];
                    NSLog(@"错误,retcode = %d, retstr = %@", resp.errCode,resp.errStr);
                    break;
            }
        }
    }
    

    以上是官方demo,如果你们公司的服务器把什么都做好了,照着实现就可以了,但是,但是,为什么但是呢?如果后台有些时间没做,就麻烦一点了.比如,时间戳,随机字符和签名加密都需要自己做的时候,加密用的是md5加密

    // md5加密
    导入头文件:#import <CommonCrypto/CommonCrypto.h>
    
    // 加密代码
    
    /**
     *  md5加密
     *
     *  @param input 需要加密的字符串
     *
     *  @return 加密后生成的字符串
     */
    + (NSString *) md5: (NSString *) input {
        
        const char *cStr = [input UTF8String];
        unsigned char digest[CC_MD5_DIGEST_LENGTH];
        CC_MD5( cStr, (CC_LONG)strlen(cStr), digest );
        NSMutableString *output = [NSMutableString stringWithCapacity:CC_MD5_DIGEST_LENGTH * 2];
        for(int i = 0; i < CC_MD5_DIGEST_LENGTH; i++)
            [output appendFormat:@"%02x", digest[i]];
    
        return  [NSString stringWithString: output];
    }
    
    
    • 先看下随机数,官方给的随机数算法
    // 官方算法解释
    微信支付API接口协议中包含字段nonce_str,主要保证签名不可预测。我们推荐生成随机数算法如下:调用随机数函数生成,将得到的值转换为字符串。
    
    // 具体在做的时候,我查了写资料,有些大神是用时间生成的,也有用uid(有的公司是token)生成的,我用的是第一种
    
    /**
     *  随机字符串
     *
     *  @return 生成一个32位的字母大小的随机字符串
     */
    + (NSString *)randomNum{
     
        NSString *timeStamp = [NSString stringWithFormat:@"%f", [[NSDate date] timeIntervalSince1970]];
    // 调用md5加密算法, WXPayTools是我创建的工具类名
        timeStamp = [WXPayTools md5:timeStamp];
        
        return [timeStamp uppercaseString];
    }
    
    • 时间戳
        
        // 获取当前时间,这个是C语言获取的
        time_t now;
        time(&now);
        // 时间戳
        NSString *timestamp = [NSString stringWithFormat:@"%ld", now];
    
       // OC的获取时间戳的方法
        NSTimeInterval interval = [[NSDate date] timeIntervalSince1970];
    
    • 签名的加密,简单来说就是把需要传的参数按照字典的格式进行排序,然后拼接,再然后添加key字典,就是添加到@"key=排序后拼接的字符串".排序的时候尽量不要手动排序,避免出错.
    官方给的解释
    签名生成的通用步骤如下:
    第一步,设所有发送或者接收到的数据为集合M,将集合M内非空参数值的参数按照参数名ASCII码从小到大排序(字典序),使用URL键值对的格式(即key1=value1&key2=value2…)拼接成字符串stringA。
    特别注意以下重要规则:
    ◆ 参数名ASCII码从小到大排序(字典序);
    ◆ 如果参数的值为空不参与签名;
    ◆ 参数名区分大小写;
    ◆ 验证调用返回或微信主动通知签名时,传送的sign参数不参与签名,将生成的签名与该sign值作校验。
    ◆ 微信接口可能增加字段,验证签名时必须支持增加的扩展字段
    第二步,在stringA最后拼接上key得到stringSignTemp字符串,并对stringSignTemp进行MD5运算,再将得到的字符串所有字符转换为大写,得到sign值signValue。
    key设置路径:微信商户平台(pay.weixin.qq.com)-->账户设置-->API安全-->密钥设置
    举例:
    假设传送的参数如下:
    
    appid:  wxd930ea5d5a258f4f
    mch_id: 10000100
    device_info:    1000
    body:   test
    nonce_str:  ibuaiVcKdpRxkhJA
    第一步:对参数按照key=value的格式,并按照参数名ASCII字典序排序如下:
    stringA="appid=wxd930ea5d5a258f4f&body=test&device_info=1000&mch_id=10000100&nonce_str=ibuaiVcKdpRxkhJA";
    第二步:拼接API密钥:
    
    stringSignTemp="stringA&key=192006250b4c09247ec02edce69f6a2d"
    sign=MD5(stringSignTemp).toUpperCase()="9A0A8659F005D6984697E2CA0A9CF3B7"
    
    
    • 下面是我demo中对签名排序后加密的做法
    
      // 获取当前时间
        time_t now;
        time(&now);
        // 时间戳
        NSString *timestamp = [NSString stringWithFormat:@"%ld", now];
        // 随机字符串
        NSString *nonceStr = [[WXPayTools md5:timestamp] uppercaseString];
        
        NSDictionary *para = @{
                               @"appid" : appID,
                               @"noncestr" : nonceStr,
                               @"package" : @"Sign=WXPay",
                               @"partnerid" : WXpartnerId,
                               @"prepayid" : prepayid,
                               @"timestamp" : timestamp
                               };
        
        NSMutableString *contentString = [NSMutableString string];
        NSArray *keys = [para allKeys];
        // 按字母排序
        NSArray *sortedArry = [keys sortedArrayUsingComparator:^NSComparisonResult(id  _Nonnull obj1, id  _Nonnull obj2) {
            return [obj1 compare:obj2 options:NSNumericSearch];
        }];
        
        // 拼接字符串
        for (NSString *categoryId  in sortedArry) {
            if (![[para objectForKey:categoryId] isEqualToString:@""] && ![categoryId isEqualToString:@"sign"] && ![categoryId isEqualToString:@"key"]) {
                [contentString appendFormat:@"%@=%@&", categoryId, para[categoryId]];
            }
        }
        // 添加key字段
        [contentString appendFormat:@"key=%@", apiKey];
        // 加密生成字符串
    // WXPayTools 是我创建的工具类名
        NSString *sign = [WXPayTools md5:contentString];
    
    • 上面的工作都做完后,就可以进行支付宝支付了.支付完成后的操作可以在代理方法里面做
        PayReq *req = [[PayReq alloc] init];
        
        req.partnerId = WXpartnerId;
        req.prepayId = prepayid;
        req.nonceStr = nonceStr;
        req.timeStamp =  timestamp.intValue;
        req.package = @"Sign=WXPay";
        
        req.sign = sign;  // 签名
        
        [WXApi sendReq:req];
    

    以上是我在做微信支付时用到的一些,上述内容都是本人新建一个demo后全部手动敲出来的,是想方便和大家交流学习,大神们轻喷,demo我上传到了github上了,只是一个样板.github的链接:https://github.com/jiuzhishengaigai/WXChatPay

    相关文章

      网友评论

      • aedbe20bb605:可以 一会玩一下
      • Misaki_yuyi:不知道楼主 有没有遇到这样的问题 唤起支付后 微信显示“支付验证签名失败”。 URL Schemes 我设置的是AppID 但是我用微信验证签名工具 去验证sign 没有错误 。不知道是为什么
        可能是含钙最高的钙钙:@Misaki_yuyi 签名是你自己生成的还是后台生成的?

      本文标题:浅谈三方支付之微信支付集成

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