外观
签名
所有 Open API 请求都必须签名。签名用于确认商户身份、防止请求被篡改,并配合 X-Nonce 防重放。
请求头
| Header | 必填 | 说明 |
|---|---|---|
X-Mch-Id | 是 | 平台商户号 merchant_no。 |
X-Timestamp | 是 | Unix 秒,允许 5 分钟时间窗。 |
X-Nonce | 是 | 随机串,用于防重放。 |
X-Signature | 是 | HMAC-SHA256 小写 hex。 |
X-Request-Id | 否 | 商户侧请求追踪 ID。 |
签名串
签名串固定 6 行,用换行符 \n 拼接:
text
METHOD
PATH
MCH_ID
TIMESTAMP
NONCE
PAYLOAD_DIGEST字段规则:
| 字段 | 说明 |
|---|---|
METHOD | 大写 HTTP method,例如 GET、POST。 |
PATH | 不带域名、不带 query 的路径,例如 /api/v1/open/pay/order。 |
MCH_ID | 请求头 X-Mch-Id 的值。 |
TIMESTAMP | 请求头 X-Timestamp 的值。 |
NONCE | 请求头 X-Nonce 的值。 |
PAYLOAD_DIGEST | hex(sha256(canonical_payload))。 |
最终签名:
text
hex(HMAC-SHA256(secret_key, sign_text))GET canonical_payload
GET 请求的 canonical_payload 是规范化 query string:
- 排除
signature和sign参数。 - 参数名按 ASCII 升序排列。
- 参数值按 RFC3986 百分号编码。
- 使用
key=value&key2=value2拼接。 - 空 query 的 canonical payload 是空字符串。
示例:
text
GET /api/v1/open/pay/query?trade_no=260703000842100181368386&trade_type=1
canonical_payload:
trade_no=260703000842100181368386&trade_type=1POST canonical_payload
POST 请求的 canonical_payload 是规范化后的加密信封 JSON。签名覆盖最终传输的 encrypted_key 和 biz_content,不覆盖业务明文 JSON。
示例信封:
json
{"biz_content":"base64...","encrypted_key":"base64..."}业务 JSON 在加密前可以有空白和字段顺序差异,但最终用于签名的信封 JSON 必须稳定。建议 SDK 使用固定 JSON 序列化方式:字段按 biz_content、encrypted_key 或确定顺序输出,无多余空白。
响应验签
Open POST 成功响应也会返回签名头:
text
X-Timestamp
X-Nonce
X-Signature
X-Request-Id商户应先验证平台响应签名,再解密 data 中的响应信封。响应验签同样使用 6 行签名串,MCH_ID 使用商户号,PAYLOAD_DIGEST 基于响应 body 的规范化 payload。
常见失败
| 现象 | 处理 |
|---|---|
Open API 签名请求头不能为空 | 检查必填签名头是否缺失。 |
| 签名错误 | 检查 PATH 是否包含 query、payload digest 是否基于正确内容。 |
| nonce 重放 | 每次请求使用新的 X-Nonce。 |
| 时间戳过期 | 使用 Unix 秒并校准服务器时间。 |