feat 对各种交易增加新的同步失败异常处理, 防止同步失败后无限进行同步

This commit is contained in:
DaxPay
2024-12-24 20:15:33 +08:00
parent b74084dd14
commit 842cf71a93
8 changed files with 105 additions and 78 deletions

View File

@@ -4,14 +4,14 @@
- [ ] 同步回调页
- [ ] 收银台功能优化
- [ ] 支持配置背景色和图标
- [ ] 对各种交易增加新的同步失败异常处理, 防止同步失败后无限进行同步
- [x] 对各种交易增加新的同步失败异常处理, 防止同步失败后无限进行同步
- [ ] 增加首页驾驶舱功能
- [ ] 商户应用要有类似删除的功能, 实现停用冻结, 但不影响数据的关联
- [ ] 同步接口优化, 返回同步完的数据
- [ ] 服务商支付支持
- [x] 服务商支付支持
- [x] 支付宝
- [ ] 微信
- [ ] 微信增加公钥证书方式
- [x] 微信
- [x] 微信增加公钥证书方式
- [ ] 分账重试
## 3.0.0.bate3: 分账
- [x] SDK接口

View File

@@ -83,12 +83,6 @@ public class PaySyncService {
try {
// 执行操作, 获取支付网关同步的结果
PaySyncResultBo syncResult = syncPayStrategy.doSync();
// 判断是否同步成功
if (!syncResult.isSyncSuccess()){
// 同步失败, 返回失败响应, 同时记录失败的日志
this.saveRecord(payOrder, syncResult, false);
throw new OperationFailException(syncResult.getSyncErrorMsg());
}
// 支付订单的网关订单号是否一致, 不一致进行更新
if (!Objects.equals(syncResult.getOutOrderNo(), payOrder.getOutOrderNo())){
payOrder.setOutOrderNo(syncResult.getOutOrderNo());
@@ -101,14 +95,17 @@ public class PaySyncService {
if (!statusSync){
this.adjustHandler(syncResult, payOrder);
}
} catch (PayFailureException e) {
} catch (Exception e) {
// 同步失败, 返回失败响应, 同时记录失败的日志
syncResult.setSyncSuccess(false);
this.saveRecord(payOrder, syncResult, false);
throw e;
syncResult.setSyncSuccess(false).setSyncErrorMsg(e.getMessage());
}
if (syncResult.isSyncSuccess()){
// 同步成功记录日志
this.saveRecord(payOrder, syncResult, !statusSync);
} else {
// 同步失败记录日志
this.saveRecord(payOrder, syncResult, true);
}
// 同步成功记录日志
this.saveRecord(payOrder, syncResult, !statusSync);
return new PaySyncResult()
.setOrderStatus(payOrder.getStatus())
.setAdjust(statusSync);
@@ -125,7 +122,10 @@ public class PaySyncService {
private boolean checkAndAdjust(PaySyncResultBo payRemoteSyncResult, PayOrder order){
var payStatus = payRemoteSyncResult.getPayStatus();
String orderStatus = order.getStatus();
// 如果本地订单为失败时, 直接返回需要进行调整
if (orderStatus.equals(FAIL.getCode())){
return false;
}
// 本地订单为支付中时, 对状态进行比较,
if (orderStatus.equals(PROGRESS.getCode())){
// 如果返回订单也是支付中
@@ -157,6 +157,8 @@ public class PaySyncService {
case CLOSE, CANCEL -> this.closeLocal(payOrder);
// 超时关闭和交易不存在(特殊) 关闭本地支付订单, 同时调用网关进行关闭, 确保后续这个订单不能被支付
case TIMEOUT -> this.closeRemote(payOrder);
// 同步失败处理
case FAIL -> this.failLocal(payOrder,payRemoteSyncResult);
default -> throw new SystemUnknownErrorException("代码有问题");
}
}
@@ -187,6 +189,17 @@ public class PaySyncService {
payOrderManager.updateById(order);
merchantNoticeService.registerPayNotice(order);
}
/**
* 同步失败
* 同步失败后, 讲订单设置为失败状态, 预防无限重试, 失败不会触发消息通知
*/
private void failLocal(PayOrder order, PaySyncResultBo syncResult) {
// 执行策略的关闭方法
order.setStatus(FAIL.getCode()).setErrorMsg(syncResult.getSyncErrorMsg());
payOrderManager.updateById(order);
}
/**
* 关闭网关交易, 同时也会关闭本地支付
* 回调: 执行所有的支付通道关闭支付逻辑

View File

@@ -3,6 +3,9 @@ package org.dromara.daxpay.service.service.trade.refund;
import cn.bootx.platform.core.exception.DataNotExistException;
import cn.bootx.platform.core.exception.ValidationFailedException;
import cn.bootx.platform.core.util.BigDecimalUtil;
import cn.hutool.core.util.StrUtil;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.dromara.daxpay.core.enums.PayRefundStatusEnum;
import org.dromara.daxpay.core.enums.PayStatusEnum;
import org.dromara.daxpay.core.enums.RefundStatusEnum;
@@ -17,11 +20,7 @@ import org.dromara.daxpay.service.entity.order.pay.PayOrder;
import org.dromara.daxpay.service.entity.order.refund.RefundOrder;
import org.dromara.daxpay.service.service.notice.MerchantNoticeService;
import org.dromara.daxpay.service.service.record.flow.TradeFlowRecordService;
import cn.hutool.core.util.StrUtil;
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.math.BigDecimal;
@@ -116,9 +115,9 @@ public class RefundAssistService {
/**
* 更新退款错误信息
*/
@Transactional(propagation = Propagation.REQUIRES_NEW, rollbackFor = Exception.class)
public void updateOrderByError(RefundOrder refundOrder, Exception e){
refundOrder.setErrorMsg(e.getMessage());
@Transactional(rollbackFor = Exception.class)
public void updateOrderByError(RefundOrder refundOrder, String message){
refundOrder.setErrorMsg(message);
refundOrder.setStatus(RefundStatusEnum.FAIL.getCode());
refundOrderManager.updateById(refundOrder);
}

View File

@@ -111,7 +111,7 @@ public class RefundService {
} catch (Exception e) {
log.error("退款出现错误", e);
// 更新退款失败的记录
refundAssistService.updateOrderByError(refundOrder, e);
refundAssistService.updateOrderByError(refundOrder, e.getMessage());
return refundAssistService.buildResult(refundOrder);
}
SpringUtil.getBean(this.getClass()).successHandler(refundOrder, payOrder, refundResultBo);
@@ -163,7 +163,7 @@ public class RefundService {
} catch (Exception e) {
log.error("重新退款失败:", e);
// 记录退款失败的记录
refundAssistService.updateOrderByError(refundOrder, e);
refundAssistService.updateOrderByError(refundOrder, e.getMessage());
// 返回错误响应对象
return refundAssistService.buildResult(refundOrder);
}

View File

@@ -1,10 +1,12 @@
package org.dromara.daxpay.service.service.trade.refund;
import cn.bootx.platform.core.exception.RepetitiveOperationException;
import com.baomidou.lock.LockInfo;
import com.baomidou.lock.LockTemplate;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.dromara.daxpay.core.enums.RefundStatusEnum;
import org.dromara.daxpay.core.enums.TradeTypeEnum;
import org.dromara.daxpay.core.exception.OperationFailException;
import org.dromara.daxpay.core.exception.PayFailureException;
import org.dromara.daxpay.core.exception.TradeNotExistException;
import org.dromara.daxpay.core.param.trade.refund.RefundSyncParam;
import org.dromara.daxpay.core.result.trade.refund.RefundSyncResult;
@@ -17,16 +19,14 @@ import org.dromara.daxpay.service.service.order.refund.RefundOrderQueryService;
import org.dromara.daxpay.service.service.record.sync.TradeSyncRecordService;
import org.dromara.daxpay.service.strategy.AbsSyncRefundOrderStrategy;
import org.dromara.daxpay.service.util.PaymentStrategyFactory;
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.Propagation;
import org.springframework.transaction.annotation.Transactional;
import java.util.Objects;
import static org.dromara.daxpay.core.enums.TradeStatusEnum.FAIL;
/**
* 支付退款同步服务类
* @author xxm
@@ -75,35 +75,33 @@ public class RefundSyncService {
try {
// 执行操作, 获取支付网关同步的结果
RefundSyncResultBo syncResultBo = syncPayStrategy.doSync();
RefundSyncResultBo syncResult = syncPayStrategy.doSync();
// 判断是否同步成功
if (!syncResultBo.isSyncSuccess()) {
// 同步失败, 返回失败响应, 同时记录失败的日志
this.saveRecord(refundOrder, syncResultBo, false);
throw new OperationFailException(syncResultBo.getSyncErrorMsg());
}
// 订单的通道交易号是否一致, 不一致进行更新
if (Objects.nonNull(syncResultBo.getOutRefundNo()) && !Objects.equals(syncResultBo.getOutRefundNo(), refundOrder.getOutRefundNo())){
refundOrder.setOutRefundNo(syncResultBo.getOutRefundNo());
if (Objects.nonNull(syncResult.getOutRefundNo()) && !Objects.equals(syncResult.getOutRefundNo(), refundOrder.getOutRefundNo())){
refundOrder.setOutRefundNo(syncResult.getOutRefundNo());
refundOrderManager.updateById(refundOrder);
}
// 判断网关状态是否和支付单一致
boolean statusSync = this.checkStatus(syncResultBo, refundOrder);
boolean statusSync = this.checkStatus(syncResult, refundOrder);
try {
// 状态不一致,执行退款单调整逻辑
if (!statusSync) {
// 如果没有支付来源, 设置支付来源为同步
this.adjustHandler(syncResultBo, refundOrder);
this.adjustHandler(syncResult, refundOrder);
}
} catch (PayFailureException e) {
} catch (Exception e) {
// 同步失败, 返回失败响应, 同时记录失败的日志
syncResultBo.setSyncSuccess(false);
this.saveRecord(refundOrder, syncResultBo, false);
throw e;
syncResult.setSyncSuccess(false).setSyncErrorMsg(e.getMessage());
}
// 判断是否同步成功
if (!syncResult.isSyncSuccess()) {
// 同步成功记录日志
this.saveRecord(refundOrder, syncResult, !statusSync);
} else {
// 同步失败, 返回失败响应, 同时记录失败的日志
this.saveRecord(refundOrder, syncResult, true);
}
// 同步成功记录日志
this.saveRecord(refundOrder, syncResultBo, !statusSync);
return new RefundSyncResult()
.setOrderStatus(refundOrder.getStatus())
.setAdjust(statusSync);
@@ -120,6 +118,10 @@ public class RefundSyncService {
private boolean checkStatus(RefundSyncResultBo syncResult, RefundOrder order){
var syncStatus = syncResult.getRefundStatus();
String orderStatus = order.getStatus();
// 如果本地订单为失败时, 直接返回需要进行调整
if (orderStatus.equals(FAIL.getCode())){
return false;
}
// 如果订单为退款中, 对状态进行比较
if (Objects.equals(orderStatus, RefundStatusEnum.SUCCESS.getCode())){
@@ -148,7 +150,8 @@ public class RefundSyncService {
switch (refundStatus) {
case SUCCESS -> refundAssistService.success(order, syncResult.getFinishTime());
case PROGRESS -> {}
case FAIL, CLOSE-> refundAssistService.close(order);
case CLOSE-> refundAssistService.close(order);
case FAIL -> refundAssistService.updateOrderByError(order, syncResult.getSyncErrorMsg());
default -> log.error("退款同步结果未知, 退款单号:{}, 错误信息:{}", order.getRefundNo(), syncResult.getSyncErrorMsg());
}
}

View File

@@ -52,6 +52,16 @@ public class TransferAssistService {
return transferOrder;
}
/**
* 更新转账错误信息
*/
@Transactional(rollbackFor = Exception.class)
public void updateOrderByError(TransferOrder transferOrder, String message){
transferOrder.setErrorMsg(message);
transferOrder.setStatus(TransferStatusEnum.FAIL.getCode());
transferOrderManager.updateById(transferOrder);
}
/**
* 转账关闭
*/

View File

@@ -89,8 +89,7 @@ public class TransferService {
} catch (Exception e) {
log.error("转账出现错误", e);
// 更新转账失败的记录
order.setStatus(TransferStatusEnum.FAIL.getCode()).setErrorMsg(e.getMessage());
transferOrderManager.updateById(order);
transferAssistService.updateOrderByError(order,e.getMessage());
return transferAssistService.buildResult(order);
}
SpringUtil.getBean(this.getClass()).successHandler(order, transferResultBo);
@@ -119,8 +118,7 @@ public class TransferService {
} catch (Exception e) {
log.error("重现转账出现错误", e);
// 更新转账失败的记录
order.setStatus(TransferStatusEnum.FAIL.getCode()).setErrorMsg(e.getMessage());
transferOrderManager.updateById(order);
transferAssistService.updateOrderByError(order,e.getMessage());
return transferAssistService.buildResult(order);
}
SpringUtil.getBean(this.getClass()).successHandler(order, transferResultBo);

View File

@@ -2,10 +2,14 @@ package org.dromara.daxpay.service.service.trade.transfer;
import cn.bootx.platform.core.exception.BizException;
import cn.bootx.platform.core.exception.RepetitiveOperationException;
import cn.hutool.core.util.StrUtil;
import cn.hutool.extra.spring.SpringUtil;
import com.baomidou.lock.LockInfo;
import com.baomidou.lock.LockTemplate;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.dromara.daxpay.core.enums.TradeTypeEnum;
import org.dromara.daxpay.core.enums.TransferStatusEnum;
import org.dromara.daxpay.core.exception.OperationFailException;
import org.dromara.daxpay.core.exception.PayFailureException;
import org.dromara.daxpay.core.exception.TradeNotExistException;
import org.dromara.daxpay.core.param.trade.transfer.TransferSyncParam;
import org.dromara.daxpay.core.result.trade.transfer.TransferSyncResult;
@@ -20,17 +24,13 @@ import org.dromara.daxpay.service.service.record.flow.TradeFlowRecordService;
import org.dromara.daxpay.service.service.record.sync.TradeSyncRecordService;
import org.dromara.daxpay.service.strategy.AbsSyncTransferOrderStrategy;
import org.dromara.daxpay.service.util.PaymentStrategyFactory;
import cn.hutool.core.util.StrUtil;
import cn.hutool.extra.spring.SpringUtil;
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.Objects;
import static org.dromara.daxpay.core.enums.TradeStatusEnum.FAIL;
/**
* 转账同步接口
* @author xxm
@@ -54,7 +54,7 @@ public class TransferSyncService {
*/
public TransferSyncResult sync(TransferSyncParam param) {
TransferOrder transferOrder = transferOrderService.findByBizOrTransferNo(param.getTransferNo(), param.getBizTransferNo(),param.getAppId())
.orElseThrow(() -> new TradeNotExistException("退款订单不存在"));
.orElseThrow(() -> new TradeNotExistException("转账订单不存在"));
// 执行订单同步逻辑
return this.syncTransferOrder(transferOrder);
}
@@ -76,12 +76,6 @@ public class TransferSyncService {
try {
// 执行操作, 获取支付网关同步的结果
var syncResult = syncPayStrategy.doSync();
// 判断是否同步成功
if (!syncResult.isSyncSuccess()){
// 同步失败, 返回失败响应, 同时记录失败的日志
this.saveRecord(transferOrder, syncResult, false);
throw new OperationFailException(syncResult.getSyncErrorMsg());
}
// 转账订单的网关订单号是否一致, 不一致进行更新
if (!Objects.equals(syncResult.getOutTransferNo(), transferOrder.getOutTransferNo())){
transferOrder.setOutTransferNo(syncResult.getOutTransferNo());
@@ -94,14 +88,20 @@ public class TransferSyncService {
if (!statusSync){
this.adjustHandler(syncResult, transferOrder);
}
} catch (PayFailureException e) {
} catch (Exception e) {
// 同步失败, 返回失败响应, 同时记录失败的日志
syncResult.setSyncSuccess(false);
this.saveRecord(transferOrder, syncResult, false);
throw e;
}
// 同步成功记录日志
this.saveRecord(transferOrder, syncResult, statusSync);
// 判断是否同步成功
if (syncResult.isSyncSuccess()){
// 同步成功
this.saveRecord(transferOrder, syncResult, statusSync);
} else {
// 同步失败, 返回失败响应, 同时记录失败的日志
this.saveRecord(transferOrder, syncResult, true);
}
return new TransferSyncResult()
.setOrderStatus(transferOrder.getStatus())
.setAdjust(statusSync);
@@ -113,13 +113,16 @@ public class TransferSyncService {
/**
* 检查状态是否一致
* @see TransferStatusEnum 退款单状态
* @see TransferStatusEnum 转账单状态
*/
private boolean checkStatus(TransferSyncResultBo syncResult, TransferOrder order){
var syncStatus = syncResult.getTransferStatus();
String orderStatus = order.getStatus();
// 如果订单为退款中, 对状态进行比较
// 如果本地订单为失败时, 直接返回需要进行调整
if (orderStatus.equals(FAIL.getCode())){
return false;
}
// 如果订单为转账中, 对状态进行比较
if (Objects.equals(orderStatus, TransferStatusEnum.SUCCESS.getCode())){
// 转账完成
if (Objects.equals(syncStatus, TransferStatusEnum.SUCCESS)) {
@@ -138,7 +141,7 @@ public class TransferSyncService {
}
/**
* 进行退款订单和支付订单的调整
* 进行转账订单和支付订单的调整
*/
private void adjustHandler(TransferSyncResultBo syncResult, TransferOrder order){
var syncStatusEnum = syncResult.getTransferStatus();
@@ -146,14 +149,15 @@ public class TransferSyncService {
switch (syncStatusEnum) {
case SUCCESS -> SpringUtil.getBean(this.getClass()).success(order, syncResult);
case PROGRESS -> {}
case FAIL,CLOSE -> transferAssistService.close(order,syncResult.getFinishTime());
case CLOSE -> transferAssistService.close(order,syncResult.getFinishTime());
case FAIL -> transferAssistService.updateOrderByError(order,syncResult.getSyncErrorMsg());
default -> throw new BizException("代码有问题");
}
}
/**
* 退款成功, 更新退款单和支付单
* 转账成功, 更新转账单和支付单
*/
@Transactional(rollbackFor = Exception.class)
public void success(TransferOrder order, TransferSyncResultBo syncResult) {