记录App支付遇到的坑,全面解析
最近公司有个项目,包括App、公众号、pc端、小程序,都涉及到了微信、支付宝支付,在此过程中花了两天时间把踩到的坑记录在此,便于后来人少走弯路。 目前只把微信支付接完了,后续会持续更新其他几端遇到的坑以及解决方案。我用的是 netcore3.1 1:App支付 第一次签名(统一支付,获取预支付id) 注意点 所有必填参数一定要按照文档实例的顺序来,下方为我的参数,一定要注意下方的参数顺序, appid、attach、body(可选)、mch_id、nonce_str、notify_url、out_trade_no、spbill_create_ip、total_fee、trade_type、sign_typeca
参数取值: appid取值:登录open.weixin.qq.com查看,注意与公众号的APPID不同 mch_id取值:https://pay.weixin.qq.com/index.php/extend/employee 进入后,点击左侧的[商户信息],会看到基本账户信息,下方有一个[微信支付商户号],这一步有很多大兄弟取错值。 nonce_str取值:直接使用官方提供的方法即可。 notify_url取值:通知url必须为直接可访问的url,不能携带参数。 out_trade_no、spbill_create_ip取值:问题不大。 total_fee取值:单位为分,一定要注意传入的参数要转化为分 trade_type取值:默认值为APP,因为我的接入方是App,所以此处是固定值,一定要注意大小写 是 APP 不是 App 或 app sign_type取值:文档其实写的很清楚,这个字段为非必填,一旦你不传,默认的加密方式为MD5,但是我从官网下的demo看到里面默认的是HMAC-SHA256,找了2小时才发下这个问题,重点来了:建议sign_type传值为MD5方式,为什么呢?因为在调起App支付页之前还需要二次签名,如果用HMAC-SHA256加密 那边可能会有问题,下面在细说。 sign取值:对上述的参数进行 MD5 或 HMAC-SHA256时,sign不参与签名。 在保证以上参数取值没问题的情况下,来说下加密的问题,下方是从测试工具截图,做一下解释 测试工具地址 https://pay.weixin.qq.com/wiki/doc/api/app/app.php?chapter=20_1 [图片] 红框里面的注意事项解释下:当前商户KEY是否正确,这个KEY的取值地址如下 https://pay.weixin.qq.com/index.php/extend/employee 进入后 点击上方的[账户中心]进入,然后在左侧有个【API安全】,进入后,注意!!找到【API密钥】,不是PAIv3!!!,重置下API密钥,这里有个生效时间15分钟,网上很多帖子说签名失败时,多次重置这个密钥就好了,我没有遇到,保证上面的参数正确就通过了。 网上很多签名失败的解决方案,本人也看了很多,基本上没用得上,比如ip黑白名单,我在本地都跑起来了,也没配置黑白名单啥的。 以下是完整的第一次签名: inputObj.SetValue("appid", AppId);//APPID
inputObj.SetValue("attach", order_body);//附加数据 可选
inputObj.SetValue("body", order_body);//订单body
inputObj.SetValue("mch_id", Mch_id);//商户号
inputObj.SetValue("nonce_str", RandomGeneratHepler.GenerateNonceStr());//随机字符串
inputObj.SetValue("notify_url", Notfy_url);//回调地址
inputObj.SetValue("out_trade_no", order_code);//订单号
inputObj.SetValue("spbill_create_ip", Spbill_ip);//终端ip
inputObj.SetValue("total_fee", total_amount);// 订单总金额 测试时,金额不能是0.01
inputObj.SetValue("trade_type", "APP");//支付方式
//inputObj.SetValue("sign_type", "HMAC-SHA256");//签名类型 可选 不选默认为MD5加密
inputObj.SetValue("sign", inputObj.MakeSign().ToUpper()); //签名加密
string xml = inputObj.ToXml();
string response = HttpService.Post(xml, Unifiedorder_url, false, timeOut);
总结下: 1:参数顺序和参数个数是否一致,包括大小写、下划线等 2:各个参数的取值问题,比如商户号、加密的key等 3:一定要有耐心!!!不要急躁,急躁解决不了任何问题,反而弄的自己没状态没心思仔细看文档。 以上是第一次签名(大概花了一下午),签名成功后会返回以下几个参数,具体如下: mch_id(商户号)、nonce_str(随机数)、appid、prepay_id(预支付id)、sign等,这些参数在App调起微信支付时进行二次签名。 第二次签名,App端调起微信支付页 由后端进行二次签名后,传给App端,App端拿到参数后发起调起。 二次签名参数: appid、partnerid(商户号)、prepayid(预支付id)、noncestr(随机数)、timestamp、package 注意点: (1)此次签名要与第一次签名完全一致,这块容易出错的地方为:如果在第一次签名时,加入sign_type,那么此处也要加入,且加密方式要与第一次加密一致。上面说到为什么要用MD5加密,是因为在我们使用HMAC-SHA256加密时,你在测试工具验证通过了,然后app端调起时一直报签名失败,明明测试工具都通过了呀,这是为啥呢?那是因为,客户端的skd版本不同,对应的加密方式默认为了MD5!!!所以,在第二次加密用HMAC-SHA256时,得到的参数返回给客户端时,一直提示签名错误!!(本人在这边卡了2个小时,很郁闷)。 (2)另外在android端调起时,如果发下ios调起了,android没调起,问题可能出现在微信的缓存中,建议用另外一台手机测试,或者把微信卸载后在安装试一下,这个问题是刚才android反馈的,可能是微信的缓存导致的。 (3)第二次签名相对于第一次要简单些,网上很多例子有误导的,比如下图标红的地方,如果你按照这个参数去签名的话,恭喜你,100%不成功!因为下方的 [图片] 参数有大写的!!!,你说坑不坑? (4)参数顺序建议按照下方实例来,我没有侧过第二次签名参数顺序不一致是否会导致签名失败。 完整的二次签名参数如下: inputObj.SetValue("appid", entity.appid);//APPID
inputObj.SetValue("partnerid", Mch_id);//商户号
inputObj.SetValue("prepayid", entity.prepay_id);//预支付id
inputObj.SetValue("noncestr", entity.nonce_str);//随机数
inputObj.SetValue("timestamp", timeStamp);//时间戳
inputObj.SetValue("package", entity.package);//扩展字段 固定值为 Sign=WXPay
//inputObj.SetValue("sign_type", "HMAC-SHA256");//签名类型 可选 不选默认为MD5加密
inputObj.SetValue("sign", inputObj.MakeSign().ToUpper());//调用签名方法,返回后需转为大写
entity为调统一支付接口返回的内容,注意这里面有个timestamp,这个是10位数的时间戳,我的取值如下 var currentDte = DateTime.Now;
DateTime dateStart = new DateTime(1970, 1, 1, 8, 0, 0);
int timeStamp = Convert.ToInt32((currentDte - dateStart).TotalSeconds);
另外 下方为两次签名的方法,供参考 /**
* @生成签名,详见签名生成算法
* @return 签名, sign字段不参加签名
*/
public string MakeSign(string signType)
{
var Api_key = Appsettings.app("WechatPay", "Pay", "Api_key");//此处的key就是上面说的API密钥,获取途径见上方说明
string str = ToUrl();//转url格式
str += "&key=" + Api_key; //在string后加入API KEY
if (signType == SIGN_TYPE_MD5)
{
var md5 = MD5.Create();
var bs = md5.ComputeHash(Encoding.UTF8.GetBytes(str));
var sb = new StringBuilder();
foreach (byte b in bs)
{
sb.Append(b.ToString("x2"));
}
return sb.ToString().ToUpper();//所有字符转为大写
}
else if (signType == SIGN_TYPE_HMAC_SHA256)
{
return CalcHMACSHA256Hash(str, Api_key);// WechatPayConfig.GetConfig().GetKey());
}
else
{
throw new WxPayException("sign_type 不合法");
}
}
/**
* @生成签名,详见签名生成算法
* @return 签名, sign字段不参加签名 SHA256
*/
public string MakeSign()
{
return MakeSign(SIGN_TYPE_MD5);//全部默认MD5加密 因为客户端sdk版本不同 加密方式默认的是MD5
}
最后唠叨几句,多点耐心,别急躁,自行看文档,祝君好运,希望对各位大兄弟有所帮助。本文是在中午吃饭的时候总结的,排版有点乱,望见谅。
如大家在对接支付遇到其他问题,也可以在下方留言,顺便告知如何解决的,为他人做些贡献。