Skip to content

签名算法

所有请求需携带 signature 参数,由以下步骤生成。

生成步骤

  1. 移除 signature 参数(如果存在)
  2. 处理数组参数,将数组转换为 JSON 字符串(不进行键排序)
  3. 按键名升序排序所有参数
  4. 拼接参数为 key=value& 格式
  5. 在末尾追加 &api_secret=你的商户密钥
  6. 对完整字符串进行 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'] = signature
java
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)
}