|
@ -3,12 +3,13 @@ package com.gxwebsoft.common.core.utils; |
|
|
import com.gxwebsoft.common.core.config.CertificateProperties; |
|
|
import com.gxwebsoft.common.core.config.CertificateProperties; |
|
|
import com.gxwebsoft.common.system.entity.Payment; |
|
|
import com.gxwebsoft.common.system.entity.Payment; |
|
|
import lombok.extern.slf4j.Slf4j; |
|
|
import lombok.extern.slf4j.Slf4j; |
|
|
|
|
|
import org.springframework.beans.factory.annotation.Value; |
|
|
import org.springframework.stereotype.Component; |
|
|
import org.springframework.stereotype.Component; |
|
|
import org.springframework.util.StringUtils; |
|
|
import org.springframework.util.StringUtils; |
|
|
|
|
|
|
|
|
/** |
|
|
/** |
|
|
* 微信支付配置验证工具 |
|
|
* 微信支付配置验证工具 |
|
|
* |
|
|
|
|
|
|
|
|
* |
|
|
* @author 科技小王子 |
|
|
* @author 科技小王子 |
|
|
* @since 2025-07-27 |
|
|
* @since 2025-07-27 |
|
|
*/ |
|
|
*/ |
|
@ -19,6 +20,9 @@ public class WechatPayConfigValidator { |
|
|
private final CertificateProperties certConfig; |
|
|
private final CertificateProperties certConfig; |
|
|
private final CertificateLoader certificateLoader; |
|
|
private final CertificateLoader certificateLoader; |
|
|
|
|
|
|
|
|
|
|
|
@Value("${spring.profiles.active}") |
|
|
|
|
|
private String activeProfile; |
|
|
|
|
|
|
|
|
public WechatPayConfigValidator(CertificateProperties certConfig, CertificateLoader certificateLoader) { |
|
|
public WechatPayConfigValidator(CertificateProperties certConfig, CertificateLoader certificateLoader) { |
|
|
this.certConfig = certConfig; |
|
|
this.certConfig = certConfig; |
|
|
this.certificateLoader = certificateLoader; |
|
|
this.certificateLoader = certificateLoader; |
|
@ -26,34 +30,34 @@ public class WechatPayConfigValidator { |
|
|
|
|
|
|
|
|
/** |
|
|
/** |
|
|
* 验证微信支付配置 |
|
|
* 验证微信支付配置 |
|
|
* |
|
|
|
|
|
|
|
|
* |
|
|
* @param payment 支付配置 |
|
|
* @param payment 支付配置 |
|
|
* @param tenantId 租户ID |
|
|
* @param tenantId 租户ID |
|
|
* @return 验证结果 |
|
|
* @return 验证结果 |
|
|
*/ |
|
|
*/ |
|
|
public ValidationResult validateWechatPayConfig(Payment payment, Integer tenantId) { |
|
|
public ValidationResult validateWechatPayConfig(Payment payment, Integer tenantId) { |
|
|
ValidationResult result = new ValidationResult(); |
|
|
ValidationResult result = new ValidationResult(); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
log.info("开始验证微信支付配置 - 租户ID: {}", tenantId); |
|
|
log.info("开始验证微信支付配置 - 租户ID: {}", tenantId); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 1. 验证基本配置
|
|
|
// 1. 验证基本配置
|
|
|
if (payment == null) { |
|
|
if (payment == null) { |
|
|
result.addError("支付配置为空"); |
|
|
result.addError("支付配置为空"); |
|
|
return result; |
|
|
return result; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (!StringUtils.hasText(payment.getMchId())) { |
|
|
if (!StringUtils.hasText(payment.getMchId())) { |
|
|
result.addError("商户号未配置"); |
|
|
result.addError("商户号未配置"); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (!StringUtils.hasText(payment.getAppId())) { |
|
|
if (!StringUtils.hasText(payment.getAppId())) { |
|
|
result.addError("应用ID未配置"); |
|
|
result.addError("应用ID未配置"); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (!StringUtils.hasText(payment.getMerchantSerialNumber())) { |
|
|
if (!StringUtils.hasText(payment.getMerchantSerialNumber())) { |
|
|
result.addError("商户证书序列号未配置"); |
|
|
result.addError("商户证书序列号未配置"); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 2. 验证 APIv3 密钥
|
|
|
// 2. 验证 APIv3 密钥
|
|
|
String apiV3Key = getValidApiV3Key(payment); |
|
|
String apiV3Key = getValidApiV3Key(payment); |
|
|
if (!StringUtils.hasText(apiV3Key)) { |
|
|
if (!StringUtils.hasText(apiV3Key)) { |
|
@ -61,35 +65,35 @@ public class WechatPayConfigValidator { |
|
|
} else { |
|
|
} else { |
|
|
validateApiV3Key(apiV3Key, result); |
|
|
validateApiV3Key(apiV3Key, result); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 3. 验证证书文件
|
|
|
// 3. 验证证书文件
|
|
|
validateCertificateFiles(tenantId, result); |
|
|
validateCertificateFiles(tenantId, result); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 4. 记录验证结果
|
|
|
// 4. 记录验证结果
|
|
|
if (result.isValid()) { |
|
|
if (result.isValid()) { |
|
|
log.info("✅ 微信支付配置验证通过 - 租户ID: {}", tenantId); |
|
|
log.info("✅ 微信支付配置验证通过 - 租户ID: {}", tenantId); |
|
|
} else { |
|
|
} else { |
|
|
log.error("❌ 微信支付配置验证失败 - 租户ID: {}, 错误: {}", tenantId, result.getErrors()); |
|
|
log.error("❌ 微信支付配置验证失败 - 租户ID: {}, 错误: {}", tenantId, result.getErrors()); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return result; |
|
|
return result; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
/** |
|
|
* 获取有效的 APIv3 密钥 |
|
|
* 获取有效的 APIv3 密钥 |
|
|
* 优先使用数据库配置,如果为空则使用配置文件默认值 |
|
|
* 优先使用数据库配置,如果为空则使用配置文件默认值 |
|
|
*/ |
|
|
*/ |
|
|
public String getValidApiV3Key(Payment payment) { |
|
|
public String getValidApiV3Key(Payment payment) { |
|
|
String apiV3Key = payment.getApiKey(); |
|
|
String apiV3Key = payment.getApiKey(); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (!StringUtils.hasText(apiV3Key)) { |
|
|
if (!StringUtils.hasText(apiV3Key)) { |
|
|
apiV3Key = certConfig.getWechatPay().getDev().getApiV3Key(); |
|
|
apiV3Key = certConfig.getWechatPay().getDev().getApiV3Key(); |
|
|
log.warn("数据库中APIv3密钥为空,使用配置文件默认值"); |
|
|
log.warn("数据库中APIv3密钥为空,使用配置文件默认值"); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return apiV3Key; |
|
|
return apiV3Key; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
/** |
|
|
* 验证 APIv3 密钥格式 |
|
|
* 验证 APIv3 密钥格式 |
|
|
*/ |
|
|
*/ |
|
@ -97,43 +101,49 @@ public class WechatPayConfigValidator { |
|
|
if (apiV3Key.length() != 32) { |
|
|
if (apiV3Key.length() != 32) { |
|
|
result.addError("APIv3密钥长度错误,应为32位,实际为: " + apiV3Key.length()); |
|
|
result.addError("APIv3密钥长度错误,应为32位,实际为: " + apiV3Key.length()); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (!apiV3Key.matches("^[a-zA-Z0-9]+$")) { |
|
|
if (!apiV3Key.matches("^[a-zA-Z0-9]+$")) { |
|
|
result.addError("APIv3密钥格式错误,应仅包含字母和数字"); |
|
|
result.addError("APIv3密钥格式错误,应仅包含字母和数字"); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
log.info("APIv3密钥验证 - 长度: {}, 格式: {}", |
|
|
|
|
|
apiV3Key.length(), |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
log.info("APIv3密钥验证 - 长度: {}, 格式: {}", |
|
|
|
|
|
apiV3Key.length(), |
|
|
apiV3Key.matches("^[a-zA-Z0-9]+$") ? "正确" : "错误"); |
|
|
apiV3Key.matches("^[a-zA-Z0-9]+$") ? "正确" : "错误"); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
/** |
|
|
* 验证证书文件 |
|
|
* 验证证书文件 |
|
|
*/ |
|
|
*/ |
|
|
private void validateCertificateFiles(Integer tenantId, ValidationResult result) { |
|
|
private void validateCertificateFiles(Integer tenantId, ValidationResult result) { |
|
|
String tenantCertPath = "dev/wechat/" + tenantId; |
|
|
|
|
|
String privateKeyPath = tenantCertPath + "/" + certConfig.getWechatPay().getDev().getPrivateKeyFile(); |
|
|
|
|
|
|
|
|
|
|
|
if (!certificateLoader.certificateExists(privateKeyPath)) { |
|
|
|
|
|
result.addError("证书文件不存在: " + privateKeyPath); |
|
|
|
|
|
return; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
try { |
|
|
|
|
|
String privateKey = certificateLoader.loadCertificatePath(privateKeyPath); |
|
|
|
|
|
log.info("✅ 证书文件验证通过: {}", privateKey); |
|
|
|
|
|
} catch (Exception e) { |
|
|
|
|
|
result.addError("证书文件加载失败: " + e.getMessage()); |
|
|
|
|
|
|
|
|
if ("dev".equals(activeProfile)) { |
|
|
|
|
|
// 开发环境证书验证
|
|
|
|
|
|
String tenantCertPath = "dev/wechat/" + tenantId; |
|
|
|
|
|
String privateKeyPath = tenantCertPath + "/" + certConfig.getWechatPay().getDev().getPrivateKeyFile(); |
|
|
|
|
|
|
|
|
|
|
|
if (!certificateLoader.certificateExists(privateKeyPath)) { |
|
|
|
|
|
result.addError("证书文件不存在: " + privateKeyPath); |
|
|
|
|
|
return; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
try { |
|
|
|
|
|
certificateLoader.loadCertificatePath(privateKeyPath); |
|
|
|
|
|
log.info("✅ 开发环境证书文件验证通过: {}", privateKeyPath); |
|
|
|
|
|
} catch (Exception e) { |
|
|
|
|
|
result.addError("证书文件加载失败: " + e.getMessage()); |
|
|
|
|
|
} |
|
|
|
|
|
} else { |
|
|
|
|
|
// 生产环境证书验证 - 跳过文件存在性检查,因为证书路径来自数据库
|
|
|
|
|
|
log.info("✅ 生产环境跳过证书文件存在性验证,使用数据库配置的证书路径"); |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
/** |
|
|
* 验证结果类 |
|
|
* 验证结果类 |
|
|
*/ |
|
|
*/ |
|
|
public static class ValidationResult { |
|
|
public static class ValidationResult { |
|
|
private boolean valid = true; |
|
|
private boolean valid = true; |
|
|
private StringBuilder errors = new StringBuilder(); |
|
|
private StringBuilder errors = new StringBuilder(); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public void addError(String error) { |
|
|
public void addError(String error) { |
|
|
this.valid = false; |
|
|
this.valid = false; |
|
|
if (errors.length() > 0) { |
|
|
if (errors.length() > 0) { |
|
@ -141,22 +151,22 @@ public class WechatPayConfigValidator { |
|
|
} |
|
|
} |
|
|
errors.append(error); |
|
|
errors.append(error); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public boolean isValid() { |
|
|
public boolean isValid() { |
|
|
return valid; |
|
|
return valid; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public String getErrors() { |
|
|
public String getErrors() { |
|
|
return errors.toString(); |
|
|
return errors.toString(); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public void logErrors() { |
|
|
public void logErrors() { |
|
|
if (!valid) { |
|
|
if (!valid) { |
|
|
log.error("配置验证失败: {}", errors.toString()); |
|
|
log.error("配置验证失败: {}", errors.toString()); |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
/** |
|
|
* 生成配置诊断报告 |
|
|
* 生成配置诊断报告 |
|
|
*/ |
|
|
*/ |
|
@ -164,41 +174,50 @@ public class WechatPayConfigValidator { |
|
|
StringBuilder report = new StringBuilder(); |
|
|
StringBuilder report = new StringBuilder(); |
|
|
report.append("=== 微信支付配置诊断报告 ===\n"); |
|
|
report.append("=== 微信支付配置诊断报告 ===\n"); |
|
|
report.append("租户ID: ").append(tenantId).append("\n"); |
|
|
report.append("租户ID: ").append(tenantId).append("\n"); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (payment != null) { |
|
|
if (payment != null) { |
|
|
report.append("商户号: ").append(payment.getMchId()).append("\n"); |
|
|
report.append("商户号: ").append(payment.getMchId()).append("\n"); |
|
|
report.append("应用ID: ").append(payment.getAppId()).append("\n"); |
|
|
report.append("应用ID: ").append(payment.getAppId()).append("\n"); |
|
|
report.append("商户证书序列号: ").append(payment.getMerchantSerialNumber()).append("\n"); |
|
|
report.append("商户证书序列号: ").append(payment.getMerchantSerialNumber()).append("\n"); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
String dbApiKey = payment.getApiKey(); |
|
|
String dbApiKey = payment.getApiKey(); |
|
|
String configApiKey = certConfig.getWechatPay().getDev().getApiV3Key(); |
|
|
String configApiKey = certConfig.getWechatPay().getDev().getApiV3Key(); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
report.append("数据库APIv3密钥: ").append(dbApiKey != null ? "已配置(" + dbApiKey.length() + "位)" : "未配置").append("\n"); |
|
|
report.append("数据库APIv3密钥: ").append(dbApiKey != null ? "已配置(" + dbApiKey.length() + "位)" : "未配置").append("\n"); |
|
|
report.append("配置文件APIv3密钥: ").append(configApiKey != null ? "已配置(" + configApiKey.length() + "位)" : "未配置").append("\n"); |
|
|
report.append("配置文件APIv3密钥: ").append(configApiKey != null ? "已配置(" + configApiKey.length() + "位)" : "未配置").append("\n"); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
String finalApiKey = getValidApiV3Key(payment); |
|
|
String finalApiKey = getValidApiV3Key(payment); |
|
|
report.append("最终使用APIv3密钥: ").append(finalApiKey != null ? "已配置(" + finalApiKey.length() + "位)" : "未配置").append("\n"); |
|
|
report.append("最终使用APIv3密钥: ").append(finalApiKey != null ? "已配置(" + finalApiKey.length() + "位)" : "未配置").append("\n"); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
} else { |
|
|
} else { |
|
|
report.append("❌ 支付配置为空\n"); |
|
|
report.append("❌ 支付配置为空\n"); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 证书文件检查
|
|
|
// 证书文件检查
|
|
|
String tenantCertPath = "dev/wechat/" + tenantId; |
|
|
|
|
|
String privateKeyPath = tenantCertPath + "/" + certConfig.getWechatPay().getDev().getPrivateKeyFile(); |
|
|
|
|
|
boolean certExists = certificateLoader.certificateExists(privateKeyPath); |
|
|
|
|
|
|
|
|
|
|
|
report.append("证书文件路径: ").append(privateKeyPath).append("\n"); |
|
|
|
|
|
report.append("证书文件存在: ").append(certExists ? "是" : "否").append("\n"); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
report.append("当前环境: ").append(activeProfile).append("\n"); |
|
|
|
|
|
if ("dev".equals(activeProfile)) { |
|
|
|
|
|
String tenantCertPath = "dev/wechat/" + tenantId; |
|
|
|
|
|
String privateKeyPath = tenantCertPath + "/" + certConfig.getWechatPay().getDev().getPrivateKeyFile(); |
|
|
|
|
|
boolean certExists = certificateLoader.certificateExists(privateKeyPath); |
|
|
|
|
|
|
|
|
|
|
|
report.append("开发环境证书文件路径: ").append(privateKeyPath).append("\n"); |
|
|
|
|
|
report.append("证书文件存在: ").append(certExists ? "是" : "否").append("\n"); |
|
|
|
|
|
} else { |
|
|
|
|
|
report.append("生产环境证书路径: 从数据库配置获取\n"); |
|
|
|
|
|
if (payment != null) { |
|
|
|
|
|
report.append("私钥文件: ").append(payment.getApiclientKey()).append("\n"); |
|
|
|
|
|
report.append("证书文件: ").append(payment.getApiclientCert()).append("\n"); |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
ValidationResult validation = validateWechatPayConfig(payment, tenantId); |
|
|
ValidationResult validation = validateWechatPayConfig(payment, tenantId); |
|
|
report.append("配置验证结果: ").append(validation.isValid() ? "通过" : "失败").append("\n"); |
|
|
report.append("配置验证结果: ").append(validation.isValid() ? "通过" : "失败").append("\n"); |
|
|
if (!validation.isValid()) { |
|
|
if (!validation.isValid()) { |
|
|
report.append("验证错误: ").append(validation.getErrors()).append("\n"); |
|
|
report.append("验证错误: ").append(validation.getErrors()).append("\n"); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
report.append("=== 诊断报告结束 ==="); |
|
|
report.append("=== 诊断报告结束 ==="); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return report.toString(); |
|
|
return report.toString(); |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|