平台通过请求头里的商户号和秘钥/签名,来验证商户的API请求。
商户号:标记所属商户,请求头里设置为:X-SN
秘钥:验证请求合法性的默认手段,请求头里设置为:X-SECRET
测试环境下:
KEY | VALUE |
---|---|
X-SN | 7CfvQJ |
X-SECRET | AQFYLJNJJ5J35PPXZ3TCNFDXDHJTQRGZ |
生产环境的商户号和秘钥,在开户邮件里获取,也可以通过支付后台查看和重置
签名验证请求合法性的升级手段,它和秘钥是二选一。默认情况下是关闭的,但是一旦您在支付后台开了验签,则必须使用签名
签名:验证请求合法性的升级手段,请求头里设置为:X-SIGN
如何签名
- 使用HMAC-SHA256算法加密。HMAC-SHA256算法接收2个参数:秘钥和加密数据。秘钥就是商户秘钥X-SECRET,加密数据就是整个请求体的字符串(注意不要对字符串进行排序,直接使用请求体字符串)。如果请求体是空,加密数据使用空字符串:“”
- 对上面得到的加密后的字节数据进行Base64编码
验签示例
以下示例最终签名结果是:vYXBBgF0QdseMnViYCfX1HykQ+TyLp0kErK8f/FRjAw=
package org.example;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.util.Base64;
public class HmacSha256Signature {
public static void main(String[] args) {
String secretKey = "AQFYLJNJJ5J35PPXZ3TCNFDXDHJTQRGZ";
/* 请注意body是无序的,不需要排序再加签和验签 */
String callbackBody = "{\"referenceId\":\"sj-test-order-1726137858\",\"orders\":[{\"id\":\"1834184726805614593\",\"msn\":\"220240912175810966987\",\"urlType\":\"URL\",\"method\":\"BANK_TRANSFER\",\"status\":\"SUCCEED\",\"amount\":100000,\"receivedAmount\":100000,\"serviceFee\":1000.0000,\"receivedTime\":1726138743000}]}";
try {
String signature = hmacSha256(secretKey, callbackBody);
System.out.println("Signature: " + signature);
} catch (NoSuchAlgorithmException | InvalidKeyException e) {
e.printStackTrace();
}
}
public static String hmacSha256(String key, String data) throws NoSuchAlgorithmException, InvalidKeyException {
Mac sha256Hmac = Mac.getInstance("HmacSHA256");
SecretKeySpec secretKey = new SecretKeySpec(key.getBytes(), "HmacSHA256");
sha256Hmac.init(secretKey);
byte[] signedBytes = sha256Hmac.doFinal(data.getBytes());
return Base64.getEncoder().encodeToString(signedBytes);
}
}
import hmac
import hashlib
import base64
# 请注意,callbackBody 是无序的,不需要排序再加签和验签
callback_body = '{"referenceId":"sj-test-order-1726137858","orders":[{"id":"1834184726805614593","msn":"220240912175810966987","urlType":"URL","method":"BANK_TRANSFER","status":"SUCCEED","amount":100000,"receivedAmount":100000,"serviceFee":1000.0000,"receivedTime":1726138743000}]}'
secret_key = "AQFYLJNJJ5J35PPXZ3TCNFDXDHJTQRGZ"
# 使用 HMAC-SHA256 对 callbackBody 进行加密
hmac_digest = hmac.new(secret_key.encode(), callback_body.encode(), hashlib.sha256).digest()
# 将二进制结果转换为 Base64 编码
signature = base64.b64encode(hmac_digest).decode()
print("Signature:", signature)
// 请注意,$callbackBody 是无序的,不需要排序再加签和验签
$callbackBody = '{"referenceId":"sj-test-order-1726137858","orders":[{"id":"1834184726805614593","msn":"220240912175810966987","urlType":"URL","method":"BANK_TRANSFER","status":"SUCCEED","amount":100000,"receivedAmount":100000,"serviceFee":1000.0000,"receivedTime":1726138743000}]}';
$secretKey = "AQFYLJNJJ5J35PPXZ3TCNFDXDHJTQRGZ";
$signature = base64_encode(hash_hmac('sha256', $callbackBody, $secretKey, true));
echo "Signature: " . $signature;
?>
回调验签
平台给商户的回调都是有签名,也是在header里标记为X-SIGN,商户可以自行决定要不要对支付的回调信息进行验签。
加签是直接对请求体字符串进行签名,不要排序之后再签名。验签也是这个过程,不要把请求体字符串转成JSON对象或者POJO之后再验签,因为各个语言、系统对格式、数字精度默认处理不一样,可能会造成验签失败