Skip to content

签名规则

为了保障 请求的安全性与完整性,我们要求对接口请求参数进行签名。主要用途如下:

  • 防止参数被篡改
  • 验证请求来源合法性
  • 保证数据完整性

参数准备规则

  • 所有 非空参数值(业务参数 + 通用参数)
  • 除了 sign 外,不排除其它字段,例如 signType仍参与
  • 交易金额:单位为分,参数值不能带小数,如:132美元,则需转换为13200分
  • 时间参数:所有涉及时间参数均使用精确到毫秒的13位数值,如:1622016572190

签名步骤

签名步骤

参数过滤并排序 -> 拼接字符串 -> 拼接密钥 -> 计算签名值

STEP 1:参数过滤并排序

将所有有效参数按照 参数名 ASCII 字典升序排序 (ASCII 从小到大)

java
// Java 伪代码
Map<String, Object> validParams = params.entrySet().stream()
    .filter(entry -> !"sign".equals(entry.getKey())
                     && entry.getValue() != null
                     && !"".equals(entry.getValue()))
    .sorted(Map.Entry.comparingByKey()) // 按 key ASCII 升序
    .collect(Collectors.toMap(
        Map.Entry::getKey,
        Map.Entry::getValue,
        (oldValue, newValue) -> oldValue,
        LinkedHashMap::new
    ));
php
// PHP 伪代码
$validParams = array_filter($params, function($value, $key) {
    return $key !== 'sign' && $value !== null && $value !== '';
}, ARRAY_FILTER_USE_BOTH);

ksort($validParams); // 按键名 ASCII 升序
javascript
const validParams = Object.keys(params)
  .filter(key => {
    const value = params[key]
    return key !== 'sign' && value !== undefined && value !== null && value !== ''
  })
  .sort()

注意

签名过程中,所有参与签名的参数必须按规则排序、拼接、并确保值准确无误 sign 参数不参与签名计算

STEP 2:拼接字符串

按照键值对格式将排序后的参数值用 &key=value 拼接成字符串

注意

如果参数值是对象(包括数组需面有对象),则需要将该对象按照键名 ASCII 字典升序排序,并JSON字符串化。

java
// Java 伪代码
String queryString = validParams.entrySet().stream()
    .map(entry -> {
        Object value = entry.getValue();
        if (value instanceof Map || value instanceof List) {
            // 对象或数组先排序再转 JSON
            value = JsonUtils.toSortedJson(value); // 假设有工具方法
        }
        return entry.getKey() + "=" + value.toString();
    })
    .collect(Collectors.joining("&"));
php
// PHP 伪代码
$queryParts = [];
foreach ($validParams as $key => $value) {
    if (is_array($value)) {
        // 对象或数组先排序再 JSON 编码
        $value = json_encode(sortArrayRecursively($value), JSON_UNESCAPED_UNICODE);
    }
    $queryParts[] = $key . '=' . $value;
}
$queryString = implode('&', $queryParts);

// sortArrayRecursively 为自定义递归排序函数,类似 javascript中 的 sortObject
javascript
const queryString = validParams
  .map(key => {
    const value = params[key]
    if (typeof value === 'object') {
      // 对象或数组先排序再 JSON.stringify
      return `${key}=${JSON.stringify(sortObject(value))}`
    }
    return `${key}=${value}`
  })
  .join('&')

// 排序对象
const sortObject = params => {
  if (Array.isArray(params)) {
    // 数组递归处理每个元素
    return params.map(item => (typeof item === 'object' && item !== null ? sortObject(item) : item))
  } else if (typeof params === 'object' && params !== null) {
    // 对象按 key 排序
    const sortedKeys = Object.keys(params).sort()
    const result = {}
    for (const key of sortedKeys) {
      const value = params[key]
      // 递归处理对象或数组
      result[key] = typeof value === 'object' && value !== null ? sortObject(value) : value
    }
    return result
  }
  return params
}

STEP 3:拼接密钥

在拼接的字符串末尾追加商户密钥(secretKey),如&key=secretKey

java
String signString = queryString + "&key=" + secretKey;
php
$signString = $queryString . "&key=" . $secretKey;
javascript
const signString = `${queryString}&key=${secretKey}`

STEP 4:计算签名值

把待签名的字符串执行 MD5 算法计算签名值,并转成大写:

java
// Java 伪代码
String sign = DigestUtils.md5Hex(signString).toUpperCase();
php
$sign = strtoupper(md5($signString));
javascript
const sign = MD5(signString).toString().toUpperCase()

示例

我们以下面参数为例:

json
{
  "mchNo": "M1769743905",
  "amount": 13200,
  "tradeNo": "2021052010001",
  "reqTime": 1622016572190,
  "billing": {
    "name": "张三",
    "phone": "13800138001"
  },
  "items": [
    {
      "name": "鞋子",
      "unitPrice": 12000,
      "currency": "USD"
    },
    {
      "name": "衣服",
      "unitPrice": 12000,
      "currency": "USD"
    }
  ]
}

商户密钥 v77ZyRbKkCEr4OPbGhiJW5ONzM2FcxvYxE2CCIm9jrvqqmQbBB7EKOOmgy6LE4Q5,则结果如下:

// => 生成的待签名字符串
amount=13200&billing={"name":"张三","phone":"13800138001"}&items=[{"currency":"USD","name":"鞋子","unitPrice":12000},{"currency":"USD","name":"衣服","unitPrice":12000}]&mchNo=M1769743905&reqTime=1622016572190&tradeNo=2021052010001&key=v77ZyRbKkCEr4OPbGhiJW5ONzM2FcxvYxE2CCIm9jrvqqmQbBB7EKOOmgy6LE4Q5

// => 签名结果
05CF9BD928B1A0413DF7B1A08C4C14A1