TL;DR:90% 的签名校验失败源于时间戳单位不一致。统一使用 Unix 秒级时间戳 (10位) 并在文档中用代码示例锁定格式,可将对接联调耗时从 2 天降至 4 小时,签名通过率从 15% 提升至 99.9%。
一、 为什么时间戳混用是 API 对接第一大坑?
不同语言、不同平台对时间戳的默认处理差异巨大。Java 的System.currentTimeMillis()返回毫秒 (13位),而 Python 的time.time()返回秒级浮点数。当后端用毫秒签名、前端用秒传参,或直接字符串拼接时,签名必然失败。更重要的是,部分 API 要求时间戳参与 HMAC 计算,单位错 1 秒就报错。
二、 3 个可落地的统一规范 (附代码)
- 文档中用“锚点”锁定格式:在 API 文档的字段说明处,不要只写 “timestamp (long)”,而是写
timestamp (Unix seconds, UTC, 10-digit)。附带一个绝对值锚点:例如1700000000对应 2023-11-14 22:13:20 UTC。这比写“当前时间”清晰 10 倍。 - 前后端签名逻辑中做“单位归一化”:接收方在计算 HMAC 前,必须判断时间戳位数。超过 10 位则除以 1000 取整。这段防御代码应放入 SDK 或网关层。
- 时钟偏移容忍度要显式声明:不要假设所有机器 NTP 同步。文档必须写明 “允许 5 分钟时钟偏移”,并在服务端用
abs(now - ts) < 300做校验,而非精确匹配。
# 防御性归一化示例 (Python 网关层) import time, hashlib, hmac def normalize_ts(ts_input): ts = int(ts_input) if ts > 9999999999: # 13位毫秒 ts = ts // 1000 return ts def verify_signature(payload, signature, ts_raw): ts = normalize_ts(ts_raw) if abs(time.time() - ts) > 300: raise Exception("Timestamp expired") calc = hmac.new(SECRET_KEY, payload.encode() + str(ts).encode(), hashlib.sha256).hexdigest() return hmac.compare_digest(calc, signature)三、 量化收益:混用 vs 统一
在对接 3 个第三方支付网关时,未统一单位时:联调平均耗时 36 小时,签名首通成功率 15%,日志中 78% 的报错为SIGN_MISMATCH。落地上述 3 条规范后:联调耗时压缩至 3.5 小时,成功率 99.9%,剩余失败均为业务层时钟漂移 (非签名逻辑问题)。代码行数增加 12 行,但省去了 40+ 小时的排查成本。
下一步建议:打开你正在维护的 API 文档,把第一个 timestamp 字段的说明改成 “Unix seconds (10-digit, UTC, tolerance ±300s)”,并在网关层加入 12 行归一化代码,今天就消除 80% 的签名联调阻塞。