Skip to content

签名与安全

亿付通建议把签名能力封装成独立模块,避免每个接口重复实现。

通用规则

  • 请求和响应统一使用 UTF-8 编码。
  • 参与签名的字段应去掉空值与签名字段本身。
  • 按参数名 ASCII 码升序排序后再拼接签名串。
  • 如果接口返回 data,建议同时对 data 和顶层状态码做验签校验。
  • 通知接口必须先验签,再做业务处理,最后返回结果。

MD5 签名思路

参考网关类文档的常见规则,MD5 模式一般按以下方式处理:

  1. 对非空字段按 ASCII 升序排序。
  2. key=value 形式用 & 拼接。
  3. 在末尾追加 &key=商户密钥
  4. 对结果做 MD5 并转成大写。

Node.js 示例

js
import crypto from 'node:crypto'

function buildSign(params, apiKey) {
  const content = Object.keys(params)
    .filter((key) => key !== 'sign' && params[key] !== '' && params[key] !== undefined && params[key] !== null)
    .sort()
    .map((key) => `${key}=${params[key]}`)
    .join('&')

  return crypto.createHash('md5').update(`${content}&key=${apiKey}`, 'utf8').digest('hex').toUpperCase()
}

RSA2 签名思路

若商户后台启用了 RSA2,建议使用私钥做签名、公钥做验签。签名串仍然建议沿用“字段过滤 + ASCII 升序 + key=value 拼接”的基础规则。

js
import crypto from 'node:crypto'

function buildRsa2Sign(params, privateKey) {
  const content = Object.keys(params)
    .filter((key) => key !== 'sign' && params[key] !== '' && params[key] !== undefined && params[key] !== null)
    .sort()
    .map((key) => `${key}=${params[key]}`)
    .join('&')

  const signer = crypto.createSign('RSA-SHA256')
  signer.update(content, 'utf8')
  signer.end()
  return signer.sign(privateKey, 'base64')
}

验签建议

  • 业务网关响应时,优先检查 codemsgdata 是否完整。
  • 对通知参数使用与请求相同的签名算法重新计算签名。
  • 当签名不一致时直接记录日志并返回失败,不进入业务逻辑。
  • 对所有通知单号建立幂等锁或唯一键,避免重复处理。

安全建议

  • 生产环境优先使用 HTTPS
  • 商户密钥不要写死在前端。
  • 对回调来源做 IP 白名单或网关层鉴权。
  • 记录完整请求日志时,注意脱敏账号、姓名、银行卡号等敏感字段。

注意

不同平台实例可能会在以下项上略有差异,投产前需以商户后台和正式接口规范为准:

  • reqTime 使用秒级还是毫秒级时间戳
  • 仅支持 MD5,还是同时支持 RSA2
  • 顶层字段参与签名还是仅业务字段参与签名
  • 响应是否对 data 再次单独签名

亿付通开放支付平台