主题
协议与签名
通用请求参数
商户开放接口支持 JSON 与 application/x-www-form-urlencoded。本文统一推荐 POST JSON;转账查询额外支持 GET 查询参数,退款查询不应使用 GET 参数。
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
| mchNo | string | 是 | 商户号 |
| appId | string | 是 | 属于该商户且已启用的应用编号 |
| sign | string | 是 | 应用私钥生成的 MD5 签名 |
| signType | string | 否 | 默认 MD5,目前只支持 MD5 |
| reqTime | string / integer | 否 | 毫秒时间戳,建议传入;当前认证器未强制校验时间窗口 |
除 sign 自身外,实际发送的全部非空字段均参与签名,包括 signType、reqTime 和扩展字段。新增字段后应重新计算签名。
计算规则
- 删除
sign,过滤null和空字符串"",保留数字0。 - 按参数名忽略大小写排序,字段名自身大小写保持原样。避免传入仅大小写不同的重复参数名。
- 将每项拼成
参数名=参数值&,末尾追加key=appSecret。 - 对完整文本计算 MD5,转为大写十六进制字符串。
签名原文不进行 URL 编码。签名后再序列化 JSON 或编码表单。布尔值按 true / false;数组、对象按不转义中文和斜杠的紧凑 JSON 序列化,保留原键顺序。建议 channelExtra 直接传 JSON 字符串,使用同一原文参与签名及请求,避免不同语言的序列化差异。
text
amount=100&appId=APP_DEMO&...&wayCode=ALI_QR&key=DEMO_SECRET上例含省略号,仅说明拼接结构,不是可计算的签名向量。完整可运行示例:
js
import { createHash } from 'node:crypto'
export function sign(params, appSecret) {
const values = Object.entries(params)
.filter(([key, value]) => key !== 'sign' && value !== null && value !== undefined && value !== '')
.sort(([a], [b]) => {
const x = a.toLowerCase(), y = b.toLowerCase()
return x < y ? -1 : x > y ? 1 : 0
})
.map(([key, value]) => {
const text = typeof value === 'object' ? JSON.stringify(value) : String(value)
return `${key}=${text}&`
}).join('')
return createHash('md5').update(values + 'key=' + appSecret, 'utf8').digest('hex').toUpperCase()
}此代码仅在 Node.js 服务端使用,完整文件位于项目 examples/sign.mjs。
响应与验签
成功响应的 sign 仅对 data 内字段签名,不要把外层 code 加入验签。
json
{
"code": 0,
"data": { "payOrderId": "P_DEMO", "mchOrderNo": "ORDER_DEMO", "state": 2 },
"sign": "对data计算的签名"
}成功响应当前不含 msg,data 为空数组时不含签名。错误响应通常仍使用 HTTP 200,应判断业务 code:
json
{ "code": 1, "msg": "验签失败" }错误响应不签名。code=0 表示接口调用成功,资金结果还需判断 state 或 orderState。收银台使用独立协议,见 收银台接口。