集成iOS微信支付时遇到一个坑,记录一下。
由于公司业务需求,需要在现有App中添加微信支付功能。于是就开始集成微信的支付功能,先看了一遍官方的文档。然后当然是看官方提供的Demo工程了,于是就下了官方提供的示例工程:(https://pay.weixin.qq.com/wiki/doc/api/app.php?chapter=11_1下的https://pay.weixin.qq.com/wiki/doc/api/download/SDKSample_ios9_v2.0.2_V3_pay.zip)。下载下来,看了一下,果然简单,发现,支付最终是调用了WXApiRequestHandler中的这个方法
+ (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"];
req.timeStamp = stamp.intValue;
req.package = [dict objectForKey:@"package"];
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 @"服务器返回错误";
}
}
最终也就是把服务器传过来的几个参数赋值,调用微信SDK接口就完事了。
于是就立刻跟服务器端同学按照demo的形式开发接口,调试。开始一切都挺顺利,调用服务器端下单接口,服务器下单,将结果订单号等参数返给客户端,都没问题,可是,最后一步,问题来了。而且是很诡异的问题:能调起微信App,但是,,在微信中却没有出现期待中的支付页面,而只是空白的页面中间有一个确定按钮,其他的啥都没有。点击该按钮,就会调回自己的App,返回错误码-2(用户取消的意思)。
相信很多同学也都遇到过类似的问题,可是我是头一次遇到啊。研究了服务器返回的各字段,该有的都有啊,包括sign字段(这个就是微信的坑所在,坑了我好长时间。。。),而且跟Demo中的测试数据格式都一样啊,没问题啊。于是,想尽各种办法,验证是不是少配置了什么东西,是不是哪里参数类型不对,等等。结果都一一排除,百思不得其解。没办法,去网上搜吧,肯定有人遇到过同样的问题。果然,有同学出现过类似问题,说问题所在是sign值生成的有问题。也就是sign生成的不对呗,可是我这里sign是服务器传过来的,于是就逼着服务器端的同学各种检查嘛。包括检查sign值生成算法,中间经历各种曲折,尝试。
最后,终于发现问题所在。不得不吐槽一下,真他妈坑人!!!
问题是这样的:开始的时候,服务器端的同学生成这个sign是用了好多字段,说白了就是他把下单的所有字段都参与了签名;而客户端所需的签名字段只有appId,partnerId,prepayId,nonceStr,timeStamp,package这些字段(注意这些字段名的大小写,这他妈简直就是坑中坑!!!!我哪来的这些字段名?官方文档上说的:https://pay.weixin.qq.com/wiki/doc/api/app.php?chapter=8_3 步奏3是不是这么写的?)。好么,发现问题了,让服务器端同学改呗,应该没有问题了。可是,还是不对!!!这时候我已经快疯了,把微信支付SDK的开发者问候了个遍,你们能体会我当时的心情吗,这点代码,不到100行,弄了两天了。
发泄过后,平静下来,生活还是要继续(哈哈,有点装了,就是活还是得干啊)。于是又再次拿出官方的Demo研究,这次果然有了新收获。打印了一下Demo中下单接口返回的数据,跟我们服务的返回结果仔细对比一下,终于发现了问题所在啊。Demo中接口(http://wxpay.weixin.qq.com/pub_v2/app/app_pay.php?plat=ios)返回的数据是这样的
{
appid = wxb4ba3c02aa476ea1;
noncestr = b56a56818039abe71c0d287ea67b19ce;
package = "Sign=WXPay";
partnerid = 10000100;
prepayid = wx20151209170117f46f5ed25f0130700650;
sign = 09F74D7C3F1BF3C0C329ED3C3EC2AC17;
timestamp = 1449651677;
}
而我们的是这样的:
{
appId = wx13******4cde9472;
nonceStr = 1005d93c01faf80cbc2225c87314c033;
packageValue = "Sign=WXPay";
partnerId = 12******01;
prepayId = wx20151209132826120c234c850066732593;
sign = F83687699E7293828FE098F6DA2CA1CF;
timeStamp = 1449638906;
};
(忽视那些*,为了安全,我后续故意处理的)
发现没有,参数名大小写! 参数名大小写! 参数名大小写! 这XX就是关键所在啊!!! 于是后面的事大家应该都能想到了:让服务器同学全用小写的参数名,重新生成签名;服务器接口更新,客户端访问,解析,调起微信客户端,微信弹出支付页面,支付,完成。
事件就这么过去了,但是经历了这些坑,我不得不发出一些感慨:原来微信有些时候也是靠不住的;在各种抱怨微信SDK文档写的不专业,Demo给的不靠谱后,静下心来想想,发现其实有些时候自己还是太浮躁了,还是不够细心。于是决定写下这篇日志,以帮助其他同样被坑的同学,也提醒自己,记下曾摔的跟头。 编程路漫漫,吾将上下而求索,骚年继续努力吧!