zoukankan      html  css  js  c++  java
  • iOS 微信支付,显示一个确定按钮的坑

    这两天在项目里面接入了微信支付,刚开始在项目里面引入了官方提供的sdk,这是下载地址https://pay.weixin.qq.com/wiki/doc/api/app/app.php?chapter=11_1,填入了在微信开发平台申请的,应用的APP_ID APP_SECRET已经PARTNER_ID,自己生成一个订单,发现可以跳转到微信进行支付,但是和后台调试的时候,拿到后台给我的订单信息,进入微信,发现只有一个确定按钮,

    IMG_1630.PNG

    当时在网上搜了不少文章,但是没有符合自己情况的,和后台一起按照步奏调试了好几天,好在问题最后解决了,分享一下给遇到同样问题的同学看看,也避免以后自己踩到同样的坑,下面,我尽可能用通俗的语言,说一下我遇到问题的原因和解决方法。
    3,首先看一下在客户端,选择一个要买的商品以后,点击去微信支付,在没有进入微信前经历了什么
    3 .1 下单 这里面呢需要提供三个参数,orderid是要买商品的id,ordertitle是在跳转到微信支付里面,展示给用户的订单标题,orderprice是商品的价钱了,iOS里面对应下订单的方法如下
         - ( NSMutableDictionary *)sendPay_demo:(NSString *)orderid title:(NSString *)ordertitle price:(NSString *)orderprice
    {
    //订单标题,展示给用户
    NSString *order_name    = ordertitle;
    //订单金额,单位(分)
    NSString *order_price   = orderprice;//1分钱测试
    //================================
    //预付单参数订单设置
    //================================
    srand( (unsigned)time(0) );
    NSString *noncestr  = [NSString stringWithFormat:@"%d", rand()];
    NSMutableDictionary *packageParams = [NSMutableDictionary dictionary];
    NSString *ii=@"079d9a74b95f41c58e636e344109ee1f";
    [packageParams setObject: appid             forKey:@"appid"];       //开放平台appid
    [packageParams setObject: mchid             forKey:@"mch_id"];      //商户号
    [packageParams setObject: @"APP-001"        forKey:@"device_info"]; //支付设备号或门店号
    NSLog(@"第一次noncestr %@",noncestr);
    [packageParams setObject: noncestr          forKey:@"nonce_str"];   //随机串
    [packageParams setObject: @"APP"            forKey:@"trade_type"];  //支付类型,固定为APP
    [packageParams setObject: order_name        forKey:@"body"];        //订单描述,展示给用户
    [packageParams setObject: NOTIFY_URL        forKey:@"notify_url"];  //支付结果异步通知
    [packageParams setObject: orderid           forKey:@"out_trade_no"];//商户订单号
    [packageParams setObject: @"196.168.1.1"    forKey:@"spbill_create_ip"];//发器支付的机器ip
    [packageParams setObject: order_price       forKey:@"total_fee"];       //订单金额,单位为分
    
    //获取prepayId(预支付交易会话标识)
    NSString *prePayid;
    prePayid            = [self sendPrepay:packageParams];
    NSLog(@"获取prepayId %@",prePayid);
    
    if ( prePayid != nil) {
        //获取到prepayid后进行第二次签名
        
        NSString    *package, *time_stamp, *nonce_str;
        //设置支付参数
        time_t now;
        time(&now);
        time_stamp  = [NSString stringWithFormat:@"%ld", now];
        nonce_str	= [WXUtil md5:time_stamp];
        //重新按提交格式组包,微信客户端暂只支持package=Sign=WXPay格式,须考虑升级后支持携带package具体参数的情况
        //package       = [NSString stringWithFormat:@"Sign=%@",package];
        package         = @"Sign=WXPay";
        //第二次签名参数列表
        NSMutableDictionary *signParams = [NSMutableDictionary dictionary];
        [signParams setObject: appid        forKey:@"appid"];
        NSLog(@"第二次noncestr %@ %@",nonce_str,time_stamp);
        [signParams setObject: nonce_str    forKey:@"noncestr"];
        [signParams setObject: package      forKey:@"package"];
        [signParams setObject: mchid        forKey:@"partnerid"];
        [signParams setObject: time_stamp   forKey:@"timestamp"];
        [signParams setObject: prePayid     forKey:@"prepayid"];
        //[signParams setObject: @"MD5"       forKey:@"signType"];
        //生成签名
        NSString *sign  = [self createMd5Sign:signParams];
        //添加签名
        [signParams setObject: sign         forKey:@"sign"];
        [debugInfo appendFormat:@"第二步签名成功,sign=%@
    ",sign];
        
        //返回参数列表
        return signParams;
        
    }else{
        [debugInfo appendFormat:@"获取prepayid失败!
    "];
    }
    return nil;
     }
    
    3.2这里面非常重要的一步是提交预支付获取prepayid,我们的错误就出在这里,获取prepayid的方法如下
        //提交预支付
       -(NSString *)sendPrepay:(NSMutableDictionary *)prePayParams
     {
    NSString *prepayid = nil;
    NSLog(@"提交预支付发送的数据%@",prePayParams);
    //获取提交支付
    NSString *send      = [self genPackage:prePayParams];
    NSLog(@"获取提交支付 send %@",send);
    //输出Debug Info
    [debugInfo appendFormat:@"API链接:%@
    ", payUrl];
    [debugInfo appendFormat:@"发送的xml:%@
    ", send];
    //发送请求post xml数据
    NSData *res = [WXUtil httpSend:payUrl method:@"POST" data:send];
    //输出Debug Info
    [debugInfo appendFormat:@"服务器返回:
    %@
    
    ",[[NSString alloc] initWithData:res encoding:NSUTF8StringEncoding]];
    XMLHelper *xml  = [[XMLHelper alloc] init];
    //开始解析
    [xml startParse:res];
    NSMutableDictionary *resParams = [xml getDict];
    NSString *return_code   = [resParams objectForKey:@"return_code"];
    NSString *result_code   = [resParams objectForKey:@"result_code"];
    if ( [return_code isEqualToString:@"SUCCESS"] )
    {
        //生成返回数据的签名
        NSString *sign      = [self createMd5Sign:resParams ];
        NSString *send_sign =[resParams objectForKey:@"sign"] ;
        //验证签名正确性
        if( [sign isEqualToString:send_sign]){
            if( [result_code isEqualToString:@"SUCCESS"]) {
                //验证业务处理状态
                prepayid    = [resParams objectForKey:@"prepay_id"];
                return_code = 0;
                [debugInfo appendFormat:@"获取预支付交易标示成功!
    "];
            }
        }else{
            last_errcode = 1;
            [debugInfo appendFormat:@"gen_sign=%@
       _sign=%@
    ",sign,send_sign];
            [debugInfo appendFormat:@"服务器返回签名验证错误!!!
    "];
        }
    }else{
        last_errcode = 2;
        [debugInfo appendFormat:@"接口返回错误!!!
    "];
    }
    return prepayid;
     }
    
    3.3 这个方法进行了第一次MD5的加密,
     //获取package带参数的签名包
    -(NSString *)genPackage:(NSMutableDictionary*)packageParams
    {
    NSString *sign;
    NSMutableString *reqPars=[NSMutableString string];
    //生成签名
    sign        = [self createMd5Sign:packageParams];
    //生成xml的package
    NSArray *keys = [packageParams allKeys];
    [reqPars appendString:@"<xml>
    "];
    for (NSString *categoryId in keys) {
        [reqPars appendFormat:@"<%@>%@</%@>
    ", categoryId, [packageParams objectForKey:categoryId],categoryId];
    }
    [reqPars appendFormat:@"<sign>%@</sign>
    </xml>", sign];
    
    return [NSString stringWithString:reqPars];
    }
    
    就是通过上面的这个方法,获得了向微信发送的生成订单xml的内容,我们后台刚开始的错误是生成sign签名的时候,他没有找到系统提供给后台的生成签名的方法,就自己写了一个方法进行签名,然后他把所有的参数,包括key进行ASCII码从小到大排序,事实上应该把除key以外的参数进行排序,且参数名要区分大小写,排序后,在得到的字符串后面在追加key.
    在微信签名的文档规则在这里https://pay.weixin.qq.com/wiki/doc/api/app/app.php?chapter=4_3,
    iOS里面签名的方法是下面这个
    -(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字段
    spkey=PARTNER_ID;
    [contentString appendFormat:@"key=%@", spkey];
    NSLog(@"数据排序前 %@ 数据排序后sortedArray %@ 先把参数排序后追加一个key后得到 %@",keys,sortedArray,contentString);
    NSLog(@"添加key字段 %@",spkey);
    //得到MD5 sign签名
    NSString *md5Sign =[WXUtil md5:contentString]; 
    //输出Debug Info
    [debugInfo appendFormat:@"MD5签名字符串:
    %@
    
    ",contentString];
    return md5Sign;
    }
    
    3.4在获得prepayid以后,还要在进行第二次签名 ios里面对应的方法是这个
    if ( prePayid != nil) {
        //获取到prepayid后进行第二次签名
        
        NSString    *package, *time_stamp, *nonce_str;
        //设置支付参数
        time_t now;
        time(&now);
        time_stamp  = [NSString stringWithFormat:@"%ld", now];
        nonce_str	= [WXUtil md5:time_stamp];
        //重新按提交格式组包,微信客户端暂只支持package=Sign=WXPay格式,须考虑升级后支持携带package具体参数的情况
        //package       = [NSString stringWithFormat:@"Sign=%@",package];
        package         = @"Sign=WXPay";
        //第二次签名参数列表
        NSMutableDictionary *signParams = [NSMutableDictionary dictionary];
        [signParams setObject: appid        forKey:@"appid"];
        NSLog(@"第二次noncestr %@ %@",nonce_str,time_stamp);
        [signParams setObject: nonce_str    forKey:@"noncestr"];
        [signParams setObject: package      forKey:@"package"];
        [signParams setObject: mchid        forKey:@"partnerid"];
        [signParams setObject: time_stamp   forKey:@"timestamp"];
        [signParams setObject: prePayid     forKey:@"prepayid"];
        //[signParams setObject: @"MD5"       forKey:@"signType"];
        //生成签名
        NSString *sign  = [self createMd5Sign:signParams];
        //添加签名
        [signParams setObject: sign         forKey:@"sign"];
        [debugInfo appendFormat:@"第二步签名成功,sign=%@
    ",sign];
        //返回参数列表
        return signParams;
    }
    
    3.5 这时候就生成了跳转到微信所需要的signParams ,然后在iOS客户端里面调用下面的方法就可以发起支付了
         NSMutableString *stamp  = [dict objectForKey:@"timestamp"];
        
        //调起微信支付
        PayReq* req             = [[PayReq alloc] init];
        req.openID              = [dict objectForKey:@"appid"];
        req.partnerId           = [dict objectForKey:@"partnerid"];
        req.prepayId            = [dict objectForKey:@"prepayid"];
        req.nonceStr            = [dict objectForKey:@"noncestr"];
        req.timeStamp           = stamp.intValue;
        req.package             = [dict objectForKey:@"package"];
        req.sign                = [dict objectForKey:@"sign"];
        BOOL status = [WXApi sendReq:req];
    
    总结一下,我开始遇到跳转微信只有一个确定按钮,就是因为服务器那边在进行签名的时候,出错了,微信支付流程就服务器在下订单前要进行一次签名,拿到prepayid以后要再进行一次签名,不知道大家遇到的问题是否和我一样,如果不一样,你刚好也解决了,欢迎补充。
  • 相关阅读:
    单例模式学习(一)
    java线程池学习(一)
    redis面试总结(二)
    redis面试总结(一)
    spark 内存溢出处理
    大数据面试总结(一)
    Spark 知识点总结--调优(一)
    组合数据类型
    一些小细节
    文件归档
  • 原文地址:https://www.cnblogs.com/sunkaifeng/p/5655804.html
Copyright © 2011-2022 走看看