导读:本期聚焦于孙悟空创作的《微信JSAPI支付调起失败?timeStamp必须用秒级时间戳而不是毫秒》,敬请观看详情。调起微信JSAPI支付时提示签名错误或参数格式不对,很多时候问题出在timeStamp上。JavaScript的Date.now返回的是毫秒级时间戳,而微信支付要求timeStamp必须是秒级字符串,直接传毫秒值会导致签名校验失败或前端报参数错误。本文详细讲解timeStamp的正确生成方式、常见报错场景、签名参与字段的拼接规则,并给出服务端与前端完整的代码示例,帮助开发者快速定位和修复这个隐蔽但高频的支付调起问题。

微信JSAPI支付是公众号内支付的主流方案,但不少开发者在调起支付时遇到过各种各样的报错,其中timeStamp参数格式问题是最高频的一个坑。JavaScript的Date.now()返回的是毫秒级时间戳,而微信支付接口要求timeStamp必须是秒级时间戳的字符串形式,一旦传错,轻则前端报参数格式错误,重则签名校验失败,而且错误提示往往不够直观,排查起来非常费时。本文将围绕这个问题展开,讲清楚timeStamp的正确格式、错误产生的典型场景以及完整的修复方案。

为什么timeStamp必须是秒级时间戳

微信支付统一下单接口以及前端调起支付的配置参数中,timeStamp字段在官方文档中明确定义为:当前时间戳,单位为秒,长度为10位数字,类型为字符串。这个规范沿用自早期的支付签名体系,属于微信支付协议层的约定,所有参与签名的字段都必须严格按照这个格式来。

问题出在JavaScript的语言特性上。Date.now()new Date().getTime()返回的都是毫秒级时间戳,长度为13位数字。很多后端用Node.js开发,或者前端直接生成时间戳传给后端签名,就很容易把毫秒值直接塞进timeStamp字段。毫秒值传给微信统一下单接口时,服务端会直接返回类似invalid time_stamp或者参数错误:timeStamp格式不符合要求的错误;如果时间戳只在后端签名用了毫秒,而前端调起时又用了秒,两边不一致还会导致签名验证失败,这是最隐蔽的情况。

另外一个容易忽略的细节是类型问题。timeStamp要求是字符串而不是数字。微信内置浏览器调起支付时的JS接口WeixinJSBridge.invoke('getBrandWCPayRequest', ...)wx.chooseWXPay都对类型敏感,如果传了数字类型,部分安卓机型上会静默失败,连报错信息都不弹出来,排查难度更大。所以正确做法是既转成秒级,又转成字符串。

错误的写法与正确的写法对比

先看典型的错误代码。下面这段Node.js代码是很多项目中常见的写法,直接用毫秒时间戳参与签名和返回给前端:

// 错误示例:使用毫秒级时间戳
const timeStamp = Date.now(); // 例如 1718900000000,13位,错误
const nonceStr = 'abcdefg123456';
const package = 'prepay_id=wx20170101abcdef';

// 签名时使用了错误的时间戳
const paySign = md5(`appId=wxAppId&nonceStr=${nonceStr}&package=${package}&signType=MD5&timeStamp=${timeStamp}&key=apiKey`);

res.json({ timeStamp, nonceStr, package, signType: 'MD5', paySign });

这段代码的问题有两个:一是Date.now()返回的是13位毫秒值,二是timeStamp是number类型直接JSON序列化后传给前端。微信支付后端在校验统一下单请求或者签名时会失败,前端调起时也可能直接提示支付失败。

正确的写法是除以1000后取整,并转成字符串:

// 正确示例:秒级时间戳 + 字符串类型
const timeStamp = String(Math.floor(Date.now() / 1000)); // 例如 "1718900000",10位字符串
const nonceStr = 'abcdefg123456';
const package = 'prepay_id=wx20170101abcdef';
const signType = 'RSA'; // V3接口使用RSA签名

// 参与签名的字段按规范拼接,timeStamp使用秒级字符串
const message = `appId=wxAppId\ntimeStamp=${timeStamp}\nnonceStr=${nonceStr}\npackage=${package}\n`;

// 使用商户私钥对报文进行SHA256 with RSA签名
const paySign = crypto.createSign('RSA-SHA256').update(message).sign(privateKey, 'base64');

res.json({ timeStamp, nonceStr, package, signType, paySign });

注意两点关键区别:第一,Math.floor(Date.now() / 1000)保证结果为10位秒级时间戳,再用String()转换类型;第二,签名时参与运算的timeStamp必须与返回给前端的timeStamp完全一致,包括值和类型。有些开发者签名时用秒、返回给前端时用毫秒,或者反过来,两边不一致必然导致调起支付返回签名验证错误

前端调起支付时的注意事项

拿到后端返回的支付参数后,前端在微信内置浏览器中调起支付。推荐优先使用JSSDK的wx.chooseWXPay方式,需要先完成JSSDK配置,然后再调起支付:

