Skip to content

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);
    }
}