Java 示例
示例为:
POST /payIn/orders/createAndPay,签名串为timestamp|nonce|rawBody。
java
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
import java.util.Base64;
import java.util.UUID;
public class PayApiClient {
static String sign(String timestamp, String nonce, String body, String secret) throws Exception {
String signData = timestamp + "|" + nonce + "|" + body;
Mac mac = Mac.getInstance("HmacSHA256");
mac.init(new SecretKeySpec(secret.getBytes(StandardCharsets.UTF_8), "HmacSHA256"));
byte[] hash = mac.doFinal(signData.getBytes(StandardCharsets.UTF_8));
return Base64.getEncoder().encodeToString(hash);
}
public static void main(String[] args) throws Exception {
String body = "{\"merchantOrderNo\":\"M202412220001\",\"amount\":\"100.00\",\"currency\":\"USD\","
+ "\"methodCode\":\"INTERNATIONAL_CARD\",\"methodData\":{\"cardNumber\":\"4111111111111111\","
+ "\"expiryMonth\":\"12\",\"expiryYear\":\"27\",\"securityCode\":\"123\"}}";
String timestamp = String.valueOf(System.currentTimeMillis());
String nonce = UUID.randomUUID().toString().replace("-", "");
String sign = sign(timestamp, nonce, body, "sk_test_9f3b8a2d7c1e4f6a8b0c2d4e6f8a1b3c");
// 用你的 HTTP 客户端发送请求,附带 Header:
// X-Merchant-Id, X-Timestamp, X-Nonce, X-Sign
}
}验签示例(回调)
rawBody 为实际接收的原始 JSON 字符串。
java
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.util.Base64;
public class WebhookVerifier {
static String sign(String timestamp, String nonce, String body, String secret) throws Exception {
String signData = timestamp + "|" + nonce + "|" + body;
Mac mac = Mac.getInstance("HmacSHA256");
mac.init(new SecretKeySpec(secret.getBytes(StandardCharsets.UTF_8), "HmacSHA256"));
byte[] hash = mac.doFinal(signData.getBytes(StandardCharsets.UTF_8));
return Base64.getEncoder().encodeToString(hash);
}
static boolean verify(String timestamp, String nonce, String rawBody, String secret, String signHeader)
throws Exception {
String expected = sign(timestamp, nonce, rawBody, secret);
return MessageDigest.isEqual(expected.getBytes(StandardCharsets.UTF_8),
signHeader.getBytes(StandardCharsets.UTF_8));
}
public static void main(String[] args) throws Exception {
String rawBody = "{\"payNo\":\"P202312230001\",\"tradeStatus\":\"SUCCESS\"}";
String timestamp = "1734921005000";
String nonce = "b2b2f3b6a6f24a4ba3dcd0e777c9a888";
String signHeader = "base64_signature_from_header";
boolean ok = verify(timestamp, nonce, rawBody, "sk_test_9f3b8a2d7c1e4f6a8b0c2d4e6f8a1b3c", signHeader);
// ok 为 true 表示验签通过
System.out.println(ok);
}
}