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=%@\n",sign];
    
    //返回参数列表
    return signParams;
    
}else{
    [debugInfo appendFormat:@"获取prepayid失败!\n"];
}
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链接:%@\n", payUrl];
[debugInfo appendFormat:@"发送的xml:%@\n", send];
//发送请求post xml数据
NSData *res = [WXUtil httpSend:payUrl method:@"POST" data:send];
//输出Debug Info
[debugInfo appendFormat:@"服务器返回:\n%@\n\n",[[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:@"获取预支付交易标示成功!\n"];
        }
    }else{
        last_errcode = 1;
        [debugInfo appendFormat:@"gen_sign=%@\n   _sign=%@\n",sign,send_sign];
        [debugInfo appendFormat:@"服务器返回签名验证错误!!!\n"];
    }
}else{
    last_errcode = 2;
    [debugInfo appendFormat:@"接口返回错误!!!\n"];
}
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>\n"];
for (NSString *categoryId in keys) {
    [reqPars appendFormat:@"<%@>%@</%@>\n", categoryId, [packageParams objectForKey:categoryId],categoryId];
}
[reqPars appendFormat:@"<sign>%@</sign>\n</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签名字符串:\n%@\n\n",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=%@\n",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以后要再进行一次签名,不知道大家遇到的问题是否和我一样,如果不一样,你刚好也解决了,欢迎补充。
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,847评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,208评论 1 292
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,587评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,942评论 0 205
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,332评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,587评论 1 218
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,853评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,568评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,273评论 1 242
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,542评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,033评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,373评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,031评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,073评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,830评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,628评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,537评论 2 269

推荐阅读更多精彩内容