How to Dynamically Retrieve and Refresh WeChat Pay V3 Platform Certificates in Java
This tutorial explains how to obtain the WeChat Pay V3 platform certificate public key, decrypt the AES‑256‑GCM encrypted response, and implement a thread‑safe dynamic refresh mechanism in Java to ensure continuous verification of payment callbacks.
1. Introduction
When calling WeChat Pay V3 APIs, the server must sign requests with its own API certificate and also verify responses using the WeChat Pay platform certificate public key. This article explains how to obtain the platform public key and refresh it dynamically.
2. Obtaining the Platform Certificate Public Key
The platform certificate is managed by WeChat and rotates periodically, so it must be fetched regularly via the /v3/certificates endpoint, which itself requires a signed request.
The platform certificate interface documentation: https://wechatpay-api.gitbook.io/wechatpay-api-v3/jie-kou-wen-dang/ping-tai-zheng-shu
3. Decrypting the Certificate and Callback Payloads
WeChat Pay encrypts sensitive fields in callback notifications and the certificate download response with AES‑256‑GCM. The response body has the following structure:
{
"data": [
{
"effective_time": "2020-10-21T14:48:49+08:00",
"encrypt_certificate": {
"algorithm": "AEAD_AES_256_GCM",
"associated_data": "certificate",
"ciphertext": "",
"nonce": "88b4e15a0db9"
},
"expire_time": "2025-10-20T14:48:49+08:00",
"serial_no": "217016F42805DD4D5442059D373F98BFC5252599"
}
]
}Use the APIv3 key to decrypt the ciphertext. A typical Java decryption method is:
public String decryptResponseBody(String apiV3Key, String associatedData, String nonce, String ciphertext) {
try {
Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
SecretKeySpec key = new SecretKeySpec(apiV3Key.getBytes(StandardCharsets.UTF_8), "AES");
GCMParameterSpec spec = new GCMParameterSpec(128, nonce.getBytes(StandardCharsets.UTF_8));
cipher.init(Cipher.DECRYPT_MODE, key, spec);
cipher.updateAAD(associatedData.getBytes(StandardCharsets.UTF_8));
byte[] bytes = cipher.doFinal(Base64Utils.decodeFromString(ciphertext));
return new String(bytes, StandardCharsets.UTF_8);
} catch (Exception e) {
throw new IllegalArgumentException(e);
}
}The same method is used to decrypt callback request bodies.
4. Dynamic Refresh
Store certificates in a thread‑safe map keyed by their serial number and refresh them when the map is empty or the serial number is missing:
// Global container for certificates (thread‑safe)
private static final Map<String, Certificate> CERTIFICATE_MAP = new ConcurrentHashMap<>();
// Refresh core logic
String publicKey = decryptResponseBody(associatedData, nonce, ciphertext);
CertificateFactory cf = CertificateFactory.getInstance("X509");
Certificate certificate = cf.generateCertificate(new ByteArrayInputStream(publicKey.getBytes(StandardCharsets.UTF_8)));
String responseSerialNo = objectNode.get("serial_no").asText();
CERTIFICATE_MAP.clear();
CERTIFICATE_MAP.put(responseSerialNo, certificate);
// Usage
if (CERTIFICATE_MAP.isEmpty() || !CERTIFICATE_MAP.containsKey(wechatpaySerial)) {
refreshCertificate();
}
Certificate certificate = CERTIFICATE_MAP.get(wechatpaySerial);5. Conclusion
Verifying responses is essential for financial security, and dynamically refreshing the WeChat Pay platform certificate eliminates expiration concerns. The next article will cover signature verification using the obtained certificate.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Programmer DD
A tinkering programmer and author of "Spring Cloud Microservices in Action"
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
