代码 支付接口报「时间戳过期 / 非法时间戳」:微信 5 分钟、Stripe 300 秒、支付宝非法 timestamp 的排查

2026-10-05 09:01:11

支付接口报「时间戳过期 / 非法时间戳」:微信 5 分钟、Stripe 300 秒、支付宝非法 timestamp 的排查

签名算法没写错,错的是签名里那个时间戳——微信支付、Stripe webhook、支付宝报这类错时,先查时钟和时区,别急着改签名代码。

先看报错原文

三类报错长得不一样,但指向同一个地方:签名本身没问题,签名里带的时间戳出问题了。看到下面这些字样,往时钟和时区方向查。

  • 微信支付 APIv3,请求被拒,HTTP 401 Unauthorized:错误信息形如 Http头Authorization中的timestamp与发起请求的时间不得超过5分钟,应答的 error detail 里会带上验签用的信息。
  • Stripe webhook:Webhook signature verification failed. Err: Timestamp outside the tolerance zone。
  • 支付宝 OpenAPI:{"code":"40002","msg":"Invalid Arguments","sub_code":"isv.invalid-timestamp","sub_msg":"非法的时间戳参数"}。

三家的容忍窗口和时间戳格式

  • 微信支付 APIv3 请求:Authorization 头里的 timestamp(Unix 秒),与发起请求时间不得超过 5 分钟;微信支付应答/回调里的 Wechatpay-Timestamp 也是 Unix 秒,官方建议商户最多允许 5 分钟偏差。微信重发某个通知回调时会重新生成时间戳和签名。
  • Stripe webhook:Stripe-Signature 头的 t= 字段是 Unix 秒,官方库默认 tolerance 是 300 秒(stripe-python/go/java 里 DEFAULT_TOLERANCE=300)。文档明确:不要设 tolerance=0,那会完全关闭防重放检查。
  • 支付宝:timestamp 是字符串,格式 yyyy-MM-dd HH:mm:ss,不是 Unix 秒。报错码 isv.invalid-timestamp,官方给的两个常见原因是:手动加签时没设到时分秒;用 SDK 但代码没取到 timestamp。

关键差异:微信/Stripe 用 Unix 秒(epoch),支付宝用本地时间字符串。把两者的格式搞混,是支付宝 isv.invalid-timestamp 的高频原因。

三类根因

  1. 服务器时钟漂移:容器、VM、云主机没开 NTP;机器休眠、快照恢复、长时间运行后漂移。表现是偶发,重启后有时又好了。
  2. 时区/格式错误:支付宝要北京时间 yyyy-MM-dd HH:mm:ss;把 Unix 秒塞进去,或把 UTC 时间当北京时间填,都会报非法时间戳。跨时区部署(海外节点)尤其容易。
  3. 延迟/重放窗口:请求经过代理、队列积压、回调延迟超过容忍窗口;或者自己把 tolerance 设得太短。

排查

先对时,看本机时间和有没有 NTP 在跑:

  • timedatectl status 看 System clock synchronized 与 NTP service。
  • date -u、date +%s 看 UNIX 时间与本地时间。
  • chrony:chronyc sources -v;ntpd:ntpq -p。看有没有 * 标记的同步源。
  • 与外部时间粗对比:curl -sI https://www.baidu.com | grep -i '^date:',拿到服务端时间再换算。
  • 容器里 date 跟宿主机不一致时,优先查宿主时钟和 --privileged/时间命名空间,容器本身一般继承宿主。

算偏差:拿到报文里的时间戳后直接比。

echo $(( $(date +%s) - 1722850421 ))

正数是对方时间比本机早多久。

微信专查一点:确认「参与签名计算的时间戳」和「Authorization 头里写的时间戳」是同一个值。官方把「签名和设置 Authorization 头时用了前后两次生成的时间戳/随机串」列为验签失败原因之一。

修复

  • 打开 NTP:systemd 机器 timedatectl set-ntp true;用 chrony 的检查 chronyc tracking 的 System time 偏移。
  • 首部/签名串/随机串用同一份:生成一次就被复用,别在几个函数里各调一次 time.Now()。
  • 容忍窗口别设 0,也别为了「稳」放大到几小时——那等于关掉防重放。Stripe 文档原话:tolerance=0 会彻底关闭 recency check。
  • 回调验签时注意:回调重发会带新的时间戳和签名,所以「延迟了很久才到的回调」不会因为时间戳旧而被拒——真正会拒的是时钟本身差得多。

边界:时间对了还报签名错误

如果时间戳差在窗口内、格式也对,仍然验签失败,问题不在时间:去查验签串构造(body 是否被框架改动)、证书/公钥是否用对。微信支付 v3 验签必须用平台证书/微信支付公钥,不能用商户 API 证书。

官方文档

  • 微信支付 APIv3 总述-如何签名和验签:https://pay.weixin.qq.com/doc/v3/merchant/4012365342
  • Stripe webhook 签名验证:https://docs.stripe.com/webhooks/signature
  • 支付宝 isv.invalid-timestamp:https://opendoc.alipay.com/support/04o2um

未实测说明:各家的容忍窗口以官方文档为准;时钟排查命令为通用做法,未在具体生产环境复现全部现象。

推荐文章

程序员茄子在线接单