支付iOS开发

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

2016-06-10  本文已影响231人  可能是含钙最高的钙钙

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

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

Paste_Image.png

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

Paste_Image.png 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
//导入头文件:
#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;
}
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];
官方给的解释
签名生成的通用步骤如下:
第一步,设所有发送或者接收到的数据为集合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"


  // 获取当前时间
    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

上一篇 下一篇

猜你喜欢

热点阅读