开发微信JSAPI支付过程中,遇到一些问题小结
前言,可能对于小白或者第一次接触微信支付的JY有帮助。以下内容中遇到的问题都是以v2版本的基础上开发的。
V2和V3版本
微信支付整体上是分为V2和V3版本的,看业务需求,自主选择合适的。大致上V2是传输的数据格式是XML的,V3传输的数据格式JSON的。V3版本再配置时,必须带上商户证书。具体操作步骤可以查看 什么是商户API证书?如何获取商户API证书?
统一下单接口V2: api.mch.weixin.qq.com/pay/unified…
统一下单接口V3: api.mch.weixin.qq.com/v3/pay/tran…
具体V2与V3的其他区别 ,可以查看 V2与V3的不同
无法将输入源“/body/xml/total_fee”映射到目标字段“标价金额”中?
出现这个问题的原因是 total_fee类型是int,单位为分,检查一下你的传参。
意思就是你传的数,不能是小数,是整数,这个整数的单位就是分,比如,你想支出0,01元,参数就是1。
微信支付提示 调用支付JSAPI缺少参数:appId,timeStamp
可以从三方面去排查一下:
1:配置授权目录了
2:前端同事确认json不是字符串转成object了
3:appId也传了的
timeStamp 这个缺失的参数,只在苹果手机上出现,安卓手机上没有出现,我要着重说一下。
$.ajax({
url:"{:url('wap/order/commit')}",
data:{ // 参数根据接口需要自行替换
goods_id:goods_id,
products_id:products_id,
goods_num:goods_num,
name:name,
phone:phone,
message:message
},
type:'post',
success:function (res){
if (res.code===0){
WeixinJSBridge.invoke(
'getBrandWCPayRequest', {
"appId":res.data.appId, //公众号ID
"timeStamp":(res.data.timeStamp).toString(), //时间戳,这里时间戳转化为字符串格式,不然IOS调用会提示缺少参数timeStamp,而且此处的时间戳必须和生成微信预支付订单使用的时间戳保持一致
"nonceStr":res.data.nonceStr, //随机串,随机串必须和生成微信预支付订单使用的随机串保持一致
"package":res.data.package, // 生成微信预支付订单返回的prepay_id,格式为:'prepay_id=微信返回的prepay_id'
"signType":res.data.signType, //微信签名方式:默认MD5
"paySign":res.data.sign //微信签名
},
function(wx_res){
if(wx_res.err_msg == "get_brand_wcpay_request:ok" ){
// 循环查询订单是否支付成功
timer = self.setInterval("checkOrderStatus("+ res.data.order_number +")",1000)
}else{
layer.open({content: '支付失败',skin: 'msg',time: 2}); //提示内容,根据自己项目自行替换功用提示框函数
}
});
}else{
layer.open({content: '下单失败',skin: 'msg',time: 2}); //提示内容,根据自己项目自行替换功用提示框函数
}
}
})
微信支付接口报【签名错误】
我是在二次签名(客户端签名错误)的过程中,参数有误,导致的错误,查看全网最全v2接口签名报错排查指引!!!!这篇文章,才有思路排查出错误。第一次签名(服务端签名正常通过),在第一次签名校验工具验证通过的情况下。我还以为第二次签名也是正常的,就没有往签名工具(网上找的)上想,其实是错的,坑死我了。两次签名最好拿校验工具都验证一下,而且签名的时候,参数大小写看好,顺序也要注意,以及再拼接上key。 微信签名校验工具
签名工具类:
/**
* 生成微信支付签名
*
* @param params 参数集合,需要包含 appid、mch_id、nonce_str、sign_type 和 key 等参数
* @param signType 签名类型,如 "MD5" 或 "HMAC-SHA256"
* @param key 商户密钥(API 密钥)
* @return 生成的签名字符串
*/
public static String generateSignature(Map<String, Object> params, String signType, String key) {
SortedMap<String, Object> sortedMap = new TreeMap<>(params);
StringBuilder sb = new StringBuilder();
for (Map.Entry<String, Object> entry : sortedMap.entrySet()) {
String k = entry.getKey();
Object v = entry.getValue();
// 排除空值和签名参数
if (v != null && !"sign".equals(k) && !"key".equals(k)) {
sb.append(k).append("=").append(v).append("&");
}
}
sb.append("key=").append(key);
String signTemp = sb.toString();
String sign = "";
try {
MessageDigest md = MessageDigest.getInstance(signType);
byte[] bytes = md.digest(signTemp.getBytes());
sign = byteArrayToHexString(bytes);
} catch (NoSuchAlgorithmException e) {
// 处理异常
e.printStackTrace();
}
return sign.toUpperCase();
}
private static String byteArrayToHexString(byte[] bytes) {
StringBuilder sb = new StringBuilder();
for (byte b : bytes) {
String hex = Integer.toHexString(b & 0xFF);
if (hex.length() == 1) {
sb.append("0");
}
sb.append(hex);
}
return sb.toString();
}
没有排查思路的话,也可以查看 微信支付接口报【签名错误】,看这一篇就够了
关于统一下单那个接口spbill_create_ip这个参数
微信官网上说终端用户ip,我看客服解释说是服务器ip 。我自己的系统是在内网部署,还有域名用的阿里云的,还有一个公网的ip。
- 内网服务器 IP:如果你的服务器部署在内网环境中,并且该服务器可以直接与微信支付接口进行通信,则可以将内网服务器的 IP 地址作为
spbill_create_ip
。 - 域名服务器 IP:如果你使用了域名解析或者 CDN(内容分发网络)等服务,并且用户访问你的网站时会经过域名服务器,则可以将域名服务器的 IP 地址作为
spbill_create_ip
。 - 公网 IP:如果你的服务器直接连接到公网,并且用户访问你的网站时可以直接获取到公网 IP 地址,则可以将公网 IP 作为
spbill_create_ip
我感觉是这个三个ip 都可以,我再排查签名错误的时候,都试过,第一次签名都可以返回正常的状态。
微信网页授权如何传递多个参数?redirect_uri如何包含参数
例如 我们在授权时的redirect_uri为www.baidu.com?a=123&b=678 ,需要传入a 和 b的参数
可以通过重定向的地址redirect_uri使用encodeURIComponent方法来进行编码,让浏览器认为redirect_uri是一个参数而不是地址符号。
let local ="http://www.baidu.com?a=123&b=678";
window.location.href = "https://open.weixin.qq.com/connect/oauth2/authorize?appid=*&redirect_uri="+encodeURIComponent(local)+"&response_type=code&scope=snsapi_userinfo&state=STATE#wechat_redirect";
redirect_uri 域名与后台配置不一致 10003(填坑解决方案
前端 获取openid 的方法
具体可以参考如下文章:
公众号支付报错:“当前页面的URL未注册”
用户实际的支付目录必须和在微信支付商户平台设置的一致,否则会报错”当前页面的URL未注册:”
注意 1.4中说的,支付授权目录校验规则说明中的第二点,就是你支付授权的目录是什么页面就是什么,比如你页面是form 页面,到公众号里必须 from/ 才能配置成功,配置上又无法访问这个页面。
所以直接配置顶级域名就可以了。
微信支付成功后,重复回调
第一,接受回调信息后,需要再返回 以下信息
<xml>
<return_code><![CDATA[SUCCESS]]></return_code>
<return_msg><![CDATA[OK]]></return_msg>
</xml>
第二,注意格式 必须是xml,否则,还会一直回调,response.setContentType(“text/xml”);
/**
* 支付接口回调
*/
@PostMapping(value = "wxCallback")
@ResponseBody
public String H5wxCallback(HttpServletRequest request, HttpServletResponse response) {
InputStream is = null;
String xmlBack = "<xml><return_code><![CDATA[FAIL]]></return_code><return_msg><![CDATA[报文为空]]></return_msg></xml> ";
try {
is = request.getInputStream();
// 将InputStream转换成String
BufferedReader reader = new BufferedReader(new InputStreamReader(is));
StringBuilder sb = new StringBuilder();
String line = null;
while ((line = reader.readLine()) != null) {
sb.append(line + "\n");
}
response.setContentType("text/xml");
xmlBack = weChatService.notify(sb.toString());
} catch (Exception e) {
System.out.println("微信支付回调通知失败:" + e);
} finally {
if (is != null) {
try {
is.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
return xmlBack;
}
最后
提供一下,查找排查文章的思路,网上搜了很多,看了许多排查相同问题的文章,质量实在不敢恭维,具体我们直接在微信支付客服哪里查找高质量的答案。