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

下面所写的微信支付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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,053评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,527评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,779评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,685评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,699评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,609评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,989评论 3 396
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,654评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,890评论 1 298
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,634评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,716评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,394评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,976评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,950评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,191评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,849评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,458评论 2 342

推荐阅读更多精彩内容

  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 171,417评论 25 707
  • 发现 关注 消息 iOS 第三方库、插件、知名博客总结 作者大灰狼的小绵羊哥哥关注 2017.06.26 09:4...
    肇东周阅读 12,016评论 4 62
  • 实现支付宝支付的准备工作: 1.向支付宝签约,成为支付宝的商户 签约完成后,支付宝会提供一些必要的数据给我们 商户...
    Anson杨春安阅读 8,181评论 0 6
  • 对着空调吹, 惬意加迷醉, 不知不觉已入睡, 梦圆与谁?
    鸿怡轩阅读 164评论 1 0
  • 欢迎关注幼儿说,用简书的妈咪,都是有品味的母亲 暑假已经过了大半个月,你家孩子是玩耍放任了大半个月?还是高强度继续...
    幼儿说阅读 353评论 0 0