授权

平台通过请求头里的商户号秘钥/签名,来验证商户的API请求。

商户号:标记所属商户,请求头里设置为:X-SN

秘钥:验证请求合法性的默认手段,请求头里设置为:X-SECRET

测试环境下:

KEYVALUE
X-SN7CfvQJ
X-SECRETAQFYLJNJJ5J35PPXZ3TCNFDXDHJTQRGZ

📘

生产环境的商户号和秘钥,在开户邮件里获取,也可以通过支付后台查看和重置

签名验证请求合法性的升级手段,它和秘钥是二选一。默认情况下是关闭的,但是一旦您在支付后台开了验签,则必须使用签名

签名:验证请求合法性的升级手段,请求头里设置为:X-SIGN

如何签名

  1. 使用HMAC-SHA256算法加密。HMAC-SHA256算法接收2个参数:秘钥和加密数据。秘钥就是商户秘钥X-SECRET,加密数据就是整个请求体的字符串(注意不要对字符串进行排序,直接使用请求体字符串)。如果请求体是空,加密数据使用空字符串:“”
  2. 对上面得到的加密后的字节数据进行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之后再验签,因为各个语言、系统对格式、数字精度默认处理不一样,可能会造成验签失败