Nodejs微信App支付以及验证签名失败错误解决方案

556 阅读1分钟

1、调用下单接口

/**
  * 微信app支付
  */
async wxAppPay(ctx, params, mchKey) {
  const orderSign = ctx.service.wx.sign(params, mchKey);

  let formData = `<xml>
    <appid>${params.appid}</appid>
    <mch_id>${params.mch_id}</mch_id>
    <nonce_str>${params.nonce_str}</nonce_str>
    <sign>${orderSign}</sign>
    <sign_type>${params.sign_type}</sign_type>
    <body>${params.body}</body>
    <out_trade_no>${params.out_trade_no}</out_trade_no>
    <total_fee>${params.total_fee}</total_fee>
    <spbill_create_ip>${params.spbill_create_ip}</spbill_create_ip>
    <notify_url>${params.notify_url}</notify_url>
    <trade_type>APP</trade_type>
    <profit_sharing>${params.profit_sharing}</profit_sharing>
    </xml>`

  // 调用统一下单接口(接口没说明,但必须为post请求)
  const res = await ctx.curl(
      "https://api.mch.weixin.qq.com/pay/unifiedorder",
      {
        method: "post",
        data: formData,
      }
  )
  const datastring = res.data.toString()

  return new Promise((resolve, reject) => xml2js.parseString(datastring, async (err, result) => {
    if (err) { 
      reject(err)
    } else {
      resolve(result)
    }
  }))
}

2、得到prepayid之后再次加密将结果返回给App

function EncryptSign(result) {
  let prepay_id = result.xml.prepay_id[0];
  const resdata = {
    appid: params.appid, // 商户 id
    partnerid: params.mch_id,
    prepayid: prepay_id,
    package: "Sign",
    noncestr: result.xml.nonce_str[0],
    timestamp: Math.floor(Date.now() / 1000).toString()
  }

  const paySign = ctx.service.wx.sign(resdata, mchKey)
  resdata.sign = paySign
  return resdata
}

正常走到这一步就ok了,顺利的话客户端就能拉起微信进行支付操作。

3、验证签名失败错误

  • 检查一下第二步的 appid partnerid prepayid package noncestr timestamp 是否为小写,没有的话改成小写不要写成驼峰

  • 官方文档上第二步的package参数为”Sign=WXPay“但是通过微信支付接口签名校验工具测试的时候发现package="Sign",所以将此参数改为Sign

  • 检查一下timestamp是否是以秒为单位的。长度为10

4、附:签名算法,微信支付接口签名校验工具地址

// 签名
sign(data, mchKey, signType = 'MD5') {
  const keys = [];
  for (const key in data) {
    if (data[key] !== undefined) {
      keys.push(key);
    }
  }
  // 字典排序=>key=value
  const stringA = keys
    .sort()
    .map(key => `${key}=${decodeURIComponent(data[key])}`)
    .join('&');
  // 拼接商户key
  const stringSignTemp = stringA + '&key=' + mchKey;
  // 加密
  let hash;
  if (signType === 'MD5') {
    hash = crypto.createHash('md5').update(stringSignTemp);
  } else {
    hash = crypto.createHmac('sha256', mchKey).update(stringSignTemp, 'utf8');
  }
  
  const paySign = hash.digest('hex').toUpperCase();
  return paySign;
}

微信支付接口签名校验工具地址