ref 交易调整功能重构

This commit is contained in:
bootx
2024-07-15 23:17:10 +08:00
parent 42c706619b
commit 913e90050d
49 changed files with 476 additions and 1028 deletions

View File

@@ -0,0 +1,20 @@
package cn.daxpay.single.service.code;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
* 退款修复方式枚举
* @author xxm
* @since 2024/1/26
*/
@Getter
@AllArgsConstructor
public enum RefundAdjustWayEnum {
SUCCESS("success","退款成功"),
FAIL("fail","退款失败");
private final String code;
private final String name;
}

View File

@@ -1,20 +0,0 @@
package cn.daxpay.single.service.code;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
* 退款修复方式枚举, 支付修复方式包含退款的修复方式, 退款修复方式是支付修复的子集
* @author xxm
* @since 2024/1/26
*/
@Getter
@AllArgsConstructor
public enum RefundRepairWayEnum {
REFUND_SUCCESS("refund_success","退款成功"),
REFUND_FAIL("refund_fail","退款失败");
private final String code;
private final String name;
}

View File

@@ -14,7 +14,6 @@ public enum TradeAdjustSourceEnum {
SYNC("sync","同步"),
CALLBACK("callback","回调"),
TASK("task","定时任务"),
;
private final String code;
private final String name;

View File

@@ -12,7 +12,7 @@ import java.util.HashMap;
import java.util.Map;
/**
* 回调信息上下文
* 回调信息上下文 针对支付和退款的回调
* @author xxm
* @since 2024/1/24
*/
@@ -49,9 +49,6 @@ public class CallbackLocal {
/** 完成时间(支付/退款) */
private LocalDateTime finishTime;
/** 修复号 */
private String repairNo;
/** 回调类型 */
private TradeTypeEnum callbackType;
@@ -61,6 +58,10 @@ public class CallbackLocal {
*/
private PayCallbackStatusEnum callbackStatus = PayCallbackStatusEnum.SUCCESS;
/** 调整号 */
private String adjustNo;
/** 提示信息 */
private String errorMsg;
}

View File

@@ -5,7 +5,6 @@ import cn.bootx.platform.common.core.util.LocalDateTimeUtil;
import cn.daxpay.single.core.code.PayChannelEnum;
import cn.daxpay.single.core.code.PayStatusEnum;
import cn.daxpay.single.service.code.PayCallbackStatusEnum;
import cn.daxpay.single.service.code.TradeAdjustSourceEnum;
import cn.daxpay.single.service.code.TradeTypeEnum;
import cn.daxpay.single.service.common.context.CallbackLocal;
import cn.daxpay.single.service.common.local.PaymentContextLocal;
@@ -71,9 +70,6 @@ public class AliPayCallbackService {
callbackService.saveCallbackRecord();
return null;
}
// 提前设置订单修复的来源
PaymentContextLocal.get().getRepairInfo().setSource(TradeAdjustSourceEnum.CALLBACK);
if (callbackType == TradeTypeEnum.PAY){
// 解析支付数据并放处理
this.resolvePayData();

View File

@@ -73,7 +73,7 @@ public class AliPaySyncService {
if (Objects.equals(tradeStatus, AliPayCode.NOTIFY_TRADE_SUCCESS) || Objects.equals(tradeStatus, AliPayCode.NOTIFY_TRADE_FINISHED)) {
// 支付完成时间
LocalDateTime payTime = LocalDateTimeUtil.of(response.getSendPayDate());
return syncResult.setSyncStatus(PaySyncStatusEnum.SUCCESS).setPayTime(payTime);
return syncResult.setSyncStatus(PaySyncStatusEnum.SUCCESS).setFinishTime(payTime);
}
// 待支付
if (Objects.equals(tradeStatus, AliPayCode.NOTIFY_WAIT_BUYER_PAY)) {

View File

@@ -5,7 +5,6 @@ import cn.daxpay.single.core.code.PayChannelEnum;
import cn.daxpay.single.core.code.PayStatusEnum;
import cn.daxpay.single.core.code.RefundStatusEnum;
import cn.daxpay.single.service.code.PayCallbackStatusEnum;
import cn.daxpay.single.service.code.TradeAdjustSourceEnum;
import cn.daxpay.single.service.code.TradeTypeEnum;
import cn.daxpay.single.service.code.UnionPayCode;
import cn.daxpay.single.service.common.context.CallbackLocal;
@@ -69,9 +68,6 @@ public class UnionPayCallbackService {
callbackService.saveCallbackRecord();
return null;
}
// 提前设置订单修复的来源
PaymentContextLocal.get().getRepairInfo().setSource(TradeAdjustSourceEnum.CALLBACK);
if (callbackType == TradeTypeEnum.PAY){
// 解析支付数据并放处理
this.resolvePayData();

View File

@@ -72,7 +72,7 @@ public class UnionPaySyncService {
String queryId = MapUtil.getStr(result, QUERY_ID);
String timeEnd = MapUtil.getStr(result, TXN_TIME);
LocalDateTime time = LocalDateTimeUtil.parse(timeEnd, DatePattern.PURE_DATETIME_PATTERN);
return syncResult.setOutOrderNo(queryId).setPayTime(time).setSyncStatus(PaySyncStatusEnum.SUCCESS);
return syncResult.setOutOrderNo(queryId).setFinishTime(time).setSyncStatus(PaySyncStatusEnum.SUCCESS);
}
// 支付超时 交易不在受理时间范围内

View File

@@ -5,7 +5,6 @@ import cn.daxpay.single.core.code.PayChannelEnum;
import cn.daxpay.single.core.code.PayStatusEnum;
import cn.daxpay.single.core.code.RefundStatusEnum;
import cn.daxpay.single.service.code.PayCallbackStatusEnum;
import cn.daxpay.single.service.code.TradeAdjustSourceEnum;
import cn.daxpay.single.service.code.TradeTypeEnum;
import cn.daxpay.single.service.common.context.CallbackLocal;
import cn.daxpay.single.service.common.local.PaymentContextLocal;
@@ -69,9 +68,6 @@ public class WeChatPayCallbackService {
callbackService.saveCallbackRecord();
return null;
}
// 提前设置订单修复的来源
PaymentContextLocal.get().getRepairInfo().setSource(TradeAdjustSourceEnum.CALLBACK);
if (callbackType == TradeTypeEnum.PAY){
// 解析支付数据并放处理
this.resolvePayData();

View File

@@ -74,7 +74,7 @@ public class WeChatPaySyncService {
if (Objects.equals(tradeStatus, WeChatPayCode.PAY_SUCCESS) || Objects.equals(tradeStatus, WeChatPayCode.PAY_ACCEPT)) {
String timeEnd = result.get(WeChatPayCode.TIME_END);
LocalDateTime time = LocalDateTimeUtil.parse(timeEnd, DatePattern.PURE_DATETIME_PATTERN);
return syncResult.setPayTime(time).setSyncStatus(PaySyncStatusEnum.SUCCESS);
return syncResult.setFinishTime(time).setSyncStatus(PaySyncStatusEnum.SUCCESS);
}
// 待支付
if (Objects.equals(tradeStatus, WeChatPayCode.PAY_NOTPAY)

View File

@@ -1,11 +1,14 @@
package cn.daxpay.single.service.core.payment.repair.param;
package cn.daxpay.single.service.core.payment.adjust.param;
import cn.daxpay.single.service.code.PayAdjustWayEnum;
import cn.daxpay.single.service.code.TradeAdjustSourceEnum;
import cn.daxpay.single.service.core.order.pay.entity.PayOrder;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.experimental.Accessors;
import java.time.LocalDateTime;
/**
* 支付订单修复参数
* @author xxm
@@ -13,14 +16,20 @@ import lombok.experimental.Accessors;
*/
@Data
@Accessors(chain = true)
public class PayRepairParam {
public class PayAdjustParam {
@Schema(description = "支付订单")
private PayOrder order;
@Schema(description = "通道交易号")
private String outTradeNo;
@Schema(description = "完成时间")
private LocalDateTime finishTime;
@Schema(description = "触发来源")
private TradeAdjustSourceEnum source;
@Schema(description = "调整方式")
private PayAdjustWayEnum adjustWay;
@Schema(description = "支付订单号")
private String orderNo;
}

View File

@@ -0,0 +1,35 @@
package cn.daxpay.single.service.core.payment.adjust.param;
import cn.daxpay.single.service.code.RefundAdjustWayEnum;
import cn.daxpay.single.service.code.TradeAdjustSourceEnum;
import cn.daxpay.single.service.core.order.refund.entity.RefundOrder;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.experimental.Accessors;
import java.time.LocalDateTime;
/**
* 退款订单修复参数
* @author xxm
* @since 2023/12/27
*/
@Data
@Accessors(chain = true)
public class RefundAdjustParam {
@Schema(description = "退款订单")
private RefundOrder order;
@Schema(description = "通道交易号")
private String outTradeNo;
@Schema(description = "完成时间")
private LocalDateTime finishTime;
@Schema(description = "触发来源")
private TradeAdjustSourceEnum source;
@Schema(description = "调整方式")
private RefundAdjustWayEnum adjustWay;
}

View File

@@ -1,4 +1,4 @@
package cn.daxpay.single.service.core.payment.repair.service;
package cn.daxpay.single.service.core.payment.adjust.service;
import cn.daxpay.single.core.code.PayStatusEnum;
import cn.daxpay.single.core.exception.OperationProcessingException;
@@ -6,11 +6,10 @@ import cn.daxpay.single.core.exception.SystemUnknownErrorException;
import cn.daxpay.single.core.exception.TradeStatusErrorException;
import cn.daxpay.single.core.util.TradeNoGenerateUtil;
import cn.daxpay.single.service.code.TradeTypeEnum;
import cn.daxpay.single.service.common.local.PaymentContextLocal;
import cn.daxpay.single.service.core.order.pay.entity.PayOrder;
import cn.daxpay.single.service.core.order.pay.service.PayOrderService;
import cn.daxpay.single.service.core.payment.notice.service.ClientNoticeService;
import cn.daxpay.single.service.core.payment.repair.param.PayRepairParam;
import cn.daxpay.single.service.core.payment.adjust.param.PayAdjustParam;
import cn.daxpay.single.service.core.record.flow.service.TradeFlowRecordService;
import cn.daxpay.single.service.core.record.repair.entity.TradeAdjustRecord;
import cn.daxpay.single.service.core.record.repair.service.TradeAdjustRecordService;
@@ -21,6 +20,7 @@ import com.baomidou.lock.LockTemplate;
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.Objects;
@@ -45,7 +45,9 @@ public class PayAdjustService {
/**
* 调整服务
*/
public String adjust(PayOrder order, PayRepairParam param){
@Transactional(rollbackFor = Exception.class)
public String adjust(PayAdjustParam param){
PayOrder order = param.getOrder();
// 添加分布式锁
LockInfo lock = lockTemplate.lock("repair:pay:" + order.getId(), 10000, 200);
if (Objects.isNull(lock)){
@@ -67,7 +69,7 @@ public class PayAdjustService {
// 根据不同的调整方式执行对应的修复逻辑
switch (param.getAdjustWay()) {
case SUCCESS:
this.success(order);
this.success(order, param);
break;
case CLOSE_LOCAL:
this.closeLocal(order);
@@ -83,7 +85,7 @@ public class PayAdjustService {
// 发送通知
clientNoticeService.registerPayNotice(order);
TradeAdjustRecord record = this.saveRecord(order, param, beforeStatus);
return record.getRepairNo();
return record.getAdjustNo();
}
/**
@@ -91,14 +93,11 @@ public class PayAdjustService {
* 同步: 将异步支付状态修改为成功
* 回调: 将异步支付状态修改为成功
*/
private void success(PayOrder order) {
// 读取支付网关中的完成时间
LocalDateTime payTime = PaymentContextLocal.get()
.getRepairInfo()
.getFinishTime();
private void success(PayOrder order, PayAdjustParam param) {
// 修改订单支付状态为成功
order.setStatus(PayStatusEnum.SUCCESS.getCode())
.setPayTime(payTime)
.setPayTime(param.getFinishTime())
.setOutOrderNo(param.getOutTradeNo())
.setCloseTime(null);
tradeFlowRecordService.savePay(order);
payOrderService.updateById(order);
@@ -129,10 +128,10 @@ public class PayAdjustService {
/**
* 保存记录
*/
private TradeAdjustRecord saveRecord(PayOrder order, PayRepairParam param, String beforeStatus){
private TradeAdjustRecord saveRecord(PayOrder order, PayAdjustParam param, String beforeStatus){
// 修复后的状态
TradeAdjustRecord record = new TradeAdjustRecord()
.setRepairNo(TradeNoGenerateUtil.adjust())
.setAdjustNo(TradeNoGenerateUtil.adjust())
.setTradeId(order.getId())
.setChannel(order.getChannel())
.setSource(param.getSource().getCode())

View File

@@ -0,0 +1,180 @@
package cn.daxpay.single.service.core.payment.adjust.service;
import cn.daxpay.single.core.code.PayOrderRefundStatusEnum;
import cn.daxpay.single.core.code.PayStatusEnum;
import cn.daxpay.single.core.code.RefundStatusEnum;
import cn.daxpay.single.core.exception.OperationProcessingException;
import cn.daxpay.single.core.exception.TradeStatusErrorException;
import cn.daxpay.single.core.util.TradeNoGenerateUtil;
import cn.daxpay.single.service.code.RefundAdjustWayEnum;
import cn.daxpay.single.service.code.TradeTypeEnum;
import cn.daxpay.single.service.core.order.pay.entity.PayOrder;
import cn.daxpay.single.service.core.order.pay.service.PayOrderQueryService;
import cn.daxpay.single.service.core.order.pay.service.PayOrderService;
import cn.daxpay.single.service.core.order.refund.dao.RefundOrderManager;
import cn.daxpay.single.service.core.order.refund.entity.RefundOrder;
import cn.daxpay.single.service.core.payment.adjust.param.RefundAdjustParam;
import cn.daxpay.single.service.core.payment.notice.service.ClientNoticeService;
import cn.daxpay.single.service.core.record.flow.service.TradeFlowRecordService;
import cn.daxpay.single.service.core.record.repair.entity.TradeAdjustRecord;
import cn.daxpay.single.service.core.record.repair.service.TradeAdjustRecordService;
import com.baomidou.lock.LockInfo;
import com.baomidou.lock.LockTemplate;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
/**
* 退款订单修复, 只有存在异步支付的退款订单才存在修复
* @author xxm
* @since 2024/1/26
*/
@Slf4j
@Service
@RequiredArgsConstructor
public class RefundAdjustService {
private final PayOrderService payOrderService;
private final PayOrderQueryService payOrderQueryService;
private final ClientNoticeService clientNoticeService;
private final RefundOrderManager refundOrderManager;
private final TradeFlowRecordService tradeFlowRecordService;
private final TradeAdjustRecordService tradeAdjustRecordService;
private final LockTemplate lockTemplate;
/**
* 调整退款单
*/
@Transactional(rollbackFor = Exception.class)
public String adjust(RefundAdjustParam param){
RefundOrder refundOrder = param.getOrder();
RefundAdjustWayEnum repairType = param.getAdjustWay();
// 添加分布式锁
LockInfo lock = lockTemplate.lock("repair:refund:" + refundOrder.getId(), 10000, 200);
if (Objects.isNull(lock)){
log.warn("当前退款订单正在调整中: {}", refundOrder.getId());
throw new OperationProcessingException("当前退款订单正在调整中");
}
// 如果到达终态不能向前回滚
if (Arrays.asList(RefundStatusEnum.SUCCESS.getCode(), RefundStatusEnum.CLOSE.getCode()).contains(refundOrder.getStatus())){
throw new TradeStatusErrorException("当前退款订单已处理完成");
}
try {
// 获取关联支付单
PayOrder payOrder = payOrderQueryService.findById(refundOrder.getOrderId()).orElseThrow(() -> new RuntimeException("支付单不存在"));
String adjustNo = null;
// 根据不同的类型执行对应的修复逻辑
if (repairType == RefundAdjustWayEnum.SUCCESS) {
adjustNo = this.success(param, payOrder);
} else if (repairType == RefundAdjustWayEnum.FAIL) {
adjustNo = this.close(param, payOrder);
} else {
log.error("走到了理论上讲不会走到的分支");
}
// 发送通知
clientNoticeService.registerRefundNotice(refundOrder);
return adjustNo;
} finally {
lockTemplate.releaseLock(lock);
}
}
/**
* 退款成功, 更新退款单和支付单
*/
private String success(RefundAdjustParam param, PayOrder payOrder) {
RefundOrder refundOrder = param.getOrder();
String beforeRefundStatus = refundOrder.getStatus();
// 订单相关状态
PayOrderRefundStatusEnum afterPayRefundStatus;
// 判断订单全部退款还是部分退款
if (Objects.equals(payOrder.getRefundableBalance(),0)){
afterPayRefundStatus = PayOrderRefundStatusEnum.REFUNDED;
} else {
afterPayRefundStatus = PayOrderRefundStatusEnum.PARTIAL_REFUND;
}
// 设置退款为完成状态和完成时间
refundOrder.setStatus(RefundStatusEnum.SUCCESS.getCode())
.setFinishTime(param.getFinishTime());
payOrder.setRefundStatus(afterPayRefundStatus.getCode());
// 更新订单和退款相关订单
payOrderService.updateById(payOrder);
refundOrderManager.updateById(refundOrder);
// 记录流水
tradeFlowRecordService.saveRefund(refundOrder);
// 发送通知
List<String> list = Arrays.asList(RefundStatusEnum.SUCCESS.getCode(), RefundStatusEnum.CLOSE.getCode(), RefundStatusEnum.FAIL.getCode());
if (list.contains(refundOrder.getStatus())){
clientNoticeService.registerRefundNotice(refundOrder);
}
// 保存调整记录
TradeAdjustRecord adjustRecord = this.saveRecord(param, beforeRefundStatus);
return adjustRecord.getAdjustNo();
}
/**
* 退款失败, 关闭退款单并将失败的退款金额归还回订单
*/
private String close(RefundAdjustParam param, PayOrder payOrder) {
RefundOrder refundOrder = param.getOrder();
String beforeRefundStatus = refundOrder.getStatus();
// 退款失败返还后的余额
int payOrderAmount = refundOrder.getAmount() + payOrder.getRefundableBalance();
// 退款失败返还后的余额+可退余额 == 订单金额 支付订单回退为为支付成功状态
if (payOrderAmount == payOrder.getAmount()){
payOrder.setStatus(PayStatusEnum.SUCCESS.getCode());
} else {
// 回归部分退款状态
payOrder.setRefundStatus(PayOrderRefundStatusEnum.PARTIAL_REFUND.getCode());
}
// 更新支付订单相关的可退款金额
payOrder.setRefundableBalance(payOrderAmount);
refundOrder.setStatus(RefundStatusEnum.CLOSE.getCode());
// 更新订单和退款相关订单
payOrderService.updateById(payOrder);
refundOrderManager.updateById(refundOrder);
TradeAdjustRecord adjustRecord = this.saveRecord(param, beforeRefundStatus);
return adjustRecord.getAdjustNo();
}
/**
* 保存退款订单的调整记录
*/
private TradeAdjustRecord saveRecord(RefundAdjustParam param, String beforeRefundStatus){
RefundOrder refundOrder = param.getOrder();
// 修复发起来源
TradeAdjustRecord record = new TradeAdjustRecord()
.setTradeId(refundOrder.getId())
.setAdjustNo(TradeNoGenerateUtil.adjust())
.setTradeNo(refundOrder.getRefundNo())
.setChannel(refundOrder.getChannel())
.setType(TradeTypeEnum.REFUND.getCode())
.setBeforeStatus(beforeRefundStatus)
.setAfterStatus(refundOrder.getStatus())
.setSource(param.getSource().getCode())
.setWay(param.getAdjustWay().getCode());
tradeAdjustRecordService.saveRecord(record);
return record;
}
}

View File

@@ -1,4 +1,4 @@
package cn.daxpay.single.service.core.payment.repair.strategy.pay;
package cn.daxpay.single.service.core.payment.adjust.strategy.pay;
import cn.daxpay.single.core.code.PayChannelEnum;
import cn.daxpay.single.service.core.channel.alipay.entity.AliPayConfig;

View File

@@ -1,4 +1,4 @@
package cn.daxpay.single.service.core.payment.repair.strategy.pay;
package cn.daxpay.single.service.core.payment.adjust.strategy.pay;
import cn.daxpay.single.core.code.PayChannelEnum;
import cn.daxpay.single.service.func.AbsPayAdjustStrategy;

View File

@@ -1,4 +1,4 @@
package cn.daxpay.single.service.core.payment.repair.strategy.pay;
package cn.daxpay.single.service.core.payment.adjust.strategy.pay;
import cn.daxpay.single.core.code.PayChannelEnum;
import cn.daxpay.single.service.core.channel.wechat.entity.WeChatPayConfig;

View File

@@ -15,8 +15,8 @@ import cn.daxpay.single.service.core.order.allocation.entity.AllocOrder;
import cn.daxpay.single.service.core.order.allocation.entity.AllocOrderDetail;
import cn.daxpay.single.service.core.payment.notice.service.ClientNoticeService;
import cn.daxpay.single.service.core.payment.sync.result.AllocRemoteSyncResult;
import cn.daxpay.single.service.core.record.sync.entity.PaySyncRecord;
import cn.daxpay.single.service.core.record.sync.service.PaySyncRecordService;
import cn.daxpay.single.service.core.record.sync.entity.TradeSyncRecord;
import cn.daxpay.single.service.core.record.sync.service.TradeSyncRecordService;
import cn.daxpay.single.service.func.AbsAllocationStrategy;
import cn.daxpay.single.service.util.PayStrategyFactory;
import com.baomidou.lock.LockInfo;
@@ -47,7 +47,7 @@ public class AllocationSyncService {
private final AllocOrderDetailManager allocOrderDetailManager;
private final PaySyncRecordService paySyncRecordService;
private final TradeSyncRecordService tradeSyncRecordService;
private final LockTemplate lockTemplate;
@@ -158,16 +158,16 @@ public class AllocationSyncService {
* 保存同步记录
*/
private void saveRecord(AllocOrder order, AllocRemoteSyncResult syncResult, String errorCode, String errorMsg){
PaySyncRecord paySyncRecord = new PaySyncRecord()
TradeSyncRecord tradeSyncRecord = new TradeSyncRecord()
.setBizTradeNo(order.getBizAllocNo())
.setTradeNo(order.getAllocNo())
.setOutTradeNo(order.getOutAllocNo())
.setSyncType(TradeTypeEnum.ALLOCATION.getCode())
.setType(TradeTypeEnum.ALLOCATION.getCode())
.setChannel(order.getChannel())
.setSyncInfo(syncResult.getSyncInfo())
.setErrorCode(errorCode)
.setErrorMsg(errorMsg)
.setClientIp(PaymentContextLocal.get().getClientInfo().getClientIp());
paySyncRecordService.saveRecord(paySyncRecord);
tradeSyncRecordService.saveRecord(tradeSyncRecord);
}
}

View File

@@ -2,14 +2,15 @@ package cn.daxpay.single.service.core.payment.callback.service;
import cn.bootx.platform.common.core.util.LocalDateTimeUtil;
import cn.daxpay.single.core.code.PayStatusEnum;
import cn.daxpay.single.service.code.PayCallbackStatusEnum;
import cn.daxpay.single.service.code.PayAdjustWayEnum;
import cn.daxpay.single.service.code.PayCallbackStatusEnum;
import cn.daxpay.single.service.code.TradeAdjustSourceEnum;
import cn.daxpay.single.service.common.context.CallbackLocal;
import cn.daxpay.single.service.common.local.PaymentContextLocal;
import cn.daxpay.single.service.core.order.pay.entity.PayOrder;
import cn.daxpay.single.service.core.order.pay.service.PayOrderQueryService;
import cn.daxpay.single.service.core.payment.repair.result.PayRepairResult;
import cn.daxpay.single.service.core.payment.repair.service.PayRepairService;
import cn.daxpay.single.service.core.payment.adjust.param.PayAdjustParam;
import cn.daxpay.single.service.core.payment.adjust.service.PayAdjustService;
import com.baomidou.lock.LockInfo;
import com.baomidou.lock.LockTemplate;
import lombok.RequiredArgsConstructor;
@@ -32,7 +33,7 @@ public class PayCallbackService {
private final PayOrderQueryService payOrderQueryService;
private final PayRepairService payRepairService;
private final PayAdjustService payAdjustService;
private final LockTemplate lockTemplate;
@@ -93,11 +94,15 @@ public class PayCallbackService {
callbackInfo.setCallbackStatus(PayCallbackStatusEnum.EXCEPTION).setErrorMsg("支付单不是待支付状态,记录回调记录");
return;
}
// 设置支付成功时间
PaymentContextLocal.get().getRepairInfo().setFinishTime(callbackInfo.getFinishTime());
// 执行支付完成修复逻辑
PayRepairResult repair = payRepairService.repair(payOrder, PayAdjustWayEnum.SUCCESS);
callbackInfo.setRepairNo(repair.getRepairNo());
// 执行支付成功的调整逻辑
PayAdjustParam param = new PayAdjustParam()
.setOrder(payOrder)
.setOutTradeNo(callbackInfo.getOutTradeNo())
.setAdjustWay(PayAdjustWayEnum.SUCCESS)
.setSource(TradeAdjustSourceEnum.CALLBACK)
.setFinishTime(callbackInfo.getFinishTime());
String adjustNo = payAdjustService.adjust(param);
callbackInfo.setAdjustNo(adjustNo);
}
/**
@@ -115,9 +120,15 @@ public class PayCallbackService {
callbackInfo.setCallbackStatus(PayCallbackStatusEnum.EXCEPTION).setErrorMsg("支付单状态非法,支付网关状态为失败,但支付单状态为已完成");
return;
}
// 执行支付关闭修复逻辑
PayRepairResult repair = payRepairService.repair(payOrder, PayAdjustWayEnum.CLOSE_LOCAL);
callbackInfo.setRepairNo(repair.getRepairNo());
// 执行支付关闭的调整逻辑
PayAdjustParam param = new PayAdjustParam()
.setOrder(payOrder)
.setOutTradeNo(callbackInfo.getOutTradeNo())
.setAdjustWay(PayAdjustWayEnum.CLOSE_LOCAL)
.setSource(TradeAdjustSourceEnum.CALLBACK)
.setFinishTime(callbackInfo.getFinishTime());
String adjustNo = payAdjustService.adjust(param);
callbackInfo.setAdjustNo(adjustNo);
}
}

View File

@@ -2,13 +2,14 @@ package cn.daxpay.single.service.core.payment.callback.service;
import cn.daxpay.single.core.code.RefundStatusEnum;
import cn.daxpay.single.service.code.PayCallbackStatusEnum;
import cn.daxpay.single.service.code.RefundRepairWayEnum;
import cn.daxpay.single.service.code.RefundAdjustWayEnum;
import cn.daxpay.single.service.code.TradeAdjustSourceEnum;
import cn.daxpay.single.service.common.context.CallbackLocal;
import cn.daxpay.single.service.common.local.PaymentContextLocal;
import cn.daxpay.single.service.core.order.refund.dao.RefundOrderManager;
import cn.daxpay.single.service.core.order.refund.entity.RefundOrder;
import cn.daxpay.single.service.core.payment.repair.result.RefundRepairResult;
import cn.daxpay.single.service.core.payment.repair.service.RefundRepairService;
import cn.daxpay.single.service.core.payment.adjust.param.RefundAdjustParam;
import cn.daxpay.single.service.core.payment.adjust.service.RefundAdjustService;
import com.baomidou.lock.LockInfo;
import com.baomidou.lock.LockTemplate;
import lombok.RequiredArgsConstructor;
@@ -28,7 +29,7 @@ import java.util.Objects;
public class RefundCallbackService {
private final RefundOrderManager refundOrderManager;
private final RefundRepairService reflectionService;
private final RefundAdjustService reflectionService;
private final LockTemplate lockTemplate;
@@ -60,13 +61,19 @@ public class RefundCallbackService {
}
// 退款成功还是失败
RefundAdjustParam param = new RefundAdjustParam()
.setOrder(refundOrder)
.setOutTradeNo(callbackInfo.getOutTradeNo())
.setSource(TradeAdjustSourceEnum.CALLBACK)
.setFinishTime(callbackInfo.getFinishTime());
if (Objects.equals(RefundStatusEnum.SUCCESS.getCode(), callbackInfo.getOutStatus())) {
PaymentContextLocal.get().getRepairInfo().setFinishTime(callbackInfo.getFinishTime());
RefundRepairResult repair = reflectionService.repair(refundOrder, RefundRepairWayEnum.REFUND_SUCCESS);
callbackInfo.setRepairNo(repair.getRepairNo());
param.setAdjustWay(RefundAdjustWayEnum.SUCCESS);
String adjustNo = reflectionService.adjust(param);
callbackInfo.setAdjustNo(adjustNo);
} else {
RefundRepairResult repair = reflectionService.repair(refundOrder, RefundRepairWayEnum.REFUND_FAIL);
callbackInfo.setRepairNo(repair.getRepairNo());
param.setAdjustWay(RefundAdjustWayEnum.FAIL);
String adjustNo = reflectionService.adjust(param);
callbackInfo.setAdjustNo(adjustNo);
}
} finally {

View File

@@ -1,16 +0,0 @@
package cn.daxpay.single.service.core.payment.repair.result;
import lombok.Data;
import lombok.experimental.Accessors;
/**
* 支付修复结果
* @author xxm
* @since 2024/1/4
*/
@Data
@Accessors(chain = true)
public class PayRepairResult {
/** 修复号 */
private String repairNo;
}

View File

@@ -1,27 +0,0 @@
package cn.daxpay.single.service.core.payment.repair.result;
import cn.daxpay.single.core.code.PayOrderRefundStatusEnum;
import cn.daxpay.single.core.code.RefundStatusEnum;
import lombok.Data;
import lombok.experimental.Accessors;
/**
* 退款修复结果
* @author xxm
* @since 2024/1/26
*/
@Data
@Accessors(chain = true)
public class RefundRepairResult {
/** 修复号 */
private String repairNo;
/** 退款修复前状态 */
private RefundStatusEnum beforeRefundStatus;
/** 退款修复后状态 */
private RefundStatusEnum afterRefundStatus;
/** 支付修复前状态 */
private PayOrderRefundStatusEnum beforePayStatus;
/** 支付修复后状态 */
private PayOrderRefundStatusEnum afterPayStatus;
}

View File

@@ -1,125 +0,0 @@
package cn.daxpay.single.service.core.payment.repair.service;
import cn.daxpay.single.core.code.PayStatusEnum;
import cn.daxpay.single.core.exception.SystemUnknownErrorException;
import cn.daxpay.single.service.code.PayAdjustWayEnum;
import cn.daxpay.single.service.common.local.PaymentContextLocal;
import cn.daxpay.single.service.core.order.pay.entity.PayOrder;
import cn.daxpay.single.service.core.order.pay.service.PayOrderService;
import cn.daxpay.single.service.core.payment.notice.service.ClientNoticeService;
import cn.daxpay.single.service.core.payment.repair.result.PayRepairResult;
import cn.daxpay.single.service.core.record.flow.service.TradeFlowRecordService;
import cn.daxpay.single.service.core.record.repair.service.PayRepairRecordService;
import cn.daxpay.single.service.func.AbsPayAdjustStrategy;
import cn.daxpay.single.service.util.PayStrategyFactory;
import com.baomidou.lock.LockInfo;
import com.baomidou.lock.LockTemplate;
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.Objects;
/**
* 支付修复服务
* @author xxm
* @since 2023/12/27
*/
@Slf4j
@Service
@RequiredArgsConstructor
public class PayRepairService {
private final PayOrderService payOrderService;
private final ClientNoticeService clientNoticeService;
private final PayRepairRecordService recordService;
private final LockTemplate lockTemplate;
private final TradeFlowRecordService tradeFlowRecordService;
/**
* 修复支付单
*/
@Transactional(rollbackFor = Exception.class)
public PayRepairResult repair(PayOrder order, PayAdjustWayEnum repairType){
// 添加分布式锁
LockInfo lock = lockTemplate.lock("repair:pay:" + order.getId(), 10000, 200);
if (Objects.isNull(lock)){
log.warn("当前支付定单正在修复中: {}", order.getId());
return new PayRepairResult();
}
// 2.1 初始化修复参数
AbsPayAdjustStrategy repairStrategy = PayStrategyFactory.create(order.getChannel(), AbsPayAdjustStrategy.class);
repairStrategy.setOrder(order);
// 2.2 执行前置处理
repairStrategy.doBeforeHandler();
// 3. 根据不同的类型执行对应的修复逻辑
switch (repairType) {
case SUCCESS:
this.success(order);
break;
case CLOSE_LOCAL:
this.closeLocal(order);
break;
case CLOSE_GATEWAY:
this.closeRemote(order, repairStrategy);
break;
default:
log.error("走到了理论上讲不会走到的分支");
throw new SystemUnknownErrorException("走到了理论上讲不会走到的分支");
}
// 设置修复iD
// 发送通知
clientNoticeService.registerPayNotice(order);
return null;
}
/**
* 变更为已支付
* 同步: 将异步支付状态修改为成功
* 回调: 将异步支付状态修改为成功
*/
private void success(PayOrder order) {
// 读取支付网关中的完成时间
LocalDateTime payTime = PaymentContextLocal.get()
.getRepairInfo()
.getFinishTime();
// 修改订单支付状态为成功
order.setStatus(PayStatusEnum.SUCCESS.getCode())
.setPayTime(payTime)
.setCloseTime(null);
tradeFlowRecordService.savePay(order);
payOrderService.updateById(order);
}
/**
* 关闭支付
* 同步/对账: 执行支付单所有的支付通道关闭支付逻辑, 不需要调用网关关闭,
*/
private void closeLocal(PayOrder order) {
// 执行策略的关闭方法
order.setStatus(PayStatusEnum.CLOSE.getCode())
// TODO 尝试是否可以使用网关返回的时间
.setCloseTime(LocalDateTime.now());
payOrderService.updateById(order);
}
/**
* 关闭网关交易, 同时也会关闭本地支付
* 回调: 执行所有的支付通道关闭支付逻辑
*/
private void closeRemote(PayOrder payOrder, AbsPayAdjustStrategy strategy) {
// 执行策略的关闭方法
strategy.doCloseRemoteHandler();
payOrder.setStatus(PayStatusEnum.CLOSE.getCode())
// TODO 尝试是否可以使用网关返回的时间
.setCloseTime(LocalDateTime.now());
payOrderService.updateById(payOrder);
}
}

View File

@@ -1,223 +0,0 @@
package cn.daxpay.single.service.core.payment.repair.service;
import cn.daxpay.single.core.code.PayOrderRefundStatusEnum;
import cn.daxpay.single.core.code.PayStatusEnum;
import cn.daxpay.single.core.code.RefundStatusEnum;
import cn.daxpay.single.service.code.TradeTypeEnum;
import cn.daxpay.single.service.code.RefundRepairWayEnum;
import cn.daxpay.single.service.common.context.AdjustLocal;
import cn.daxpay.single.service.common.local.PaymentContextLocal;
import cn.daxpay.single.service.core.order.pay.entity.PayOrder;
import cn.daxpay.single.service.core.order.pay.service.PayOrderQueryService;
import cn.daxpay.single.service.core.order.pay.service.PayOrderService;
import cn.daxpay.single.service.core.order.refund.dao.RefundOrderManager;
import cn.daxpay.single.service.core.order.refund.entity.RefundOrder;
import cn.daxpay.single.service.core.payment.notice.service.ClientNoticeService;
import cn.daxpay.single.service.core.payment.repair.result.RefundRepairResult;
import cn.daxpay.single.service.core.record.flow.service.TradeFlowRecordService;
import cn.daxpay.single.service.core.record.repair.entity.PayRepairRecord;
import cn.daxpay.single.service.core.record.repair.service.PayRepairRecordService;
import cn.daxpay.single.core.util.TradeNoGenerateUtil;
import com.baomidou.lock.LockInfo;
import com.baomidou.lock.LockTemplate;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
/**
* 退款订单修复, 只有存在异步支付的退款订单才存在修复
* @author xxm
* @since 2024/1/26
*/
@Slf4j
@Service
@RequiredArgsConstructor
public class RefundRepairService {
private final PayOrderService payOrderService;
private final PayOrderQueryService payOrderQueryService;
private final ClientNoticeService clientNoticeService;
private final RefundOrderManager refundOrderManager;
private final PayRepairRecordService recordService;
private final LockTemplate lockTemplate;
private final TradeFlowRecordService tradeFlowRecordService;
/**
* 修复退款单
*/
@Transactional(rollbackFor = Exception.class)
public RefundRepairResult repair(RefundOrder refundOrder, RefundRepairWayEnum repairType){
// 添加分布式锁
LockInfo lock = lockTemplate.lock("repair:refund:" + refundOrder.getId(), 10000, 200);
if (Objects.isNull(lock)){
log.warn("当前退款单正在修复中: {}", refundOrder.getId());
return new RefundRepairResult();
}
try {
// 获取关联支付单
PayOrder payOrder = payOrderQueryService.findById(refundOrder.getOrderId())
.orElseThrow(() -> new RuntimeException("支付单不存在"));
// 根据不同的类型执行对应的修复逻辑
RefundRepairResult repairResult = new RefundRepairResult();
if (Objects.requireNonNull(repairType) == RefundRepairWayEnum.REFUND_SUCCESS) {
repairResult = this.success(refundOrder, payOrder);
} else if (repairType == RefundRepairWayEnum.REFUND_FAIL) {
repairResult = this.close(refundOrder, payOrder);
} else {
log.error("走到了理论上讲不会走到的分支");
}
// 设置修复ID并保存修复记录
repairResult.setRepairNo(TradeNoGenerateUtil.adjust());
// 支付修复记录
PayRepairRecord payRepairRecord = this.payRepairRecord(payOrder, repairType, repairResult);
// 退款修复记录
PayRepairRecord refundRepairRecord = this.refundRepairRecord(refundOrder, repairType, repairResult);
// 发送通知
clientNoticeService.registerRefundNotice(refundOrder);
recordService.saveAllRecord(Arrays.asList(payRepairRecord, refundRepairRecord));
return repairResult;
} finally {
lockTemplate.releaseLock(lock);
}
}
/**
* 退款成功, 更新退款单和支付单
*/
private RefundRepairResult success(RefundOrder refundOrder, PayOrder payOrder) {
AdjustLocal repairInfo = PaymentContextLocal.get().getRepairInfo();
// 订单相关状态
PayOrderRefundStatusEnum beforePayStatus = PayOrderRefundStatusEnum.findByCode(payOrder.getRefundStatus());
PayOrderRefundStatusEnum afterPayRefundStatus;
RefundStatusEnum beforeRefundStatus = RefundStatusEnum.findByCode(refundOrder.getStatus());
// 判断订单全部退款还是部分退款
if (Objects.equals(payOrder.getRefundableBalance(),0)){
afterPayRefundStatus = PayOrderRefundStatusEnum.REFUNDED;
} else {
afterPayRefundStatus = PayOrderRefundStatusEnum.PARTIAL_REFUND;
}
// 设置退款为完成状态和完成时间
refundOrder.setStatus(RefundStatusEnum.SUCCESS.getCode())
.setFinishTime(repairInfo.getFinishTime());
payOrder.setRefundStatus(afterPayRefundStatus.getCode());
// 更新订单和退款相关订单
payOrderService.updateById(payOrder);
refundOrderManager.updateById(refundOrder);
// 记录流水
tradeFlowRecordService.saveRefund(refundOrder);
// 发送通知
List<String> list = Arrays.asList(RefundStatusEnum.SUCCESS.getCode(), RefundStatusEnum.CLOSE.getCode(), RefundStatusEnum.FAIL.getCode());
if (list.contains(refundOrder.getStatus())){
clientNoticeService.registerRefundNotice(refundOrder);
}
return new RefundRepairResult()
.setBeforePayStatus(beforePayStatus)
.setAfterPayStatus(afterPayRefundStatus)
.setBeforeRefundStatus(beforeRefundStatus)
.setAfterRefundStatus(RefundStatusEnum.SUCCESS);
}
/**
* 退款失败, 关闭退款单并将失败的退款金额归还回订单
*/
private RefundRepairResult close(RefundOrder refundOrder, PayOrder payOrder) {
// 要返回的状态
RefundRepairResult repairResult = new RefundRepairResult();
// 订单修复前状态
PayOrderRefundStatusEnum beforePayStatus = PayOrderRefundStatusEnum.findByCode(payOrder.getRefundStatus());
RefundStatusEnum beforeRefundStatus = RefundStatusEnum.findByCode(refundOrder.getStatus());
repairResult.setBeforePayStatus(beforePayStatus)
.setBeforeRefundStatus(beforeRefundStatus);
// 退款失败返还后的余额
int payOrderAmount = refundOrder.getAmount() + payOrder.getRefundableBalance();
// 退款失败返还后的余额+可退余额 == 订单金额 支付订单回退为为支付成功状态
if (payOrderAmount == payOrder.getAmount()){
payOrder.setStatus(PayStatusEnum.SUCCESS.getCode());
repairResult.setAfterPayStatus(PayOrderRefundStatusEnum.NO_REFUND);
} else {
// 回归部分退款状态
payOrder.setRefundStatus(PayOrderRefundStatusEnum.PARTIAL_REFUND.getCode());
repairResult.setAfterPayStatus(PayOrderRefundStatusEnum.PARTIAL_REFUND);
}
// 更新支付订单相关的可退款金额
payOrder.setRefundableBalance(payOrderAmount);
refundOrder.setStatus(RefundStatusEnum.CLOSE.getCode());
// 更新订单和退款相关订单
payOrderService.updateById(payOrder);
refundOrderManager.updateById(refundOrder);
return repairResult;
}
/**
* 支付订单的修复记录
* 支付完成 -> 退款
* 退款 -> 全部退款
* @param repairType 支付订单修复方式状态编码跟退款修复的状态一致,
*/
private PayRepairRecord payRepairRecord(PayOrder order, RefundRepairWayEnum repairType, RefundRepairResult repairResult){
// 修复前的状态
String beforeStatus = Optional.ofNullable(repairResult.getBeforePayStatus())
.map(PayOrderRefundStatusEnum::getCode).orElse(null);
// 修复发起来源
String source = PaymentContextLocal.get()
.getRepairInfo()
.getSource().getCode();
return new PayRepairRecord()
.setTradeId(order.getId())
.setTradeNo(order.getOrderNo())
.setRepairNo(repairResult.getRepairNo())
.setRepairType(TradeTypeEnum.PAY.getCode())
.setRepairSource(source)
.setRepairWay(repairType.getCode())
.setChannel(order.getChannel())
.setBeforeStatus(beforeStatus)
.setAfterStatus(repairResult.getAfterPayStatus().getCode());
}
/**
* 退款订单的修复记录
* 退款中 -> 退款成功
*/
private PayRepairRecord refundRepairRecord(RefundOrder refundOrder, RefundRepairWayEnum repairType, RefundRepairResult repairResult){
// 修复后的状态
String afterStatus = Optional.ofNullable(repairResult.getAfterRefundStatus()).map(RefundStatusEnum::getCode).orElse(null);
// 修复发起来源
String source = PaymentContextLocal.get()
.getRepairInfo()
.getSource().getCode();
return new PayRepairRecord()
.setTradeId(refundOrder.getId())
.setRepairNo(repairResult.getRepairNo())
.setTradeNo(refundOrder.getRefundNo())
.setChannel(refundOrder.getChannel())
.setRepairType(TradeTypeEnum.REFUND.getCode())
.setBeforeStatus(repairResult.getBeforeRefundStatus().getCode())
.setAfterStatus(afterStatus)
.setRepairSource(source)
.setRepairWay(repairType.getCode());
}
}

View File

@@ -29,8 +29,8 @@ public class PayRemoteSyncResult {
*/
private String outOrderNo;
/** 支付完成时间(通常用于接收异步支付返回的时间) */
private LocalDateTime payTime;
/** 支付完成时间 */
private LocalDateTime finishTime;
/** 同步时网关返回的对象, 序列化为json字符串 */
private String syncInfo;

View File

@@ -27,7 +27,7 @@ public class RefundRemoteSyncResult {
private String syncInfo;
/**
* 外部三方支付网关生成的退款交易, 用与将记录关联起来
* 外部三方支付网关生成的退款交易, 用与将记录关联起来
*/
private String outRefundNo;

View File

@@ -9,21 +9,21 @@ import cn.daxpay.single.core.code.PaySyncStatusEnum;
import cn.daxpay.single.core.exception.*;
import cn.daxpay.single.core.param.payment.pay.PaySyncParam;
import cn.daxpay.single.core.result.sync.PaySyncResult;
import cn.daxpay.single.service.code.TradeAdjustSourceEnum;
import cn.daxpay.single.service.code.PayAdjustWayEnum;
import cn.daxpay.single.service.code.TradeAdjustSourceEnum;
import cn.daxpay.single.service.code.TradeTypeEnum;
import cn.daxpay.single.service.common.context.AdjustLocal;
import cn.daxpay.single.service.common.local.PaymentContextLocal;
import cn.daxpay.single.service.core.order.pay.entity.PayOrder;
import cn.daxpay.single.service.core.order.pay.service.PayOrderQueryService;
import cn.daxpay.single.service.core.order.pay.service.PayOrderService;
import cn.daxpay.single.service.core.payment.repair.result.PayRepairResult;
import cn.daxpay.single.service.core.payment.repair.service.PayRepairService;
import cn.daxpay.single.service.core.payment.adjust.param.PayAdjustParam;
import cn.daxpay.single.service.core.payment.adjust.service.PayAdjustService;
import cn.daxpay.single.service.core.payment.sync.result.PayRemoteSyncResult;
import cn.daxpay.single.service.core.record.sync.entity.PaySyncRecord;
import cn.daxpay.single.service.core.record.sync.service.PaySyncRecordService;
import cn.daxpay.single.service.core.record.sync.entity.TradeSyncRecord;
import cn.daxpay.single.service.core.record.sync.service.TradeSyncRecordService;
import cn.daxpay.single.service.func.AbsPaySyncStrategy;
import cn.daxpay.single.service.util.PayStrategyFactory;
import cn.hutool.core.util.StrUtil;
import com.baomidou.lock.LockInfo;
import com.baomidou.lock.LockTemplate;
import lombok.RequiredArgsConstructor;
@@ -52,9 +52,9 @@ public class PaySyncService {
private final PayOrderService payOrderService;
private final PaySyncRecordService paySyncRecordService;
private final TradeSyncRecordService tradeSyncRecordService;
private final PayRepairService repairService;
private final PayAdjustService payAdjustService;
private final LockTemplate lockTemplate;
@@ -93,9 +93,9 @@ public class PaySyncService {
// 执行操作, 获取支付网关同步的结果
PayRemoteSyncResult payRemoteSyncResult = syncPayStrategy.doSyncStatus();
// 判断是否同步成功
if (Objects.equals(payRemoteSyncResult.getSyncStatus(), PaySyncStatusEnum.FAIL)){
if (Objects.equals(payRemoteSyncResult.getSyncStatus(), FAIL)){
// 同步失败, 返回失败响应, 同时记录失败的日志
this.saveRecord(payOrder, payRemoteSyncResult, false, null, payRemoteSyncResult.getErrorMsg());
this.saveRecord(payOrder, payRemoteSyncResult, null);
throw new OperationFailException(payRemoteSyncResult.getErrorMsg());
}
// 支付订单的网关订单号是否一致, 不一致进行更新
@@ -105,28 +105,20 @@ public class PaySyncService {
}
// 判断网关状态是否和支付单一致, 同时特定情况下更新网关同步状态
boolean statusSync = this.checkAndAdjustSyncStatus(payRemoteSyncResult,payOrder);
PayRepairResult repairResult = new PayRepairResult();
String adjustNo = null;
try {
// 状态不一致,执行支付单修复逻辑
if (!statusSync){
// 如果没有修复触发来源, 设置修复触发来源为同步
AdjustLocal repairInfo = PaymentContextLocal.get().getRepairInfo();
if (Objects.isNull(repairInfo.getSource())){
repairInfo.setSource(TradeAdjustSourceEnum.SYNC);
}
// 设置支付单完成时间
repairInfo.setFinishTime(payRemoteSyncResult.getPayTime());
repairResult = this.repairHandler(payRemoteSyncResult, payOrder);
adjustNo = this.repairHandler(payRemoteSyncResult, payOrder);
}
} catch (PayFailureException e) {
// 同步失败, 返回失败响应, 同时记录失败的日志
payRemoteSyncResult.setSyncStatus(PaySyncStatusEnum.FAIL);
this.saveRecord(payOrder, payRemoteSyncResult, false, null, e.getMessage());
payRemoteSyncResult.setSyncStatus(FAIL);
this.saveRecord(payOrder, payRemoteSyncResult, null);
throw e;
}
// 同步成功记录日志
this.saveRecord( payOrder, payRemoteSyncResult, !statusSync, repairResult.getRepairNo(), null);
this.saveRecord( payOrder, payRemoteSyncResult, adjustNo);
return new PaySyncResult().setStatus(payRemoteSyncResult.getSyncStatus().getCode());
} finally {
lockTemplate.releaseLock(lock);
@@ -153,7 +145,7 @@ public class PaySyncService {
// 判断支付单是否支付超时, 如果待支付状态下触发超时
if (LocalDateTimeUtil.le(order.getExpiredTime(), LocalDateTime.now())){
// 将支付单同步状态状态调整为支付超时, 进行订单的关闭
payRemoteSyncResult.setSyncStatus(PaySyncStatusEnum.TIMEOUT);
payRemoteSyncResult.setSyncStatus(TIMEOUT);
return false;
}
return true;
@@ -171,7 +163,7 @@ public class PaySyncService {
// 退款状态不做额外处理, 需要通过退款接口进行处理
if (!Objects.equals(order.getRefundStatus(),PayOrderRefundStatusEnum.NO_REFUND.getCode())
|| syncStatus.equals(PaySyncStatusEnum.REFUND)){
|| syncStatus.equals(REFUND)){
return true;
}
return false;
@@ -179,30 +171,34 @@ public class PaySyncService {
/**
* 根据同步的结果对支付单进行修复处理
* @return 调整单号, 如果为空, 说明订单未做调整
*/
private PayRepairResult repairHandler(PayRemoteSyncResult payRemoteSyncResult, PayOrder payOrder){
private String repairHandler(PayRemoteSyncResult payRemoteSyncResult, PayOrder payOrder){
PaySyncStatusEnum syncStatusEnum = payRemoteSyncResult.getSyncStatus();
PayRepairResult repair = new PayRepairResult();
PayAdjustParam param = new PayAdjustParam()
.setOrder(payOrder)
.setSource(TradeAdjustSourceEnum.SYNC)
.setFinishTime(payRemoteSyncResult.getFinishTime());
// 对支付网关同步的结果进行处理
switch (syncStatusEnum) {
// 支付成功 支付宝退款时也是支付成功状态, 除非支付完成
case SUCCESS: {
repair = repairService.repair(payOrder, PayAdjustWayEnum.SUCCESS);
break;
param.setAdjustWay(PayAdjustWayEnum.SUCCESS);
return payAdjustService.adjust(param);
}
case REFUND:
throw new TradeStatusErrorException("支付订单为退款状态,请通过执行对应的退款订单进行同步,来更新具体为什么类型退款状态");
// 交易关闭和未找到, 都对本地支付订单进行关闭, 不需要再调用网关进行关闭
case CLOSED:
case NOT_FOUND: {
repair = repairService.repair(payOrder, PayAdjustWayEnum.CLOSE_LOCAL);
break;
param.setAdjustWay(PayAdjustWayEnum.CLOSE_LOCAL);
return payAdjustService.adjust(param);
}
// 超时关闭和交易不存在(特殊) 关闭本地支付订单, 同时调用网关进行关闭, 确保后续这个订单不能被支付
case TIMEOUT:
case NOT_FOUND_UNKNOWN:{
repair = repairService.repair(payOrder, PayAdjustWayEnum.CLOSE_GATEWAY);
break;
param.setAdjustWay(PayAdjustWayEnum.CLOSE_GATEWAY);
return payAdjustService.adjust(param);
}
// 调用出错
case FAIL: {
@@ -214,7 +210,7 @@ public class PaySyncService {
throw new SystemUnknownErrorException("代码有问题");
}
}
return repair;
return null;
}
@@ -222,24 +218,23 @@ public class PaySyncService {
* 保存同步记录
* @param payOrder 支付单
* @param payRemoteSyncResult 同步结果
* @param repair 是否修复
* @param repairOrderNo 修复号
* @param errorMsg 错误信息
*/
private void saveRecord(PayOrder payOrder, PayRemoteSyncResult payRemoteSyncResult, boolean repair, String repairOrderNo, String errorMsg){
PaySyncRecord paySyncRecord = new PaySyncRecord()
private void saveRecord(PayOrder payOrder, PayRemoteSyncResult payRemoteSyncResult, String repairOrderNo){
TradeSyncRecord tradeSyncRecord = new TradeSyncRecord()
.setBizTradeNo(payOrder.getBizOrderNo())
.setTradeNo(payOrder.getOrderNo())
.setOutTradeNo(payOrder.getOutOrderNo())
.setOutTradeStatus(payRemoteSyncResult.getSyncStatus().getCode())
.setSyncType(TradeTypeEnum.PAY.getCode())
.setType(TradeTypeEnum.PAY.getCode())
.setChannel(payOrder.getChannel())
.setSyncInfo(payRemoteSyncResult.getSyncInfo())
.setRepair(repair)
.setRepairNo(repairOrderNo)
.setErrorMsg(errorMsg)
.setAdjust(StrUtil.isNotBlank(repairOrderNo))
.setAdjustNo(repairOrderNo)
.setErrorCode(payRemoteSyncResult.getErrorCode())
.setErrorMsg(payRemoteSyncResult.getErrorMsg())
.setClientIp(PaymentContextLocal.get().getClientInfo().getClientIp());
paySyncRecordService.saveRecord(paySyncRecord);
tradeSyncRecordService.saveRecord(tradeSyncRecord);
}
}

View File

@@ -9,21 +9,21 @@ import cn.daxpay.single.core.exception.PayFailureException;
import cn.daxpay.single.core.exception.TradeNotExistException;
import cn.daxpay.single.core.param.payment.refund.RefundSyncParam;
import cn.daxpay.single.core.result.sync.RefundSyncResult;
import cn.daxpay.single.service.code.RefundAdjustWayEnum;
import cn.daxpay.single.service.code.TradeAdjustSourceEnum;
import cn.daxpay.single.service.code.TradeTypeEnum;
import cn.daxpay.single.service.code.RefundRepairWayEnum;
import cn.daxpay.single.service.common.context.AdjustLocal;
import cn.daxpay.single.service.common.local.PaymentContextLocal;
import cn.daxpay.single.service.core.order.refund.dao.RefundOrderManager;
import cn.daxpay.single.service.core.order.refund.entity.RefundOrder;
import cn.daxpay.single.service.core.order.refund.service.RefundOrderQueryService;
import cn.daxpay.single.service.core.payment.repair.result.RefundRepairResult;
import cn.daxpay.single.service.core.payment.repair.service.RefundRepairService;
import cn.daxpay.single.service.core.payment.adjust.param.RefundAdjustParam;
import cn.daxpay.single.service.core.payment.adjust.service.RefundAdjustService;
import cn.daxpay.single.service.core.payment.sync.result.RefundRemoteSyncResult;
import cn.daxpay.single.service.core.record.sync.entity.PaySyncRecord;
import cn.daxpay.single.service.core.record.sync.service.PaySyncRecordService;
import cn.daxpay.single.service.core.record.sync.entity.TradeSyncRecord;
import cn.daxpay.single.service.core.record.sync.service.TradeSyncRecordService;
import cn.daxpay.single.service.func.AbsRefundSyncStrategy;
import cn.daxpay.single.service.util.PayStrategyFactory;
import cn.hutool.core.util.StrUtil;
import com.baomidou.lock.LockInfo;
import com.baomidou.lock.LockTemplate;
import lombok.RequiredArgsConstructor;
@@ -47,9 +47,9 @@ public class RefundSyncService {
private final RefundOrderQueryService refundOrderQueryService;
private final PaySyncRecordService paySyncRecordService;
private final TradeSyncRecordService tradeSyncRecordService;
private final RefundRepairService repairService;
private final RefundAdjustService repairService;
private final LockTemplate lockTemplate;
@@ -90,7 +90,7 @@ public class RefundSyncService {
// 判断是否同步成功
if (Objects.equals(refundRemoteSyncResult.getSyncStatus(), RefundSyncStatusEnum.FAIL)) {
// 同步失败, 返回失败响应, 同时记录失败的日志
this.saveRecord(refundOrder, refundRemoteSyncResult, false, null, refundRemoteSyncResult.getErrorMsg());
this.saveRecord(refundOrder, refundRemoteSyncResult, null);
throw new OperationFailException(refundRemoteSyncResult.getErrorMsg());
}
// 订单的通道交易号是否一致, 不一致进行更新
@@ -100,26 +100,21 @@ public class RefundSyncService {
}
// 判断网关状态是否和支付单一致
boolean statusSync = this.checkSyncStatus(refundRemoteSyncResult, refundOrder);
RefundRepairResult repairResult = new RefundRepairResult();
String adjustNo = null;
try {
// 状态不一致,执行退款单修复逻辑
if (!statusSync) {
// 如果没有支付来源, 设置支付来源为同步
AdjustLocal repairInfo = PaymentContextLocal.get().getRepairInfo();
if (Objects.isNull(repairInfo.getSource())){
repairInfo.setSource(TradeAdjustSourceEnum.SYNC);
}
repairInfo.setFinishTime(refundRemoteSyncResult.getFinishTime());
repairResult = this.repairHandler(refundRemoteSyncResult, refundOrder);
adjustNo = this.repairHandler(refundRemoteSyncResult, refundOrder);
}
} catch (PayFailureException e) {
// 同步失败, 返回失败响应, 同时记录失败的日志
refundRemoteSyncResult.setSyncStatus(RefundSyncStatusEnum.FAIL);
this.saveRecord(refundOrder, refundRemoteSyncResult, false, null, e.getMessage());
this.saveRecord(refundOrder, refundRemoteSyncResult, null);
throw e;
}
// 同步成功记录日志
this.saveRecord(refundOrder, refundRemoteSyncResult, !statusSync, repairResult.getRepairNo(), null);
this.saveRecord(refundOrder, refundRemoteSyncResult, adjustNo);
return new RefundSyncResult().setStatus(refundRemoteSyncResult.getSyncStatus().getCode());
} finally {
lockTemplate.releaseLock(lock);
@@ -155,55 +150,59 @@ public class RefundSyncService {
}
/**
* 进行退款订单和支付订单的补偿
* 进行退款订单和支付订单的调整
*/
private RefundRepairResult repairHandler(RefundRemoteSyncResult syncResult, RefundOrder order){
private String repairHandler(RefundRemoteSyncResult syncResult, RefundOrder order){
RefundSyncStatusEnum syncStatusEnum = syncResult.getSyncStatus();
RefundRepairResult repair = new RefundRepairResult();
RefundAdjustParam param = new RefundAdjustParam()
.setOrder(order)
.setOutTradeNo(syncResult.getOutRefundNo())
.setSource(TradeAdjustSourceEnum.SYNC)
.setFinishTime(syncResult.getFinishTime());
// 对支付网关同步的结果进行处理
switch (syncStatusEnum) {
case SUCCESS:
repair = repairService.repair(order, RefundRepairWayEnum.REFUND_SUCCESS);
break;
param.setAdjustWay(RefundAdjustWayEnum.SUCCESS);
return repairService.adjust(param);
case PROGRESS:
// 不进行处理
break;
case FAIL: {
repair = repairService.repair(order, RefundRepairWayEnum.REFUND_FAIL);
break;
param.setAdjustWay(RefundAdjustWayEnum.FAIL);
return repairService.adjust(param);
}
case NOT_FOUND:
repair = repairService.repair(order, RefundRepairWayEnum.REFUND_FAIL);
break;
param.setAdjustWay(RefundAdjustWayEnum.FAIL);
return repairService.adjust(param);
default: {
throw new BizException("代码有问题");
}
}
return repair;
return null;
}
/**
* 保存同步记录, 使用新事务进行保存
* @param refundOrder 支付单
* @param syncResult 同步结果
* @param repair 是否修复
* @param repairOrderNo 修复号
* @param errorMsg 错误信息
*/
private void saveRecord(RefundOrder refundOrder, RefundRemoteSyncResult syncResult, boolean repair, String repairOrderNo, String errorMsg){
PaySyncRecord paySyncRecord = new PaySyncRecord()
private void saveRecord(RefundOrder refundOrder, RefundRemoteSyncResult syncResult, String repairOrderNo){
TradeSyncRecord tradeSyncRecord = new TradeSyncRecord()
.setTradeNo(refundOrder.getRefundNo())
.setBizTradeNo(refundOrder.getBizRefundNo())
.setOutTradeNo(syncResult.getOutRefundNo())
.setOutTradeStatus(syncResult.getSyncStatus().getCode())
.setSyncType(TradeTypeEnum.REFUND.getCode())
.setType(TradeTypeEnum.REFUND.getCode())
.setChannel(refundOrder.getChannel())
.setSyncInfo(syncResult.getSyncInfo())
.setRepair(repair)
.setRepairNo(repairOrderNo)
.setErrorMsg(errorMsg)
.setAdjust(StrUtil.isNotBlank(repairOrderNo))
.setAdjustNo(repairOrderNo)
.setErrorCode(syncResult.getErrorCode())
.setErrorMsg(syncResult.getErrorMsg())
.setClientIp(PaymentContextLocal.get().getClientInfo().getClientIp());
paySyncRecordService.saveRecord(paySyncRecord);
tradeSyncRecordService.saveRecord(tradeSyncRecord);
}
}

View File

@@ -54,7 +54,7 @@ public class PayCallbackRecordService {
.setChannel(callbackInfo.getChannel())
.setNotifyInfo(JSONUtil.toJsonStr(callbackInfo.getCallbackParam()))
.setCallbackType(callbackInfo.getCallbackType().getCode())
.setRepairOrderNo(callbackInfo.getRepairNo())
.setRepairOrderNo(callbackInfo.getAdjustNo())
.setStatus(callbackInfo.getCallbackStatus().getCode())
.setErrorMsg(callbackInfo.getErrorMsg());
callbackRecordManager.save(payNotifyRecord);

View File

@@ -1,18 +0,0 @@
package cn.daxpay.single.service.core.record.repair.convert;
import cn.daxpay.single.service.core.record.repair.entity.PayRepairRecord;
import cn.daxpay.single.service.dto.record.repair.PayRepairRecordDto;
import org.mapstruct.Mapper;
import org.mapstruct.factory.Mappers;
/**
*
* @author xxm
* @since 2024/1/6
*/
@Mapper
public interface PayRepairRecordConvert {
PayRepairRecordConvert CONVERT = Mappers.getMapper(PayRepairRecordConvert.class);
PayRepairRecordDto convert(PayRepairRecord in);
}

View File

@@ -1,33 +0,0 @@
package cn.daxpay.single.service.core.record.repair.dao;
import cn.bootx.platform.common.core.rest.param.PageParam;
import cn.bootx.platform.common.mybatisplus.impl.BaseManager;
import cn.bootx.platform.common.mybatisplus.util.MpUtil;
import cn.bootx.platform.common.query.generator.QueryGenerator;
import cn.daxpay.single.service.core.record.repair.entity.PayRepairRecord;
import cn.daxpay.single.service.param.record.PayRepairRecordQuery;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Repository;
/**
*
* @author xxm
* @since 2024/1/6
*/
@Slf4j
@Repository
@RequiredArgsConstructor
public class PayRepairRecordManager extends BaseManager<PayRepairRecordMapper, PayRepairRecord> {
/**
* 分页
*/
public Page<PayRepairRecord> page(PageParam pageParam, PayRepairRecordQuery query){
Page<PayRepairRecord> mpPage = MpUtil.getMpPage(pageParam, PayRepairRecord.class);
QueryWrapper<PayRepairRecord> generator = QueryGenerator.generator(query);
return page(mpPage, generator);
}
}

View File

@@ -1,14 +0,0 @@
package cn.daxpay.single.service.core.record.repair.dao;
import cn.daxpay.single.service.core.record.repair.entity.PayRepairRecord;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Mapper;
/**
*
* @author xxm
* @since 2024/1/6
*/
@Mapper
public interface PayRepairRecordMapper extends BaseMapper<PayRepairRecord> {
}

View File

@@ -1,98 +0,0 @@
package cn.daxpay.single.service.core.record.repair.entity;
import cn.bootx.platform.common.core.function.EntityBaseFunction;
import cn.bootx.platform.common.mybatisplus.base.MpCreateEntity;
import cn.bootx.table.modify.annotation.DbColumn;
import cn.bootx.table.modify.annotation.DbTable;
import cn.bootx.table.modify.mysql.annotation.DbMySqlIndex;
import cn.daxpay.single.core.code.PayStatusEnum;
import cn.daxpay.single.service.code.TradeAdjustSourceEnum;
import cn.daxpay.single.service.code.PayAdjustWayEnum;
import cn.daxpay.single.service.code.TradeTypeEnum;
import cn.daxpay.single.service.code.RefundRepairWayEnum;
import cn.daxpay.single.service.core.record.repair.convert.PayRepairRecordConvert;
import cn.daxpay.single.service.dto.record.repair.PayRepairRecordDto;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
/**
* 调整
* 支付修复记录 包括支付修复记录和退款修复记录
* @author xxm
* @since 2024/1/6
*/
@EqualsAndHashCode(callSuper = true)
@Data
@Accessors(chain = true)
@TableName("pay_repair_record")
@DbTable(comment = "支付修复记录")
public class PayRepairRecord extends MpCreateEntity implements EntityBaseFunction<PayRepairRecordDto> {
/**
* 修复号
* 如果一次修复产生的修复记录有多个记录, 使用这个作为关联
*/
@DbColumn(comment = "修复号", length = 32, isNull = false)
private String repairNo;
/** 支付ID/退款ID */
@DbColumn(comment = "本地订单ID", isNull = false)
private Long tradeId;
/**
* 本地交易号, 支付号/退款号
*/
@DbMySqlIndex(comment = "本地交易号索引")
@DbColumn(comment = "本地交易号", length = 32, isNull = false)
private String tradeNo;
/**
* 修复类型 支付修复/退款修复
* @see TradeTypeEnum
*/
@DbColumn(comment = "修复类型", length = 20, isNull = false)
private String repairType;
/**
* 修复来源
* @see TradeAdjustSourceEnum
*/
@DbColumn(comment = "修复来源", length = 20, isNull = false)
private String repairSource;
/**
* 修复方式
* @see PayAdjustWayEnum
* @see RefundRepairWayEnum
*/
@DbColumn(comment = "修复方式", length = 20, isNull = false)
private String repairWay;
/** 修复的通道 */
@DbColumn(comment = "修复的通道", length = 20, isNull = false)
private String channel;
/**
* 修复前状态
* @see PayStatusEnum
*/
@DbColumn(comment = "修复前状态", length = 20, isNull = false)
private String beforeStatus;
/**
* 修复后状态
* @see PayStatusEnum
*/
@DbColumn(comment = "修复后状态", length = 20, isNull = false)
private String afterStatus;
/**
* 转换
*/
@Override
public PayRepairRecordDto toDto() {
return PayRepairRecordConvert.CONVERT.convert(this);
}
}

View File

@@ -26,10 +26,9 @@ public class TradeAdjustRecord extends MpCreateEntity implements EntityBaseFunct
/**
* 调整号
* 如果记录有多个, 使用这个作为关联
*/
@DbColumn(comment = "修复", length = 32, isNull = false)
private String repairNo;
@DbColumn(comment = "调整", length = 32, isNull = false)
private String adjustNo;
/** 交易ID */
@DbColumn(comment = "本地订单ID", isNull = false)

View File

@@ -1,58 +0,0 @@
package cn.daxpay.single.service.core.record.repair.service;
import cn.bootx.platform.common.core.exception.DataNotExistException;
import cn.bootx.platform.common.core.rest.PageResult;
import cn.bootx.platform.common.core.rest.param.PageParam;
import cn.bootx.platform.common.mybatisplus.util.MpUtil;
import cn.daxpay.single.service.core.record.repair.dao.PayRepairRecordManager;
import cn.daxpay.single.service.core.record.repair.entity.PayRepairRecord;
import cn.daxpay.single.service.dto.record.repair.PayRepairRecordDto;
import cn.daxpay.single.service.param.record.PayRepairRecordQuery;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import java.util.List;
/**
*
* @author xxm
* @since 2024/1/6
*/
@Slf4j
@Service
@RequiredArgsConstructor
public class PayRepairRecordService {
private final PayRepairRecordManager repairRecordManager;
/**
* 根据id查询
*/
public PayRepairRecordDto findById(Long id) {
return repairRecordManager.findById(id).map(PayRepairRecord::toDto).orElseThrow(DataNotExistException::new);
}
/**
* 分页查询
*/
public PageResult<PayRepairRecordDto> page(PageParam pageParam, PayRepairRecordQuery param){
return MpUtil.convert2DtoPageResult(repairRecordManager.page(pageParam,param));
}
/**
* 保存记录
*/
@Transactional(propagation = Propagation.REQUIRES_NEW, rollbackFor = Exception.class)
public void saveRecord(PayRepairRecord record){
repairRecordManager.save(record);
}
/**
* 保存记录
*/
@Transactional(propagation = Propagation.REQUIRES_NEW, rollbackFor = Exception.class)
public void saveAllRecord(List<PayRepairRecord> records){
repairRecordManager.saveAll(records);
}
}

View File

@@ -17,7 +17,7 @@ import org.springframework.transaction.annotation.Transactional;
import java.util.List;
/**
*
* 交易调整记录
* @author xxm
* @since 2024/7/15
*/

View File

@@ -1,19 +0,0 @@
package cn.daxpay.single.service.core.record.sync.convert;
import cn.daxpay.single.service.core.record.sync.entity.PaySyncRecord;
import cn.daxpay.single.service.dto.record.sync.PaySyncRecordDto;
import org.mapstruct.Mapper;
import org.mapstruct.factory.Mappers;
/**
* 支付同步记录同步
* @author xxm
* @since 2023/7/14
*/
@Mapper
public interface PaySyncRecordConvert {
PaySyncRecordConvert CONVERT = Mappers.getMapper(PaySyncRecordConvert.class);
PaySyncRecordDto convert(PaySyncRecord in);
}

View File

@@ -0,0 +1,19 @@
package cn.daxpay.single.service.core.record.sync.convert;
import cn.daxpay.single.service.core.record.sync.entity.TradeSyncRecord;
import cn.daxpay.single.service.dto.record.sync.SyncRecordDto;
import org.mapstruct.Mapper;
import org.mapstruct.factory.Mappers;
/**
* 支付同步记录同步
* @author xxm
* @since 2023/7/14
*/
@Mapper
public interface TradeSyncRecordConvert {
TradeSyncRecordConvert CONVERT = Mappers.getMapper(TradeSyncRecordConvert.class);
SyncRecordDto convert(TradeSyncRecord in);
}

View File

@@ -4,8 +4,8 @@ import cn.bootx.platform.common.core.rest.param.PageParam;
import cn.bootx.platform.common.mybatisplus.impl.BaseManager;
import cn.bootx.platform.common.mybatisplus.util.MpUtil;
import cn.bootx.platform.common.query.generator.QueryGenerator;
import cn.daxpay.single.service.core.record.sync.entity.PaySyncRecord;
import cn.daxpay.single.service.param.record.PaySyncRecordQuery;
import cn.daxpay.single.service.core.record.sync.entity.TradeSyncRecord;
import cn.daxpay.single.service.param.record.TradeSyncRecordQuery;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import lombok.RequiredArgsConstructor;
@@ -20,14 +20,14 @@ import org.springframework.stereotype.Repository;
@Slf4j
@Repository
@RequiredArgsConstructor
public class PaySyncRecordManager extends BaseManager<PaySyncRecordMapper, PaySyncRecord> {
public class TradeSyncRecordManager extends BaseManager<TradeSyncRecordMapper, TradeSyncRecord> {
/**
* 分页
*/
public Page<PaySyncRecord> page(PageParam pageParam, PaySyncRecordQuery query) {
Page<PaySyncRecord> mpPage = MpUtil.getMpPage(pageParam, PaySyncRecord.class);
QueryWrapper<PaySyncRecord> generator = QueryGenerator.generator(query);
public Page<TradeSyncRecord> page(PageParam pageParam, TradeSyncRecordQuery query) {
Page<TradeSyncRecord> mpPage = MpUtil.getMpPage(pageParam, TradeSyncRecord.class);
QueryWrapper<TradeSyncRecord> generator = QueryGenerator.generator(query);
return page(mpPage, generator);
}

View File

@@ -1,6 +1,6 @@
package cn.daxpay.single.service.core.record.sync.dao;
import cn.daxpay.single.service.core.record.sync.entity.PaySyncRecord;
import cn.daxpay.single.service.core.record.sync.entity.TradeSyncRecord;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Mapper;
@@ -10,5 +10,5 @@ import org.apache.ibatis.annotations.Mapper;
* @since 2023/7/14
*/
@Mapper
public interface PaySyncRecordMapper extends BaseMapper<PaySyncRecord> {
public interface TradeSyncRecordMapper extends BaseMapper<TradeSyncRecord> {
}

View File

@@ -11,24 +11,24 @@ import cn.daxpay.single.core.code.PayChannelEnum;
import cn.daxpay.single.core.code.PaySyncStatusEnum;
import cn.daxpay.single.core.code.RefundSyncStatusEnum;
import cn.daxpay.single.service.code.TradeTypeEnum;
import cn.daxpay.single.service.core.record.sync.convert.PaySyncRecordConvert;
import cn.daxpay.single.service.dto.record.sync.PaySyncRecordDto;
import cn.daxpay.single.service.core.record.sync.convert.TradeSyncRecordConvert;
import cn.daxpay.single.service.dto.record.sync.SyncRecordDto;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
/**
* 支付同步订单
* 交易同步记录
* @author xxm
* @since 2023/7/14
*/
@EqualsAndHashCode(callSuper = true)
@Data
@DbTable(comment = "支付同步订单")
@DbTable(comment = "交易同步记录")
@Accessors(chain = true)
@TableName("pay_sync_record")
public class PaySyncRecord extends MpCreateEntity implements EntityBaseFunction<PaySyncRecordDto> {
@TableName("pay_trade_sync_record")
public class TradeSyncRecord extends MpCreateEntity implements EntityBaseFunction<SyncRecordDto> {
/** 本地交易号 */
@DbMySqlIndex(comment = "本地交易号索引")
@@ -58,13 +58,13 @@ public class PaySyncRecord extends MpCreateEntity implements EntityBaseFunction<
* @see TradeTypeEnum
*/
@DbColumn(comment = "同步类型", length = 20, isNull = false)
private String syncType;
private String type;
/**
* 同步的异步通道
* 同步通道
* @see PayChannelEnum#getCode()
*/
@DbColumn(comment = "同步的异步通道", length = 20, isNull = false)
@DbColumn(comment = "同步通道", length = 20, isNull = false)
private String channel;
/** 网关返回的同步消息 */
@@ -73,14 +73,14 @@ public class PaySyncRecord extends MpCreateEntity implements EntityBaseFunction<
private String syncInfo;
/**
* 支付单如果状态不一致, 是否进行修复
* 支付单如果状态不一致, 是否进行调整
*/
@DbColumn(comment = "是否进行修复", isNull = false)
private boolean repair;
@DbColumn(comment = "是否进行调整", isNull = false)
private boolean adjust;
/** 修复单号 */
@DbColumn(comment = "修复单", length = 32)
private String repairNo;
@DbColumn(comment = "调整记录", length = 32)
private String adjustNo;
/** 错误码 */
@DbColumn(comment = "错误码", length = 10)
@@ -98,7 +98,7 @@ public class PaySyncRecord extends MpCreateEntity implements EntityBaseFunction<
* 转换
*/
@Override
public PaySyncRecordDto toDto() {
return PaySyncRecordConvert.CONVERT.convert(this);
public SyncRecordDto toDto() {
return TradeSyncRecordConvert.CONVERT.convert(this);
}
}

View File

@@ -4,10 +4,10 @@ import cn.bootx.platform.common.core.exception.DataNotExistException;
import cn.bootx.platform.common.core.rest.PageResult;
import cn.bootx.platform.common.core.rest.param.PageParam;
import cn.bootx.platform.common.mybatisplus.util.MpUtil;
import cn.daxpay.single.service.core.record.sync.dao.PaySyncRecordManager;
import cn.daxpay.single.service.core.record.sync.entity.PaySyncRecord;
import cn.daxpay.single.service.dto.record.sync.PaySyncRecordDto;
import cn.daxpay.single.service.param.record.PaySyncRecordQuery;
import cn.daxpay.single.service.core.record.sync.dao.TradeSyncRecordManager;
import cn.daxpay.single.service.core.record.sync.entity.TradeSyncRecord;
import cn.daxpay.single.service.dto.record.sync.SyncRecordDto;
import cn.daxpay.single.service.param.record.TradeSyncRecordQuery;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
@@ -23,30 +23,30 @@ import org.springframework.transaction.annotation.Transactional;
@Slf4j
@Service
@RequiredArgsConstructor
public class PaySyncRecordService {
private final PaySyncRecordManager orderManager;
public class TradeSyncRecordService {
private final TradeSyncRecordManager orderManager;
/**
* 分页查询
*/
public PageResult<PaySyncRecordDto> page(PageParam pageParam, PaySyncRecordQuery query) {
Page<PaySyncRecord> page = orderManager.page(pageParam, query);
public PageResult<SyncRecordDto> page(PageParam pageParam, TradeSyncRecordQuery query) {
Page<TradeSyncRecord> page = orderManager.page(pageParam, query);
return MpUtil.convert2DtoPageResult(page);
}
/**
* 根据id查询
*/
public PaySyncRecordDto findById(Long id) {
return orderManager.findById(id).map(PaySyncRecord::toDto).orElseThrow(DataNotExistException::new);
public SyncRecordDto findById(Long id) {
return orderManager.findById(id).map(TradeSyncRecord::toDto).orElseThrow(DataNotExistException::new);
}
/**
* 记录同步记录 同步支付单的不进行记录
*/
@Transactional(propagation = Propagation.REQUIRES_NEW, rollbackFor = Exception.class)
public void saveRecord(PaySyncRecord paySyncRecord){
orderManager.save(paySyncRecord);
public void saveRecord(TradeSyncRecord tradeSyncRecord){
orderManager.save(tradeSyncRecord);
}
}

View File

@@ -1,81 +0,0 @@
package cn.daxpay.single.service.dto.record.repair;
import cn.bootx.platform.common.core.rest.dto.BaseDto;
import cn.daxpay.single.core.code.PayStatusEnum;
import cn.daxpay.single.service.code.TradeAdjustSourceEnum;
import cn.daxpay.single.service.code.PayAdjustWayEnum;
import cn.daxpay.single.service.code.TradeTypeEnum;
import cn.daxpay.single.service.code.RefundRepairWayEnum;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
/**
* 支付修复记录
* @author xxm
* @since 2024/1/9
*/
@EqualsAndHashCode(callSuper = true)
@Data
@Accessors(chain = true)
@Schema(title = "支付修复记录")
public class PayRepairRecordDto extends BaseDto {
/**
* 修复号
* 如果一次修复产生的修复记录有多个记录, 使用这个作为关联
*/
@Schema(description = "修复号")
private String repairNo;
/** 支付ID/退款ID */
@Schema(description = "本地订单ID")
private Long tradeId;
/**
* 本地交易号, 支付号/退款号
*/
@Schema(description = "本地业务号")
private String tradeNo;
/**
* 修复方式
* @see PayAdjustWayEnum
* @see RefundRepairWayEnum
*/
@Schema(description = "修复方式")
private String repairWay;
/**
* 修复类型 支付修复/退款修复
* @see TradeTypeEnum
*/
@Schema(description = "修复类型")
private String repairType;
/**
* 修复来源
* @see TradeAdjustSourceEnum
*/
@Schema(description = "修复来源")
private String repairSource;
/** 修复的异步通道 */
@Schema(description = "修复的异步通道")
private String channel;
/**
* 修复前状态
* @see PayStatusEnum
*/
@Schema(description = "修复前状态")
private String beforeStatus;
/**
* 修复后状态
* @see PayStatusEnum
*/
@Schema(description = "修复后状态")
private String afterStatus;
}

View File

@@ -21,7 +21,7 @@ import lombok.experimental.Accessors;
@Data
@Accessors(chain = true)
@Schema(title = "支付同步订单")
public class PaySyncRecordDto extends BaseDto {
public class SyncRecordDto extends BaseDto {
/** 本地交易号 */
@Schema(description = "本地订单ID")

View File

@@ -1,77 +0,0 @@
package cn.daxpay.single.service.param.record;
import cn.bootx.platform.common.core.annotation.QueryParam;
import cn.daxpay.single.core.code.PayStatusEnum;
import cn.daxpay.single.service.code.TradeAdjustSourceEnum;
import cn.daxpay.single.service.code.PayAdjustWayEnum;
import cn.daxpay.single.service.code.TradeTypeEnum;
import cn.daxpay.single.service.code.RefundRepairWayEnum;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.experimental.Accessors;
/**
* 支付修复记录查询参数
* @author xxm
* @since 2024/1/9
*/
@QueryParam(type = QueryParam.CompareTypeEnum.EQ)
@Data
@Accessors(chain = true)
@Schema(title = "支付修复记录查询参数")
public class PayRepairRecordQuery {
/**
* 修复号
* 如果一次修复产生的修复记录有多个记录, 使用这个作为关联
*/
@Schema(description = "修复号")
private String repairNo;
/**
* 本地交易号, 支付号/退款号
*/
@Schema(description = "本地业务号")
private String tradeNo;
/**
* 修复类型 支付修复/退款修复
* @see TradeTypeEnum
*/
@Schema(description = "修复类型")
private String repairType;
/**
* 修复来源
* @see TradeAdjustSourceEnum
*/
@Schema(description = "修复来源")
private String repairSource;
/**
* 修复方式
* @see PayAdjustWayEnum
* @see RefundRepairWayEnum
*/
@Schema(description = "修复方式")
private String repairWay;
/** 修复的异步通道 */
@Schema(description = "修复的通道")
private String channel;
/**
* 修复前状态
* @see PayStatusEnum
*/
@Schema(description = "修复前状态")
private String beforeStatus;
/**
* 修复后状态
* @see PayStatusEnum
*/
@Schema(description = "修复后状态")
private String afterStatus;
}

View File

@@ -12,15 +12,15 @@ import lombok.Data;
import lombok.experimental.Accessors;
/**
* 支付同步记录查询参数
* 交易同步记录查询参数
* @author xxm
* @since 2024/1/9
*/
@QueryParam(type = QueryParam.CompareTypeEnum.EQ)
@Data
@Accessors(chain = true)
@Schema(title = "支付同步记录查询参数")
public class PaySyncRecordQuery {
@Schema(title = "交易同步记录查询参数")
public class TradeSyncRecordQuery {
/** 本地交易号 */
@Schema(description = "本地交易号")

View File

@@ -1,8 +1,6 @@
package cn.daxpay.single.service.task;
import cn.daxpay.single.core.param.payment.pay.PaySyncParam;
import cn.daxpay.single.service.code.TradeAdjustSourceEnum;
import cn.daxpay.single.service.common.local.PaymentContextLocal;
import cn.daxpay.single.service.core.payment.pay.dao.PayExpiredTimeRepository;
import cn.daxpay.single.service.core.payment.sync.service.PaySyncService;
import com.baomidou.lock.LockInfo;
@@ -47,8 +45,6 @@ public class PayExpiredTimeTask implements Job {
continue;
}
try {
// 设置补偿来源为定时任务
PaymentContextLocal.get().getRepairInfo().setSource(TradeAdjustSourceEnum.TASK);
// 执行同步操作, 网关同步时会对支付的进行状态的处理
PaySyncParam paySyncParam = new PaySyncParam();
paySyncParam.setOrderNo(orderNo);