主题
签名算法
所有请求需携带 signature 参数,由以下步骤生成。
生成步骤
- 移除
signature参数(如果存在) - 处理数组参数,将数组转换为 JSON 字符串(不进行键排序)
- 按键名升序排序所有参数
- 拼接参数为
key=value&格式 - 在末尾追加
&api_secret=你的商户密钥 - 对完整字符串进行 MD5 加密并转为大写
最终签名
签名结果为大写的 32 位 MD5 字符串,例如 D866A542EB56BBD26857B552C432E9D6。
代码示例
php
function generateSignature($params, $apiSecret) {
// 1. 移除 signature 参数
unset($params['signature']);
// 2. 处理数组参数
$processedParams = [];
foreach ($params as $key => $value) {
if (is_array($value)) {
$processedParams[$key] = json_encode($value, JSON_UNESCAPED_UNICODE);
} else {
$processedParams[$key] = $value;
}
}
// 3. 按键名排序
ksort($processedParams);
// 4. 拼接参数
$stringToSign = '';
foreach ($processedParams as $key => $value) {
if ($value !== '' && $value !== null) {
$stringToSign .= $key . '=' . $value . '&';
}
}
// 5. 移除最后的 & 并追加 api_secret
$stringToSign = rtrim($stringToSign, '&') . '&api_secret=' . $apiSecret;
// 6. 生成 MD5 签名并转为大写
return strtoupper(md5($stringToSign));
}
// 使用示例
$params = [
'api_key' => 'your_api_key',
'timestamp' => time(),
'nonce' => 'random_string_16',
'order_type' => 1,
'cny_amount' => 1000.00
];
$apiSecret = 'your_api_secret';
$signature = generateSignature($params, $apiSecret);
$params['signature'] = $signature;javascript
const crypto = require('crypto');
function generateSignature(params, apiSecret) {
// 1. 移除 signature 参数
delete params.signature;
// 2. 处理数组参数
const processedParams = {};
Object.keys(params).sort().forEach(key => {
if (Array.isArray(params[key])) {
processedParams[key] = JSON.stringify(params[key]);
} else {
processedParams[key] = params[key];
}
});
// 3. 拼接参数
const stringToSign = Object.keys(processedParams)
.filter(key => processedParams[key] !== '' && processedParams[key] !== null)
.map(key => `${key}=${processedParams[key]}`)
.join('&') + `&api_secret=${apiSecret}`;
// 4. 生成 MD5 签名并转为大写
return crypto.createHash('md5').update(stringToSign).digest('hex').toUpperCase();
}
// 使用示例
const params = {
api_key: 'your_api_key',
timestamp: Math.floor(Date.now() / 1000),
nonce: 'random_string_16',
order_type: 1,
cny_amount: 1000.00
};
const apiSecret = 'your_api_secret';
const signature = generateSignature(params, apiSecret);
params.signature = signature;python
import hashlib
import json
import time
import random
import string
def generate_signature(params, api_secret):
# 1. 移除 signature 参数
if 'signature' in params:
del params['signature']
# 2. 处理数组参数
processed_params = {}
for key in sorted(params.keys()):
value = params[key]
if isinstance(value, list):
processed_params[key] = json.dumps(value, ensure_ascii=False)
else:
processed_params[key] = value
# 3. 拼接参数
param_pairs = []
for key, value in processed_params.items():
if value != '' and value is not None:
param_pairs.append(f"{key}={value}")
string_to_sign = "&".join(param_pairs) + f"&api_secret={api_secret}"
# 4. 生成 MD5 签名并转为大写
return hashlib.md5(string_to_sign.encode('utf-8')).hexdigest().upper()
# 使用示例
params = {
'api_key': 'your_api_key',
'timestamp': int(time.time()),
'nonce': ''.join(random.choices(string.ascii_letters + string.digits, k=16)),
'order_type': 1,
'cny_amount': 1000.00
}
api_secret = 'your_api_secret'
signature = generate_signature(params, api_secret)
params['signature'] = signaturejava
import java.security.MessageDigest;
import java.util.Map;
import java.util.TreeMap;
public class SignUtil {
public static String generateSignature(Map<String, Object> params, String apiSecret) throws Exception {
// 1. 移除 signature 参数
params.remove("signature");
// 2 & 3. 处理数组参数 + 按键名升序(TreeMap 自动排序)
TreeMap<String, String> sorted = new TreeMap<>();
for (Map.Entry<String, Object> entry : params.entrySet()) {
Object value = entry.getValue();
if (value != null && !"".equals(value.toString())) {
sorted.put(entry.getKey(), value.toString());
}
}
// 4. 拼接参数(跳过空值)
StringBuilder sb = new StringBuilder();
for (Map.Entry<String, String> entry : sorted.entrySet()) {
sb.append(entry.getKey()).append('=').append(entry.getValue()).append('&');
}
// 5. 移除最后的 & 并追加 api_secret
if (sb.length() > 0) {
sb.setLength(sb.length() - 1);
}
sb.append("&api_secret=").append(apiSecret);
// 6. 生成 MD5 签名并转为大写
return md5Hex(sb.toString()).toUpperCase();
}
private static String md5Hex(String input) throws Exception {
MessageDigest md = MessageDigest.getInstance("MD5");
byte[] digest = md.digest(input.getBytes("UTF-8"));
StringBuilder hex = new StringBuilder();
for (byte b : digest) {
hex.append(String.format("%02x", b));
}
return hex.toString();
}
public static void main(String[] args) throws Exception {
Map<String, Object> params = new java.util.HashMap<>();
params.put("api_key", "your_api_key");
params.put("timestamp", System.currentTimeMillis() / 1000);
params.put("nonce", "random_string_16");
params.put("order_type", 1);
params.put("cny_amount", 1000.00);
String apiSecret = "your_api_secret";
String signature = generateSignature(params, apiSecret);
params.put("signature", signature);
System.out.println("signature = " + signature);
}
}go
package main
import (
"crypto/md5"
"encoding/hex"
"fmt"
"sort"
"strings"
)
// GenerateSignature 生成请求签名(与后端算法一致)
func GenerateSignature(params map[string]interface{}, apiSecret string) string {
// 1. 复制参数,移除 signature
data := make(map[string]string)
for k, v := range params {
if k == "signature" {
continue
}
data[k] = toStr(v)
}
// 2 & 3. 按键名升序
keys := make([]string, 0, len(data))
for k := range data {
keys = append(keys, k)
}
sort.Strings(keys)
// 4. 拼接参数(跳过空值)
pairs := make([]string, 0, len(keys))
for _, k := range keys {
v := data[k]
if v != "" {
pairs = append(pairs, k+"="+v)
}
}
// 5. 追加 api_secret
str := strings.Join(pairs, "&") + "&api_secret=" + apiSecret
// 6. MD5 大写
sum := md5.Sum([]byte(str))
return strings.ToUpper(hex.EncodeToString(sum[:]))
}
func toStr(v interface{}) string {
switch val := v.(type) {
case string:
return val
case float64:
return strings.TrimRight(strings.TrimRight(fmt.Sprintf("%v", val), "0"), ".")
default:
return fmt.Sprintf("%v", val)
}
}
func main() {
params := map[string]interface{}{
"api_key": "your_api_key",
"timestamp": 1699065600,
"nonce": "random_string_16",
"order_type": 1,
"cny_amount": 1000.00,
}
apiSecret := "your_api_secret"
signature := GenerateSignature(params, apiSecret)
params["signature"] = signature
fmt.Println("signature =", signature)
}