跳到主要内容

签名示例

本文档提供与平台当前规则一致的签名示例。统一规则如下:

  1. 移除 sign 字段
  2. 过滤值为 null 和空字符串的字段
  3. 按键名 ASCII 升序排序
  4. 拼接为 key=value&key=value
  5. 使用 HMAC-SHA256 计算摘要
  6. 将摘要结果做 Base64 编码

注意:签名内容使用原始参数值,不要额外做 URL 编码。

示例参数

{
"merchantId": "MCH_123456",
"merchantOrderNo": "ORDER_123456",
"amount": "1000000",
"currency": "VND",
"notificationUrl": "https://api.example.com/notify",
"paymentNo": "PAY_123456"
}

拼接后的签名串:

amount=1000000&currency=VND&merchantId=MCH_123456&merchantOrderNo=ORDER_123456&notificationUrl=https://api.example.com/notify&paymentNo=PAY_123456

signKey = your_secret_key 时,签名结果为:

69kFPVLYmSdqNszSsY5U/L4Rs531x0z2lnQlcRQKUg8=

JavaScript 示例

const crypto = require('crypto');

function buildSignContent(data) {
return Object.entries(data)
.filter(([key, value]) => key !== 'sign' && value !== null && value !== '')
.sort(([a], [b]) => a.localeCompare(b))
.map(([key, value]) => `${key}=${String(value)}`)
.join('&');
}

function generateSign(data, signKey) {
const content = buildSignContent(data);
return crypto
.createHmac('sha256', signKey)
.update(content, 'utf8')
.digest('base64');
}

const requestData = {
merchantId: 'MCH_123456',
merchantOrderNo: 'ORDER_123456',
amount: '1000000',
currency: 'VND',
notificationUrl: 'https://api.example.com/notify',
paymentNo: 'PAY_123456',
};

console.log(buildSignContent(requestData));
console.log(generateSign(requestData, 'your_secret_key'));

Python 示例

import base64
import hashlib
import hmac


def build_sign_content(data: dict) -> str:
items = [
(key, str(value))
for key, value in data.items()
if key != "sign" and value is not None and value != ""
]
items.sort(key=lambda item: item[0])
return "&".join(f"{key}={value}" for key, value in items)


def generate_sign(data: dict, sign_key: str) -> str:
content = build_sign_content(data)
digest = hmac.new(
sign_key.encode("utf-8"),
content.encode("utf-8"),
hashlib.sha256,
).digest()
return base64.b64encode(digest).decode("utf-8")


request_data = {
"merchantId": "MCH_123456",
"merchantOrderNo": "ORDER_123456",
"amount": "1000000",
"currency": "VND",
"notificationUrl": "https://api.example.com/notify",
"paymentNo": "PAY_123456",
}

print(build_sign_content(request_data))
print(generate_sign(request_data, "your_secret_key"))

Java 示例

import java.nio.charset.StandardCharsets;
import java.util.Base64;
import java.util.Map;
import java.util.TreeMap;
import java.util.stream.Collectors;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;

public class SignatureExample {
public static String buildSignContent(Map<String, Object> data) {
Map<String, String> filtered = new TreeMap<>();
for (Map.Entry<String, Object> entry : data.entrySet()) {
String key = entry.getKey();
Object value = entry.getValue();
if (!"sign".equals(key) && value != null && !"".equals(value)) {
filtered.put(key, String.valueOf(value));
}
}
return filtered.entrySet().stream()
.map(entry -> entry.getKey() + "=" + entry.getValue())
.collect(Collectors.joining("&"));
}

public static String generateSign(Map<String, Object> data, String signKey) throws Exception {
String content = buildSignContent(data);
Mac mac = Mac.getInstance("HmacSHA256");
mac.init(new SecretKeySpec(signKey.getBytes(StandardCharsets.UTF_8), "HmacSHA256"));
byte[] digest = mac.doFinal(content.getBytes(StandardCharsets.UTF_8));
return Base64.getEncoder().encodeToString(digest);
}
}

PHP 示例

<?php

function buildSignContent(array $data): string
{
unset($data['sign']);

$filtered = array_filter(
$data,
fn ($value) => $value !== null && $value !== ''
);

ksort($filtered, SORT_STRING);

return implode('&', array_map(
fn ($key, $value) => $key . '=' . strval($value),
array_keys($filtered),
array_values($filtered)
));
}

function generateSign(array $data, string $signKey): string
{
$content = buildSignContent($data);
$digest = hash_hmac('sha256', $content, $signKey, true);
return base64_encode($digest);
}

$requestData = [
'merchantId' => 'MCH_123456',
'merchantOrderNo' => 'ORDER_123456',
'amount' => '1000000',
'currency' => 'VND',
'notificationUrl' => 'https://api.example.com/notify',
'paymentNo' => 'PAY_123456',
];

echo buildSignContent($requestData) . PHP_EOL;
echo generateSign($requestData, 'your_secret_key') . PHP_EOL;

Go 示例

package main

import (
"crypto/hmac"
"crypto/sha256"
"encoding/base64"
"fmt"
"sort"
"strings"
)

func buildSignContent(data map[string]string) string {
delete(data, "sign")

keys := make([]string, 0, len(data))
for key, value := range data {
if value != "" {
keys = append(keys, key)
}
}

sort.Strings(keys)

parts := make([]string, 0, len(keys))
for _, key := range keys {
parts = append(parts, key+"="+data[key])
}

return strings.Join(parts, "&")
}

func generateSign(data map[string]string, signKey string) string {
content := buildSignContent(data)
mac := hmac.New(sha256.New, []byte(signKey))
mac.Write([]byte(content))
return base64.StdEncoding.EncodeToString(mac.Sum(nil))
}

func main() {
requestData := map[string]string{
"merchantId": "MCH_123456",
"merchantOrderNo": "ORDER_123456",
"amount": "1000000",
"currency": "VND",
"notificationUrl": "https://api.example.com/notify",
"paymentNo": "PAY_123456",
}

fmt.Println(buildSignContent(requestData))
fmt.Println(generateSign(requestData, "your_secret_key"))
}