You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
476 lines
19 KiB
476 lines
19 KiB
package com.gxwebsoft.payment.service.impl;
|
|
|
|
import com.gxwebsoft.payment.constants.PaymentConstants;
|
|
import com.gxwebsoft.payment.dto.PaymentRequest;
|
|
import com.gxwebsoft.payment.dto.PaymentResponse;
|
|
import com.gxwebsoft.payment.enums.PaymentType;
|
|
import com.gxwebsoft.payment.exception.PaymentException;
|
|
import com.gxwebsoft.payment.service.PaymentService;
|
|
import com.gxwebsoft.payment.strategy.PaymentStrategy;
|
|
import lombok.extern.slf4j.Slf4j;
|
|
import org.springframework.stereotype.Service;
|
|
import org.springframework.util.StringUtils;
|
|
|
|
import javax.annotation.PostConstruct;
|
|
import javax.annotation.Resource;
|
|
import java.math.BigDecimal;
|
|
import java.util.*;
|
|
import java.util.concurrent.ConcurrentHashMap;
|
|
import java.util.stream.Collectors;
|
|
|
|
/**
|
|
* 统一支付服务实现
|
|
* 基于策略模式实现多种支付方式的统一管理
|
|
*
|
|
* @author 科技小王子
|
|
* @since 2025-01-26
|
|
*/
|
|
@Slf4j
|
|
@Service
|
|
public class PaymentServiceImpl implements PaymentService {
|
|
|
|
/**
|
|
* 支付策略映射表
|
|
*/
|
|
private final Map<PaymentType, PaymentStrategy> strategyMap = new ConcurrentHashMap<>();
|
|
|
|
/**
|
|
* 注入所有支付策略实现
|
|
*/
|
|
@Resource
|
|
private List<PaymentStrategy> paymentStrategies;
|
|
|
|
/**
|
|
* 初始化策略映射
|
|
*/
|
|
@PostConstruct
|
|
public void initStrategies() {
|
|
if (paymentStrategies != null && !paymentStrategies.isEmpty()) {
|
|
for (PaymentStrategy strategy : paymentStrategies) {
|
|
try {
|
|
PaymentType paymentType = strategy.getSupportedPaymentType();
|
|
strategyMap.put(paymentType, strategy);
|
|
log.info("注册支付策略: {} -> {}", paymentType.getName(), strategy.getClass().getSimpleName());
|
|
} catch (Exception e) {
|
|
log.warn("注册支付策略失败: {}, 错误: {}", strategy.getClass().getSimpleName(), e.getMessage());
|
|
}
|
|
}
|
|
}
|
|
log.info("支付策略初始化完成,共注册 {} 种支付方式", strategyMap.size());
|
|
|
|
if (strategyMap.isEmpty()) {
|
|
log.warn("⚠️ 没有可用的支付策略,支付功能将不可用");
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public PaymentResponse createPayment(PaymentRequest request) throws PaymentException {
|
|
log.info("{}, 支付类型: {}, 租户ID: {}, 用户ID: {}, 金额: {}",
|
|
PaymentConstants.LogMessage.PAYMENT_START, request.getPaymentType(),
|
|
request.getTenantId(), request.getUserId(), request.getFormattedAmount());
|
|
|
|
try {
|
|
// 基础参数验证
|
|
validatePaymentRequest(request);
|
|
|
|
// 获取支付策略
|
|
PaymentStrategy strategy = getPaymentStrategy(request.getPaymentType());
|
|
|
|
// 执行支付
|
|
PaymentResponse response = strategy.createPayment(request);
|
|
|
|
log.info("{}, 支付类型: {}, 租户ID: {}, 订单号: {}, 金额: {}",
|
|
PaymentConstants.LogMessage.PAYMENT_SUCCESS, request.getPaymentType(),
|
|
request.getTenantId(), response.getOrderNo(), request.getFormattedAmount());
|
|
|
|
return response;
|
|
|
|
} catch (PaymentException e) {
|
|
log.error("{}, 支付类型: {}, 租户ID: {}, 错误: {}",
|
|
PaymentConstants.LogMessage.PAYMENT_FAILED, request.getPaymentType(),
|
|
request.getTenantId(), e.getMessage());
|
|
throw e;
|
|
} catch (Exception e) {
|
|
log.error("{}, 支付类型: {}, 租户ID: {}, 系统错误: {}",
|
|
PaymentConstants.LogMessage.PAYMENT_FAILED, request.getPaymentType(),
|
|
request.getTenantId(), e.getMessage(), e);
|
|
throw PaymentException.systemError("支付创建失败: " + e.getMessage(), e);
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public PaymentResponse queryPayment(String orderNo, PaymentType paymentType, Integer tenantId) throws PaymentException {
|
|
log.info("开始查询支付状态, 支付类型: {}, 租户ID: {}, 订单号: {}",
|
|
paymentType, tenantId, orderNo);
|
|
|
|
try {
|
|
// 参数验证
|
|
validateQueryParams(orderNo, paymentType, tenantId);
|
|
|
|
// 获取支付策略
|
|
PaymentStrategy strategy = getPaymentStrategy(paymentType);
|
|
|
|
// 检查是否支持查询
|
|
if (!strategy.supportQuery()) {
|
|
throw PaymentException.unsupportedPayment("该支付方式不支持查询", paymentType);
|
|
}
|
|
|
|
// 执行查询
|
|
PaymentResponse response = strategy.queryPayment(orderNo, tenantId);
|
|
|
|
log.info("支付状态查询成功, 支付类型: {}, 租户ID: {}, 订单号: {}, 状态: {}",
|
|
paymentType, tenantId, orderNo, response.getPaymentStatus());
|
|
|
|
return response;
|
|
|
|
} catch (PaymentException e) {
|
|
log.error("支付状态查询失败, 支付类型: {}, 租户ID: {}, 订单号: {}, 错误: {}",
|
|
paymentType, tenantId, orderNo, e.getMessage());
|
|
throw e;
|
|
} catch (Exception e) {
|
|
log.error("支付状态查询系统错误, 支付类型: {}, 租户ID: {}, 订单号: {}, 错误: {}",
|
|
paymentType, tenantId, orderNo, e.getMessage(), e);
|
|
throw PaymentException.systemError("支付查询失败: " + e.getMessage(), e);
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public String handlePaymentNotify(PaymentType paymentType, Map<String, String> headers, String body, Integer tenantId) throws PaymentException {
|
|
log.info("{}, 支付类型: {}, 租户ID: {}",
|
|
PaymentConstants.LogMessage.NOTIFY_START, paymentType, tenantId);
|
|
|
|
try {
|
|
// 参数验证
|
|
validateNotifyParams(paymentType, headers, body, tenantId);
|
|
|
|
// 获取支付策略
|
|
PaymentStrategy strategy = getPaymentStrategy(paymentType);
|
|
|
|
// 检查是否需要异步通知
|
|
if (!strategy.needNotify()) {
|
|
log.warn("该支付方式不需要异步通知, 支付类型: {}", paymentType);
|
|
return PaymentConstants.Wechat.NOTIFY_SUCCESS;
|
|
}
|
|
|
|
// 处理回调
|
|
String result = strategy.handleNotify(headers, body, tenantId);
|
|
|
|
log.info("{}, 支付类型: {}, 租户ID: {}",
|
|
PaymentConstants.LogMessage.NOTIFY_SUCCESS, paymentType, tenantId);
|
|
|
|
return result;
|
|
|
|
} catch (PaymentException e) {
|
|
log.error("{}, 支付类型: {}, 租户ID: {}, 错误: {}",
|
|
PaymentConstants.LogMessage.NOTIFY_FAILED, paymentType, tenantId, e.getMessage());
|
|
throw e;
|
|
} catch (Exception e) {
|
|
log.error("{}, 支付类型: {}, 租户ID: {}, 系统错误: {}",
|
|
PaymentConstants.LogMessage.NOTIFY_FAILED, paymentType, tenantId, e.getMessage(), e);
|
|
throw PaymentException.systemError("支付回调处理失败: " + e.getMessage(), e);
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public PaymentResponse refund(String orderNo, String refundNo, PaymentType paymentType,
|
|
BigDecimal totalAmount, BigDecimal refundAmount,
|
|
String reason, Integer tenantId) throws PaymentException {
|
|
log.info("{}, 支付类型: {}, 租户ID: {}, 订单号: {}, 退款单号: {}, 退款金额: {}",
|
|
PaymentConstants.LogMessage.REFUND_START, paymentType, tenantId, orderNo, refundNo, refundAmount);
|
|
|
|
try {
|
|
// 参数验证
|
|
validateRefundParams(orderNo, refundNo, paymentType, totalAmount, refundAmount, tenantId);
|
|
|
|
// 获取支付策略
|
|
PaymentStrategy strategy = getPaymentStrategy(paymentType);
|
|
|
|
// 检查是否支持退款
|
|
if (!strategy.supportRefund()) {
|
|
throw PaymentException.unsupportedPayment("该支付方式不支持退款", paymentType);
|
|
}
|
|
|
|
// 执行退款
|
|
PaymentResponse response = strategy.refund(orderNo, refundNo, totalAmount, refundAmount, reason, tenantId);
|
|
|
|
log.info("{}, 支付类型: {}, 租户ID: {}, 订单号: {}, 退款单号: {}, 退款金额: {}",
|
|
PaymentConstants.LogMessage.REFUND_SUCCESS, paymentType, tenantId, orderNo, refundNo, refundAmount);
|
|
|
|
return response;
|
|
|
|
} catch (PaymentException e) {
|
|
log.error("{}, 支付类型: {}, 租户ID: {}, 订单号: {}, 退款单号: {}, 错误: {}",
|
|
PaymentConstants.LogMessage.REFUND_FAILED, paymentType, tenantId, orderNo, refundNo, e.getMessage());
|
|
throw e;
|
|
} catch (Exception e) {
|
|
log.error("{}, 支付类型: {}, 租户ID: {}, 订单号: {}, 退款单号: {}, 系统错误: {}",
|
|
PaymentConstants.LogMessage.REFUND_FAILED, paymentType, tenantId, orderNo, refundNo, e.getMessage(), e);
|
|
throw PaymentException.systemError("退款申请失败: " + e.getMessage(), e);
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public PaymentResponse queryRefund(String refundNo, PaymentType paymentType, Integer tenantId) throws PaymentException {
|
|
log.info("开始查询退款状态, 支付类型: {}, 租户ID: {}, 退款单号: {}",
|
|
paymentType, tenantId, refundNo);
|
|
|
|
try {
|
|
// 参数验证
|
|
validateRefundQueryParams(refundNo, paymentType, tenantId);
|
|
|
|
// 获取支付策略
|
|
PaymentStrategy strategy = getPaymentStrategy(paymentType);
|
|
|
|
// 检查是否支持退款查询
|
|
if (!strategy.supportRefund()) {
|
|
throw PaymentException.unsupportedPayment("该支付方式不支持退款查询", paymentType);
|
|
}
|
|
|
|
// 执行查询
|
|
PaymentResponse response = strategy.queryRefund(refundNo, tenantId);
|
|
|
|
log.info("退款状态查询成功, 支付类型: {}, 租户ID: {}, 退款单号: {}, 状态: {}",
|
|
paymentType, tenantId, refundNo, response.getPaymentStatus());
|
|
|
|
return response;
|
|
|
|
} catch (PaymentException e) {
|
|
log.error("退款状态查询失败, 支付类型: {}, 租户ID: {}, 退款单号: {}, 错误: {}",
|
|
paymentType, tenantId, refundNo, e.getMessage());
|
|
throw e;
|
|
} catch (Exception e) {
|
|
log.error("退款状态查询系统错误, 支付类型: {}, 租户ID: {}, 退款单号: {}, 错误: {}",
|
|
paymentType, tenantId, refundNo, e.getMessage(), e);
|
|
throw PaymentException.systemError("退款查询失败: " + e.getMessage(), e);
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public boolean closeOrder(String orderNo, PaymentType paymentType, Integer tenantId) throws PaymentException {
|
|
log.info("开始关闭订单, 支付类型: {}, 租户ID: {}, 订单号: {}",
|
|
paymentType, tenantId, orderNo);
|
|
|
|
try {
|
|
// 参数验证
|
|
validateCloseParams(orderNo, paymentType, tenantId);
|
|
|
|
// 获取支付策略
|
|
PaymentStrategy strategy = getPaymentStrategy(paymentType);
|
|
|
|
// 检查是否支持关闭订单
|
|
if (!strategy.supportClose()) {
|
|
throw PaymentException.unsupportedPayment("该支付方式不支持关闭订单", paymentType);
|
|
}
|
|
|
|
// 执行关闭
|
|
boolean result = strategy.closeOrder(orderNo, tenantId);
|
|
|
|
log.info("订单关闭{}, 支付类型: {}, 租户ID: {}, 订单号: {}",
|
|
result ? "成功" : "失败", paymentType, tenantId, orderNo);
|
|
|
|
return result;
|
|
|
|
} catch (PaymentException e) {
|
|
log.error("订单关闭失败, 支付类型: {}, 租户ID: {}, 订单号: {}, 错误: {}",
|
|
paymentType, tenantId, orderNo, e.getMessage());
|
|
throw e;
|
|
} catch (Exception e) {
|
|
log.error("订单关闭系统错误, 支付类型: {}, 租户ID: {}, 订单号: {}, 错误: {}",
|
|
paymentType, tenantId, orderNo, e.getMessage(), e);
|
|
throw PaymentException.systemError("订单关闭失败: " + e.getMessage(), e);
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public List<PaymentType> getSupportedPaymentTypes() {
|
|
return new ArrayList<>(strategyMap.keySet());
|
|
}
|
|
|
|
@Override
|
|
public boolean isPaymentTypeSupported(PaymentType paymentType) {
|
|
return strategyMap.containsKey(paymentType);
|
|
}
|
|
|
|
@Override
|
|
public boolean isRefundSupported(PaymentType paymentType) {
|
|
PaymentStrategy strategy = strategyMap.get(paymentType);
|
|
return strategy != null && strategy.supportRefund();
|
|
}
|
|
|
|
@Override
|
|
public boolean isQuerySupported(PaymentType paymentType) {
|
|
PaymentStrategy strategy = strategyMap.get(paymentType);
|
|
return strategy != null && strategy.supportQuery();
|
|
}
|
|
|
|
@Override
|
|
public boolean isCloseSupported(PaymentType paymentType) {
|
|
PaymentStrategy strategy = strategyMap.get(paymentType);
|
|
return strategy != null && strategy.supportClose();
|
|
}
|
|
|
|
@Override
|
|
public boolean isNotifyNeeded(PaymentType paymentType) {
|
|
PaymentStrategy strategy = strategyMap.get(paymentType);
|
|
return strategy != null && strategy.needNotify();
|
|
}
|
|
|
|
@Override
|
|
public void validatePaymentRequest(PaymentRequest request) throws PaymentException {
|
|
if (request == null) {
|
|
throw PaymentException.paramError("支付请求不能为空");
|
|
}
|
|
|
|
if (request.getPaymentType() == null) {
|
|
throw PaymentException.paramError("支付类型不能为空");
|
|
}
|
|
|
|
if (request.getTenantId() == null || request.getTenantId() <= 0) {
|
|
throw PaymentException.paramError("租户ID不能为空且必须大于0");
|
|
}
|
|
|
|
if (request.getUserId() == null || request.getUserId() <= 0) {
|
|
throw PaymentException.paramError("用户ID不能为空且必须大于0");
|
|
}
|
|
|
|
if (request.getAmount() == null || request.getAmount().compareTo(BigDecimal.ZERO) <= 0) {
|
|
throw PaymentException.amountError("支付金额必须大于0");
|
|
}
|
|
|
|
if (!StringUtils.hasText(request.getSubject())) {
|
|
throw PaymentException.paramError("订单标题不能为空");
|
|
}
|
|
|
|
// 检查支付类型是否支持
|
|
if (!isPaymentTypeSupported(request.getPaymentType())) {
|
|
throw PaymentException.unsupportedPayment("不支持的支付类型: " + request.getPaymentType(), request.getPaymentType());
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public Map<String, Object> getPaymentStrategyInfo(PaymentType paymentType) {
|
|
PaymentStrategy strategy = strategyMap.get(paymentType);
|
|
if (strategy == null) {
|
|
return null;
|
|
}
|
|
|
|
Map<String, Object> info = new HashMap<>();
|
|
info.put("paymentType", paymentType);
|
|
info.put("strategyName", strategy.getStrategyName());
|
|
info.put("strategyDescription", strategy.getStrategyDescription());
|
|
info.put("supportRefund", strategy.supportRefund());
|
|
info.put("supportQuery", strategy.supportQuery());
|
|
info.put("supportClose", strategy.supportClose());
|
|
info.put("needNotify", strategy.needNotify());
|
|
return info;
|
|
}
|
|
|
|
@Override
|
|
public List<Map<String, Object>> getAllPaymentStrategyInfo() {
|
|
return strategyMap.keySet().stream()
|
|
.map(this::getPaymentStrategyInfo)
|
|
.collect(Collectors.toList());
|
|
}
|
|
|
|
/**
|
|
* 获取支付策略
|
|
*/
|
|
private PaymentStrategy getPaymentStrategy(PaymentType paymentType) throws PaymentException {
|
|
PaymentStrategy strategy = strategyMap.get(paymentType);
|
|
if (strategy == null) {
|
|
throw PaymentException.unsupportedPayment("不支持的支付类型: " + paymentType, paymentType);
|
|
}
|
|
return strategy;
|
|
}
|
|
|
|
/**
|
|
* 验证查询参数
|
|
*/
|
|
private void validateQueryParams(String orderNo, PaymentType paymentType, Integer tenantId) throws PaymentException {
|
|
if (!StringUtils.hasText(orderNo)) {
|
|
throw PaymentException.paramError("订单号不能为空");
|
|
}
|
|
if (paymentType == null) {
|
|
throw PaymentException.paramError("支付类型不能为空");
|
|
}
|
|
if (tenantId == null || tenantId <= 0) {
|
|
throw PaymentException.paramError("租户ID不能为空且必须大于0");
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 验证回调参数
|
|
*/
|
|
private void validateNotifyParams(PaymentType paymentType, Map<String, String> headers, String body, Integer tenantId) throws PaymentException {
|
|
if (paymentType == null) {
|
|
throw PaymentException.paramError("支付类型不能为空");
|
|
}
|
|
if (headers == null || headers.isEmpty()) {
|
|
throw PaymentException.paramError("请求头不能为空");
|
|
}
|
|
if (!StringUtils.hasText(body)) {
|
|
throw PaymentException.paramError("请求体不能为空");
|
|
}
|
|
if (tenantId == null || tenantId <= 0) {
|
|
throw PaymentException.paramError("租户ID不能为空且必须大于0");
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 验证退款参数
|
|
*/
|
|
private void validateRefundParams(String orderNo, String refundNo, PaymentType paymentType,
|
|
BigDecimal totalAmount, BigDecimal refundAmount, Integer tenantId) throws PaymentException {
|
|
if (!StringUtils.hasText(orderNo)) {
|
|
throw PaymentException.paramError("订单号不能为空");
|
|
}
|
|
if (!StringUtils.hasText(refundNo)) {
|
|
throw PaymentException.paramError("退款单号不能为空");
|
|
}
|
|
if (paymentType == null) {
|
|
throw PaymentException.paramError("支付类型不能为空");
|
|
}
|
|
if (totalAmount == null || totalAmount.compareTo(BigDecimal.ZERO) <= 0) {
|
|
throw PaymentException.amountError("订单总金额必须大于0");
|
|
}
|
|
if (refundAmount == null || refundAmount.compareTo(BigDecimal.ZERO) <= 0) {
|
|
throw PaymentException.amountError("退款金额必须大于0");
|
|
}
|
|
if (refundAmount.compareTo(totalAmount) > 0) {
|
|
throw PaymentException.amountError("退款金额不能大于订单总金额");
|
|
}
|
|
if (tenantId == null || tenantId <= 0) {
|
|
throw PaymentException.paramError("租户ID不能为空且必须大于0");
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 验证退款查询参数
|
|
*/
|
|
private void validateRefundQueryParams(String refundNo, PaymentType paymentType, Integer tenantId) throws PaymentException {
|
|
if (!StringUtils.hasText(refundNo)) {
|
|
throw PaymentException.paramError("退款单号不能为空");
|
|
}
|
|
if (paymentType == null) {
|
|
throw PaymentException.paramError("支付类型不能为空");
|
|
}
|
|
if (tenantId == null || tenantId <= 0) {
|
|
throw PaymentException.paramError("租户ID不能为空且必须大于0");
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 验证关闭订单参数
|
|
*/
|
|
private void validateCloseParams(String orderNo, PaymentType paymentType, Integer tenantId) throws PaymentException {
|
|
if (!StringUtils.hasText(orderNo)) {
|
|
throw PaymentException.paramError("订单号不能为空");
|
|
}
|
|
if (paymentType == null) {
|
|
throw PaymentException.paramError("支付类型不能为空");
|
|
}
|
|
if (tenantId == null || tenantId <= 0) {
|
|
throw PaymentException.paramError("租户ID不能为空且必须大于0");
|
|
}
|
|
}
|
|
}
|