ref 退款流程重构

This commit is contained in:
xxm1995
2024-04-22 17:58:32 +08:00
parent a35fc472fc
commit a62f441195
33 changed files with 88 additions and 384 deletions

View File

@@ -18,7 +18,7 @@ public class PaySyncParam extends PaymentCommonParam {
@Schema(description = "订单号")
private String orderNo;
@Schema(description = "业务号")
private String outTradeNo;
@Schema(description = "商户业务号")
private String bizOrderNo;
}

View File

@@ -17,10 +17,10 @@ import lombok.experimental.Accessors;
@Schema(title = "退款响应参数")
public class RefundResult extends CommonResult {
@Schema(description = "退款ID")
private Long refundId;
@Schema(description = "商户退款订单号")
private String bizRefundNo;
@Schema(description = "退款订单号, 如果请求时未传, 则默认使用退款ID")
@Schema(description = "退款订单号")
private String refundNo;
@Schema(description = "退款状态")

View File

@@ -1,14 +1,11 @@
package cn.bootx.platform.daxpay.service.common.context;
import cn.bootx.platform.daxpay.code.RefundStatusEnum;
import cn.bootx.platform.daxpay.service.core.order.refund.entity.RefundChannelOrder;
import cn.bootx.platform.daxpay.service.core.order.refund.entity.RefundOrder;
import cn.bootx.platform.daxpay.service.core.order.refund.entity.RefundOrderExtra;
import lombok.Data;
import lombok.experimental.Accessors;
import java.util.List;
/**
* 异步退款信息
* @author xxm
@@ -21,7 +18,7 @@ public class RefundLocal {
/**
* 第三方支付网关生成的退款订单号, 用与将记录关联起来
*/
private String gatewayOrderNo;
private String outRefundNo;
/**
* 退款状态, 默认为成功, 通常含有异步支付时, 才会出现别的状态
@@ -39,7 +36,4 @@ public class RefundLocal {
/** 退款订单扩展 */
private RefundOrderExtra runOrderExtra;
/** 通道退款订单 */
private List<RefundChannelOrder> refundChannelOrders;
}

View File

