iOS微信支付的那些坑

作者: Lonely__M | 来源:发表于2016-02-01 13:48 被阅读13712次

    前言

    一直以为支付宝的文档写的已经够烂了,后来我才发现我太年轻了,那是因为我还没有遇到微信的文档,简直坑爹。。。<心中一万只草泥马...>

    1. 支付业务流程
    商户系统和微信支付系统主要交互说明:
    步骤1:用户在商户APP中选择商品,提交订单,选择微信支付。
    步骤2:商户后台收到用户支付单,调用微信支付统一下单接口。参见【[统一下单API](https://pay.weixin.qq.com/wiki/doc/api/app.php?chapter=9_1)】。
    步骤3:统一下单接口返回正常的prepay_id,再按签名规范重新生成签名后,将数据传输给APP。参与签名的字段名为appId,partnerId,prepayId,nonceStr,timeStamp,package。注意:package的值格式为Sign=WXPay
    步骤4:商户APP调起微信支付。api参见本章节【[app端开发步骤说明](https://pay.weixin.qq.com/wiki/doc/api/app.php?chapter=8_5)】
    步骤5:商户后台接收支付通知。api参见【[支付结果通知API](https://pay.weixin.qq.com/wiki/doc/api/app.php?chapter=9_7)】
    步骤6:商户后台查询支付结果。,api参见【[查询订单API](https://pay.weixin.qq.com/wiki/doc/api/app.php?chapter=9_2)】
    
    Paste_Image.png

    注意

    1.微信支付现在已经很便捷了,提供了统一下单接口,自己的后台服务器调用该接口,获取prepayId
    2.最重要的一步,也是网上现在坑最多的一步,调用统一下单接口后,微信返回格式如下,而这么多数据中,真正对我们有用的只有 预支付订单号prepayId,微信返回的sign没有任何用,我们得根据签名规范重新生成签名(关于这点,上面的流程交互中就有提到),然后将重新生成的签名返回给APP端,用于APP端调用微信支付。(当然重新生成签名的步骤也可以放在APP端来做,不过为了安全考虑,建议放在服务端处理,下面也会给出APP端签名的代码)

    调用统一下单接口 微信返回数据 一定要重新生成签名

    Tip

    很多人都遇到这样的问题,参数都处理好调用微信支付,结果进入微信界面后发现只有一个确定按钮,返回后错误码为 -2,造成这个原因的就是因为参数不对,而且大多是都是因为签名 sign 不对,没有进行二次签名,而直接拿微信返回的sign当作参数 , 所以调用微信支付接口时,一定要用 重新签名的sign!!!!

    • APP端调用支付时,sign不要用调用统一下单接口返回的sign,而要自己重新生成,生成规则见微信提供的签名生成算法
      Paste_Image.png

    相关代码如下:

    • APP端打开微信微信支付代码
    #pragma mark -  微信支付
    - (void)wechatPay:(WechatOrderModel *)model
    {
    
        //调起微信支付 model 为自己服务器返回的参数
        PayReq *req = [[PayReq alloc] init];
        req.partnerId = model.partnerid;
        req.prepayId = model.prepayid;
        req.nonceStr = model.noncestr;
        req.timeStamp = model.timestamp.intValue;
        req.package = model.package;
        req.sign = model.sign;//sign 为自己服务器获取到预支付订单号prepayId后,重新生成的签名,当然本地也可以生成签名,签名代码如下
        [WXApi sendReq:req];
        
        //日志输出
        NSLog(@"微信支付请求参数 =====  appid=%@\npartid=%@\nprepayid=%@\nnoncestr=%@\ntimestamp=%ld\npackage=%@\nsign=%@",model.appid,req.partnerId,req.prepayId,req.nonceStr,(long)req.timeStamp,req.package,req.sign);
    }
    
    • APP端重新生成签名(建议放在服务端处理)
    
    #pragma mark -  微信支付本地签名
    //创建发起支付时的sign签名
    -(NSString *)createMD5SingForPayWithAppID:(NSString *)appid_key partnerid:(NSString *)partnerid_key prepayid:(NSString *)prepayid_key package:(NSString *)package_key noncestr:(NSString *)noncestr_key timestamp:(UInt32)timestamp_key{
        NSMutableDictionary *signParams = [NSMutableDictionary dictionary];
        [signParams setObject:appid_key forKey:@"appid"];//微信appid 例如wxfb132134e5342
        [signParams setObject:noncestr_key forKey:@"noncestr"];//随机字符串
        [signParams setObject:package_key forKey:@"package"];//扩展字段  参数为 Sign=WXPay
        [signParams setObject:partnerid_key forKey:@"partnerid"];//商户账号
        [signParams setObject:prepayid_key forKey:@"prepayid"];//此处为统一下单接口返回的预支付订单号
        [signParams setObject:[NSString stringWithFormat:@"%u",timestamp_key] forKey:@"timestamp"];//时间戳
        
        NSMutableString *contentString  =[NSMutableString string];
        NSArray *keys = [signParams allKeys];
        //按字母顺序排序
        NSArray *sortedArray = [keys sortedArrayUsingComparator:^NSComparisonResult(id obj1, id obj2) {
            return [obj1 compare:obj2 options:NSNumericSearch];
        }];
        //拼接字符串
        for (NSString *categoryId in sortedArray) {
            if (   ![[signParams objectForKey:categoryId] isEqualToString:@""]
                && ![[signParams objectForKey:categoryId] isEqualToString:@"sign"]
                && ![[signParams objectForKey:categoryId] isEqualToString:@"key"]
                )
            {
                [contentString appendFormat:@"%@=%@&", categoryId, [signParams objectForKey:categoryId]];
            }
        }
        //添加商户密钥key字段  API 密钥
        [contentString appendFormat:@"key=%@", @"商户密钥"];
        NSString *result = [contentString md5String];//md5加密
        return result;
    }
    
    • MD5 加密方法
    
    /**
     *  MD5 加密
     *
     *  @return 加密后字符串
     */
    - (NSString *)md5String
    {
        if(self == nil || [self length] == 0) return nil;
        unsigned char digest[CC_MD5_DIGEST_LENGTH], i;
        CC_MD5([self UTF8String], (int)[self lengthOfBytesUsingEncoding:NSUTF8StringEncoding], digest);
        NSMutableString *ms = [NSMutableString string];
        for(i=0;i<CC_MD5_DIGEST_LENGTH;i++)
        {
            [ms appendFormat: @"%02x", (int)(digest[i])];
        }
        return [ms copy];
    }
    

    至此微信集成结束,坑的地方就是 app端调用微信支付时的签名参数sign一定要重新生成,不要使用统一下单接口返回的sign,切记!!!!

    记录点滴,与君共勉。

    相关文章

      网友评论

      • b64aae7304a8:谢谢,他又没有说app拉起需要哪些参数签名,真的辣鸡
      • 花前月下:感谢楼主的文章,就是碰到了签名的问题。楼主讲解的很详细。
      • Python数据分析实战:求一个能支付的demo
      • d07b20e1df83:最后需要把字符串做大写处理
      • 顺其自然JX:大神,我想问一下,客户端向服务器发送商品数据,和服务器签名后把信息返回给客服的是点击微信支付完成的吗?
      • 栀厄:博主,我是小白,能说说,放在服务端处理是什么意思吗“?做微信支付只要获取返回回来的prepayid就可以了吗?SIgn就是model付给创建的对象的属性就可以了吗?抱歉问题有点多,希望您能回答@!
      • 阳光的大男孩儿:我想通过app端直接获取prepayid 行不行呢?
        Lonely__M:@依然那么爱你forever 可以,调用微信接口就好了
      • 阳光的大男孩儿:博主你知道如何用客户端获取prepayid吗?那个同意下单的api,数据是xml类型的,我不知道该怎么办了?我就想要拿到prepayid。
      • 系统盘:同求啊,楼主,集成微信支付,官网说的根本搞不定,有没有demo借鉴一下,现在的集成好像和之前不一样
      • 买了否冷_:求一个demo :smile:
      • 葉糖糖:良心前人呀
      • StoneLeon:前人入坑,后人填土. :+1:

      本文标题:iOS微信支付的那些坑

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