更新完成:完成微信支付的功能开发和测试
This commit is contained in:
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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";
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user