平台分配的商户访问标识
签名说明
概述
所有 API 请求都必须经过认证和签名,以确保请求的安全性和完整性。
认证方式
Header 认证
每个请求必须在请求头中包含以下字段:
请求签名
示例
http
POST /api/v2/pay/submit
Content-Type: application/json
x-access-key: MERCHANT_001
signature: a1b2c3d4e5f6...签名算法
签名计算步骤
步骤一:准备参数
收集所有 body 参数,并排除:
signature字段本身- 值为
null或空字符串的参数
请求参数示例
json
{
"timestamp": "1742205605123",
"orderAmount": "12000",
"merchantOrderNo": "BDC202604170001",
"payMethod": "KBZPAY",
"customerName": "Rahim",
"customerPhone": "01712345678",
"customerEmail": "pkbuyer@gmail.com",
"callbackUrl": "https://merchant.example.com/payments/notify"
}步骤二:参数排序
按参数名字母顺序(a-z)对所有参数进行排序。
示例
排序后:
callbackUrl, customerEmail, customerName, customerPhone, merchantOrderNo, orderAmount, payMethod, timestamp步骤三:拼接字符串
将排序后的参数按 key=value 格式拼接,参数之间用 & 连接。参数值按请求中的原始字符串参与签名。
示例
callbackUrl=https://merchant.example.com/payments/notify&customerEmail=pkbuyer@gmail.com&customerName=Rahim&customerPhone=01712345678&merchantOrderNo=BDC202604170001&orderAmount=12000&payMethod=KBZPAY×tamp=1742205605123步骤四:追加密钥
将商户密钥(Secret Key)追加到拼接字符串的末尾。
示例
callbackUrl=https://merchant.example.com/payments/notify&customerEmail=pkbuyer@gmail.com&customerName=Rahim&customerPhone=01712345678&merchantOrderNo=BDC202604170001&orderAmount=12000&payMethod=KBZPAY×tamp=1742205605123{SECRET_KEY}步骤五:计算哈希
使用 SHA256 算法计算哈希值。
步骤六:转小写
将哈希结果转换为小写十六进制字符串。
最终签名
如果以上示例使用的密钥为 demo_secret_key_bd,则签名结果为:
9819cec934445dbf9a810941a63e563fd7f3f2c2d0e33359103b644d47c3863c异步通知的验签规则与以上一致,回调中的签名字段同样为 signature,验签时应排除 signature 字段本身。
签名示例代码
Java
java
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Map;
import java.util.TreeMap;
public class SignUtil {
public static String sign(Map<String, Object> params, String secretKey) throws NoSuchAlgorithmException {
// 1. 使用 TreeMap 按字母顺序排序,过滤空值和签名字段
TreeMap<String, String> sortedParams = new TreeMap<>();
for (Map.Entry<String, Object> entry : params.entrySet()) {
String key = entry.getKey();
Object value = entry.getValue();
// 过滤空值、null 值和签名字段
if (value != null && !"".equals(value) && !"signature".equals(key)) {
sortedParams.put(key, value.toString());
}
}
// 2. 拼接参数为 key=value&key=value 格式
StringBuilder sb = new StringBuilder();
for (Map.Entry<String, String> entry : sortedParams.entrySet()) {
if (sb.length() > 0) {
sb.append("&");
}
sb.append(entry.getKey()).append("=").append(entry.getValue());
}
// 3. 追加密钥
sb.append(secretKey);
// 4. 计算 SHA256
MessageDigest md = MessageDigest.getInstance("SHA-256");
byte[] hash = md.digest(sb.toString().getBytes(StandardCharsets.UTF_8));
// 5. 转换为小写十六进制
StringBuilder hexString = new StringBuilder();
for (byte b : hash) {
String hex = Integer.toHexString(b & 0xff);
if (hex.length() == 1) {
hexString.append('0');
}
hexString.append(hex);
}
return hexString.toString().toLowerCase();
}
}Node.js
javascript
const crypto = require('crypto');
function generateSignature(params, secretKey) {
// 1. 过滤空值和签名字段
const filteredParams = {};
for (const [key, value] of Object.entries(params)) {
if (value !== null && value !== '' && key !== 'signature') {
filteredParams[key] = value;
}
}
// 2. 按字母顺序排序
const sortedKeys = Object.keys(filteredParams).sort();
// 3. 拼接参数
const signStr = sortedKeys
.map(key => `${key}=${filteredParams[key]}`)
.join('&');
// 4. 追加密钥并计算 SHA256
const finalStr = signStr + secretKey;
return crypto.createHash('sha256').update(finalStr).digest('hex').toLowerCase();
}Java 调用示例
java
Map<String, Object> params = new HashMap<>();
params.put("timestamp", "1742205605123");
params.put("orderAmount", "12000");
params.put("merchantOrderNo", "BDC202604170001");
params.put("payMethod", "KBZPAY");
params.put("customerName", "Rahim");
params.put("customerPhone", "01712345678");
params.put("customerEmail", "pkbuyer@gmail.com");
params.put("callbackUrl", "https://merchant.example.com/payments/notify");
String signature = SignUtil.sign(params, "demo_secret_key_bd");
System.out.println(signature);
// 9819cec934445dbf9a810941a63e563fd7f3f2c2d0e33359103b644d47c3863cNode.js 调用示例
javascript
const params = {
timestamp: '1742205605123',
orderAmount: '12000',
merchantOrderNo: 'BDC202604170001',
payMethod: 'KBZPAY',
customerName: 'Rahim',
customerPhone: '01712345678',
customerEmail: 'pkbuyer@gmail.com',
callbackUrl: 'https://merchant.example.com/payments/notify',
};
const signature = generateSignature(params, 'demo_secret_key_bd');
console.log(signature);
// 9819cec934445dbf9a810941a63e563fd7f3f2c2d0e33359103b644d47c3863c