// 后端接口返回的支付参数
fetch('/api/pay/create').then(res => res.json()).then(data => {
    wx.chooseWXPay({
        timestamp: data.timeStamp, // 注意字段名是timestamp,值为10位秒级字符串
        nonceStr: data.nonceStr,
        package: data.package,
        signType: data.signType,
        paySign: data.paySign,
        success: function (res) {
            // 支付成功后的回调,建议以后端查单结果为准
            if (res.errMsg === 'chooseWXPay:ok') {
                location.href = '/pay/result?orderId=' + orderId;
            }
        },
        cancel: function (res) {
            // 用户取消支付
        },
        fail: function (res) {
            alert('调起支付失败:' + res.errMsg);
        }
    });
});

如果不引入JSSDK,也可以使用微信内置浏览器提供的WeixinJSBridge,这时字段名是驼峰的timeStamp

function onBridgeReady(payParams) {
    WeixinJSBridge.invoke('getBrandWCPayRequest', {
        appId: payParams.appId,
        timeStamp: payParams.timeStamp, // 10位秒级字符串
        nonceStr: payParams.nonceStr,
        package: payParams.package,
        signType: payParams.signType,
        paySign: payParams.paySign
    }, function (res) {
        if (res.err_msg === 'get_brand_wcpay_request:ok') {
            // 支付成功
        }
    });
}

if (typeof WeixinJSBridge === 'undefined') {
    if (document.addEventListener) {
        document.addEventListener('WeixinJSBridgeReady', onBridgeReady, false);
    }
} else {
    onBridgeReady(payParams);
}

这里有一个经典陷阱需要注意:JSSDK方式下字段名是小写开头的timestamp,而WeixinJSBridge方式下是timeStamp,两种方式字段名不同。如果字段名写错,支付会直接报chooseWXPay:fail,这也经常和timeStamp问题混淆在一起排查。

排查思路与常见错误信息汇总

当调起支付失败时,可以按照以下顺序排查:

  • 检查timeStamp位数:正常应为10位数字,如果是13位说明传了毫秒值;如果是其他位数则说明生成逻辑有bug。
  • 检查timeStamp类型:使用typeof timeStamp确认是string而不是number。
  • 检查签名一致性:后端签名时的timeStamp和返回前端的timeStamp必须完全相同,可以在前端打印出来逐一比对。
  • 检查当前时间与微信服务器时间偏差:如果服务器时间不准,即使格式正确也可能报错,可通过NTP同步服务器时间。
  • 检查签名算法:V3接口使用RSA-SHA256,V2接口使用MD5或HMAC-SHA256,签名类型和密钥不能混用。

常见错误信息与timeStamp相关的对应关系如下:

错误信息可能原因
chooseWXPay:failtimeStamp为毫秒值、类型错误或字段名拼写不对
签名验证错误签名参与字段与调起参数不一致,或timeStamp单位错误
invalid time_stamp统一下单时timeStamp格式非法或服务器时间偏差过大
get_brand_wcpay_request:failWeixinJSBridge调起参数中timeStamp格式不符

最后提醒一点,timeStamp的正确生成应该只发生在服务端,不要让前端生成时间戳再传给后端签名。前端时间可以随意被用户修改,而且参与签名的参数必须由可信方统一生成。养成String(Math.floor(Date.now() / 1000))这个固定写法的习惯,并且封装成独立的工具函数在项目中复用,就能从根源上避免这个问题的再次出现。

微信JSAPI支付timeStamp秒级时间戳WeixinJSBridge支付修改时间:2026-08-31 06:38:32

免责声明:​ 已尽一切努力确保本网站所含信息的准确性。网站内容多为原创整理与精心编撰,观点力求客观中立。本站旨在免费分享,内容仅供个人学习、研究或参考使用。若引用了第三方作品,版权归原作者所有。如内容涉及您的权益,请联系我们处理。
内容垂直聚焦
专注技术核心技术栏目,确保每篇文章深度聚焦于实用技能。从代码技巧到架构设计,为用户提供无干扰的纯技术知识沉淀,精准满足专业提升需求。
知识结构清晰
覆盖从开发到部署的全链路。AI、前端、编程、数据库、服务器、建站、系统层层递进,构建清晰学习路径,帮助用户系统化掌握开发与运维所需的核心技术。
深度技术解析
拒绝泛泛而谈,深入技术细节与实践难点。无论是数据库优化还是服务器配置,均结合真实场景与代码示例进行剖析,致力于提供可直接应用于工作的解决方案。
专业领域覆盖
精准对应开发生命周期。从前端界面到后端编程,从数据库操作到服务器运维,形成完整闭环,一站式满足全栈工程师和运维人员的技术需求。
即学即用高效
内容强调实操性,步骤清晰、代码完整。用户可根据教程直接复现和应用于自身项目,显著缩短从学习到实践的距离,快速解决开发中的具体问题。
持续更新保障
专注既定技术方向进行长期、稳定的内容输出。确保各栏目技术文章持续更新迭代,紧跟主流技术发展趋势,为用户提供经久不衰的学习价值。