更新完成:完成微信支付的功能开发和测试

This commit is contained in:
2025-07-27 23:29:12 +08:00
parent 803c6344ab
commit 9b08ad04f6
16 changed files with 731 additions and 370 deletions

View File

@@ -0,0 +1,204 @@
package com.gxwebsoft.common.core.utils;
import com.gxwebsoft.common.core.config.CertificateProperties;
import com.gxwebsoft.common.system.entity.Payment;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
/**
* 微信支付配置验证工具
*
* @author 科技小王子
* @since 2025-07-27
*/
@Slf4j
@Component
public class WechatPayConfigValidator {
private final CertificateProperties certConfig;
private final CertificateLoader certificateLoader;
public WechatPayConfigValidator(CertificateProperties certConfig, CertificateLoader certificateLoader) {
this.certConfig = certConfig;
this.certificateLoader = certificateLoader;
}
/**
* 验证微信支付配置
*
* @param payment 支付配置
* @param tenantId 租户ID
* @return 验证结果
*/
public ValidationResult validateWechatPayConfig(Payment payment, Integer tenantId) {
ValidationResult result = new ValidationResult();
log.info("开始验证微信支付配置 - 租户ID: {}", tenantId);
// 1. 验证基本配置
if (payment == null) {
result.addError("支付配置为空");
return result;
}
if (!StringUtils.hasText(payment.getMchId())) {
result.addError("商户号未配置");
}
if (!StringUtils.hasText(payment.getAppId())) {
result.addError("应用ID未配置");
}
if (!StringUtils.hasText(payment.getMerchantSerialNumber())) {
result.addError("商户证书序列号未配置");
}
// 2. 验证 APIv3 密钥
String apiV3Key = getValidApiV3Key(payment);
if (!StringUtils.hasText(apiV3Key)) {
result.addError("APIv3密钥未配置");
} else {
validateApiV3Key(apiV3Key, result);
}
// 3. 验证证书文件
validateCertificateFiles(tenantId, result);
// 4. 记录验证结果
if (result.isValid()) {
log.info("✅ 微信支付配置验证通过 - 租户ID: {}", tenantId);
} else {
log.error("❌ 微信支付配置验证失败 - 租户ID: {}, 错误: {}", tenantId, result.getErrors());
}
return result;
}
/**
* 获取有效的 APIv3 密钥
* 优先使用数据库配置,如果为空则使用配置文件默认值
*/
public String getValidApiV3Key(Payment payment) {
String apiV3Key = payment.getApiKey();
if (!StringUtils.hasText(apiV3Key)) {
apiV3Key = certConfig.getWechatPay().getDev().getApiV3Key();
log.warn("数据库中APIv3密钥为空使用配置文件默认值");
}
return apiV3Key;
}
/**
* 验证 APIv3 密钥格式
*/
private void validateApiV3Key(String apiV3Key, ValidationResult result) {
if (apiV3Key.length() != 32) {
result.addError("APIv3密钥长度错误应为32位实际为: " + apiV3Key.length());
}
if (!apiV3Key.matches("^[a-zA-Z0-9]+$")) {
result.addError("APIv3密钥格式错误应仅包含字母和数字");
}
log.info("APIv3密钥验证 - 长度: {}, 格式: {}",
apiV3Key.length(),
apiV3Key.matches("^[a-zA-Z0-9]+$") ? "正确" : "错误");
}
/**
* 验证证书文件
*/
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());
}
}
/**
* 验证结果类
*/
public static class ValidationResult {
private boolean valid = true;
private StringBuilder errors = new StringBuilder();
public void addError(String error) {
this.valid = false;
if (errors.length() > 0) {
errors.append("; ");
}
errors.append(error);
}
public boolean isValid() {
return valid;
}
public String getErrors() {
return errors.toString();
}
public void logErrors() {
if (!valid) {
log.error("配置验证失败: {}", errors.toString());
}
}
}
/**
* 生成配置诊断报告
*/
public String generateDiagnosticReport(Payment payment, Integer tenantId) {
StringBuilder report = new StringBuilder();
report.append("=== 微信支付配置诊断报告 ===\n");
report.append("租户ID: ").append(tenantId).append("\n");
if (payment != null) {
report.append("商户号: ").append(payment.getMchId()).append("\n");
report.append("应用ID: ").append(payment.getAppId()).append("\n");
report.append("商户证书序列号: ").append(payment.getMerchantSerialNumber()).append("\n");
String dbApiKey = payment.getApiKey();
String configApiKey = certConfig.getWechatPay().getDev().getApiV3Key();
report.append("数据库APIv3密钥: ").append(dbApiKey != null ? "已配置(" + dbApiKey.length() + "位)" : "未配置").append("\n");
report.append("配置文件APIv3密钥: ").append(configApiKey != null ? "已配置(" + configApiKey.length() + "位)" : "未配置").append("\n");
String finalApiKey = getValidApiV3Key(payment);
report.append("最终使用APIv3密钥: ").append(finalApiKey != null ? "已配置(" + finalApiKey.length() + "位)" : "未配置").append("\n");
} else {
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");
ValidationResult validation = validateWechatPayConfig(payment, tenantId);
report.append("配置验证结果: ").append(validation.isValid() ? "通过" : "失败").append("\n");
if (!validation.isValid()) {
report.append("验证错误: ").append(validation.getErrors()).append("\n");
}
report.append("=== 诊断报告结束 ===");
return report.toString();
}
}

