小程序支付时返回签名失败,但测试环境正常,怎么办?

Mr-紫晨 阅读 107

我正在开发小程序的订单支付功能,用微信JSAPI支付时,线上环境一直报错“签名失败”,但同样的代码在沙箱环境能成功。我检查了nonceStr、timestamp这些参数,都跟后端返回的一致。

这是前端调支付的代码片段:


<template>
  <button @click="payOrder">去支付</button>
</template>

<script>
export default {
  methods: {
    async payOrder() {
      const res = await this.$api.getPayParams(); // 后端返回prepay_id等参数
      wx.requestPayment({
        timeStamp: res.timeStamp, // 13位毫秒时间戳
        nonceStr: res.nonceStr,
        package: <code>prepay_id=${res.prepayId}</code>,
        signType: 'MD5',
        paySign: res.paySign, // 前端重新计算的签名和后端返回的不一致?
        success() {},
        fail(err) {
          console.log(err); // {"errMsg":"requestPayment:fail 签名失败"}
        }
      })
    }
  }
}
</script>

已经尝试过把timestamp改成10位秒时间戳、调整参数排序重新生成sign,但线上环境还是失败。测试环境用的沙箱密钥没问题,生产环境的商户密钥有没有特殊要求?

我来解答 赞 9 收藏
二维码
手机扫码查看
2 条解答
玉丹 Dev
看了你的代码,问题应该出在几个地方,我来帮你捋一下。

首先最明显的坑:timeStamp 必须是10位秒级时间戳,你代码注释里写的是13位毫秒,这个微信会直接拒掉。虽然你说改过了,但得确认后端生成签名时用的也是10位的,前后端必须完全一致。

然后你代码里有个逻辑让我很困惑,注释写的是"前端重新计算的签名"?签名这东西应该完全由后端生成,前端只管拿过来用,千万别在前端再算一遍,既不安全也容易出问题。

后端生成paySign时,参数得按这个来:
appId、timeStamp、nonceStr、package、signType这五个参数按字典序排序,拼接成字符串后加上&key=商户密钥,再MD5并转大写。注意是appId,不是appid,大小写敏感的。

还有几个常见的坑点你得排查下:

生产环境的商户API密钥,是不是在商户平台重新设置过?如果换过密钥,后端代码里配置的要同步更新。沙箱环境有独立的沙箱密钥,跟生产环境不互通,这个你提到的方向是对的。

package字段的值要注意,应该是prepay_id=xxx这种格式,不是直接传prepay_id的值。

给你一个后端生成签名的参考写法,PHP版本的:

public function generatePaySign($appId, $timeStamp, $nonceStr, $prepayId, $signType = 'MD5')
{
$params = [
'appId' => $appId,
'timeStamp' => (string)$timeStamp, // 10位秒级时间戳
'nonceStr' => $nonceStr,
'package' => 'prepay_id=' . $prepayId,
'signType' => $signType,
];

ksort($params); // 字典序排序

$str = '';
foreach ($params as $key => $value) {
$str .= $key . '=' . $value . '&';
}
$str .= 'key=' . config('payment.mch_key'); // 商户API密钥

return strtoupper(md5($str));
}


前端改成一个更清晰的调用方式:

async payOrder() {
const res = await this.$api.getPayParams();

wx.requestPayment({
timeStamp: String(res.timeStamp), // 确保是字符串类型的10位时间戳
nonceStr: res.nonceStr,
package: prepay_id=${res.prepayId},
signType: 'MD5',
paySign: res.paySign, // 直接用后端返回的,别自己算
success() {},
fail(err) {
console.log(err);
}
});
}


这样更清晰,职责分离明确。你重点检查下后端的密钥配置和签名生成逻辑,前端这块别动,直接用后端返回的值就行。
点赞 2
2026-03-01 16:07
夏侯艳玲
生产环境的商户密钥和沙箱环境不一样,你得确认后端用的是正式环境的商户密钥,不是沙箱密钥。还有,timeStamp必须是10位秒级时间戳,不是13位毫秒级的,改一下试试。前端别自己拼签名,直接用后端返回的paySign

如果还是不行,把参数打印出来,仔细核对大小写和空格,微信那边对这些特别敏感,差不多就行的日子早就过去了。
点赞 2
2026-02-15 07:02