大改:重构项目
This commit is contained in:
@@ -0,0 +1,222 @@
|
|||||||
|
package com.gxwebsoft.common.core.utils;
|
||||||
|
|
||||||
|
import com.gxwebsoft.common.system.entity.Payment;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Paths;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 微信支付配置诊断工具
|
||||||
|
* 用于排查微信支付签名验证失败等问题
|
||||||
|
*
|
||||||
|
* @author 科技小王子
|
||||||
|
* @since 2025-07-27
|
||||||
|
*/
|
||||||
|
@Slf4j
|
||||||
|
@Component
|
||||||
|
public class WechatPayDiagnostic {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 诊断微信支付配置
|
||||||
|
*
|
||||||
|
* @param payment 支付配置
|
||||||
|
* @param privateKeyPath 私钥路径
|
||||||
|
* @param environment 环境标识
|
||||||
|
*/
|
||||||
|
public void diagnosePaymentConfig(Payment payment, String privateKeyPath, String environment) {
|
||||||
|
log.info("=== 微信支付配置诊断开始 ===");
|
||||||
|
log.info("环境: {}", environment);
|
||||||
|
|
||||||
|
// 1. 检查支付配置基本信息
|
||||||
|
checkBasicConfig(payment);
|
||||||
|
|
||||||
|
// 2. 检查证书文件
|
||||||
|
checkCertificateFiles(payment, privateKeyPath, environment);
|
||||||
|
|
||||||
|
// 3. 检查配置完整性
|
||||||
|
checkConfigCompleteness(payment);
|
||||||
|
|
||||||
|
log.info("=== 微信支付配置诊断结束 ===");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 检查基本配置信息
|
||||||
|
*/
|
||||||
|
private void checkBasicConfig(Payment payment) {
|
||||||
|
log.info("--- 基本配置检查 ---");
|
||||||
|
|
||||||
|
if (payment == null) {
|
||||||
|
log.error("❌ 支付配置为空");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
log.info("支付配置ID: {}", payment.getId());
|
||||||
|
log.info("支付方式名称: {}", payment.getName());
|
||||||
|
log.info("支付类型: {}", payment.getType());
|
||||||
|
log.info("支付代码: {}", payment.getCode());
|
||||||
|
log.info("状态: {}", payment.getStatus());
|
||||||
|
|
||||||
|
// 检查关键字段
|
||||||
|
checkField("应用ID", payment.getAppId());
|
||||||
|
checkField("商户号", payment.getMchId());
|
||||||
|
checkField("商户证书序列号", payment.getMerchantSerialNumber());
|
||||||
|
checkField("API密钥", payment.getApiKey(), true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 检查证书文件
|
||||||
|
*/
|
||||||
|
private void checkCertificateFiles(Payment payment, String privateKeyPath, String environment) {
|
||||||
|
log.info("--- 证书文件检查 ---");
|
||||||
|
|
||||||
|
// 检查私钥文件
|
||||||
|
if (privateKeyPath != null) {
|
||||||
|
checkFileExists("私钥文件", privateKeyPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 生产环境检查证书文件
|
||||||
|
if (!"dev".equals(environment)) {
|
||||||
|
if (payment.getApiclientCert() != null) {
|
||||||
|
log.info("商户证书文件配置: {}", payment.getApiclientCert());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (payment.getPubKey() != null) {
|
||||||
|
log.info("公钥文件配置: {}", payment.getPubKey());
|
||||||
|
log.info("公钥ID: {}", payment.getPubKeyId());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 检查配置完整性
|
||||||
|
*/
|
||||||
|
private void checkConfigCompleteness(Payment payment) {
|
||||||
|
log.info("--- 配置完整性检查 ---");
|
||||||
|
|
||||||
|
boolean isComplete = true;
|
||||||
|
|
||||||
|
if (isEmpty(payment.getMchId())) {
|
||||||
|
log.error("❌ 商户号未配置");
|
||||||
|
isComplete = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isEmpty(payment.getMerchantSerialNumber())) {
|
||||||
|
log.error("❌ 商户证书序列号未配置");
|
||||||
|
isComplete = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isEmpty(payment.getApiKey())) {
|
||||||
|
log.error("❌ API密钥未配置");
|
||||||
|
isComplete = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isEmpty(payment.getAppId())) {
|
||||||
|
log.error("❌ 应用ID未配置");
|
||||||
|
isComplete = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isComplete) {
|
||||||
|
log.info("✅ 配置完整性检查通过");
|
||||||
|
} else {
|
||||||
|
log.error("❌ 配置不完整,请补充缺失的配置项");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 检查字段是否为空
|
||||||
|
*/
|
||||||
|
private void checkField(String fieldName, String value) {
|
||||||
|
checkField(fieldName, value, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 检查字段是否为空
|
||||||
|
*/
|
||||||
|
private void checkField(String fieldName, String value, boolean isSensitive) {
|
||||||
|
if (isEmpty(value)) {
|
||||||
|
log.warn("⚠️ {}: 未配置", fieldName);
|
||||||
|
} else {
|
||||||
|
if (isSensitive) {
|
||||||
|
log.info("✅ {}: 已配置(长度:{})", fieldName, value.length());
|
||||||
|
} else {
|
||||||
|
log.info("✅ {}: {}", fieldName, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 检查文件是否存在
|
||||||
|
*/
|
||||||
|
private void checkFileExists(String fileName, String filePath) {
|
||||||
|
try {
|
||||||
|
File file = new File(filePath);
|
||||||
|
if (file.exists() && file.isFile()) {
|
||||||
|
log.info("✅ {}: 文件存在 - {}", fileName, filePath);
|
||||||
|
log.info(" 文件大小: {} bytes", file.length());
|
||||||
|
|
||||||
|
// 检查文件内容格式
|
||||||
|
if (filePath.endsWith(".pem")) {
|
||||||
|
checkPemFileFormat(fileName, filePath);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
log.error("❌ {}: 文件不存在 - {}", fileName, filePath);
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("❌ {}: 检查文件时出错 - {} ({})", fileName, filePath, e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 检查PEM文件格式
|
||||||
|
*/
|
||||||
|
private void checkPemFileFormat(String fileName, String filePath) {
|
||||||
|
try {
|
||||||
|
String content = Files.readString(Paths.get(filePath));
|
||||||
|
if (content.contains("-----BEGIN") && content.contains("-----END")) {
|
||||||
|
log.info("✅ {}: PEM格式正确", fileName);
|
||||||
|
} else {
|
||||||
|
log.warn("⚠️ {}: PEM格式可能有问题", fileName);
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.warn("⚠️ {}: 无法读取文件内容进行格式检查 ({})", fileName, e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 检查字符串是否为空
|
||||||
|
*/
|
||||||
|
private boolean isEmpty(String str) {
|
||||||
|
return str == null || str.trim().isEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 生成诊断报告
|
||||||
|
*/
|
||||||
|
public String generateDiagnosticReport(Payment payment, String environment) {
|
||||||
|
StringBuilder report = new StringBuilder();
|
||||||
|
report.append("🔍 微信支付配置诊断报告\n");
|
||||||
|
report.append("========================\n\n");
|
||||||
|
|
||||||
|
report.append("环境: ").append(environment).append("\n");
|
||||||
|
report.append("租户ID: ").append(payment != null ? payment.getTenantId() : "未知").append("\n");
|
||||||
|
report.append("商户号: ").append(payment != null ? payment.getMchId() : "未配置").append("\n");
|
||||||
|
report.append("应用ID: ").append(payment != null ? payment.getAppId() : "未配置").append("\n\n");
|
||||||
|
|
||||||
|
report.append("🚨 常见问题排查:\n");
|
||||||
|
report.append("1. 商户证书序列号是否正确\n");
|
||||||
|
report.append("2. APIv3密钥是否正确\n");
|
||||||
|
report.append("3. 私钥文件是否正确\n");
|
||||||
|
report.append("4. 微信支付平台证书是否过期\n");
|
||||||
|
report.append("5. 网络连接是否正常\n\n");
|
||||||
|
|
||||||
|
report.append("💡 建议解决方案:\n");
|
||||||
|
report.append("1. 使用自动证书配置(RSAAutoCertificateConfig)\n");
|
||||||
|
report.append("2. 在微信商户平台重新下载证书\n");
|
||||||
|
report.append("3. 检查商户平台API安全设置\n");
|
||||||
|
|
||||||
|
return report.toString();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -11,6 +11,7 @@
|
|||||||
import com.gxwebsoft.common.core.utils.RedisUtil;
|
import com.gxwebsoft.common.core.utils.RedisUtil;
|
||||||
import com.gxwebsoft.common.core.utils.CertificateLoader;
|
import com.gxwebsoft.common.core.utils.CertificateLoader;
|
||||||
import com.gxwebsoft.common.core.service.PaymentCacheService;
|
import com.gxwebsoft.common.core.service.PaymentCacheService;
|
||||||
|
import com.gxwebsoft.common.core.utils.WechatPayDiagnostic;
|
||||||
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 com.gxwebsoft.common.system.param.PaymentParam;
|
import com.gxwebsoft.common.system.param.PaymentParam;
|
||||||
@@ -73,6 +74,8 @@ import com.gxwebsoft.common.core.service.PaymentCacheService;
|
|||||||
private PaymentCacheService paymentCacheService;
|
private PaymentCacheService paymentCacheService;
|
||||||
@Resource
|
@Resource
|
||||||
private WechatCertAutoConfig wechatCertAutoConfig;
|
private WechatCertAutoConfig wechatCertAutoConfig;
|
||||||
|
@Resource
|
||||||
|
private WechatPayDiagnostic wechatPayDiagnostic;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public PageResult<ShopOrder> pageRel(ShopOrderParam param) {
|
public PageResult<ShopOrder> pageRel(ShopOrderParam param) {
|
||||||
@@ -154,7 +157,14 @@ import com.gxwebsoft.common.core.service.PaymentCacheService;
|
|||||||
if (StrUtil.isNotBlank(payment.getNotifyUrl())) {
|
if (StrUtil.isNotBlank(payment.getNotifyUrl())) {
|
||||||
request.setNotifyUrl(payment.getNotifyUrl().concat("/").concat(order.getTenantId().toString()));
|
request.setNotifyUrl(payment.getNotifyUrl().concat("/").concat(order.getTenantId().toString()));
|
||||||
}
|
}
|
||||||
|
System.out.println("=== 发起微信支付请求 ===");
|
||||||
|
System.out.println("请求参数: " + request);
|
||||||
|
|
||||||
PrepayWithRequestPaymentResponse response = service.prepayWithRequestPayment(request);
|
PrepayWithRequestPaymentResponse response = service.prepayWithRequestPayment(request);
|
||||||
|
|
||||||
|
System.out.println("=== 微信支付响应成功 ===");
|
||||||
|
System.out.println("预支付ID: " + response.getPackageVal());
|
||||||
|
|
||||||
orderInfo.put("provider", "wxpay");
|
orderInfo.put("provider", "wxpay");
|
||||||
orderInfo.put("timeStamp", response.getTimeStamp());
|
orderInfo.put("timeStamp", response.getTimeStamp());
|
||||||
orderInfo.put("nonceStr", response.getNonceStr());
|
orderInfo.put("nonceStr", response.getNonceStr());
|
||||||
@@ -167,6 +177,26 @@ import com.gxwebsoft.common.core.service.PaymentCacheService;
|
|||||||
System.err.println("=== 创建微信支付订单失败 ===");
|
System.err.println("=== 创建微信支付订单失败 ===");
|
||||||
System.err.println("错误信息: " + e.getMessage());
|
System.err.println("错误信息: " + e.getMessage());
|
||||||
System.err.println("错误类型: " + e.getClass().getName());
|
System.err.println("错误类型: " + e.getClass().getName());
|
||||||
|
|
||||||
|
// 特殊处理签名验证失败的情况
|
||||||
|
if (e.getMessage() != null && e.getMessage().contains("signature is incorrect")) {
|
||||||
|
System.err.println("🔍 签名验证失败诊断:");
|
||||||
|
System.err.println("1. 检查商户证书序列号是否正确");
|
||||||
|
System.err.println("2. 检查APIv3密钥是否正确");
|
||||||
|
System.err.println("3. 检查私钥文件是否正确");
|
||||||
|
System.err.println("4. 检查微信支付平台证书是否过期");
|
||||||
|
System.err.println("5. 建议使用自动证书配置避免证书管理问题");
|
||||||
|
System.err.println("当前支付配置:");
|
||||||
|
try {
|
||||||
|
final Payment paymentInfo = getPayment(order);
|
||||||
|
System.err.println(" - 商户号: " + paymentInfo.getMchId());
|
||||||
|
System.err.println(" - 序列号: " + paymentInfo.getMerchantSerialNumber());
|
||||||
|
System.err.println(" - 环境: " + active);
|
||||||
|
} catch (Exception ex) {
|
||||||
|
System.err.println(" - 无法获取支付配置信息: " + ex.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
throw new RuntimeException("创建支付订单失败:" + e.getMessage(), e);
|
throw new RuntimeException("创建支付订单失败:" + e.getMessage(), e);
|
||||||
}
|
}
|
||||||
@@ -257,6 +287,10 @@ import com.gxwebsoft.common.core.service.PaymentCacheService;
|
|||||||
String apiclientCert = null;
|
String apiclientCert = null;
|
||||||
String pubKey = null;
|
String pubKey = null;
|
||||||
|
|
||||||
|
// 运行配置诊断
|
||||||
|
System.out.println("=== 运行微信支付配置诊断 ===");
|
||||||
|
wechatPayDiagnostic.diagnosePaymentConfig(payment, null, active);
|
||||||
|
|
||||||
// 开发环境配置 - 使用自动证书配置
|
// 开发环境配置 - 使用自动证书配置
|
||||||
if (active.equals("dev")) {
|
if (active.equals("dev")) {
|
||||||
// 构建包含租户号的证书路径: dev/wechat/{tenantId}/
|
// 构建包含租户号的证书路径: dev/wechat/{tenantId}/
|
||||||
@@ -278,6 +312,9 @@ import com.gxwebsoft.common.core.service.PaymentCacheService;
|
|||||||
System.out.println("私钥完整路径: " + privateKey);
|
System.out.println("私钥完整路径: " + privateKey);
|
||||||
System.out.println("证书加载完成 - 私钥文件: " + privateKey);
|
System.out.println("证书加载完成 - 私钥文件: " + privateKey);
|
||||||
System.out.println("使用自动证书配置,无需手动加载微信支付平台证书");
|
System.out.println("使用自动证书配置,无需手动加载微信支付平台证书");
|
||||||
|
|
||||||
|
// 更新诊断信息,包含私钥路径
|
||||||
|
wechatPayDiagnostic.diagnosePaymentConfig(payment, privateKey, active);
|
||||||
} else {
|
} else {
|
||||||
// 生产环境配置 - 从容器证书目录加载
|
// 生产环境配置 - 从容器证书目录加载
|
||||||
final String certRootPath = certConfig.getCertRootPath(); // /www/wwwroot/file.ws
|
final String certRootPath = certConfig.getCertRootPath(); // /www/wwwroot/file.ws
|
||||||
@@ -358,23 +395,45 @@ import com.gxwebsoft.common.core.service.PaymentCacheService;
|
|||||||
throw new RuntimeException("微信支付配置失败,请先在商户平台开启API安全功能", e);
|
throw new RuntimeException("微信支付配置失败,请先在商户平台开启API安全功能", e);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// 生产环境兼容公钥配置
|
// 生产环境优先使用自动证书配置
|
||||||
if (payment.getPubKey() != null && !payment.getPubKey().isEmpty()) {
|
System.out.println("=== 生产环境使用自动证书配置 ===");
|
||||||
config = new RSAPublicKeyConfig.Builder()
|
System.out.println("商户号: " + payment.getMchId());
|
||||||
.merchantId(payment.getMchId())
|
System.out.println("序列号: " + payment.getMerchantSerialNumber());
|
||||||
.privateKeyFromPath(privateKey)
|
System.out.println("API密钥: 已配置(长度:" + payment.getApiKey().length() + ")");
|
||||||
.publicKeyFromPath(pubKey)
|
|
||||||
.publicKeyId(payment.getPubKeyId())
|
try {
|
||||||
.merchantSerialNumber(payment.getMerchantSerialNumber())
|
// 优先使用自动证书配置,避免证书过期和序列号不匹配问题
|
||||||
.apiV3Key(payment.getApiKey())
|
config = wechatCertAutoConfig.createAutoConfig(
|
||||||
.build();
|
payment.getMchId(),
|
||||||
} else {
|
privateKey,
|
||||||
config = new RSAConfig.Builder()
|
payment.getMerchantSerialNumber(),
|
||||||
.merchantId(payment.getMchId())
|
payment.getApiKey()
|
||||||
.privateKeyFromPath(privateKey)
|
);
|
||||||
.merchantSerialNumber(payment.getMerchantSerialNumber())
|
System.out.println("✅ 生产环境自动证书配置成功");
|
||||||
.wechatPayCertificatesFromPath(apiclientCert)
|
} catch (Exception autoConfigException) {
|
||||||
.build();
|
System.err.println("⚠️ 自动证书配置失败,回退到手动证书配置");
|
||||||
|
System.err.println("自动配置错误: " + autoConfigException.getMessage());
|
||||||
|
|
||||||
|
// 回退到手动证书配置
|
||||||
|
if (payment.getPubKey() != null && !payment.getPubKey().isEmpty()) {
|
||||||
|
System.out.println("使用RSA公钥配置");
|
||||||
|
config = new RSAPublicKeyConfig.Builder()
|
||||||
|
.merchantId(payment.getMchId())
|
||||||
|
.privateKeyFromPath(privateKey)
|
||||||
|
.publicKeyFromPath(pubKey)
|
||||||
|
.publicKeyId(payment.getPubKeyId())
|
||||||
|
.merchantSerialNumber(payment.getMerchantSerialNumber())
|
||||||
|
.apiV3Key(payment.getApiKey())
|
||||||
|
.build();
|
||||||
|
} else {
|
||||||
|
System.out.println("使用RSA证书配置");
|
||||||
|
config = new RSAConfig.Builder()
|
||||||
|
.merchantId(payment.getMchId())
|
||||||
|
.privateKeyFromPath(privateKey)
|
||||||
|
.merchantSerialNumber(payment.getMerchantSerialNumber())
|
||||||
|
.wechatPayCertificatesFromPath(apiclientCert)
|
||||||
|
.build();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user