View File

@@ -11,6 +11,7 @@ import com.gxwebsoft.common.core.config.CertificateProperties;
import com.gxwebsoft.common.core.utils.RedisUtil;
import com.gxwebsoft.common.core.utils.CertificateLoader;
import com.gxwebsoft.common.core.utils.WechatCertAutoConfig;
import com.gxwebsoft.common.core.utils.WechatPayConfigValidator;
import com.gxwebsoft.common.core.web.BaseController;
import com.gxwebsoft.common.system.entity.Payment;
import com.gxwebsoft.shop.service.ShopOrderGoodsService;
@@ -70,6 +71,8 @@ public class ShopOrderController extends BaseController {
private CertificateLoader certificateLoader;
@Resource
private WechatCertAutoConfig wechatCertAutoConfig;
@Resource
private WechatPayConfigValidator wechatPayConfigValidator;
@Value("${spring.profiles.active}")
String active;
@@ -221,6 +224,15 @@ public class ShopOrderController extends BaseController {
logger.info("开始处理微信支付异步通知 - 租户ID: {}", tenantId);
logger.info("支付配置信息 - 商户号: {}, 应用ID: {}", payment.getMchId(), payment.getAppId());
// 验证微信支付配置
WechatPayConfigValidator.ValidationResult validation = wechatPayConfigValidator.validateWechatPayConfig(payment, tenantId);
if (!validation.isValid()) {
logger.error("❌ 微信支付配置验证失败: {}", validation.getErrors());
logger.info("📋 配置诊断报告:\n{}", wechatPayConfigValidator.generateDiagnosticReport(payment, tenantId));
throw new RuntimeException("微信支付配置验证失败: " + validation.getErrors());
}
logger.info("✅ 微信支付配置验证通过");
RequestParam requestParam = new RequestParam.Builder()
.serialNumber(header.get("wechatpay-serial"))
.nonce(header.get("wechatpay-nonce"))
@@ -236,8 +248,25 @@ public class ShopOrderController extends BaseController {
// 开发环境 - 构建包含租户号的私钥路径
String tenantCertPath = "dev/wechat/" + tenantId;
String privateKeyPath = tenantCertPath + "/" + certConfig.getWechatPay().getDev().getPrivateKeyFile();
logger.info("开发环境异步通知证书路径: {}", privateKeyPath);
logger.info("租户ID: {}, 证书目录: {}", tenantId, tenantCertPath);
// 检查证书文件是否存在
if (!certificateLoader.certificateExists(privateKeyPath)) {
logger.error("证书文件不存在: {}", privateKeyPath);
throw new RuntimeException("证书文件不存在: " + privateKeyPath);
}
String privateKey = certificateLoader.loadCertificatePath(privateKeyPath);
String apiV3Key = certConfig.getWechatPay().getDev().getApiV3Key();
// 使用验证器获取有效的 APIv3 密钥
String apiV3Key = wechatPayConfigValidator.getValidApiV3Key(payment);
logger.info("私钥文件加载成功: {}", privateKey);
logger.info("使用APIv3密钥来源: {}", payment.getApiKey() != null && !payment.getApiKey().trim().isEmpty() ? "数据库配置" : "配置文件默认");
logger.info("APIv3密钥长度: {}", apiV3Key != null ? apiV3Key.length() : 0);
logger.info("商户证书序列号: {}", payment.getMerchantSerialNumber());
// 使用自动证书配置
config = new RSAAutoCertificateConfig.Builder()
@@ -247,7 +276,7 @@ public class ShopOrderController extends BaseController {
.apiV3Key(apiV3Key)
.build();
logger.info("开发环境使用自动证书配置创建通知解析器");
logger.info("开发环境使用自动证书配置创建通知解析器成功");
} else {
// 生产环境 - 使用自动证书配置
final String certRootPath = certConfig.getCertRootPath();
@@ -268,19 +297,30 @@ public class ShopOrderController extends BaseController {
.apiV3Key(apiV3Key)
.build();
logger.info("生产环境使用自动证书配置创建通知解析器");
logger.info("生产环境使用自动证书配置创建通知解析器成功");
}
} catch (Exception e) {
logger.error("创建通知配置失败", e);
logger.error("创建通知配置失败 - 租户ID: {}, 商户号: {}", tenantId, payment.getMchId(), e);
logger.error("🔍 错误详情: {}", e.getMessage());
logger.error("💡 请检查:");
logger.error("1. 证书文件是否存在且路径正确");
logger.error("2. APIv3密钥是否配置正确");
logger.error("3. 商户证书序列号是否正确");
logger.error("4. 网络连接是否正常");
throw new RuntimeException("微信支付通知配置失败: " + e.getMessage(), e);
}
// 初始化 NotificationParser
NotificationParser parser = new NotificationParser(config);
logger.info("✅ 通知解析器创建成功,准备解析异步通知");
// 以支付通知回调为例,验签、解密并转换成 Transaction
try {
logger.info("开始解析微信支付异步通知...");
Transaction transaction = parser.parse(requestParam, Transaction.class);
logger.info("✅ 异步通知解析成功 - 交易状态: {}, 商户订单号: {}",
transaction.getTradeStateDesc(), transaction.getOutTradeNo());
if (StrUtil.equals("支付成功", transaction.getTradeStateDesc())) {
final String outTradeNo = transaction.getOutTradeNo();
final String transactionId = transaction.getTransactionId();
@@ -315,10 +355,20 @@ public class ShopOrderController extends BaseController {
return "SUCCESS";
}
}
} catch (Exception $e) {
System.out.println($e.getMessage());
} catch (Exception e) {
logger.error("❌ 处理微信支付异步通知失败 - 租户ID: {}, 商户号: {}", tenantId, payment.getMchId(), e);
logger.error("🔍 异常详情: {}", e.getMessage());
logger.error("💡 可能的原因:");
logger.error("1. 证书配置错误或证书文件损坏");
logger.error("2. 微信支付平台证书已过期");
logger.error("3. 签名验证失败");
logger.error("4. 请求参数格式错误");
// 返回失败,微信会重试
return "fail";
}
logger.warn("⚠️ 异步通知处理完成但未找到匹配的支付成功状态");
return "fail";
}