v2 协议规范
本页是商户 API v2 的协议规范:加密、签名、防重放与请求/响应格式。对接前请先在 凭据与密钥 页确认已拿到 4 项凭据;写完代码后务必用 上线自检 selfcheck 跑通整条链路。
1. 协议概览
商户与平台的所有业务请求/响应/回调均经过:
- 加密:RSA-OAEP(MGF1 = SHA-1),保护请求/响应内容。
- 签名:SHA256withRSA,保证完整性与来源可信。
- 防重放:时间戳 + nonce,请求与回调双向校验。
业务参数以 JSON 表示,加密为密文后作为 HTTP body 传输。
2. 核心规范(务必逐字节对齐)
2.1 加密:RSA-OAEP,MGF1 = SHA-1
- 明文按 214 字节分段,逐段
RSA-OAEP加密,密文拼接后整体 Base64。 - 解密:Base64 解码后按 256 字节分段,逐段解密后拼接。
- ⚠️ 头号互通坑:OAEP 的哈希必须是 SHA-1。很多语言默认 OAEP-SHA256,两边不一致会解不开。各语言显式写法:
// 默认即 SHA-1
openssl_public_encrypt($d, $o, $k, OPENSSL_PKCS1_OAEP_PADDING)padding.OAEP(mgf=MGF1(SHA1()), algorithm=SHA1(), label=None){ padding: RSA_PKCS1_OAEP_PADDING, oaepHash: 'sha1' }rsa.EncryptOAEP(sha1.New(), rand.Reader, pub, msg, nil)Cipher.getInstance("RSA/ECB/OAEPWithSHA-1AndMGF1Padding")2.2 签名:SHA256withRSA(PKCS#1 v1.5 签名填充)
2.3 canonical 签名串(规范,不是示例)
签名 / 验签的输入必须是下面这串,三段以**换行符 \n(LF,0x0A)**连接,逐字节一致,否则 100% 验签失败:
ANONY-TIMESTAMP + "\n" + ANONY-NONCE + "\n" + <body>- 上行请求:
<body>= 加密后的请求体(即 HTTP body,Base64 密文)。 - 下行响应 / 回调:
<body>= 响应体(Base64 密文)。
2.4 防重放
ANONY-TIMESTAMP:秒级 Unix 时间戳。服务端时间窗 ±5 分钟(即 ±300 秒),超出拒绝。ANONY-NONCE:≥16 字节 CSPRNG 随机串(hex / base64 均可,建议定长 hex)。服务端对(商户号, nonce)去重,重复即拒绝。- 回调方向同样如此:平台回调你时也会带
ANONY-TIMESTAMP/ANONY-NONCE,你必须自行校验时间窗并对 nonce 去重,以拒绝被重放的回调(建议用 RedisSET key 1 NX EX 900之类的原子去重,过期 ≥ 15 分钟)。回调侧的完整处理步骤见 回调机制。
3. 请求与响应格式
3.1 上行请求头
ANONY-APP-ID: <商户号>
ANONY-TOKEN: <token>
ANONY-TIMESTAMP: <秒级时间戳>
ANONY-NONCE: <≥16字节随机串>
ANONY-SIGN: <canonical 串的 SHA256withRSA 签名, base64>
请求体(body): <业务 JSON 经 OAEP 加密后的 base64 密文>其中 ANONY-TOKEN 通过 getToken 获取。
3.2 下行响应体
{ "code": 10000, "data": "<base64 密文>", "message": "success" }code == 10000表示成功;data是 OAEP 加密的 base64 密文,需验签后解密。- 响应头带
ANONY-TIMESTAMP/ANONY-NONCE/ANONY-SIGN。 - ⚠️ 解析外层 JSON 请使用标准 JSON 库(响应体里
/可能被转义为\/,标准库会自动还原;不要手写字符串切割)。
3.3 处理下行的步骤
- 用标准 JSON 库解析外层
{code,data,message}。 - 校验
ANONY-TIMESTAMP在 ±5 分钟内,且ANONY-NONCE未见过(去重)。 - 用
canonical(ts, nonce, data)+ 平台公钥 验签。 - 用商户私钥 OAEP 解密
data,得到业务 JSON。
code != 10000 时的错误码含义见 错误码;验签/解密失败的排查见 排障指南。
4. 实现细节补充
token 生命周期(实现细节补充)
ANONY-TOKEN 采用滑动过期 8 小时 + 绝对上限 24 小时:每次成功调用会刷新 8 小时有效期,但自签发起最长存活 24 小时。收到 tokenExpired 时重新调用 getToken 即可,无需在本地精确计时。
请求体大小上限(实现细节补充)
请求体(加密后的 Base64 密文)上限为 128KB,超限会在协议校验之前被直接拒绝(报 paramError)。正常业务请求远小于该上限;若触发,请检查是否误将大对象塞进了业务 JSON。
v1 协议简述(实现细节补充,新商户请忽略)
存量老商户使用 v1 协议:MD5withRSA 验签(对密文 body)+ PKCS#1 v1.5 分段加解密,无时间戳/nonce 防重放头。v1 仅面向存量商户,新接入一律 v2。协议版本由平台侧数据库按商户号决定,请求侧无法通过任何头或参数切换/降级(防降级攻击);从 v1 升级 v2 需先通过 selfcheck 自检,再由平台完成切换。
5. 安全须知
- 私钥仅存于你的服务端,切勿写入前端、日志或第三方。
- 始终校验下行签名后再使用数据;始终对回调做时间窗 + nonce 去重。
- 时间戳依赖系统时钟,请确保服务器 NTP 同步(否则易触发 ±5 分钟超窗)。
- 算法分支由平台按商户号决定,请求侧无法更改(防降级)。
下一步:按 API 参考 逐个接口对接,或直接使用 官方 SDK(PHP / Python / Node / Go / Java)跳过手写协议细节。