feat(payment): 初始化支付模块核心代码
- 添加支付常量类PaymentConstants,定义支付状态、微信、支付宝、银联等相关常量 - 创建微信支付类型常量类WechatPayType,支持JSAPI、NATIVE、H5、APP支付方式 - 新增支付控制器PaymentController,提供创建支付、查询状态、退款等统一接口 - 实现支付回调控制器PaymentNotifyController,处理微信、支付宝、银联异步通知 - 添加支付请求数据传输对象PaymentRequest,支持多种支付方式参数校验 - 定义支付响应、状态更新请求等相关DTO类- 集成Swagger注解,完善接口文档说明- 添加参数校验和异常处理机制,确保支付流程安全可靠
This commit is contained in:
338
java/payment/service/WxPayConfigService.java
Normal file
338
java/payment/service/WxPayConfigService.java
Normal file
@@ -0,0 +1,338 @@
|
||||
package com.gxwebsoft.payment.service;
|
||||
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.gxwebsoft.common.core.config.CertificateProperties;
|
||||
import com.gxwebsoft.common.core.service.CertificateService;
|
||||
import com.gxwebsoft.common.core.utils.RedisUtil;
|
||||
import com.gxwebsoft.common.core.utils.WxNativeUtil;
|
||||
import com.gxwebsoft.common.system.entity.Payment;
|
||||
import com.gxwebsoft.common.system.param.PaymentParam;
|
||||
import com.gxwebsoft.common.system.service.PaymentService;
|
||||
import com.gxwebsoft.payment.exception.PaymentException;
|
||||
import com.wechat.pay.java.core.Config;
|
||||
import com.wechat.pay.java.core.RSAAutoCertificateConfig;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.core.io.ClassPathResource;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.io.IOException;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* 微信支付配置服务
|
||||
* 负责管理微信支付的配置信息和证书
|
||||
*
|
||||
* @author 科技小王子
|
||||
* @since 2025-01-26
|
||||
*/
|
||||
@Slf4j
|
||||
@Service
|
||||
public class WxPayConfigService {
|
||||
|
||||
@Resource
|
||||
private RedisUtil redisUtil;
|
||||
|
||||
@Resource
|
||||
private CertificateService certificateService;
|
||||
|
||||
@Resource
|
||||
private CertificateProperties certificateProperties;
|
||||
|
||||
@Resource
|
||||
private PaymentService paymentService;
|
||||
|
||||
@Value("${spring.profiles.active:dev}")
|
||||
private String activeProfile;
|
||||
|
||||
/**
|
||||
* 获取支付配置信息(Payment对象)- 公开方法
|
||||
*
|
||||
* @param tenantId 租户ID
|
||||
* @return 支付配置信息
|
||||
* @throws PaymentException 配置获取失败时抛出
|
||||
*/
|
||||
public Payment getPaymentConfigForStrategy(Integer tenantId) throws PaymentException {
|
||||
if (tenantId == null) {
|
||||
throw PaymentException.paramError("租户ID不能为空");
|
||||
}
|
||||
return getPaymentConfig(tenantId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取微信支付配置
|
||||
*
|
||||
* @param tenantId 租户ID
|
||||
* @return 微信支付配置
|
||||
* @throws PaymentException 配置获取失败时抛出
|
||||
*/
|
||||
public Config getWxPayConfig(Integer tenantId) throws PaymentException {
|
||||
if (tenantId == null) {
|
||||
throw PaymentException.paramError("租户ID不能为空");
|
||||
}
|
||||
|
||||
// 先从缓存获取已构建的配置
|
||||
Config cachedConfig = WxNativeUtil.getConfig(tenantId);
|
||||
if (cachedConfig != null) {
|
||||
log.debug("从缓存获取微信支付配置成功,租户ID: {}", tenantId);
|
||||
return cachedConfig;
|
||||
}
|
||||
|
||||
// 构建新的配置
|
||||
Config newConfig = buildWxPayConfig(tenantId);
|
||||
|
||||
// 缓存配置
|
||||
WxNativeUtil.addConfig(tenantId, newConfig);
|
||||
log.info("微信支付配置创建并缓存成功,租户ID: {}", tenantId);
|
||||
|
||||
return newConfig;
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建微信支付配置
|
||||
*/
|
||||
private Config buildWxPayConfig(Integer tenantId) throws PaymentException {
|
||||
try {
|
||||
// 获取支付配置信息
|
||||
Payment payment = getPaymentConfig(tenantId);
|
||||
|
||||
// 获取证书文件路径
|
||||
String certificatePath = getCertificatePath(tenantId, payment);
|
||||
|
||||
// 创建微信支付配置对象
|
||||
return createWxPayConfig(payment, certificatePath);
|
||||
|
||||
} catch (Exception e) {
|
||||
if (e instanceof PaymentException) {
|
||||
throw e;
|
||||
}
|
||||
throw PaymentException.systemError("构建微信支付配置失败: " + e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取支付配置信息
|
||||
* 优先从缓存获取,缓存没有则查询数据库,最后兜底到开发环境测试配置
|
||||
*/
|
||||
private Payment getPaymentConfig(Integer tenantId) throws PaymentException {
|
||||
String cacheKey = "Payment:wxPay:" + tenantId;
|
||||
Payment payment = redisUtil.get(cacheKey, Payment.class);
|
||||
System.out.println("payment = " + payment);
|
||||
if (payment != null) {
|
||||
log.debug("从缓存获取支付配置成功,租户ID: {}", tenantId);
|
||||
// return payment;
|
||||
}
|
||||
|
||||
// 缓存中没有,尝试从数据库查询
|
||||
try {
|
||||
final PaymentParam paymentParam = new PaymentParam();
|
||||
paymentParam.setType(102);
|
||||
paymentParam.setTenantId(tenantId);
|
||||
|
||||
log.debug("查询数据库支付配置,参数: type=102, tenantId={}", tenantId);
|
||||
payment = paymentService.getByType(paymentParam);
|
||||
log.debug("数据库查询结果: {}", payment != null ? "找到配置" : "未找到配置");
|
||||
|
||||
if (payment != null) {
|
||||
log.info("从数据库获取支付配置成功,租户ID: {},将缓存配置", tenantId);
|
||||
// 将查询到的配置缓存起来,缓存1天
|
||||
redisUtil.set(cacheKey, payment, 1L, TimeUnit.DAYS);
|
||||
return payment;
|
||||
} else {
|
||||
log.warn("数据库中未找到支付配置,租户ID: {}, type: 102", tenantId);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.error("从数据库查询支付配置失败,租户ID: {},错误: {}", tenantId, e.getMessage(), e);
|
||||
// 抛出更详细的异常信息
|
||||
throw PaymentException.systemError("查询支付配置失败,租户ID: " + tenantId + ",错误: " + e.getMessage(), e);
|
||||
}
|
||||
|
||||
// 数据库也没有配置
|
||||
if (!"dev".equals(activeProfile)) {
|
||||
throw PaymentException.systemError("微信支付配置未找到,租户ID: " + tenantId + ",请检查数据库配置", null);
|
||||
}
|
||||
|
||||
log.debug("开发环境模式,将使用测试配置,租户ID: {}", tenantId);
|
||||
// 开发环境返回测试Payment配置
|
||||
return createDevTestPayment(tenantId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取证书文件路径
|
||||
*/
|
||||
private String getCertificatePath(Integer tenantId, Payment payment) throws PaymentException {
|
||||
if ("dev".equals(activeProfile)) {
|
||||
return getDevCertificatePath(tenantId);
|
||||
} else {
|
||||
return getProdCertificatePath(payment);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取开发环境证书路径
|
||||
*/
|
||||
private String getDevCertificatePath(Integer tenantId) throws PaymentException {
|
||||
try {
|
||||
// 根据租户ID构建证书路径
|
||||
String certPath = "dev/wechat/" + tenantId + "/apiclient_key.pem";
|
||||
ClassPathResource resource = new ClassPathResource(certPath);
|
||||
|
||||
if (!resource.exists()) {
|
||||
throw PaymentException.systemError("开发环境微信支付证书文件不存在: " + certPath, null);
|
||||
}
|
||||
|
||||
String absolutePath = resource.getFile().getAbsolutePath();
|
||||
log.debug("开发环境证书路径: {}", absolutePath);
|
||||
return absolutePath;
|
||||
|
||||
} catch (IOException e) {
|
||||
throw PaymentException.systemError("获取开发环境证书路径失败: " + e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取生产环境证书路径
|
||||
*/
|
||||
private String getProdCertificatePath(Payment payment) throws PaymentException {
|
||||
if (payment == null || payment.getApiclientKey() == null || payment.getApiclientKey().trim().isEmpty()) {
|
||||
throw PaymentException.systemError("生产环境支付配置或证书密钥文件为空", null);
|
||||
}
|
||||
|
||||
try {
|
||||
// 使用微信支付证书路径
|
||||
String certificatePath = certificateService.getWechatPayCertPath(payment.getApiclientKey());
|
||||
if (certificatePath == null) {
|
||||
throw PaymentException.systemError("证书文件路径获取失败,证书文件: " + payment.getApiclientKey(), null);
|
||||
}
|
||||
|
||||
log.debug("生产环境证书路径: {}", certificatePath);
|
||||
return certificatePath;
|
||||
|
||||
} catch (Exception e) {
|
||||
throw PaymentException.systemError("获取生产环境证书路径失败: " + e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建微信支付配置对象
|
||||
*/
|
||||
private Config createWxPayConfig(Payment payment, String certificatePath) throws PaymentException {
|
||||
try {
|
||||
if ("dev".equals(activeProfile) && payment == null) {
|
||||
// 开发环境测试配置
|
||||
return createDevTestConfig(certificatePath);
|
||||
} else if (payment != null) {
|
||||
// 正常配置
|
||||
return createNormalConfig(payment, certificatePath);
|
||||
} else {
|
||||
throw PaymentException.systemError("无法创建微信支付配置:配置信息不完整", null);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
if (e instanceof PaymentException) {
|
||||
throw e;
|
||||
}
|
||||
throw PaymentException.systemError("创建微信支付配置对象失败: " + e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建开发环境测试Payment配置
|
||||
*/
|
||||
private Payment createDevTestPayment(Integer tenantId) {
|
||||
Payment testPayment = new Payment();
|
||||
testPayment.setTenantId(tenantId);
|
||||
testPayment.setType(102); // Native支付
|
||||
testPayment.setAppId("wxa67c676fc445590e"); // 开发环境测试AppID
|
||||
testPayment.setMchId("1246610101"); // 开发环境测试商户号
|
||||
testPayment.setMerchantSerialNumber("48749613B40AA8F1D768583FC352358E13EB5AF0");
|
||||
testPayment.setApiKey(certificateProperties.getWechatPay().getDev().getApiV3Key());
|
||||
testPayment.setNotifyUrl("http://frps-10550.s209.websoft.top/api/payment/notify");
|
||||
testPayment.setName("微信Native支付-开发环境");
|
||||
testPayment.setStatus(true); // 启用
|
||||
|
||||
log.info("创建开发环境测试Payment配置,租户ID: {}, AppID: {}, 商户号: {}",
|
||||
tenantId, testPayment.getAppId(), testPayment.getMchId());
|
||||
|
||||
return testPayment;
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建开发环境测试配置
|
||||
*/
|
||||
private Config createDevTestConfig(String certificatePath) throws PaymentException {
|
||||
String testMerchantId = "1246610101";
|
||||
String testMerchantSerialNumber = "48749613B40AA8F1D768583FC352358E13EB5AF0";
|
||||
String testApiV3Key = certificateProperties.getWechatPay().getDev().getApiV3Key();
|
||||
|
||||
if (testApiV3Key == null || testApiV3Key.trim().isEmpty()) {
|
||||
throw PaymentException.systemError("开发环境APIv3密钥未配置", null);
|
||||
}
|
||||
|
||||
log.info("使用开发环境测试配置");
|
||||
log.debug("测试商户号: {}", testMerchantId);
|
||||
log.debug("测试序列号: {}", testMerchantSerialNumber);
|
||||
|
||||
return new RSAAutoCertificateConfig.Builder()
|
||||
.merchantId(testMerchantId)
|
||||
.privateKeyFromPath(certificatePath)
|
||||
.merchantSerialNumber(testMerchantSerialNumber)
|
||||
.apiV3Key(testApiV3Key)
|
||||
.build();
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建正常配置
|
||||
*/
|
||||
private Config createNormalConfig(Payment payment, String certificatePath) throws PaymentException {
|
||||
// 验证配置完整性
|
||||
validatePaymentConfig(payment);
|
||||
|
||||
log.info("使用数据库支付配置");
|
||||
log.debug("商户号: {}", payment.getMchId());
|
||||
|
||||
return new RSAAutoCertificateConfig.Builder()
|
||||
.merchantId(payment.getMchId())
|
||||
.privateKeyFromPath(certificatePath)
|
||||
.merchantSerialNumber(payment.getMerchantSerialNumber())
|
||||
.apiV3Key(payment.getApiKey())
|
||||
.build();
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证支付配置完整性
|
||||
*/
|
||||
private void validatePaymentConfig(Payment payment) throws PaymentException {
|
||||
if (payment == null) {
|
||||
throw PaymentException.systemError("支付配置为空", null);
|
||||
}
|
||||
|
||||
if (payment.getMchId() == null || payment.getMchId().trim().isEmpty()) {
|
||||
throw PaymentException.systemError("商户号(mchId)未配置", null);
|
||||
}
|
||||
|
||||
if (payment.getMerchantSerialNumber() == null || payment.getMerchantSerialNumber().trim().isEmpty()) {
|
||||
throw PaymentException.systemError("商户证书序列号(merchantSerialNumber)未配置", null);
|
||||
}
|
||||
|
||||
if (payment.getApiKey() == null || payment.getApiKey().trim().isEmpty()) {
|
||||
throw PaymentException.systemError("APIv3密钥(apiKey)未配置", null);
|
||||
}
|
||||
|
||||
if (payment.getApiclientKey() == null || payment.getApiclientKey().trim().isEmpty()) {
|
||||
throw PaymentException.systemError("证书文件名(apiclientKey)未配置", null);
|
||||
}
|
||||
|
||||
log.debug("支付配置验证通过,租户ID: {}, 商户号: {}", payment.getTenantId(), payment.getMchId());
|
||||
}
|
||||
|
||||
/**
|
||||
* 清除指定租户的配置缓存
|
||||
*
|
||||
* @param tenantId 租户ID
|
||||
*/
|
||||
public void clearConfigCache(Integer tenantId) {
|
||||
WxNativeUtil.addConfig(tenantId, null);
|
||||
log.info("清除微信支付配置缓存,租户ID: {}", tenantId);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user