WeChat Pay V3: Parse API Certificates & Generate Secure Signatures in Java
This guide walks you through the essential steps for handling WeChat Pay V3 integration, including obtaining and parsing the API certificate with Java's KeyStore, extracting the key pair, constructing the required signature string, performing SHA256withRSA signing, and assembling the Authorization token for secure payment requests.
1. Introduction
Recently I have been working on WeChat Pay integration, and the certificates can be quite troublesome, so it is necessary to share some experience to reduce the pitfalls when developing WeChat Pay. The current WeChat Pay API has evolved to the V3 version and adopts a popular Restful style.
Today I share the hardest part of WeChat Pay – the signature . Although many useful SDKs exist, understanding the signature deeply is still helpful.
2. API Certificate
To ensure the security of sensitive financial data and guarantee that transactions are flawless, the authoritative CA certificate (API certificate) issued by WeChat Pay provides a private key for signing. You can set and obtain the API certificate through the merchant platform.
Note that the first time you set it up you will be prompted to download; later downloads are no longer provided. See the documentation for details.
After setting, find the zip package, unzip it, and for Java development you only need to focus on the apiclient_cert.p12 file, which contains the public and private keys. Place it on the server and use Java to parse the .p12 file to obtain the key pair.
Be sure to keep the certificate secure on the server; it involves fund security.
Parsing API Certificate
The next step is parsing the certificate. Many methods exist online; here we use a more "formal" approach with JDK's security package java.security.KeyStore.
WeChat Pay API certificates use the PKCS12 algorithm. By using KeyStore we obtain a KeyPair and the certificate serial number serialNumber. I have encapsulated a utility class (you handle the serial number yourself):
import org.springframework.core.io.ClassPathResource;
import java.security.KeyPair;
import java.security.KeyStore;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.cert.X509Certificate;
/**
* KeyPairFactory
*
* @author dax
* @since 13:41
*/
public class KeyPairFactory {
private KeyStore store;
private final Object lock = new Object();
/**
* Get public and private key.
*
* @param keyPath the key path
* @param keyAlias the key alias
* @param keyPass password
* @return the key pair
*/
public KeyPair createPKCS12(String keyPath, String keyAlias, String keyPass) {
ClassPathResource resource = new ClassPathResource(keyPath);
char[] pem = keyPass.toCharArray();
try {
synchronized (lock) {
if (store == null) {
synchronized (lock) {
store = KeyStore.getInstance("PKCS12");
store.load(resource.getInputStream(), pem);
}
}
}
X509Certificate certificate = (X509Certificate) store.getCertificate(keyAlias);
certificate.checkValidity();
// certificate serial number is also useful
String serialNumber = certificate.getSerialNumber().toString(16).toUpperCase();
// public key
PublicKey publicKey = certificate.getPublicKey();
// private key
PrivateKey storeKey = (PrivateKey) store.getKey(keyAlias, pem);
return new KeyPair(publicKey, storeKey);
} catch (Exception e) {
throw new IllegalStateException("Cannot load keys from store: " + resource, e);
}
}
}It resembles the public‑private key extraction method used in Spring Security JWT tutorials.
This method has three parameters, which need explanation: keyPath – the classpath location of apiclient_cert.p12, usually placed under resources. keyAlias – the certificate alias; WeChat documentation does not provide it, but debugging shows it is fixed as Tenpay Certificate. keyPass – the certificate password, which defaults to the merchant ID ( mchid), the numeric string you see when logging into the merchant platform as a super administrator.
3. V3 Signature
The WeChat Pay V3 signature is carried in the HTTP request header as a specially encoded string for the WeChat Pay server to verify the request source, ensuring the request is authentic.
Signature Format
The signature string consists of five lines, each terminated by a newline character \n:
HTTP request method
URL
timestamp
nonce string
request bodyHTTP request method – e.g., POST.
URL – the path part of the API endpoint, e.g., /v3/pay/transactions/app. If there are query parameters, append ‘?’ and the query string.
Timestamp – server system timestamp, obtained via System.currentTimeMillis() / 1000.
Nonce string – a random string such as 593BEC0C930BF1AFEB40B4A08C8FB242.
Request body – empty string "" for GET; for POST or PUT use the actual JSON payload; for image upload APIs use the meta JSON.
Generating the Signature
We sign the assembled string with the merchant private key using SHA256withRSA, then Base64‑encode the result. Core Java code:
/**
* V3 SHA256withRSA signature.
*
* @param method request method (GET, POST, PUT, DELETE, etc.)
* @param canonicalUrl e.g. https://api.mch.weixin.qq.com/v3/pay/transactions/app?version=1 → /v3/pay/transactions/app?version=1
* @param timestamp current timestamp (must match the token timestamp)
* @param nonceStr random string (must match the token nonce)
* @param body request body ("" for GET, JSON for POST)
* @param keyPair merchant API certificate key pair (private key)
* @return the signature string
*/
@SneakyThrows
String sign(String method, String canonicalUrl, long timestamp, String nonceStr, String body, KeyPair keyPair) {
String signatureStr = Stream.of(method, canonicalUrl, String.valueOf(timestamp), nonceStr, body)
.collect(Collectors.joining("
", "", "
"));
Signature sign = Signature.getInstance("SHA256withRSA");
sign.initSign(keyPair.getPrivate());
sign.update(signatureStr.getBytes(StandardCharsets.UTF_8));
return Base64Utils.encodeToString(sign.sign());
}4. Using the Signature
After generating the signature, combine it with other parameters to form a Token placed in the Authorization header:
Authorization: WECHATPAY2-SHA256-RSA2048 {Token}The token consists of five parts:
Merchant ID ( mchid)
Certificate serial number ( serial_no)
Nonce string ( nonce_str)
Timestamp ( timestamp)
Signature ( signature)
Code to build the token:
/**
* Generate Token.
*
* @param mchId merchant ID
* @param nonceStr random string
* @param timestamp timestamp
* @param serialNo certificate serial number
* @param signature signature
* @return the token string
*/
String token(String mchId, String nonceStr, long timestamp, String serialNo, String signature) {
final String TOKEN_PATTERN = "mchid=\"%s\",nonce_str=\"%s\",timestamp=\"%d\",serial_no=\"%s\",signature=\"%s\"";
return String.format(TOKEN_PATTERN, wechatPayProperties.getMchId(), nonceStr, timestamp, serialNo, signature);
}5. Summary
This article provided a complete analysis of the challenging signature aspect of WeChat Pay V3, explained how to parse the API certificate in Java, generate the required signature string, and assemble the Authorization token, helping developers solve concrete problems in payment development.
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.
