mirror of
https://gitee.com/dromara/dax-pay.git
synced 2025-09-02 10:36:57 +00:00
feat 退款同步+退款修复策略
This commit is contained in:
20
_doc/Task.md
20
_doc/Task.md
@@ -90,21 +90,27 @@
|
||||
- [x] 支付宝对账单下载异常排查-支付宝每日都会生成对账单, 哪怕为空, 也会生成
|
||||
- [x] 订单修复记录前端显示调整
|
||||
- 2044-01-30:
|
||||
- [x] 退款接口更改为先落库, 后更新
|
||||
- [ ] 增加退款同步策略, 对退款中的状态的退款订单进行处理
|
||||
- [ ] 退款操作支持重试
|
||||
- [ ] 支付通道对出现疑似退款的订单进行**报错提醒**, 通过退款同步进行补偿
|
||||
- [x] 退款接口更改为先落库, 后更新, 同时退款余额先先进行扣减, 根据退款状态进行处理
|
||||
- [x] 增加退款同步策略, 对退款中的状态的退款订单进行处理
|
||||
- [x] 修改退款补偿处理, 更改为退款粒度为整个退款单, 要不全部成功, 要不全部失败
|
||||
- [x] 支付通道对出现疑似退款的订单进行**报错提醒**, 通过退款同步进行补偿
|
||||
- 2024-01-31:
|
||||
- [ ] 微信退款同步策略
|
||||
- [ ] 退款操作支持重试
|
||||
2.0.1 版本内容
|
||||
- [ ] 支付流程也改为先落库后支付情况, 避免极端情况导致掉单
|
||||
- [ ] 完善各种同步支付方式
|
||||
- [ ] 增加聚合支付功能支持
|
||||
- [ ] 增加手机用户收银台功能
|
||||
**任务池**
|
||||
- [ ] 微信退款状态不一致补偿
|
||||
- [ ] ~~微信退款状态不一致补偿~~
|
||||
- [ ] 支付SDK编写
|
||||
- [ ] 接入支付网关的演示项目
|
||||
- [ ] 支付宝关闭支付时支持撤销方式,
|
||||
- [ ] 支持转账操作, 通过支付通道专有参数进行实现, 转账时只能单个通道进行操作
|
||||
- [ ] 支付成功回调后, 如果订单已超时, 则进入待退款订单中,提示进行退款,或者自动退款
|
||||
- [ ] 退款状态同步逻辑
|
||||
- [ ] 支付状态同步处理考虑退款情况
|
||||
- [ ] ~~退款状态同步逻辑~~
|
||||
- [ ] ~~支付状态同步处理考虑退款情况~~
|
||||
- [ ] 增加回调机制(通知客户端)
|
||||
- [ ] 增加消息通知机制(通知客户端)
|
||||
- [ ] 新增支付单预警功能, 处理支付单与网关状态不一致且无法自动修复的情况
|
||||
|
@@ -7,7 +7,7 @@ import cn.bootx.platform.common.core.rest.ResResult;
|
||||
import cn.bootx.platform.common.core.rest.param.PageParam;
|
||||
import cn.bootx.platform.daxpay.param.pay.PayCloseParam;
|
||||
import cn.bootx.platform.daxpay.param.pay.PaySyncParam;
|
||||
import cn.bootx.platform.daxpay.result.pay.PaySyncResult;
|
||||
import cn.bootx.platform.daxpay.result.pay.SyncResult;
|
||||
import cn.bootx.platform.daxpay.service.core.order.pay.entity.PayOrder;
|
||||
import cn.bootx.platform.daxpay.service.core.order.pay.service.PayChannelOrderService;
|
||||
import cn.bootx.platform.daxpay.service.core.order.pay.service.PayOrderExtraService;
|
||||
@@ -78,8 +78,8 @@ public class PayOrderController {
|
||||
}
|
||||
|
||||
@Operation(summary = "同步支付状态")
|
||||
@PostMapping("/sync")
|
||||
public ResResult<PaySyncResult> sync(Long id){
|
||||
@PostMapping("/syncById")
|
||||
public ResResult<SyncResult> syncById(Long id){
|
||||
PaySyncParam param = new PaySyncParam();
|
||||
param.setPaymentId(id);
|
||||
return Res.ok(paySyncService.sync(param));
|
||||
|
@@ -6,10 +6,11 @@ import cn.bootx.platform.common.core.rest.ResResult;
|
||||
import cn.bootx.platform.common.core.rest.param.PageParam;
|
||||
import cn.bootx.platform.common.spring.util.WebServletUtil;
|
||||
import cn.bootx.platform.daxpay.param.pay.RefundParam;
|
||||
import cn.bootx.platform.daxpay.result.pay.PaySyncResult;
|
||||
import cn.bootx.platform.daxpay.param.pay.RefundSyncParam;
|
||||
import cn.bootx.platform.daxpay.result.pay.SyncResult;
|
||||
import cn.bootx.platform.daxpay.service.core.order.refund.service.PayRefundOrderQueryService;
|
||||
import cn.bootx.platform.daxpay.service.core.order.refund.service.PayRefundOrderService;
|
||||
import cn.bootx.platform.daxpay.service.core.payment.refund.service.PayRefundService;
|
||||
import cn.bootx.platform.daxpay.service.core.payment.sync.service.PayRefundSyncService;
|
||||
import cn.bootx.platform.daxpay.service.dto.order.refund.PayRefundOrderDto;
|
||||
import cn.bootx.platform.daxpay.service.dto.order.refund.RefundChannelOrderDto;
|
||||
import cn.bootx.platform.daxpay.service.param.order.PayOrderRefundParam;
|
||||
@@ -37,7 +38,7 @@ import java.util.Optional;
|
||||
public class PayRefundOrderController {
|
||||
private final PayRefundOrderQueryService payRefundQueryService;
|
||||
private final PayRefundService payRefundService;
|
||||
private final PayRefundOrderService payRefundOrderService;
|
||||
private final PayRefundSyncService refundSyncService;
|
||||
|
||||
|
||||
@Operation(summary = "分页查询")
|
||||
@@ -84,7 +85,9 @@ public class PayRefundOrderController {
|
||||
|
||||
@Operation(summary = "退款同步")
|
||||
@PostMapping("/syncById")
|
||||
public ResResult<PaySyncResult> syncById(Long id){
|
||||
return Res.ok(payRefundOrderService.syncById(id));
|
||||
public ResResult<SyncResult> syncById(Long id){
|
||||
RefundSyncParam refundSyncParam = new RefundSyncParam();
|
||||
refundSyncParam.setRefundId(id);
|
||||
return Res.ok(refundSyncService.sync(refundSyncParam));
|
||||
}
|
||||
}
|
||||
|
@@ -15,13 +15,13 @@ import lombok.EqualsAndHashCode;
|
||||
public class RefundSyncParam extends PayCommonParam {
|
||||
|
||||
/**
|
||||
* 部分退款时 refundId 和 refundNo 必传一个, 同时传输时,以 refundId 为准
|
||||
* 退款订单ID,refundId和refundNo 必传一个, 同时传输时,以 refundId 为准
|
||||
*/
|
||||
@Schema(description = "退款订单ID")
|
||||
private Long refundId;
|
||||
|
||||
/**
|
||||
* 退款订单号,部分退款时 refundId 和 refundNo 必传一个,同时传输时,以 refundId 为准
|
||||
* 退款订单号,refundId和refundNo 必传一个,同时传输时,以 refundId 为准
|
||||
*/
|
||||
@Schema(description = "退款订单号")
|
||||
private String refundNo;
|
||||
|
@@ -1,5 +1,6 @@
|
||||
package cn.bootx.platform.daxpay.result.pay;
|
||||
|
||||
import cn.bootx.platform.daxpay.code.PayRefundSyncStatusEnum;
|
||||
import cn.bootx.platform.daxpay.code.PaySyncStatusEnum;
|
||||
import cn.bootx.platform.daxpay.result.CommonResult;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
@@ -17,12 +18,13 @@ import static cn.bootx.platform.daxpay.code.PaySyncStatusEnum.FAIL;
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@Data
|
||||
@Accessors(chain = true)
|
||||
@Schema(title = "支付单同步结果")
|
||||
public class PaySyncResult extends CommonResult {
|
||||
@Schema(title = "同步结果")
|
||||
public class SyncResult extends CommonResult {
|
||||
|
||||
/**
|
||||
* 支付网关同步状态
|
||||
* @see PaySyncStatusEnum
|
||||
* @see PayRefundSyncStatusEnum
|
||||
*/
|
||||
@Schema(description = "支付网关同步状态")
|
||||
private String gatewayStatus = FAIL.getCode();
|
@@ -7,7 +7,7 @@ import cn.bootx.platform.daxpay.result.DaxResult;
|
||||
import cn.bootx.platform.daxpay.result.order.PayOrderResult;
|
||||
import cn.bootx.platform.daxpay.result.order.RefundOrderResult;
|
||||
import cn.bootx.platform.daxpay.result.pay.PayResult;
|
||||
import cn.bootx.platform.daxpay.result.pay.PaySyncResult;
|
||||
import cn.bootx.platform.daxpay.result.pay.SyncResult;
|
||||
import cn.bootx.platform.daxpay.result.pay.RefundResult;
|
||||
import cn.bootx.platform.daxpay.service.annotation.PaymentApi;
|
||||
import cn.bootx.platform.daxpay.service.core.order.pay.service.PayOrderQueryService;
|
||||
@@ -86,7 +86,7 @@ public class UniPayController {
|
||||
@PaymentApi("syncPay")
|
||||
@Operation(summary = "支付状态同步")
|
||||
@PostMapping("/syncPay")
|
||||
public DaxResult<PaySyncResult> syncPay(@RequestBody PaySyncParam param){
|
||||
public DaxResult<SyncResult> syncPay(@RequestBody PaySyncParam param){
|
||||
return DaxRes.ok(paySyncService.sync(param));
|
||||
}
|
||||
|
||||
|
@@ -7,7 +7,7 @@ import cn.bootx.platform.common.spring.exception.RetryableException;
|
||||
import cn.bootx.platform.daxpay.code.PayWayEnum;
|
||||
import cn.bootx.platform.daxpay.exception.pay.PayFailureException;
|
||||
import cn.bootx.platform.daxpay.param.pay.PayChannelParam;
|
||||
import cn.bootx.platform.daxpay.result.pay.PaySyncResult;
|
||||
import cn.bootx.platform.daxpay.result.pay.SyncResult;
|
||||
import cn.bootx.platform.daxpay.service.code.WeChatPayCode;
|
||||
import cn.bootx.platform.daxpay.service.code.WeChatPayWay;
|
||||
import cn.bootx.platform.daxpay.service.common.context.AsyncPayLocal;
|
||||
@@ -268,9 +268,9 @@ public class WeChatPayService {
|
||||
@Async("bigExecutor")
|
||||
@Retryable(value = RetryableException.class, maxAttempts = 10, backoff = @Backoff(value = 5000L))
|
||||
public void rotationSync(PayOrder payOrder) {
|
||||
PaySyncResult paySyncResult = paySyncService.syncPayOrder(payOrder);
|
||||
SyncResult syncResult = paySyncService.syncPayOrder(payOrder);
|
||||
// 不为支付中状态后, 调用系统同步更新状态, 支付状态则继续重试
|
||||
if (Objects.equals(PAY_WAIT.getCode(), paySyncResult.getGatewayStatus())) {
|
||||
if (Objects.equals(PAY_WAIT.getCode(), syncResult.getGatewayStatus())) {
|
||||
throw new RetryableException();
|
||||
}
|
||||
}
|
||||
|
@@ -85,10 +85,9 @@ public class WeChatPaySyncService {
|
||||
return syncResult.setSyncStatus(PaySyncStatusEnum.PAY_WAIT);
|
||||
}
|
||||
|
||||
// 已退款/退款中 触发一下退款记录的查询
|
||||
// 已退款/退款中
|
||||
if (Objects.equals(tradeStatus, WeChatPayCode.PAY_REFUND)) {
|
||||
// this.syncRefundStatus(order, weChatPayConfig);
|
||||
// TODO 特殊处理, 提示用户走退款同步
|
||||
return syncResult.setSyncStatus(PaySyncStatusEnum.REFUND);
|
||||
}
|
||||
// 已关闭
|
||||
if (Objects.equals(tradeStatus, WeChatPayCode.PAY_CLOSED)
|
||||
|
@@ -1,34 +0,0 @@
|
||||
package cn.bootx.platform.daxpay.service.core.order.refund.service;
|
||||
|
||||
import cn.bootx.platform.common.core.exception.DataNotExistException;
|
||||
import cn.bootx.platform.daxpay.result.pay.PaySyncResult;
|
||||
import cn.bootx.platform.daxpay.service.core.order.refund.dao.PayRefundOrderManager;
|
||||
import cn.bootx.platform.daxpay.service.core.order.refund.entity.PayRefundOrder;
|
||||
import cn.bootx.platform.daxpay.service.core.payment.sync.service.PayRefundSyncService;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
/**
|
||||
* 退款订单服务类
|
||||
* @author xxm
|
||||
* @since 2024/1/29
|
||||
*/
|
||||
@Slf4j
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class PayRefundOrderService {
|
||||
private final PayRefundOrderManager refundOrderManager;
|
||||
|
||||
private final PayRefundSyncService refundSyncService;;
|
||||
|
||||
/**
|
||||
* 退款同步
|
||||
*/
|
||||
public PaySyncResult syncById(Long id){
|
||||
PayRefundOrder refundOrder = refundOrderManager.findById(id)
|
||||
.orElseThrow(() -> new DataNotExistException("退款订单不存在"));
|
||||
return refundSyncService.syncRefundOrder(refundOrder);
|
||||
}
|
||||
|
||||
}
|
@@ -2,7 +2,7 @@ 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.*;
|
||||
import cn.bootx.platform.daxpay.service.core.payment.repair.strategy.pay.*;
|
||||
import cn.bootx.platform.daxpay.service.func.AbsPayRepairStrategy;
|
||||
import cn.hutool.core.collection.CollectionUtil;
|
||||
import cn.hutool.extra.spring.SpringUtil;
|
||||
@@ -26,7 +26,7 @@ import static cn.bootx.platform.daxpay.code.PayChannelEnum.ASYNC_TYPE_CODE;
|
||||
public class PayRepairStrategyFactory {
|
||||
/**
|
||||
* 根据传入的支付通道创建策略
|
||||
* @return 支付策略
|
||||
* @return 支付修复策略
|
||||
*/
|
||||
public static AbsPayRepairStrategy create(PayChannelEnum channelEnum) {
|
||||
|
||||
|
@@ -0,0 +1,114 @@
|
||||
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.refund.*;
|
||||
import cn.bootx.platform.daxpay.service.func.AbsRefundRepairStrategy;
|
||||
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.*;
|
||||
|
||||
|
||||
/**
|
||||
* 支付修复策略工厂类
|
||||
* @author xxm
|
||||
* @since 2023/12/29
|
||||
*/
|
||||
@UtilityClass
|
||||
public class RefundRepairStrategyFactory {
|
||||
/**
|
||||
* 根据传入的通道创建策略
|
||||
* @return 退款修复策略
|
||||
*/
|
||||
public static AbsRefundRepairStrategy create(PayChannelEnum channelEnum) {
|
||||
|
||||
AbsRefundRepairStrategy strategy;
|
||||
switch (channelEnum) {
|
||||
case ALI:
|
||||
strategy = SpringUtil.getBean(AliRefundRepairStrategy.class);
|
||||
break;
|
||||
case WECHAT:
|
||||
strategy = SpringUtil.getBean(WeChatRefundRepairStrategy.class);
|
||||
break;
|
||||
case UNION_PAY:
|
||||
strategy = SpringUtil.getBean(UnionRefundRepairStrategy.class);
|
||||
break;
|
||||
case CASH:
|
||||
strategy = SpringUtil.getBean(CashRefundRepairStrategy.class);
|
||||
break;
|
||||
case WALLET:
|
||||
strategy = SpringUtil.getBean(WalletRefundRepairStrategy.class);
|
||||
break;
|
||||
case VOUCHER:
|
||||
strategy = SpringUtil.getBean(VoucherRefundRepairStrategy.class);
|
||||
break;
|
||||
default:
|
||||
throw new PayUnsupportedMethodException();
|
||||
}
|
||||
return strategy;
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据传入的支付类型批量创建策略, 异步支付在后面
|
||||
*/
|
||||
public static List<AbsRefundRepairStrategy> createAsyncLast(List<String> channelCodes) {
|
||||
return create(channelCodes, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据传入的支付类型批量创建策略, 异步支付在前面
|
||||
*/
|
||||
public static List<AbsRefundRepairStrategy> createAsyncFront(List<String> channelCodes) {
|
||||
return create(channelCodes, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据传入的支付类型批量创建策略
|
||||
* @param asyncSort 是否异步支付在后面
|
||||
* @return 支付策略
|
||||
*/
|
||||
private static List<AbsRefundRepairStrategy> 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(RefundRepairStrategyFactory::create)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
}
|
@@ -1,6 +1,6 @@
|
||||
package cn.bootx.platform.daxpay.service.core.payment.repair.service;
|
||||
|
||||
import cn.bootx.platform.common.core.exception.DataNotExistException;
|
||||
import cn.bootx.platform.common.core.function.CollectorsFunction;
|
||||
import cn.bootx.platform.daxpay.code.PayRefundStatusEnum;
|
||||
import cn.bootx.platform.daxpay.code.PayStatusEnum;
|
||||
import cn.bootx.platform.daxpay.service.code.PaymentTypeEnum;
|
||||
@@ -14,19 +14,20 @@ import cn.bootx.platform.daxpay.service.core.order.refund.dao.PayRefundChannelOr
|
||||
import cn.bootx.platform.daxpay.service.core.order.refund.dao.PayRefundOrderManager;
|
||||
import cn.bootx.platform.daxpay.service.core.order.refund.entity.PayRefundChannelOrder;
|
||||
import cn.bootx.platform.daxpay.service.core.order.refund.entity.PayRefundOrder;
|
||||
import cn.bootx.platform.daxpay.service.core.payment.repair.factory.RefundRepairStrategyFactory;
|
||||
import cn.bootx.platform.daxpay.service.core.payment.repair.result.RefundRepairResult;
|
||||
import cn.bootx.platform.daxpay.service.core.record.repair.entity.PayRepairRecord;
|
||||
import cn.bootx.platform.daxpay.service.core.record.repair.service.PayRepairRecordService;
|
||||
import cn.bootx.platform.daxpay.service.func.AbsRefundRepairStrategy;
|
||||
import cn.hutool.core.util.IdUtil;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.Arrays;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.*;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* 退款订单修复, 只有存在异步支付的退款订单才存在修复
|
||||
@@ -49,7 +50,7 @@ public class RefundRepairService {
|
||||
private final PayRepairRecordService recordService;
|
||||
|
||||
/**
|
||||
* 修复支付单
|
||||
* 修复退款单
|
||||
*/
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public RefundRepairResult repair(PayRefundOrder refundOrder, RefundRepairWayEnum repairType){
|
||||
@@ -58,19 +59,29 @@ public class RefundRepairService {
|
||||
PayOrder payOrder = payOrderManager.findById(refundOrder.getPaymentId())
|
||||
.orElseThrow(() -> new RuntimeException("支付单不存在"));
|
||||
// 关联支付通道支付单
|
||||
PayChannelOrder payChannelOrder = payChannelOrderManager.findByPaymentIdAndChannel(payOrder.getId(), payOrder.getAsyncChannel())
|
||||
.orElseThrow(DataNotExistException::new);
|
||||
Map<String, PayChannelOrder> payChannelOrderMap = payChannelOrderManager.findAllByPaymentId(refundOrder.getPaymentId())
|
||||
.stream()
|
||||
.collect(Collectors.toMap(PayChannelOrder::getChannel, Function.identity(), CollectorsFunction::retainLatest));
|
||||
// 异步通道退款单
|
||||
PayRefundChannelOrder refundChannelOrder = refundChannelOrderManager.findByRefundIdAndChannel(refundOrder.getId(), payOrder.getAsyncChannel())
|
||||
.orElseThrow(DataNotExistException::new);
|
||||
Map<String, PayRefundChannelOrder> refundChannelOrderMap = refundChannelOrderManager.findAllByRefundId(refundOrder.getId())
|
||||
.stream()
|
||||
.collect(Collectors.toMap(PayRefundChannelOrder::getChannel, Function.identity(), CollectorsFunction::retainLatest));
|
||||
|
||||
// 2 初始化修复参数
|
||||
List<String> channels = new ArrayList<>(payChannelOrderMap.keySet());
|
||||
List<AbsRefundRepairStrategy> repairStrategies = RefundRepairStrategyFactory.createAsyncLast(channels);
|
||||
for (AbsRefundRepairStrategy repairStrategy : repairStrategies) {
|
||||
PayChannelOrder payChannelOrder = payChannelOrderMap.get(repairStrategy.getChannel().getCode());
|
||||
PayRefundChannelOrder payRefundChannelOrder = refundChannelOrderMap.get(repairStrategy.getChannel().getCode());
|
||||
repairStrategy.initRepairParam(refundOrder, payRefundChannelOrder, payOrder, payChannelOrder);
|
||||
}
|
||||
|
||||
// 根据不同的类型执行对应的修复逻辑
|
||||
RefundRepairResult repairResult = new RefundRepairResult();
|
||||
//TODO 整个退款单是一个状态, 最终结果要么全部成功, 要么全部回退
|
||||
if (Objects.requireNonNull(repairType) == RefundRepairWayEnum.SUCCESS) {
|
||||
repairResult = this.success(refundOrder,payOrder,refundChannelOrder,payChannelOrder);
|
||||
repairResult = this.success(refundOrder,payOrder,repairStrategies);
|
||||
} else if (repairType == RefundRepairWayEnum.FAIL) {
|
||||
repairResult = this.closeLocal(refundOrder,payOrder,refundChannelOrder,payChannelOrder);
|
||||
repairResult = this.close(refundOrder,payOrder,repairStrategies);
|
||||
} else {
|
||||
log.error("走到了理论上讲不会走到的分支");
|
||||
}
|
||||
@@ -87,13 +98,7 @@ public class RefundRepairService {
|
||||
/**
|
||||
* 退款成功, 更新退款单和支付单
|
||||
*/
|
||||
private RefundRepairResult success(PayRefundOrder refundOrder, PayOrder payOrder, PayRefundChannelOrder refundChannelOrder, PayChannelOrder payChannelOrder) {
|
||||
// 更新通道支付单全部退款还是部分退款
|
||||
if (Objects.equals(payChannelOrder.getRefundableBalance(),0)){
|
||||
payChannelOrder.setStatus(PayStatusEnum.REFUNDED.getCode());
|
||||
} else {
|
||||
payChannelOrder.setStatus(PayStatusEnum.PARTIAL_REFUND.getCode());
|
||||
}
|
||||
private RefundRepairResult success(PayRefundOrder refundOrder, PayOrder payOrder, List<AbsRefundRepairStrategy> repairStrategies) {
|
||||
|
||||
// 订单相关状态
|
||||
PayStatusEnum beforePayStatus = PayStatusEnum.findByCode(refundOrder.getStatus());
|
||||
@@ -108,14 +113,25 @@ public class RefundRepairService {
|
||||
}
|
||||
// 设置退款为完成状态
|
||||
refundOrder.setStatus(PayRefundStatusEnum.SUCCESS.getCode());
|
||||
refundChannelOrder.setStatus(PayRefundStatusEnum.SUCCESS.getCode())
|
||||
.setRefundTime(LocalDateTime.now());
|
||||
payOrder.setStatus(afterPayRefundStatus.getCode());
|
||||
|
||||
// 执行退款成功逻辑
|
||||
repairStrategies.forEach(AbsRefundRepairStrategy::doSuccessHandler);
|
||||
// 获取要更新的数据
|
||||
List<PayChannelOrder> payChannelOrders = repairStrategies.stream()
|
||||
.map(AbsRefundRepairStrategy::getPayChannelOrder)
|
||||
.collect(Collectors.toList());
|
||||
List<PayRefundChannelOrder> refundChannelOrders = repairStrategies
|
||||
.stream()
|
||||
.map(AbsRefundRepairStrategy::getRefundChannelOrder)
|
||||
.collect(Collectors.toList());
|
||||
|
||||
// 更新订单和退款相关订单
|
||||
payChannelOrderManager.updateById(payChannelOrder);
|
||||
payOrderManager.updateById(payOrder);
|
||||
refundOrderManager.updateById(refundOrder);
|
||||
refundChannelOrderManager.updateById(refundChannelOrder);
|
||||
payChannelOrderManager.updateAllById(payChannelOrders);
|
||||
refundChannelOrderManager.updateAllById(refundChannelOrders);
|
||||
|
||||
return new RefundRepairResult()
|
||||
.setBeforePayStatus(beforePayStatus)
|
||||
.setAfterPayStatus(afterPayRefundStatus)
|
||||
@@ -125,9 +141,9 @@ public class RefundRepairService {
|
||||
|
||||
|
||||
/**
|
||||
* 退款失败, 将失败的退款金额归还回订单
|
||||
* 退款失败, 关闭退款单并将失败的退款金额归还回订单
|
||||
*/
|
||||
private RefundRepairResult closeLocal(PayRefundOrder refundOrder, PayOrder payOrder, PayRefundChannelOrder refundChannelOrder, PayChannelOrder payChannelOrder) {
|
||||
private RefundRepairResult close(PayRefundOrder refundOrder, PayOrder payOrder, List<AbsRefundRepairStrategy> repairStrategies) {
|
||||
// 要返回的状态
|
||||
RefundRepairResult repairResult = new RefundRepairResult();
|
||||
|
||||
@@ -138,40 +154,38 @@ public class RefundRepairService {
|
||||
.setBeforeRefundStatus(beforeRefundStatus);
|
||||
|
||||
// 退款失败返还后的余额
|
||||
int payOrderAmount = refundChannelOrder.getAmount() + payOrder.getRefundableBalance();
|
||||
int payChannelOrderAmount = refundChannelOrder.getAmount() + payChannelOrder.getRefundableBalance();
|
||||
int payOrderAmount = refundOrder.getAmount() + payOrder.getRefundableBalance();
|
||||
// 退款失败返还后的余额+可退余额 == 订单金额 支付订单回退为为支付成功状态
|
||||
if (payOrderAmount == payOrder.getAmount()){
|
||||
payOrder.setStatus(PayStatusEnum.SUCCESS.getCode());
|
||||
// 说明这个退款只有异步这一个支付, 所以也可以直接回退
|
||||
payChannelOrder.setStatus(PayStatusEnum.SUCCESS.getCode());
|
||||
repairResult.setAfterPayStatus(PayStatusEnum.SUCCESS);
|
||||
} else {
|
||||
// 回归部分退款状态
|
||||
payOrder.setStatus(PayStatusEnum.PARTIAL_REFUND.getCode());
|
||||
repairResult.setAfterPayStatus(PayStatusEnum.PARTIAL_REFUND);
|
||||
}
|
||||
|
||||
// 更新支付订单相关的可退款金额
|
||||
payOrder.setRefundableBalance(payOrderAmount);
|
||||
payChannelOrder.setRefundableBalance(payChannelOrderAmount);
|
||||
refundOrder.setStatus(PayRefundStatusEnum.CLOSE.getCode());
|
||||
|
||||
// 判断退款订单是否只有异步通道一个关联的通道退款单, 退款值一致说明是一个
|
||||
if (Objects.equals(refundOrder.getAmount(), refundChannelOrder.getAmount())){
|
||||
// 退款单和通道退款单统一设置为失败
|
||||
refundOrder.setStatus(PayRefundStatusEnum.FAIL.getCode());
|
||||
refundChannelOrder.setStatus(PayRefundStatusEnum.FAIL.getCode());
|
||||
repairResult.setAfterRefundStatus(PayRefundStatusEnum.FAIL);
|
||||
} else {
|
||||
// 退款单设置为部分成功状态, 通道退款单设置为失败状态
|
||||
refundOrder.setStatus(PayRefundStatusEnum.FAIL.getCode());
|
||||
refundChannelOrder.setStatus(PayRefundStatusEnum.FAIL.getCode());
|
||||
}
|
||||
// 执行关闭退款逻辑
|
||||
repairStrategies.forEach(AbsRefundRepairStrategy::doCloseHandler);
|
||||
|
||||
// 获取要更新的数据
|
||||
List<PayChannelOrder> payChannelOrders = repairStrategies.stream()
|
||||
.map(AbsRefundRepairStrategy::getPayChannelOrder)
|
||||
.collect(Collectors.toList());
|
||||
List<PayRefundChannelOrder> refundChannelOrders = repairStrategies
|
||||
.stream()
|
||||
.map(AbsRefundRepairStrategy::getRefundChannelOrder)
|
||||
.collect(Collectors.toList());
|
||||
|
||||
// 更新订单和退款相关订单
|
||||
payChannelOrderManager.updateById(payChannelOrder);
|
||||
payChannelOrderManager.updateAllById(payChannelOrders);
|
||||
payOrderManager.updateById(payOrder);
|
||||
refundOrderManager.updateById(refundOrder);
|
||||
refundChannelOrderManager.updateById(refundChannelOrder);
|
||||
refundChannelOrderManager.updateAllById(refundChannelOrders);
|
||||
return repairResult;
|
||||
}
|
||||
|
||||
|
@@ -1,4 +1,4 @@
|
||||
package cn.bootx.platform.daxpay.service.core.payment.repair.strategy;
|
||||
package cn.bootx.platform.daxpay.service.core.payment.repair.strategy.pay;
|
||||
|
||||
import cn.bootx.platform.daxpay.code.PayChannelEnum;
|
||||
import cn.bootx.platform.daxpay.code.PayStatusEnum;
|
@@ -1,4 +1,4 @@
|
||||
package cn.bootx.platform.daxpay.service.core.payment.repair.strategy;
|
||||
package cn.bootx.platform.daxpay.service.core.payment.repair.strategy.pay;
|
||||
|
||||
import cn.bootx.platform.daxpay.code.PayChannelEnum;
|
||||
import cn.bootx.platform.daxpay.code.PayStatusEnum;
|
@@ -1,4 +1,4 @@
|
||||
package cn.bootx.platform.daxpay.service.core.payment.repair.strategy;
|
||||
package cn.bootx.platform.daxpay.service.core.payment.repair.strategy.pay;
|
||||
|
||||
import cn.bootx.platform.daxpay.code.PayChannelEnum;
|
||||
import cn.bootx.platform.daxpay.service.func.AbsPayRepairStrategy;
|
@@ -1,4 +1,4 @@
|
||||
package cn.bootx.platform.daxpay.service.core.payment.repair.strategy;
|
||||
package cn.bootx.platform.daxpay.service.core.payment.repair.strategy.pay;
|
||||
|
||||
import cn.bootx.platform.daxpay.code.PayChannelEnum;
|
||||
import cn.bootx.platform.daxpay.code.PayStatusEnum;
|
@@ -1,4 +1,4 @@
|
||||
package cn.bootx.platform.daxpay.service.core.payment.repair.strategy;
|
||||
package cn.bootx.platform.daxpay.service.core.payment.repair.strategy.pay;
|
||||
|
||||
import cn.bootx.platform.daxpay.code.PayChannelEnum;
|
||||
import cn.bootx.platform.daxpay.code.PayStatusEnum;
|
@@ -1,4 +1,4 @@
|
||||
package cn.bootx.platform.daxpay.service.core.payment.repair.strategy;
|
||||
package cn.bootx.platform.daxpay.service.core.payment.repair.strategy.pay;
|
||||
|
||||
import cn.bootx.platform.daxpay.code.PayChannelEnum;
|
||||
import cn.bootx.platform.daxpay.code.PayStatusEnum;
|
@@ -0,0 +1,30 @@
|
||||
package cn.bootx.platform.daxpay.service.core.payment.repair.strategy.refund;
|
||||
|
||||
import cn.bootx.platform.daxpay.code.PayChannelEnum;
|
||||
import cn.bootx.platform.daxpay.service.func.AbsRefundRepairStrategy;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.context.annotation.Scope;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import static org.springframework.beans.factory.config.BeanDefinition.SCOPE_PROTOTYPE;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author xxm
|
||||
* @since 2024/1/30
|
||||
*/
|
||||
@Slf4j
|
||||
@Scope(SCOPE_PROTOTYPE)
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class AliRefundRepairStrategy extends AbsRefundRepairStrategy {
|
||||
|
||||
/**
|
||||
* 策略标识
|
||||
*/
|
||||
@Override
|
||||
public PayChannelEnum getChannel() {
|
||||
return PayChannelEnum.ALI;
|
||||
}
|
||||
}
|
@@ -0,0 +1,29 @@
|
||||
package cn.bootx.platform.daxpay.service.core.payment.repair.strategy.refund;
|
||||
|
||||
import cn.bootx.platform.daxpay.code.PayChannelEnum;
|
||||
import cn.bootx.platform.daxpay.service.func.AbsRefundRepairStrategy;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.context.annotation.Scope;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import static org.springframework.beans.factory.config.BeanDefinition.SCOPE_PROTOTYPE;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author xxm
|
||||
* @since 2024/1/30
|
||||
*/
|
||||
@Slf4j
|
||||
@Scope(SCOPE_PROTOTYPE)
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class CashRefundRepairStrategy extends AbsRefundRepairStrategy {
|
||||
/**
|
||||
* 策略标识
|
||||
*/
|
||||
@Override
|
||||
public PayChannelEnum getChannel() {
|
||||
return PayChannelEnum.CASH;
|
||||
}
|
||||
}
|
@@ -0,0 +1,29 @@
|
||||
package cn.bootx.platform.daxpay.service.core.payment.repair.strategy.refund;
|
||||
|
||||
import cn.bootx.platform.daxpay.code.PayChannelEnum;
|
||||
import cn.bootx.platform.daxpay.service.func.AbsRefundRepairStrategy;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.context.annotation.Scope;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import static org.springframework.beans.factory.config.BeanDefinition.SCOPE_PROTOTYPE;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author xxm
|
||||
* @since 2024/1/30
|
||||
*/
|
||||
@Slf4j
|
||||
@Scope(SCOPE_PROTOTYPE)
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class UnionRefundRepairStrategy extends AbsRefundRepairStrategy {
|
||||
/**
|
||||
* 策略标识
|
||||
*/
|
||||
@Override
|
||||
public PayChannelEnum getChannel() {
|
||||
return PayChannelEnum.UNION_PAY;
|
||||
}
|
||||
}
|
@@ -0,0 +1,29 @@
|
||||
package cn.bootx.platform.daxpay.service.core.payment.repair.strategy.refund;
|
||||
|
||||
import cn.bootx.platform.daxpay.code.PayChannelEnum;
|
||||
import cn.bootx.platform.daxpay.service.func.AbsRefundRepairStrategy;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.context.annotation.Scope;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import static org.springframework.beans.factory.config.BeanDefinition.SCOPE_PROTOTYPE;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author xxm
|
||||
* @since 2024/1/30
|
||||
*/
|
||||
@Slf4j
|
||||
@Scope(SCOPE_PROTOTYPE)
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class VoucherRefundRepairStrategy extends AbsRefundRepairStrategy {
|
||||
/**
|
||||
* 策略标识
|
||||
*/
|
||||
@Override
|
||||
public PayChannelEnum getChannel() {
|
||||
return PayChannelEnum.VOUCHER;
|
||||
}
|
||||
}
|
@@ -0,0 +1,29 @@
|
||||
package cn.bootx.platform.daxpay.service.core.payment.repair.strategy.refund;
|
||||
|
||||
import cn.bootx.platform.daxpay.code.PayChannelEnum;
|
||||
import cn.bootx.platform.daxpay.service.func.AbsRefundRepairStrategy;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.context.annotation.Scope;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import static org.springframework.beans.factory.config.BeanDefinition.SCOPE_PROTOTYPE;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author xxm
|
||||
* @since 2024/1/30
|
||||
*/
|
||||
@Slf4j
|
||||
@Scope(SCOPE_PROTOTYPE)
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class WalletRefundRepairStrategy extends AbsRefundRepairStrategy {
|
||||
/**
|
||||
* 策略标识
|
||||
*/
|
||||
@Override
|
||||
public PayChannelEnum getChannel() {
|
||||
return PayChannelEnum.WALLET;
|
||||
}
|
||||
}
|
@@ -0,0 +1,29 @@
|
||||
package cn.bootx.platform.daxpay.service.core.payment.repair.strategy.refund;
|
||||
|
||||
import cn.bootx.platform.daxpay.code.PayChannelEnum;
|
||||
import cn.bootx.platform.daxpay.service.func.AbsRefundRepairStrategy;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.context.annotation.Scope;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import static org.springframework.beans.factory.config.BeanDefinition.SCOPE_PROTOTYPE;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author xxm
|
||||
* @since 2024/1/30
|
||||
*/
|
||||
@Slf4j
|
||||
@Scope(SCOPE_PROTOTYPE)
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class WeChatRefundRepairStrategy extends AbsRefundRepairStrategy {
|
||||
/**
|
||||
* 策略标识
|
||||
*/
|
||||
@Override
|
||||
public PayChannelEnum getChannel() {
|
||||
return PayChannelEnum.WECHAT;
|
||||
}
|
||||
}
|
@@ -1,14 +1,20 @@
|
||||
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.daxpay.code.PayRefundSyncStatusEnum;
|
||||
import cn.bootx.platform.daxpay.exception.pay.PayFailureException;
|
||||
import cn.bootx.platform.daxpay.param.pay.RefundSyncParam;
|
||||
import cn.bootx.platform.daxpay.result.pay.PaySyncResult;
|
||||
import cn.bootx.platform.daxpay.result.pay.SyncResult;
|
||||
import cn.bootx.platform.daxpay.service.code.PayRepairSourceEnum;
|
||||
import cn.bootx.platform.daxpay.service.code.PaymentTypeEnum;
|
||||
import cn.bootx.platform.daxpay.service.code.RefundRepairWayEnum;
|
||||
import cn.bootx.platform.daxpay.service.common.context.RepairLocal;
|
||||
import cn.bootx.platform.daxpay.service.common.local.PaymentContextLocal;
|
||||
import cn.bootx.platform.daxpay.service.core.order.refund.dao.PayRefundOrderManager;
|
||||
import cn.bootx.platform.daxpay.service.core.order.refund.entity.PayRefundOrder;
|
||||
import cn.bootx.platform.daxpay.service.core.payment.repair.result.RefundRepairResult;
|
||||
import cn.bootx.platform.daxpay.service.core.payment.repair.service.RefundRepairService;
|
||||
import cn.bootx.platform.daxpay.service.core.payment.sync.factory.RefundSyncStrategyFactory;
|
||||
import cn.bootx.platform.daxpay.service.core.payment.sync.result.RefundGatewaySyncResult;
|
||||
import cn.bootx.platform.daxpay.service.core.record.sync.entity.PaySyncRecord;
|
||||
@@ -37,13 +43,15 @@ public class PayRefundSyncService {
|
||||
|
||||
private final PaySyncRecordService paySyncRecordService;
|
||||
|
||||
private final RefundRepairService repairService;
|
||||
|
||||
private final LockTemplate lockTemplate;
|
||||
|
||||
/**
|
||||
* 退款同步, 开启一个新的事务, 不受外部抛出异常的影响
|
||||
*/
|
||||
@Transactional(propagation = Propagation.REQUIRES_NEW, rollbackFor = Exception.class)
|
||||
public void sync(RefundSyncParam param){
|
||||
public SyncResult sync(RefundSyncParam param){
|
||||
// 先获取退款单
|
||||
PayRefundOrder requestOrder;
|
||||
if (Objects.nonNull(param.getRefundId())){
|
||||
@@ -55,24 +63,21 @@ public class PayRefundSyncService {
|
||||
}
|
||||
// 如果不是异步支付, 直接返回返回
|
||||
if (!requestOrder.isAsyncPay()){
|
||||
// TODO 需要限制同步的请求不进行同步
|
||||
return;
|
||||
return new SyncResult().setSuccess(false).setRepair(false).setErrorMsg("订单没有异步通道的退款,不需要同步");
|
||||
}
|
||||
this.syncRefundOrder(requestOrder);
|
||||
|
||||
return this.syncRefundOrder(requestOrder);
|
||||
}
|
||||
|
||||
/**
|
||||
* 退款订单信息同步
|
||||
*/
|
||||
@Transactional(propagation = Propagation.REQUIRES_NEW, rollbackFor = Exception.class)
|
||||
public PaySyncResult syncRefundOrder(PayRefundOrder refundOrder) {
|
||||
public SyncResult syncRefundOrder(PayRefundOrder refundOrder) {
|
||||
// 加锁
|
||||
LockInfo lock = lockTemplate.lock("sync:refund:" + refundOrder.getId());
|
||||
if (Objects.isNull(lock)) {
|
||||
throw new RepetitiveOperationException("退款同步处理中,请勿重复操作");
|
||||
}
|
||||
|
||||
try {
|
||||
// 获取支付同步策略类
|
||||
AbsRefundSyncStrategy syncPayStrategy = RefundSyncStrategyFactory.create(refundOrder.getAsyncChannel());
|
||||
@@ -83,14 +88,13 @@ public class PayRefundSyncService {
|
||||
// 判断是否同步成功
|
||||
if (Objects.equals(syncResult.getSyncStatus(), PayRefundSyncStatusEnum.FAIL)) {
|
||||
// 同步失败, 返回失败响应, 同时记录失败的日志
|
||||
return new PaySyncResult().setErrorMsg(syncResult.getErrorMsg());
|
||||
return new SyncResult().setErrorMsg(syncResult.getErrorMsg());
|
||||
}
|
||||
|
||||
// 判断网关状态是否和支付单一致, 同时特定情况下更新网关同步状态
|
||||
boolean statusSync = this.checkAndAdjustSyncStatus(syncResult, refundOrder);
|
||||
// 判断网关状态是否和支付单一致, 同时特定情况下更新网关同步状态
|
||||
boolean statusSync = this.checkSyncStatus(syncResult, refundOrder);
|
||||
RefundRepairResult repairResult = new RefundRepairResult();
|
||||
try {
|
||||
// // 状态不一致,执行支付单修复逻辑
|
||||
// 状态不一致,执行退款单修复逻辑
|
||||
if (!statusSync) {
|
||||
repairResult = this.repairHandler(syncResult, refundOrder);
|
||||
}
|
||||
@@ -98,11 +102,11 @@ public class PayRefundSyncService {
|
||||
// 同步失败, 返回失败响应, 同时记录失败的日志
|
||||
syncResult.setSyncStatus(PayRefundSyncStatusEnum.FAIL);
|
||||
this.saveRecord(refundOrder, syncResult, false, null, e.getMessage());
|
||||
return new PaySyncResult().setErrorMsg(e.getMessage());
|
||||
return new SyncResult().setErrorMsg(e.getMessage());
|
||||
}
|
||||
// // 同步成功记录日志
|
||||
// 同步成功记录日志
|
||||
this.saveRecord(refundOrder, syncResult, !statusSync, repairResult.getRepairId(), null);
|
||||
return new PaySyncResult()
|
||||
return new SyncResult()
|
||||
.setGatewayStatus(syncResult.getSyncStatus().getCode())
|
||||
.setSuccess(true)
|
||||
.setRepair(!statusSync)
|
||||
@@ -113,13 +117,45 @@ public class PayRefundSyncService {
|
||||
}
|
||||
|
||||
|
||||
|
||||
private boolean checkAndAdjustSyncStatus(RefundGatewaySyncResult syncResult, PayRefundOrder order){
|
||||
return true;
|
||||
/**
|
||||
* 检查状态是否一致
|
||||
*/
|
||||
private boolean checkSyncStatus(RefundGatewaySyncResult syncResult, PayRefundOrder order){
|
||||
PayRefundSyncStatusEnum syncStatus = syncResult.getSyncStatus();
|
||||
String orderStatus = order.getStatus();
|
||||
return Objects.equals(orderStatus, syncStatus.getCode());
|
||||
}
|
||||
|
||||
/**
|
||||
* 进行退款订单和支付订单的补偿
|
||||
*/
|
||||
private RefundRepairResult repairHandler(RefundGatewaySyncResult syncResult, PayRefundOrder order){
|
||||
return null;
|
||||
PayRefundSyncStatusEnum syncStatusEnum = syncResult.getSyncStatus();
|
||||
// 如果没有支付来源, 设置支付来源为同步
|
||||
RepairLocal repairInfo = PaymentContextLocal.get().getRepairInfo();
|
||||
if (Objects.isNull(repairInfo.getSource())){
|
||||
repairInfo.setSource(PayRepairSourceEnum.SYNC);
|
||||
}
|
||||
RefundRepairResult repair = new RefundRepairResult();
|
||||
// 对支付网关同步的结果进行处理
|
||||
switch (syncStatusEnum) {
|
||||
// 调用出错
|
||||
case SUCCESS:
|
||||
repair = repairService.repair(order, RefundRepairWayEnum.SUCCESS);
|
||||
break;
|
||||
case REFUNDING:
|
||||
// 不进行处理 TODO 添加重试
|
||||
log.warn("退款状态同步接口调用出错");
|
||||
break;
|
||||
case FAIL: {
|
||||
repair = repairService.repair(order, RefundRepairWayEnum.FAIL);
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
throw new BizException("代码有问题");
|
||||
}
|
||||
}
|
||||
return repair;
|
||||
}
|
||||
|
||||
|
||||
@@ -135,6 +171,7 @@ public class PayRefundSyncService {
|
||||
PaySyncRecord paySyncRecord = new PaySyncRecord()
|
||||
.setOrderId(payOrder.getId())
|
||||
.setOrderNo(payOrder.getBusinessNo())
|
||||
.setSyncType(PaymentTypeEnum.REFUND.getCode())
|
||||
.setAsyncChannel(payOrder.getAsyncChannel())
|
||||
.setSyncInfo(syncResult.getSyncInfo())
|
||||
.setGatewayStatus(syncResult.getSyncStatus().getCode())
|
||||
|
@@ -7,9 +7,10 @@ import cn.bootx.platform.daxpay.code.PayStatusEnum;
|
||||
import cn.bootx.platform.daxpay.code.PaySyncStatusEnum;
|
||||
import cn.bootx.platform.daxpay.exception.pay.PayFailureException;
|
||||
import cn.bootx.platform.daxpay.param.pay.PaySyncParam;
|
||||
import cn.bootx.platform.daxpay.result.pay.PaySyncResult;
|
||||
import cn.bootx.platform.daxpay.result.pay.SyncResult;
|
||||
import cn.bootx.platform.daxpay.service.code.PayRepairSourceEnum;
|
||||
import cn.bootx.platform.daxpay.service.code.PayRepairWayEnum;
|
||||
import cn.bootx.platform.daxpay.service.code.PaymentTypeEnum;
|
||||
import cn.bootx.platform.daxpay.service.common.context.RepairLocal;
|
||||
import cn.bootx.platform.daxpay.service.common.local.PaymentContextLocal;
|
||||
import cn.bootx.platform.daxpay.service.core.order.pay.entity.PayOrder;
|
||||
@@ -58,7 +59,7 @@ public class PaySyncService {
|
||||
* 支付同步, 开启一个新的事务, 不受外部抛出异常的影响
|
||||
*/
|
||||
@Transactional(propagation = Propagation.REQUIRES_NEW, rollbackFor = Exception.class)
|
||||
public PaySyncResult sync(PaySyncParam param) {
|
||||
public SyncResult sync(PaySyncParam param) {
|
||||
PayOrder payOrder = null;
|
||||
if (Objects.nonNull(param.getPaymentId())){
|
||||
payOrder = payOrderQueryService.findById(param.getPaymentId())
|
||||
@@ -70,7 +71,7 @@ public class PaySyncService {
|
||||
}
|
||||
// 如果不是异步支付, 直接返回返回
|
||||
if (!payOrder.isAsyncPay()){
|
||||
return new PaySyncResult().setSuccess(false).setRepair(false).setErrorMsg("订单没有异步支付方式,不需要同步");
|
||||
return new SyncResult().setSuccess(false).setRepair(false).setErrorMsg("订单没有异步支付方式,不需要同步");
|
||||
}
|
||||
// 执行订单同步逻辑
|
||||
return this.syncPayOrder(payOrder);
|
||||
@@ -82,7 +83,7 @@ public class PaySyncService {
|
||||
* todo 需要进行异常处理, 现在会有 Transaction rolled back because it has been marked as rollback-only 问题
|
||||
*/
|
||||
@Transactional(propagation = Propagation.REQUIRES_NEW, rollbackFor = Exception.class)
|
||||
public PaySyncResult syncPayOrder(PayOrder payOrder) {
|
||||
public SyncResult syncPayOrder(PayOrder payOrder) {
|
||||
// 加锁
|
||||
LockInfo lock = lockTemplate.lock("sync:payment" + payOrder.getId());
|
||||
if (Objects.isNull(lock)){
|
||||
@@ -99,7 +100,7 @@ public class PaySyncService {
|
||||
if (Objects.equals(syncResult.getSyncStatus(), PaySyncStatusEnum.FAIL)){
|
||||
// 同步失败, 返回失败响应, 同时记录失败的日志
|
||||
this.saveRecord(payOrder, syncResult, false, null, syncResult.getErrorMsg());
|
||||
return new PaySyncResult().setErrorMsg(syncResult.getErrorMsg());
|
||||
return new SyncResult().setErrorMsg(syncResult.getErrorMsg());
|
||||
}
|
||||
|
||||
// 判断网关状态是否和支付单一致, 同时特定情况下更新网关同步状态
|
||||
@@ -114,12 +115,12 @@ public class PaySyncService {
|
||||
// 同步失败, 返回失败响应, 同时记录失败的日志
|
||||
syncResult.setSyncStatus(PaySyncStatusEnum.FAIL);
|
||||
this.saveRecord(payOrder, syncResult, false, null, e.getMessage());
|
||||
return new PaySyncResult().setErrorMsg(e.getMessage());
|
||||
return new SyncResult().setErrorMsg(e.getMessage());
|
||||
}
|
||||
|
||||
// 同步成功记录日志
|
||||
this.saveRecord( payOrder, syncResult, !statusSync, repairResult.getRepairId(), null);
|
||||
return new PaySyncResult()
|
||||
return new SyncResult()
|
||||
.setGatewayStatus(syncResult.getSyncStatus().getCode())
|
||||
.setSuccess(true)
|
||||
.setRepair(!statusSync)
|
||||
@@ -234,6 +235,7 @@ public class PaySyncService {
|
||||
PaySyncRecord paySyncRecord = new PaySyncRecord()
|
||||
.setOrderId(payOrder.getId())
|
||||
.setOrderNo(payOrder.getBusinessNo())
|
||||
.setSyncType(PaymentTypeEnum.PAY.getCode())
|
||||
.setAsyncChannel(payOrder.getAsyncChannel())
|
||||
.setSyncInfo(syncResult.getSyncInfo())
|
||||
.setGatewayStatus(syncResult.getSyncStatus().getCode())
|
||||
|
@@ -4,6 +4,7 @@ import cn.bootx.platform.common.core.function.EntityBaseFunction;
|
||||
import cn.bootx.platform.common.mybatisplus.base.MpCreateEntity;
|
||||
import cn.bootx.platform.daxpay.code.PayChannelEnum;
|
||||
import cn.bootx.platform.daxpay.code.PaySyncStatusEnum;
|
||||
import cn.bootx.platform.daxpay.service.code.PaymentTypeEnum;
|
||||
import cn.bootx.platform.daxpay.service.core.record.sync.convert.PaySyncRecordConvert;
|
||||
import cn.bootx.platform.daxpay.service.dto.record.sync.PaySyncRecordDto;
|
||||
import cn.bootx.table.modify.annotation.DbColumn;
|
||||
@@ -35,7 +36,14 @@ public class PaySyncRecord extends MpCreateEntity implements EntityBaseFunction<
|
||||
@DbColumn(comment = "本地业务号")
|
||||
private String orderNo;
|
||||
|
||||
@DbColumn(comment = "同步通道")
|
||||
/**
|
||||
* 同步类型 支付/退款
|
||||
* @see PaymentTypeEnum
|
||||
*/
|
||||
@DbColumn(comment = "同步类型")
|
||||
private String syncType;
|
||||
|
||||
@DbColumn(comment = "同步的异步通道")
|
||||
private String syncChannel;
|
||||
|
||||
/**
|
||||
|
@@ -1,12 +1,15 @@
|
||||
package cn.bootx.platform.daxpay.service.func;
|
||||
|
||||
import cn.bootx.platform.daxpay.code.PayRefundStatusEnum;
|
||||
import cn.bootx.platform.daxpay.code.PayStatusEnum;
|
||||
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.PayRefundChannelOrder;
|
||||
import cn.bootx.platform.daxpay.service.core.order.refund.entity.PayRefundOrder;
|
||||
import cn.bootx.platform.daxpay.service.core.payment.sync.result.RefundGatewaySyncResult;
|
||||
import lombok.Getter;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* 支付退款修复策略
|
||||
* @author xxm
|
||||
@@ -22,10 +25,10 @@ public abstract class AbsRefundRepairStrategy implements PayStrategy{
|
||||
/**
|
||||
* 初始化参数
|
||||
*/
|
||||
public void initParam(PayRefundOrder refundOrder,
|
||||
PayRefundChannelOrder refundChannelOrder,
|
||||
PayOrder payOrder,
|
||||
PayChannelOrder payChannelOrder){
|
||||
public void initRepairParam(PayRefundOrder refundOrder,
|
||||
PayRefundChannelOrder refundChannelOrder,
|
||||
PayOrder payOrder,
|
||||
PayChannelOrder payChannelOrder){
|
||||
this.refundOrder = refundOrder;
|
||||
this.refundChannelOrder = refundChannelOrder;
|
||||
this.payOrder = payOrder;
|
||||
@@ -34,8 +37,42 @@ public abstract class AbsRefundRepairStrategy implements PayStrategy{
|
||||
|
||||
|
||||
/**
|
||||
* 异步支付单与支付网关进行状态比对后的结果
|
||||
* 退款成功修复
|
||||
*/
|
||||
public abstract RefundGatewaySyncResult doSyncStatus();
|
||||
public void doSuccessHandler(){
|
||||
PayChannelOrder payChannelOrder = this.getPayChannelOrder();
|
||||
PayRefundChannelOrder refundChannelOrder = this.getRefundChannelOrder();
|
||||
|
||||
// 判断是全部退款还是部分退款
|
||||
if (Objects.equals(payChannelOrder.getRefundableBalance(), 0)){
|
||||
//全部退款
|
||||
payChannelOrder.setStatus(PayStatusEnum.REFUNDED.getCode());
|
||||
} else {
|
||||
// 部分退款
|
||||
payChannelOrder.setStatus(PayStatusEnum.PARTIAL_REFUND.getCode());
|
||||
|
||||
}
|
||||
refundChannelOrder.setStatus(PayRefundStatusEnum.SUCCESS.getCode());
|
||||
}
|
||||
|
||||
/**
|
||||
* 退款失败, 关闭退款订单
|
||||
*/
|
||||
public void doCloseHandler(){
|
||||
PayChannelOrder payChannelOrder = this.getPayChannelOrder();
|
||||
PayRefundChannelOrder refundChannelOrder = this.getRefundChannelOrder();
|
||||
int refundableBalance = payChannelOrder.getRefundableBalance() + payChannelOrder.getAmount();
|
||||
payChannelOrder.setRefundableBalance(refundableBalance);
|
||||
// 判断是支付完成还是部分退款
|
||||
if (Objects.equals(payChannelOrder.getRefundableBalance(), payChannelOrder.getAmount())){
|
||||
// 全部退款
|
||||
payChannelOrder.setStatus(PayStatusEnum.SUCCESS.getCode());
|
||||
} else {
|
||||
// 部分退款
|
||||
payChannelOrder.setStatus(PayStatusEnum.PARTIAL_REFUND.getCode());
|
||||
|
||||
}
|
||||
refundChannelOrder.setStatus(PayRefundStatusEnum.CLOSE.getCode());
|
||||
}
|
||||
|
||||
}
|
||||
|
Reference in New Issue
Block a user