@@ -49,7 +49,7 @@ public class AliPayRefundService {
}
// 默认为退款中状态
refundInfo.setStatus(RefundStatusEnum.PROGRESS)
.setGatewayOrderNo(response.getTradeNo());
.setOutRefundNo(response.getTradeNo());
// 接口返回fund_change=Y为退款成功fund_change=N或无此字段值返回时需通过退款查询接口进一步确认退款状态
if (response.getFundChange().equals("Y")){

View File

@@ -4,7 +4,6 @@ import cn.bootx.platform.daxpay.code.RefundStatusEnum;
import cn.bootx.platform.daxpay.service.code.UnionPayCode;
import cn.bootx.platform.daxpay.service.common.context.RefundLocal;
import cn.bootx.platform.daxpay.service.common.local.PaymentContextLocal;
import cn.bootx.platform.daxpay.service.core.order.pay.entity.PayChannelOrder;
import cn.bootx.platform.daxpay.service.core.order.pay.entity.PayOrder;
import cn.bootx.platform.daxpay.service.core.order.refund.entity.RefundOrder;
import cn.bootx.platform.daxpay.service.sdk.union.api.UnionPayKit;
@@ -29,15 +28,15 @@ public class UnionPayRefundService {
/**
* 退款方法
*/
public void refund(RefundOrder refundOrder, PayOrder payOrder, int amount, PayChannelOrder channelOrder, UnionPayKit unionPayKit) {
public void refund(RefundOrder refundOrder, PayOrder payOrder, int amount, UnionPayKit unionPayKit) {
// 金额转换
BigDecimal refundAmount = BigDecimal.valueOf(amount * 0.01);
BigDecimal orderAmount = BigDecimal.valueOf(channelOrder.getAmount() * 0.01);
BigDecimal orderAmount = BigDecimal.valueOf(payOrder.getAmount() * 0.01);
UnionRefundOrder unionRefundOrder = new UnionRefundOrder();
unionRefundOrder.setRefundNo(refundOrder.getRefundNo());
unionRefundOrder.setTradeNo(String.valueOf(payOrder.getGatewayOrderNo()));
unionRefundOrder.setTradeNo(String.valueOf(payOrder.getOrderNo()));
unionRefundOrder.setRefundAmount(refundAmount);
unionRefundOrder.setTotalAmount(orderAmount);
UnionRefundResult refund = unionPayKit.refund(unionRefundOrder);
@@ -45,6 +44,6 @@ public class UnionPayRefundService {
String gatewayNo = (String) refund.getAttr(UnionPayCode.QUERY_ID);
// 云闪付退款是否成功需要查询状态, 所以设置为退款中状态
RefundLocal refundInfo = PaymentContextLocal.get().getRefundInfo();
refundInfo.setStatus(RefundStatusEnum.PROGRESS).setGatewayOrderNo(gatewayNo);
refundInfo.setStatus(RefundStatusEnum.PROGRESS).setOutRefundNo(gatewayNo);
}
}

View File

@@ -5,7 +5,6 @@ import cn.bootx.platform.daxpay.exception.pay.PayFailureException;
import cn.bootx.platform.daxpay.service.common.context.RefundLocal;
import cn.bootx.platform.daxpay.service.common.local.PaymentContextLocal;
import cn.bootx.platform.daxpay.service.core.channel.wechat.entity.WeChatPayConfig;
import cn.bootx.platform.daxpay.service.core.order.pay.entity.PayChannelOrder;
import cn.bootx.platform.daxpay.service.core.order.refund.entity.RefundOrder;
import cn.hutool.core.codec.Base64;
import cn.hutool.core.util.StrUtil;
@@ -37,9 +36,9 @@ public class WechatPayRefundService {
* 退款方法
* 微信需要同时传输订单金额或退款金额
*/
public void refund(RefundOrder refundOrder, int amount, PayChannelOrder orderChannel, WeChatPayConfig weChatPayConfig) {
public void refund(RefundOrder refundOrder, int amount, WeChatPayConfig weChatPayConfig) {
String refundFee = String.valueOf(amount);
String totalFee = String.valueOf(orderChannel.getAmount());
String totalFee = String.valueOf(refundOrder.getAmount());
// 设置退款信息
RefundLocal refundInfo = PaymentContextLocal.get().getRefundInfo();
Map<String, String> params = RefundModel.builder()
@@ -68,7 +67,7 @@ public class WechatPayRefundService {
this.verifyErrorMsg(result);
// 微信退款是否成功需要查询状态或者回调, 所以设置为退款中状态
refundInfo.setStatus(RefundStatusEnum.PROGRESS)
.setGatewayOrderNo(result.get(CALLBACK_REFUND_ID));
.setOutRefundNo(result.get(CALLBACK_REFUND_ID));
}
/**

View File

@@ -54,17 +54,17 @@ public class PayOrderQueryService {
}
/**
* 根据商户订单号查询
* 根据订单号查询
*/
public Optional<PayOrder> findByOrderNo(String businessNo) {
return payOrderManager.findByOrderNo(businessNo);
public Optional<PayOrder> findByOrderNo(String orderNo) {
return payOrderManager.findByOrderNo(orderNo);
}
/**
* 根据订单号查询
* 根据商户订单号查询
*/
public Optional<PayOrder> findByOutOrderNo(String businessNo) {
return payOrderManager.findByBizOrderNo(businessNo);
public Optional<PayOrder> findByBizOrderNo(String bizOrderNo) {
return payOrderManager.findByBizOrderNo(bizOrderNo);
}
/**

View File

@@ -55,7 +55,7 @@ public class RefundOrder extends MpBaseEntity implements EntityBaseFunction<Refu
/** 三方支付系统退款交易号 */
@DbColumn(comment = "三方支付系统退款交易号")
private String outOrderNo;
private String outRefundNo;
/**
* 退款通道

View File

@@ -60,7 +60,7 @@ public class PayCloseService {
.orElseThrow(() -> new PayFailureException("未查询到支付订单"));
}
if (Objects.isNull(payOrder)){
payOrder = payOrderQueryService.findByOutOrderNo(param.getBusinessNo())
payOrder = payOrderQueryService.findByBizOrderNo(param.getBusinessNo())
.orElseThrow(() -> new PayFailureException("未查询到支付订单"));
}
LockInfo lock = lockTemplate.lock("payment:close:" + payOrder.getId(),10000, 50);

View File

@@ -74,11 +74,7 @@ public class ClientNoticeAssistService {
/**
* 构建出退款通知任务对象
*/
public ClientNoticeTask buildRefundTask(RefundOrder order, RefundOrderExtra orderExtra, List<RefundChannelOrder> channelOrders){
// 组装内容
List<RefundChannelResult> channels = channelOrders.stream()
.map(o->new RefundChannelResult().setChannel(o.getChannel()).setAmount(o.getAmount()))
.collect(Collectors.toList());
public ClientNoticeTask buildRefundTask(RefundOrder order, RefundOrderExtra orderExtra){
RefundNoticeResult payNoticeResult = new RefundNoticeResult()
.setRefundId(order.getId())

View File

@@ -1,17 +1,13 @@
package cn.bootx.platform.daxpay.service.core.payment.notice.service;
import cn.bootx.platform.common.core.exception.RepetitiveOperationException;
import cn.bootx.platform.common.core.util.CollUtil;
import cn.bootx.platform.common.core.util.LocalDateTimeUtil;
import cn.bootx.platform.common.redis.RedisClient;
import cn.bootx.platform.daxpay.service.code.ClientNoticeSendTypeEnum;
import cn.bootx.platform.daxpay.service.core.order.pay.dao.PayChannelOrderManager;
import cn.bootx.platform.daxpay.service.core.order.pay.dao.PayOrderExtraManager;
import cn.bootx.platform.daxpay.service.core.order.pay.entity.PayOrder;
import cn.bootx.platform.daxpay.service.core.order.pay.entity.PayOrderExtra;
import cn.bootx.platform.daxpay.service.core.order.refund.dao.RefundChannelOrderManager;
import cn.bootx.platform.daxpay.service.core.order.refund.dao.RefundOrderExtraManager;
import cn.bootx.platform.daxpay.service.core.order.refund.entity.RefundChannelOrder;
import cn.bootx.platform.daxpay.service.core.order.refund.entity.RefundOrder;
import cn.bootx.platform.daxpay.service.core.order.refund.entity.RefundOrderExtra;
import cn.bootx.platform.daxpay.service.core.task.notice.dao.ClientNoticeTaskManager;
@@ -45,10 +41,6 @@ public class ClientNoticeService {
private final PayOrderExtraManager payOrderExtraManager;
private final PayChannelOrderManager payChannelOrderManager;
private final RefundChannelOrderManager refundChannelOrderManager;
private final RefundOrderExtraManager refundOrderExtraManager;
private final ClientNoticeAssistService clientNoticeAssistService;
@@ -126,10 +118,9 @@ public class ClientNoticeService {
* 注册退款消息通知任务
* @param order 退款订单
* @param orderExtra 退款订单扩展信息
* @param channelOrders 退款通道订单
*/
@Async("bigExecutor")
public void registerRefundNotice(RefundOrder order, RefundOrderExtra orderExtra, List<RefundChannelOrder> channelOrders) {
public void registerRefundNotice(RefundOrder order, RefundOrderExtra orderExtra) {
// 退款订单扩展信息为空则进行查询
if (Objects.isNull(orderExtra)){
Optional<RefundOrderExtra> extraOpt = refundOrderExtraManager.findById(order.getId());
@@ -145,12 +136,8 @@ public class ClientNoticeService {
return;
}
// 通道退款订单为空则进行查询
if (CollUtil.isEmpty(channelOrders)){
channelOrders = refundChannelOrderManager.findAllByRefundId(order.getId());
}
// 创建通知任务并保存
ClientNoticeTask task = clientNoticeAssistService.buildRefundTask(order, orderExtra, channelOrders);
ClientNoticeTask task = clientNoticeAssistService.buildRefundTask(order, orderExtra);
try {
taskManager.save(task);
} catch (Exception e) {

View File

@@ -183,7 +183,7 @@ public class PayAssistService {
*/
public PayOrder getOrderAndCheck(String bizOrderNo) {
// 根据订单查询支付记录
PayOrder payOrder = payOrderQueryService.findByOutOrderNo(bizOrderNo)
PayOrder payOrder = payOrderQueryService.findByBizOrderNo(bizOrderNo)
.orElse(null);
if (Objects.nonNull(payOrder)) {
// 待支付

View File

@@ -82,7 +82,7 @@ public class RefundAssistService {
public RefundOrder getRefundOrder(RefundParam param){
// 查询退款单
RefundOrder refundOrder = null;
if (Objects.nonNull(param.getRefundNo())){
if (Objects.nonNull(param.getrefun())){
refundOrder = refundOrderManager.findById(param.getRefundNo())
.orElseThrow(() -> new DataNotExistException("未查询到支付订单"));
}
@@ -103,7 +103,7 @@ public class RefundAssistService {
.orElseThrow(() -> new PayFailureException("未查询到支付订单"));
}
if (Objects.isNull(payOrder)){
payOrder = payOrderQueryService.findByOutOrderNo(param.getBizOrderNo())
payOrder = payOrderQueryService.findByBizOrderNo(param.getBizOrderNo())
.orElseThrow(() -> new PayFailureException("未查询到支付订单"));
}
return payOrder;
@@ -150,6 +150,7 @@ public class RefundAssistService {
.setTitle(payOrder.getTitle())
.setReason(refundParam.getReason());
refundOrderManager.save(refundOrder);
// 生成退款扩展订单
NoticeLocal notice = PaymentContextLocal.get().getNoticeInfo();
RefundOrderExtra orderExtra = new RefundOrderExtra()
.setClientIp(refundParam.getClientIp())
@@ -166,10 +167,10 @@ public class RefundAssistService {
* 更新退款成功信息
*/
@Transactional(rollbackFor = Exception.class)
public void updateOrder(RefundOrder refundOrder, RefundOrderExtra orderExtra){
public void updateOrder(RefundOrder refundOrder){
RefundLocal asyncRefundInfo = PaymentContextLocal.get().getRefundInfo();
refundOrder.setStatus(asyncRefundInfo.getStatus().getCode())
.setGatewayOrderNo(asyncRefundInfo.getGatewayOrderNo());
.setRefundNo(asyncRefundInfo.getOutRefundNo());
// 退款成功更新退款时间
if (Objects.equals(refundOrder.getStatus(), SUCCESS.getCode())){
refundOrder.setRefundTime(LocalDateTime.now());

View File

@@ -99,29 +99,22 @@ public class RefundService {
refundStrategy.doBeforeRefundHandler();
// 退款操作的预处理, 使用独立的新事物进行发起, 返回创建成功的退款订单, 成功后才可以进行下一阶段的操作
RefundOrder refundOrder = SpringUtil.getBean(this.getClass()).preRefundMethod(refundParam, payOrder, refundStrategy);
RefundOrder refundOrder = SpringUtil.getBean(this.getClass()).preRefundMethod(param, payOrder);
try {
// 3.2 执行退款策略
refundStrategy.forEach(AbsRefundStrategy::doRefundHandler);
// 3.3 执行退款发起成功后操作
refundStrategy.forEach(AbsRefundStrategy::doSuccessHandler);
// 4.进行成功处理, 分别处理退款订单, 通道退款订单, 支付订单
List<RefundChannelOrder> refundChannelOrders = refundStrategy.stream()
.map(AbsRefundStrategy::getRefundChannelOrder)
.collect(Collectors.toList());
this.successHandler(refundOrder, refundChannelOrders, payOrder);
return new RefundResult()
.setRefundId(refundOrder.getId())
.setRefundNo(refundParam.getRefundNo());
refundStrategy.doRefundHandler();
}
catch (Exception e) {
// 5. 失败处理, 所有记录都会回滚, 可以调用重新
PaymentContextLocal.get().getRefundInfo().setStatus(RefundStatusEnum.FAIL);
this.errorHandler(refundOrder);
throw e;
// 记录退款失败的记录
refundAssistService.updateOrderByError(refundOrder);
}
SpringUtil.getBean(this.getClass()).successHandler(refundOrder, payOrder);
return new RefundResult()
.setBizRefundNo(refundOrder.getBizRefundNo())
.setRefundNo(refundOrder.getRefundNo());
}
/**
@@ -130,7 +123,7 @@ public class RefundService {
@Transactional(rollbackFor = Exception.class )
public RefundOrder preRefundMethod(RefundParam refundParam, PayOrder payOrder) {
// --------------------------- 支付订单 -------------------------------------
// 预扣支付相关订单要退款的金额并进行更新
// 预扣支付订单要退款的金额并进行更新
int orderRefundableBalance = payOrder.getRefundableBalance() - refundParam.getAmount();
payOrder.setRefundableBalance(orderRefundableBalance)
.setStatus(PayStatusEnum.REFUNDING.getCode());
@@ -147,7 +140,7 @@ public class RefundService {
public RefundResult repeatRefund(RefundParam param, RefundOrder refundOrder){
// 退款失败才可以重新发起退款, 重新发起退款
if (!Objects.equals(refundOrder.getStatus(), RefundStatusEnum.FAIL.getCode())){
throw new PayFailureException("退款状态不正确, 无法重新发起退款");
throw new PayFailureException("只有失败状态的才可以重新发起退款");
}
AbsRefundStrategy refundStrategy = RefundStrategyFactory.create(refundOrder.getRefundNo());
@@ -172,18 +165,19 @@ public class RefundService {
catch (Exception e) {
// 5. 失败处理, 所有记录都会回滚, 可以调用退款重试
PaymentContextLocal.get().getRefundInfo().setStatus(RefundStatusEnum.FAIL);
this.errorHandler(refundOrder);
throw e;
// 记录退款失败的记录
refundAssistService.updateOrderByError(refundOrder);
}
}
/**
* 成功处理, 更新退款订单, 退款通道订单, 支付订单, 支付通道订单
*/
private void successHandler(RefundOrder refundOrder, List<RefundChannelOrder> refundChannelOrders, PayOrder payOrder) {
@Transactional(rollbackFor = Exception.class)
public void successHandler(RefundOrder refundOrder, PayOrder payOrder) {
RefundLocal refundInfo = PaymentContextLocal.get().getRefundInfo();
// 剩余可退款余额
int refundableBalance = refundOrder.getRefundableBalance();
int refundableBalance = payOrder.getRefundableBalance();
// 设置支付订单状态
if (refundInfo.getStatus() == RefundStatusEnum.PROGRESS) {
payOrder.setStatus(PayStatusEnum.REFUNDING.getCode());
@@ -195,20 +189,13 @@ public class RefundService {
payOrderService.updateById(payOrder);
// 更新退款订单和相关通道订单
refundAssistService.updateOrder(refundOrder,refundChannelOrders);
refundAssistService.updateOrder(refundOrder);
// 发送通知
List<String> list = Arrays.asList(RefundStatusEnum.SUCCESS.getCode(), RefundStatusEnum.CLOSE.getCode(), RefundStatusEnum.FAIL.getCode());
if (list.contains(refundOrder.getStatus())){
clientNoticeService.registerRefundNotice(refundOrder, refundInfo.getRunOrderExtra(), refundChannelOrders);
clientNoticeService.registerRefundNotice(refundOrder, refundInfo.getRunOrderExtra());
}
}
/**
* 失败处理, 只更新退款订单, 通道订单不进行错误更新
*/
private void errorHandler(RefundOrder refundOrder) {
// 记录退款失败的记录
refundAssistService.updateOrderByError(refundOrder);
}
}

View File

@@ -1,8 +1,6 @@
package cn.bootx.platform.daxpay.service.core.payment.refund.strategy;
import cn.bootx.platform.daxpay.code.PayChannelEnum;
import cn.bootx.platform.daxpay.code.RefundStatusEnum;
import cn.bootx.platform.daxpay.service.common.local.PaymentContextLocal;
import cn.bootx.platform.daxpay.service.core.channel.alipay.entity.AliPayConfig;
import cn.bootx.platform.daxpay.service.core.channel.alipay.service.AliPayConfigService;
import cn.bootx.platform.daxpay.service.core.channel.alipay.service.AliPayRefundService;
@@ -11,8 +9,6 @@ import lombok.RequiredArgsConstructor;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
import java.util.Objects;
import static org.springframework.beans.factory.config.BeanDefinition.SCOPE_PROTOTYPE;
/**
@@ -27,8 +23,6 @@ public class AliRefundStrategy extends AbsRefundStrategy {
private final AliPayConfigService alipayConfigService;
private final AliPayRefundService aliRefundService;
private final AliPayRecordService aliRecordService;;
private final PayChannelOrderService payChannelOrderService;
/**
* 策略标识
*
@@ -54,25 +48,6 @@ public class AliRefundStrategy extends AbsRefundStrategy {
*/
@Override
public void doRefundHandler() {
aliRefundService.refund(this.getRefundOrder(), this.getRefundChannelParam().getAmount());
}
/**
* 退款发起成功操作
*/
@Override
public void doSuccessHandler() {
// 更新退款订单数据状态
RefundStatusEnum refundStatusEnum = PaymentContextLocal.get()
.getRefundInfo()
.getStatus();
this.getRefundChannelOrder().setStatus(refundStatusEnum.getCode());
// 更新支付通道订单中的属性
payChannelOrderService.updateAsyncPayRefund(this.getPayChannelOrder(), this.getRefundChannelOrder());
// 如果退款完成, 保存流水记录
if (Objects.equals(RefundStatusEnum.SUCCESS.getCode(), refundStatusEnum.getCode())) {
aliRecordService.refund(this.getRefundOrder(), this.getRefundChannelOrder());
}
aliRefundService.refund(this.getRefundOrder(), this.getRefundParam().getAmount());
}
}

View File

@@ -1,8 +1,6 @@
package cn.bootx.platform.daxpay.service.core.payment.refund.strategy;
import cn.bootx.platform.daxpay.code.PayChannelEnum;
import cn.bootx.platform.daxpay.code.RefundStatusEnum;
import cn.bootx.platform.daxpay.service.common.local.PaymentContextLocal;
import cn.bootx.platform.daxpay.service.core.channel.union.entity.UnionPayConfig;
import cn.bootx.platform.daxpay.service.core.channel.union.service.UnionPayConfigService;
import cn.bootx.platform.daxpay.service.core.channel.union.service.UnionPayRefundService;
@@ -12,8 +10,6 @@ import lombok.RequiredArgsConstructor;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
import java.util.Objects;
import static org.springframework.beans.factory.config.BeanDefinition.SCOPE_PROTOTYPE;
/**
@@ -30,10 +26,6 @@ public class UnionRefundStrategy extends AbsRefundStrategy {
private final UnionPayConfigService unionPayConfigService;
private final UnionPayRecordService unionPayRecordService;
private final PayChannelOrderService payChannelOrderService;
private UnionPayConfig unionPayConfig;
/**
@@ -61,26 +53,6 @@ public class UnionRefundStrategy extends AbsRefundStrategy {
@Override
public void doRefundHandler() {
UnionPayKit unionPayKit = unionPayConfigService.initPayService(unionPayConfig);
unionPayRefundService.refund(this.getRefundOrder(), this.getPayOrder(), this.getRefundChannelParam().getAmount(), this.getPayChannelOrder(), unionPayKit);
unionPayRefundService.refund(this.getRefundOrder(), this.getPayOrder(), this.getRefundParam().getAmount(), unionPayKit);
}
/**
* 退款发起成功操作
*/
@Override
public void doSuccessHandler() {
// 更新退款订单数据状态
RefundStatusEnum refundStatusEnum = PaymentContextLocal.get()
.getRefundInfo()
.getStatus();
this.getRefundChannelOrder().setStatus(refundStatusEnum.getCode());
// 更新支付通道订单中的属性
payChannelOrderService.updateAsyncPayRefund(this.getPayChannelOrder(), this.getRefundChannelOrder());
// 如果退款完成, 保存流水记录
if (Objects.equals(RefundStatusEnum.SUCCESS.getCode(), refundStatusEnum.getCode())) {
unionPayRecordService.refund(this.getRefundOrder(), this.getRefundChannelOrder());
}
}
}

View File

@@ -1,22 +1,15 @@
package cn.bootx.platform.daxpay.service.core.payment.refund.strategy;
import cn.bootx.platform.daxpay.code.PayChannelEnum;
import cn.bootx.platform.daxpay.code.PayStatusEnum;
import cn.bootx.platform.daxpay.code.RefundStatusEnum;
import cn.bootx.platform.daxpay.param.channel.WalletPayParam;
import cn.bootx.platform.daxpay.service.core.channel.wallet.entity.Wallet;
import cn.bootx.platform.daxpay.service.core.channel.wallet.service.WalletPayService;
import cn.bootx.platform.daxpay.service.core.channel.wallet.service.WalletQueryService;
import cn.bootx.platform.daxpay.service.core.order.pay.entity.PayChannelOrder;
import cn.bootx.platform.daxpay.service.core.order.pay.entity.PayOrder;
import cn.bootx.platform.daxpay.service.func.AbsRefundStrategy;
import cn.hutool.json.JSONUtil;
import lombok.RequiredArgsConstructor;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
import java.util.Objects;
import static org.springframework.beans.factory.config.BeanDefinition.SCOPE_PROTOTYPE;
/**
@@ -31,8 +24,6 @@ public class WalletRefundStrategy extends AbsRefundStrategy {
private final WalletPayService walletPayService;
private final WalletRecordService walletRecordService;
private final WalletQueryService walletQueryService;
private WalletPayParam walletPayParam;
@@ -50,31 +41,13 @@ public class WalletRefundStrategy extends AbsRefundStrategy {
}
/**
* 退款前对处理
*/
@Override
public void initRefundParam(PayOrder payOrder, PayChannelOrder payChannelOrder) {
// 先设置参数
super.initRefundParam(payOrder, payChannelOrder);
// 从通道扩展参数中取出钱包参数
String channelExtra = this.getPayChannelOrder().getChannelExtra();
this.walletPayParam = JSONUtil.toBean(channelExtra, WalletPayParam.class);
}
/**
* 退款前对处理
*/
@Override
public void doBeforeRefundHandler() {
// 如果任务执行完成, 则跳过
if (Objects.equals(this.getRefundChannelOrder().getStatus(), RefundStatusEnum.SUCCESS.getCode())){
return;
}
//
if (!this.getPayOrder().isAsyncPay()) {
this.wallet = walletQueryService.getWallet(this.walletPayParam);
}
// 获取钱包
this.wallet = walletQueryService.getWallet(this.walletPayParam);
}
/**
@@ -82,30 +55,7 @@ public class WalletRefundStrategy extends AbsRefundStrategy {
*/
@Override
public void doRefundHandler() {
// 如果任务执行完成, 则跳过
if (Objects.equals(this.getRefundChannelOrder().getStatus(), RefundStatusEnum.SUCCESS.getCode())){
return;
}
// 不包含异步支付, 则只在支付订单中进行扣减, 等待异步退款完成, 再进行退款
if (!this.getPayOrder().isAsyncPay()){
walletPayService.refund(this.wallet, this.getRefundChannelParam().getAmount());
walletRecordService.refund(this.getRefundChannelOrder(), this.getPayOrder().getTitle(), this.wallet);
}
walletPayService.refund(this.wallet, this.getRefundParam().getAmount());
}
/**
* 退款发起成功操作, 异步支付通道需要进行重写
*/
@Override
public void doSuccessHandler() {
// 包含异步支付, 变更状态到退款中
if (this.getPayOrder().isAsyncPay()) {
this.getPayChannelOrder().setStatus(PayStatusEnum.REFUNDING.getCode());
this.getRefundChannelOrder().setStatus(RefundStatusEnum.PROGRESS.getCode());
} else{
// 同步支付, 直接标识状态为退款完成
super.doSuccessHandler();
}
}
}

View File

@@ -1,8 +1,6 @@
package cn.bootx.platform.daxpay.service.core.payment.refund.strategy;
import cn.bootx.platform.daxpay.code.PayChannelEnum;
import cn.bootx.platform.daxpay.code.RefundStatusEnum;
import cn.bootx.platform.daxpay.service.common.local.PaymentContextLocal;
import cn.bootx.platform.daxpay.service.core.channel.wechat.entity.WeChatPayConfig;
import cn.bootx.platform.daxpay.service.core.channel.wechat.service.WeChatPayConfigService;
import cn.bootx.platform.daxpay.service.core.channel.wechat.service.WechatPayRefundService;
@@ -11,8 +9,6 @@ import lombok.RequiredArgsConstructor;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
import java.util.Objects;
import static org.springframework.beans.factory.config.BeanDefinition.SCOPE_PROTOTYPE;
/**
@@ -29,10 +25,6 @@ public class WeChatRefundStrategy extends AbsRefundStrategy {
private final WechatPayRefundService wechatPayRefundService;
private final WeChatPayRecordService weChatPayRecordService;
private final PayChannelOrderService payChannelOrderService;
private WeChatPayConfig weChatPayConfig;
/**
@@ -59,25 +51,7 @@ public class WeChatRefundStrategy extends AbsRefundStrategy {
*/
@Override
public void doRefundHandler() {
wechatPayRefundService.refund(this.getRefundOrder(), this.getRefundChannelParam().getAmount(), this.getPayChannelOrder(), this.weChatPayConfig);
wechatPayRefundService.refund(this.getRefundOrder(), this.getRefundParam().getAmount(), this.weChatPayConfig);
}
/**
* 退款发起成功操作
*/
@Override
public void doSuccessHandler() {
// 更新退款订单数据状态
RefundStatusEnum refundStatusEnum = PaymentContextLocal.get()
.getRefundInfo()
.getStatus();
this.getRefundChannelOrder().setStatus(refundStatusEnum.getCode());
// 更新支付通道订单中的属性
payChannelOrderService.updateAsyncPayRefund(this.getPayChannelOrder(), this.getRefundChannelOrder());
// 如果退款完成, 保存流水记录
if (Objects.equals(RefundStatusEnum.SUCCESS.getCode(), refundStatusEnum.getCode())) {
weChatPayRecordService.refund(this.getRefundOrder(), this.getRefundChannelOrder());
}
}
}

View File

@@ -2,20 +2,13 @@ package cn.bootx.platform.daxpay.service.core.payment.repair.factory;
import cn.bootx.platform.daxpay.code.PayChannelEnum;
import cn.bootx.platform.daxpay.exception.pay.PayUnsupportedMethodException;
import cn.bootx.platform.daxpay.service.core.payment.repair.strategy.pay.*;
import cn.bootx.platform.daxpay.service.core.payment.repair.strategy.pay.AliPayRepairStrategy;
import cn.bootx.platform.daxpay.service.core.payment.repair.strategy.pay.UnionPayRepairStrategy;
import cn.bootx.platform.daxpay.service.core.payment.repair.strategy.pay.WalletPayRepairStrategy;
import cn.bootx.platform.daxpay.service.core.payment.repair.strategy.pay.WeChatPayRepairStrategy;
import cn.bootx.platform.daxpay.service.func.AbsPayRepairStrategy;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.extra.spring.SpringUtil;
import lombok.experimental.UtilityClass;
import lombok.val;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;
import static cn.bootx.platform.daxpay.code.PayChannelEnum.ASYNC_TYPE_CODE;
/**
* 支付修复策略工厂类
@@ -28,8 +21,8 @@ public class PayRepairStrategyFactory {
* 根据传入的支付通道创建策略
* @return 支付修复策略
*/
public static AbsPayRepairStrategy create(PayChannelEnum channelEnum) {
public static AbsPayRepairStrategy create(String channel) {
PayChannelEnum channelEnum = PayChannelEnum.findByCode(channel);
AbsPayRepairStrategy strategy;
switch (channelEnum) {
case ALI:
@@ -50,58 +43,4 @@ public class PayRepairStrategyFactory {
return strategy;
}
/**
* 根据传入的支付类型批量创建策略, 异步支付在后面
*/
public static List<AbsPayRepairStrategy> createAsyncLast(List<String> channelCodes) {
return create(channelCodes, true);
}
/**
* 根据传入的支付类型批量创建策略, 异步支付在前面
*/
public static List<AbsPayRepairStrategy> createAsyncFront(List<String> channelCodes) {
return create(channelCodes, false);
}
/**
* 根据传入的支付类型批量创建策略
* @param asyncSort 是否异步支付在后面
* @return 支付策略
*/
private static List<AbsPayRepairStrategy> create(List<String> channelCodes, boolean asyncSort) {
if (CollectionUtil.isEmpty(channelCodes)) {
return Collections.emptyList();
}
// 同步支付
val syncChannels = channelCodes.stream()
.filter(code -> !ASYNC_TYPE_CODE.contains(code))
.map(PayChannelEnum::findByCode)
.collect(Collectors.toList());
// 异步支付
val asyncChannels = channelCodes.stream()
.filter(ASYNC_TYPE_CODE::contains)
.map(PayChannelEnum::findByCode)
.collect(Collectors.toList());
List<PayChannelEnum> sortList = new ArrayList<>(channelCodes.size());
// 异步在后面
if (asyncSort) {
sortList.addAll(syncChannels);
sortList.addAll(asyncChannels);
}
else {
sortList.addAll(asyncChannels);
sortList.addAll(syncChannels);
}
// 此处有一个根据Type的反转排序
return sortList.stream()
.filter(Objects::nonNull)
.map(PayRepairStrategyFactory::create)
.collect(Collectors.toList());
}
}

View File

@@ -61,38 +61,29 @@ public class PayRepairService {
return new PayRepairResult();
}
// 1. 获取支付单管理的通道支付订单
Map<String, PayChannelOrder> channelOrderMap = channelOrderManager.findAllByPaymentId(order.getOrderNo())
.stream()
.collect(Collectors.toMap(PayChannelOrder::getChannel, Function.identity(), CollectorsFunction::retainLatest));
List<String> channels = new ArrayList<>(channelOrderMap.keySet());
// 2.1 初始化修复参数
List<AbsPayRepairStrategy> repairStrategies = PayRepairStrategyFactory.createAsyncLast(channels);
for (AbsPayRepairStrategy repairStrategy : repairStrategies) {
repairStrategy.initRepairParam(order,channelOrderMap.get(repairStrategy.getChannel().getCode()));
}
AbsPayRepairStrategy repairStrategy = PayRepairStrategyFactory.create(order.getChannel());
repairStrategy.setOrder(order);
// 2.2 执行前置处理
repairStrategies.forEach(AbsPayRepairStrategy::doBeforeHandler);
repairStrategy.doBeforeHandler();
// 3. 根据不同的类型执行对应的修复逻辑
PayRepairResult repairResult = new PayRepairResult().setBeforeStatus(PayStatusEnum.findByCode(order.getStatus()));
switch (repairType) {
case PAY_SUCCESS:
this.success(order, repairStrategies);
this.success(order, repairstrategies);
repairResult.setAfterPayStatus(PayStatusEnum.SUCCESS);
break;
case CLOSE_LOCAL:
this.closeLocal(order, repairStrategies);
this.closeLocal(order, repairstrategies);
repairResult.setAfterPayStatus(PayStatusEnum.CLOSE);
break;
case PROGRESS:
this.waitPay(order, repairStrategies);
this.waitPay(order, repairstrategies);
repairResult.setAfterPayStatus(PayStatusEnum.PROGRESS);
break;
case CLOSE_GATEWAY:
this.closeGateway(order, repairStrategies);
this.closeGateway(order, repairstrategies);
repairResult.setAfterPayStatus(PayStatusEnum.CLOSE);
break;
default:
@@ -103,7 +94,7 @@ public class PayRepairService {
repairResult.setRepairNo(IdUtil.getSnowflakeNextIdStr());
// 发送通知
List<PayChannelOrder> channelOrders = repairStrategies.stream()
List<PayChannelOrder> channelOrders = repairstrategies.stream()
.map(AbsPayRepairStrategy::getChannelOrder)
.collect(Collectors.toList());
// 更新通道订单

View File

@@ -54,8 +54,6 @@ public class RefundRepairService {
private final RefundOrderManager refundOrderManager;
private final RefundChannelOrderManager refundChannelOrderManager;
private final PayRepairRecordService recordService;
private final LockTemplate lockTemplate;

View File

@@ -30,8 +30,6 @@ public class AliPayRepairStrategy extends AbsPayRepairStrategy {
private final AliPayConfigService aliPayConfigService;
private final AliPayRecordService aliRecordService;
/**
* 策略标识
*/
@@ -59,8 +57,6 @@ public class AliPayRepairStrategy extends AbsPayRepairStrategy {
.getFinishTime();
this.getChannelOrder().setStatus(PayStatusEnum.SUCCESS.getCode())
.setPayTime(payTime);
// 支付完成, 保存记录
aliRecordService.pay(this.getOrder(), this.getChannelOrder());
}
/**

View File

@@ -24,8 +24,6 @@ import static org.springframework.beans.factory.config.BeanDefinition.SCOPE_PROT
@RequiredArgsConstructor
public class UnionPayRepairStrategy extends AbsPayRepairStrategy {
private final UnionPayRecordService unionPayRecordService;
/**
* 策略标识
*/
@@ -44,7 +42,6 @@ public class UnionPayRepairStrategy extends AbsPayRepairStrategy {
.getFinishTime();
this.getChannelOrder().setStatus(PayStatusEnum.SUCCESS.getCode())
.setPayTime(payTime);
unionPayRecordService.pay(this.getOrder(), this.getChannelOrder());
}
/**

View File

@@ -30,8 +30,6 @@ public class WalletPayRepairStrategy extends AbsPayRepairStrategy {
private final WalletQueryService walletQueryService;
private final WalletRecordService walletRecordService;
private Wallet wallet;
@@ -59,8 +57,7 @@ public class WalletPayRepairStrategy extends AbsPayRepairStrategy {
*/
@Override
public void doCloseLocalHandler() {
walletPayService.close(this.getChannelOrder(),this.wallet);
walletRecordService.payClose(this.getChannelOrder(), this.getOrder().getTitle(), this.wallet);
walletPayService.close(this.getOrder(),this.wallet);
this.getChannelOrder().setStatus(PayStatusEnum.CLOSE.getCode());
}
}

View File

@@ -30,8 +30,6 @@ public class WeChatPayRepairStrategy extends AbsPayRepairStrategy {
private final WeChatPayConfigService weChatPayConfigService;
private final WeChatPayRecordService weChatPayRecordService;
private WeChatPayConfig weChatPayConfig;
/**
@@ -68,8 +66,6 @@ public class WeChatPayRepairStrategy extends AbsPayRepairStrategy {
.getFinishTime();
this.getChannelOrder().setStatus(PayStatusEnum.SUCCESS.getCode())
.setPayTime(payTime);
// 保存流水记录
weChatPayRecordService.pay(this.getOrder(), this.getChannelOrder());
}
/**

View File

@@ -23,8 +23,6 @@ import static org.springframework.beans.factory.config.BeanDefinition.SCOPE_PROT
@RequiredArgsConstructor
public class AliRefundRepairStrategy extends AbsRefundRepairStrategy {
private final AliPayRecordService aliPayRecordService;
/**
* 策略标识
*/
@@ -45,7 +43,5 @@ public class AliRefundRepairStrategy extends AbsRefundRepairStrategy {
super.doSuccessHandler();
// 异步支付需要追加完成时间
this.getRefundChannelOrder().setRefundTime(finishTime);
// 记录退款成功记录
aliPayRecordService.refund(this.getRefundOrder(), this.getRefundChannelOrder());
}
}

View File

@@ -33,8 +33,6 @@ public class WalletRefundRepairStrategy extends AbsRefundRepairStrategy {
private final WalletPayService walletPayService;
private final WalletRecordService walletRecordService;
private final WalletQueryService walletQueryService;
private Wallet wallet;
@@ -76,6 +74,5 @@ public class WalletRefundRepairStrategy extends AbsRefundRepairStrategy {
refundChannelOrder.setStatus(RefundStatusEnum.SUCCESS.getCode());
// 退款真正执行和保存
walletPayService.refund(this.wallet, this.getRefundChannelOrder().getAmount());
walletRecordService.refund(this.getRefundChannelOrder(), this.getPayOrder().getTitle(), this.wallet);
}
}

View File

@@ -23,8 +23,6 @@ import static org.springframework.beans.factory.config.BeanDefinition.SCOPE_PROT
@RequiredArgsConstructor
public class WeChatRefundRepairStrategy extends AbsRefundRepairStrategy {
private final WeChatPayRecordService wechatPayRecordService;
/**
* 策略标识
*/
@@ -45,7 +43,5 @@ public class WeChatRefundRepairStrategy extends AbsRefundRepairStrategy {
super.doSuccessHandler();
// 异步支付需要追加完成时间
this.getRefundChannelOrder().setRefundTime(finishTime);
// 记录退款成功记录
wechatPayRecordService.refund(this.getRefundOrder(), this.getRefundChannelOrder());
}
}

View File

@@ -3,6 +3,7 @@ package cn.bootx.platform.daxpay.service.core.payment.sync.service;
import cn.bootx.platform.common.core.exception.BizException;
import cn.bootx.platform.common.core.exception.RepetitiveOperationException;
import cn.bootx.platform.common.core.util.LocalDateTimeUtil;
import cn.bootx.platform.daxpay.code.PayChannelEnum;
import cn.bootx.platform.daxpay.code.PayStatusEnum;
import cn.bootx.platform.daxpay.code.PaySyncStatusEnum;
import cn.bootx.platform.daxpay.exception.pay.PayFailureException;
@@ -64,16 +65,16 @@ public class PaySyncService {
@Transactional(propagation = Propagation.REQUIRES_NEW, rollbackFor = Exception.class)
public SyncResult sync(PaySyncParam param) {
PayOrder payOrder = null;
if (Objects.nonNull(param.getPaymentId())){
payOrder = payOrderQueryService.findById(param.getPaymentId())
if (Objects.nonNull(param.getOrderNo())){
payOrder = payOrderQueryService.findByOrderNo(param.getOrderNo())
.orElseThrow(() -> new PayFailureException("未查询到支付订单"));
}
if (Objects.isNull(payOrder)){
payOrder = payOrderQueryService.findByOutOrderNo(param.getBusinessNo())
payOrder = payOrderQueryService.findByBizOrderNo(param.getBizOrderNo())
.orElseThrow(() -> new PayFailureException("未查询到支付订单"));
}
// 如果不是异步支付, 直接返回返回
if (!payOrder.isAsyncPay()){
// 钱包支付直接返回结果
if (PayChannelEnum.WALLET.getCode().equals(payOrder.getChannel())){
throw new PayFailureException("订单没有异步支付方式,不需要同步");
}
// 执行订单同步逻辑
@@ -95,7 +96,7 @@ public class PaySyncService {
try {
// 获取支付同步策略类
AbsPaySyncStrategy syncPayStrategy = PaySyncStrategyFactory.create(payOrder.getAsyncChannel());
AbsPaySyncStrategy syncPayStrategy = PaySyncStrategyFactory.create(payOrder.getChannel());
syncPayStrategy.initPayParam(payOrder);
// 执行操作, 获取支付网关同步的结果
PayGatewaySyncResult syncResult = syncPayStrategy.doSyncStatus();
@@ -106,8 +107,8 @@ public class PaySyncService {
throw new PayFailureException(syncResult.getErrorMsg());
}
// 支付订单的网关订单号是否一致, 不一致进行更新
if (!Objects.equals(syncResult.getGatewayOrderNo(), payOrder.getGatewayOrderNo())){
payOrder.setGatewayOrderNo(syncResult.getGatewayOrderNo());
if (!Objects.equals(syncResult.getGatewayOrderNo(), payOrder.getOutOrderNo())){
payOrder.setOutOrderNo(syncResult.getGatewayOrderNo());
payOrderService.updateById(payOrder);
}
// 判断网关状态是否和支付单一致, 同时特定情况下更新网关同步状态
@@ -247,9 +248,9 @@ public class PaySyncService {
private void saveRecord(PayOrder payOrder, PayGatewaySyncResult syncResult, boolean repair, String repairOrderNo, String errorMsg){
PaySyncRecord paySyncRecord = new PaySyncRecord()
.setOrderId(payOrder.getId())
.setOrderNo(payOrder.getBusinessNo())
.setOrderNo(payOrder.getOrderNo())
.setSyncType(PaymentTypeEnum.PAY.getCode())
.setAsyncChannel(payOrder.getAsyncChannel())
.setChannel(payOrder.getChannel())
.setSyncInfo(syncResult.getSyncInfo())
.setGatewayStatus(syncResult.getSyncStatus().getCode())
.setRepairOrder(repair)

View File

@@ -203,7 +203,7 @@ public class RefundSyncService {
.setOrderId(refundOrder.getId())
.setOrderNo(refundOrder.getRefundNo())
.setSyncType(PaymentTypeEnum.REFUND.getCode())
.setAsyncChannel(refundOrder.getAsyncChannel())
.setChannel(refundOrder.getAsyncChannel())
.setGatewayOrderNo(syncResult.getGatewayOrderNo())
.setSyncInfo(syncResult.getSyncInfo())
.setGatewayStatus(syncResult.getSyncStatus().getCode())

View File

@@ -29,11 +29,11 @@ import lombok.experimental.Accessors;
@TableName("pay_sync_record")
public class PaySyncRecord extends MpCreateEntity implements EntityBaseFunction<PaySyncRecordDto> {
/** 本地订单ID */
/** 本地订单IDb标识 */
@DbColumn(comment = "本地订单ID")
private Long orderId;
/** 本地业务号 */
/** 本地订单号 */
@DbColumn(comment = "本地业务号")
private String orderNo;
@@ -53,7 +53,7 @@ public class PaySyncRecord extends MpCreateEntity implements EntityBaseFunction<
* @see PayChannelEnum#getCode()
*/
@DbColumn(comment = "同步的异步通道")
private String asyncChannel;
private String channel;
/** 网关返回的同步消息 */
@DbMySqlFieldType(MySqlFieldTypeEnum.LONGTEXT)

View File

@@ -1,6 +1,5 @@
package cn.bootx.platform.daxpay.service.func;
import cn.bootx.platform.daxpay.service.core.order.pay.entity.PayChannelOrder;
import cn.bootx.platform.daxpay.service.core.order.pay.entity.PayOrder;
import lombok.Getter;
import lombok.Setter;
@@ -19,17 +18,6 @@ public abstract class AbsPayRepairStrategy implements PayStrategy{
/** 支付订单 */
private PayOrder order = null;
/** 通道支付订单 */
private PayChannelOrder channelOrder = null;
/**
* 初始化修复参数
*/
public void initRepairParam(PayOrder order, PayChannelOrder channelOrder){
this.order = order;
this.channelOrder = channelOrder;
}
/**
* 修复前处理
*/

View File

@@ -1,17 +1,11 @@
package cn.bootx.platform.daxpay.service.func;
import cn.bootx.platform.daxpay.code.PayStatusEnum;
import cn.bootx.platform.daxpay.code.RefundStatusEnum;
import cn.bootx.platform.daxpay.exception.pay.PayFailureException;
import cn.bootx.platform.daxpay.service.core.order.pay.entity.PayChannelOrder;
import cn.bootx.platform.daxpay.param.payment.refund.RefundParam;
import cn.bootx.platform.daxpay.service.core.order.pay.entity.PayOrder;
import cn.bootx.platform.daxpay.service.core.order.refund.entity.RefundChannelOrder;
import cn.bootx.platform.daxpay.service.core.order.refund.entity.RefundOrder;
import lombok.Getter;
import lombok.Setter;
import java.time.LocalDateTime;
/**
* 抽象支付退款策略
*
@@ -28,6 +22,9 @@ public abstract class AbsRefundStrategy implements PayStrategy{
/** 退款订单 */
private RefundOrder refundOrder = null;
/** 退款参数 */
private RefundParam refundParam;
/**
* 退款前对处理, 主要进行配置的加载和检查
@@ -40,23 +37,4 @@ public abstract class AbsRefundStrategy implements PayStrategy{
*/
public abstract void doRefundHandler();
/**
* 退款发起成功操作
*/
public void doSuccessHandler() {
// 更新退款订单数据状态和完成时间
this.getRefundChannelOrder()
.setStatus(RefundStatusEnum.SUCCESS.getCode())
.setRefundTime(LocalDateTime.now());
// 支付通道订单可退余额
int refundableBalance = this.getPayChannelOrder()
.getRefundableBalance() - this.getRefundChannelOrder().getAmount();
// 如果可退金额为0说明已经全部退款
PayStatusEnum status = refundableBalance == 0 ? PayStatusEnum.REFUNDED : PayStatusEnum.PARTIAL_REFUND;
this.getPayChannelOrder()
.setRefundableBalance(refundableBalance)
.setStatus(status.getCode());
}
}