Visitar URL original
【微信支付】支持完全公钥模式 by SynchPj · Pull Request #3587 · binarywang/WxJava · GitHub
Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
🎨 【微信支付】支持完全公钥模式
  • Loading branch information
SynchPj
SynchPj committed May 14, 2025
commit 31b4b05acc01b693c70a922d71129f03cd50e1f4
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,16 @@ static Verifier build(
return null;
}

/**
* 针对完全使用公钥的场景
* @param publicKeyId 公钥id
* @param publicKey 公钥
* @return
*/
static Verifier buildPublicCertVerifier(String publicKeyId, PublicKey publicKey) {
return getPublicCertVerifier(publicKeyId, publicKey, null);
}

/**
* 获取证书验证器.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -232,6 +232,11 @@ public class WxPayConfig {
*/
private boolean strictlyNeedWechatPaySerial = false;

/**
* 是否完全使用公钥模式(用以微信从平台证书到公钥的灰度切换),默认不使用
*/
private boolean fullPublicKeyModel = false;

/**
* 返回所设置的微信支付接口请求地址域名.
*
Expand Down Expand Up @@ -289,48 +294,76 @@ public CloseableHttpClient initApiV3HttpClient() throws WxPayException {
if (StringUtils.isBlank(this.getApiV3Key())) {
throw new WxPayException("请确保apiV3Key值已设置");
}

// 尝试从p12证书中加载私钥和证书
PrivateKey merchantPrivateKey = null;
X509Certificate certificate = null;
Object[] objects = this.p12ToPem();
if (objects != null) {
merchantPrivateKey = (PrivateKey) objects[0];
certificate = (X509Certificate) objects[1];
this.certSerialNo = certificate.getSerialNumber().toString(16).toUpperCase();
}
try {
if (merchantPrivateKey == null && StringUtils.isNotBlank(this.getPrivateKeyPath())) {
try (InputStream keyInputStream = this.loadConfigInputStream(this.getPrivateKeyString(), this.getPrivateKeyPath(),
this.privateKeyContent, "privateKeyPath")) {
merchantPrivateKey = PemUtils.loadPrivateKey(keyInputStream);
PrivateKey merchantPrivateKey = null;
PublicKey publicKey = null;

// 使用完全公钥模式时,只加载公钥相关配置,避免下载平台证书使灰度切换无法达到100%覆盖
if (this.fullPublicKeyModel) {
if (StringUtils.isBlank(this.getCertSerialNo())) {
throw new WxPayException("使用公钥模式时,请确保certSerialNo(apiV3证书序列号)值已设置");
}
}
if (certificate == null && StringUtils.isBlank(this.getCertSerialNo()) && StringUtils.isNotBlank(this.getPrivateCertPath())) {
try (InputStream certInputStream = this.loadConfigInputStream(this.getPrivateCertString(), this.getPrivateCertPath(),
this.privateCertContent, "privateCertPath")) {
certificate = PemUtils.loadCertificate(certInputStream);
if (StringUtils.isBlank(this.getPublicKeyId())) {
throw new WxPayException("使用公钥模式时,请确保publicKeyId值已设置");
}
this.certSerialNo = certificate.getSerialNumber().toString(16).toUpperCase();
}
PublicKey publicKey = null;
if (this.getPublicKeyString() != null || this.getPublicKeyPath() != null || this.publicKeyContent != null) {
if (StringUtils.isBlank(this.getPublicKeyString()) && StringUtils.isBlank(this.getPublicKeyPath()) && this.getPublicKeyContent() == null) {
throw new WxPayException("使用公钥模式时,请确保publicKeyString/publicKeyPath/publicKeyContent其中一项值已设置");
}

try (InputStream pubInputStream =
this.loadConfigInputStream(this.getPublicKeyString(), this.getPublicKeyPath(),
this.publicKeyContent, "publicKeyPath")) {
this.getPublicKeyContent(), "publicKeyPath")) {
publicKey = PemUtils.loadPublicKey(pubInputStream);
}
} else {
// 使用完全公钥模式时,同时兼容平台证书和公钥
X509Certificate certificate = null;
// 尝试从p12证书中加载私钥和证书
Object[] objects = this.p12ToPem();
if (objects != null) {
merchantPrivateKey = (PrivateKey) objects[0];
certificate = (X509Certificate) objects[1];
this.certSerialNo = certificate.getSerialNumber().toString(16).toUpperCase();
}
if (certificate == null && StringUtils.isBlank(this.getCertSerialNo()) && StringUtils.isNotBlank(this.getPrivateCertPath())) {
try (InputStream certInputStream = this.loadConfigInputStream(this.getPrivateCertString(), this.getPrivateCertPath(),
this.privateCertContent, "privateCertPath")) {
certificate = PemUtils.loadCertificate(certInputStream);
}
this.certSerialNo = certificate.getSerialNumber().toString(16).toUpperCase();
}
if (this.getPublicKeyString() != null || this.getPublicKeyPath() != null || this.publicKeyContent != null) {
if (StringUtils.isBlank(this.getPublicKeyId())) {
throw new WxPayException("请确保和publicKeyId配套使用");
}
try (InputStream pubInputStream =
this.loadConfigInputStream(this.getPublicKeyString(), this.getPublicKeyPath(),
this.publicKeyContent, "publicKeyPath")) {
publicKey = PemUtils.loadPublicKey(pubInputStream);
}
}
}

// 加载api私钥
if (merchantPrivateKey == null && StringUtils.isNotBlank(this.getPrivateKeyPath())) {
try (InputStream keyInputStream = this.loadConfigInputStream(this.getPrivateKeyString(), this.getPrivateKeyPath(),
this.privateKeyContent, "privateKeyPath")) {
merchantPrivateKey = PemUtils.loadPrivateKey(keyInputStream);
}
}

//构造Http Proxy正向代理
WxPayHttpProxy wxPayHttpProxy = getWxPayHttpProxy();

// 构造证书验签器
Verifier certificatesVerifier = VerifierBuilder.build(
this.getCertSerialNo(), this.getMchId(), this.getApiV3Key(), merchantPrivateKey, wxPayHttpProxy,
this.getCertAutoUpdateTime(), this.getPayBaseUrl(),
this.getPublicKeyId(), publicKey
);
Verifier certificatesVerifier;
if (this.fullPublicKeyModel) {
certificatesVerifier = VerifierBuilder.buildPublicCertVerifier(this.publicKeyId, publicKey);
} else {
certificatesVerifier = VerifierBuilder.build(
this.getCertSerialNo(), this.getMchId(), this.getApiV3Key(), merchantPrivateKey, wxPayHttpProxy,
this.getCertAutoUpdateTime(), this.getPayBaseUrl(), this.getPublicKeyId(), publicKey);
}

WxPayV3HttpClientBuilder wxPayV3HttpClientBuilder = WxPayV3HttpClientBuilder.create()
.withMerchant(mchId, certSerialNo, merchantPrivateKey)
Expand Down