diff --git a/_doc/Task.md b/_doc/Task.md index 629fb540..075d995f 100644 --- a/_doc/Task.md +++ b/_doc/Task.md @@ -4,14 +4,14 @@ - [ ] 同步回调页 - [ ] 收银台功能优化 - [ ] 支持配置背景色和图标 -- [ ] 对各种交易增加新的同步失败异常处理, 防止同步失败后无限进行同步 +- [x] 对各种交易增加新的同步失败异常处理, 防止同步失败后无限进行同步 - [ ] 增加首页驾驶舱功能 - [ ] 商户应用要有类似删除的功能, 实现停用冻结, 但不影响数据的关联 - [ ] 同步接口优化, 返回同步完的数据 -- [ ] 服务商支付支持 +- [x] 服务商支付支持 - [x] 支付宝 - - [ ] 微信 -- [ ] 微信增加公钥证书方式 + - [x] 微信 +- [x] 微信增加公钥证书方式 - [ ] 分账重试 ## 3.0.0.bate3: 分账 - [x] SDK接口 diff --git a/daxpay-single/daxpay-single-service/src/main/java/org/dromara/daxpay/service/service/trade/pay/PaySyncService.java b/daxpay-single/daxpay-single-service/src/main/java/org/dromara/daxpay/service/service/trade/pay/PaySyncService.java index 33827a86..dba18b35 100644 --- a/daxpay-single/daxpay-single-service/src/main/java/org/dromara/daxpay/service/service/trade/pay/PaySyncService.java +++ b/daxpay-single/daxpay-single-service/src/main/java/org/dromara/daxpay/service/service/trade/pay/PaySyncService.java @@ -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); + } + /** * 关闭网关交易, 同时也会关闭本地支付 * 回调: 执行所有的支付通道关闭支付逻辑 diff --git a/daxpay-single/daxpay-single-service/src/main/java/org/dromara/daxpay/service/service/trade/refund/RefundAssistService.java b/daxpay-single/daxpay-single-service/src/main/java/org/dromara/daxpay/service/service/trade/refund/RefundAssistService.java index 95efb06a..aeacade5 100644 --- a/daxpay-single/daxpay-single-service/src/main/java/org/dromara/daxpay/service/service/trade/refund/RefundAssistService.java +++ b/daxpay-single/daxpay-single-service/src/main/java/org/dromara/daxpay/service/service/trade/refund/RefundAssistService.java @@ -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); } diff --git a/daxpay-single/daxpay-single-service/src/main/java/org/dromara/daxpay/service/service/trade/refund/RefundService.java b/daxpay-single/daxpay-single-service/src/main/java/org/dromara/daxpay/service/service/trade/refund/RefundService.java index f8fff02e..5baa8b91 100644 --- a/daxpay-single/daxpay-single-service/src/main/java/org/dromara/daxpay/service/service/trade/refund/RefundService.java +++ b/daxpay-single/daxpay-single-service/src/main/java/org/dromara/daxpay/service/service/trade/refund/RefundService.java @@ -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); } diff --git a/daxpay-single/daxpay-single-service/src/main/java/org/dromara/daxpay/service/service/trade/refund/RefundSyncService.java b/daxpay-single/daxpay-single-service/src/main/java/org/dromara/daxpay/service/service/trade/refund/RefundSyncService.java index b76d63f4..4f7e0584 100644 --- a/daxpay-single/daxpay-single-service/src/main/java/org/dromara/daxpay/service/service/trade/refund/RefundSyncService.java +++ b/daxpay-single/daxpay-single-service/src/main/java/org/dromara/daxpay/service/service/trade/refund/RefundSyncService.java @@ -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()); } } diff --git a/daxpay-single/daxpay-single-service/src/main/java/org/dromara/daxpay/service/service/trade/transfer/TransferAssistService.java b/daxpay-single/daxpay-single-service/src/main/java/org/dromara/daxpay/service/service/trade/transfer/TransferAssistService.java index ab5aef76..abe46923 100644 --- a/daxpay-single/daxpay-single-service/src/main/java/org/dromara/daxpay/service/service/trade/transfer/TransferAssistService.java +++ b/daxpay-single/daxpay-single-service/src/main/java/org/dromara/daxpay/service/service/trade/transfer/TransferAssistService.java @@ -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); + } + /** * 转账关闭 */ diff --git a/daxpay-single/daxpay-single-service/src/main/java/org/dromara/daxpay/service/service/trade/transfer/TransferService.java b/daxpay-single/daxpay-single-service/src/main/java/org/dromara/daxpay/service/service/trade/transfer/TransferService.java index 2a2c35d1..534e37f0 100644 --- a/daxpay-single/daxpay-single-service/src/main/java/org/dromara/daxpay/service/service/trade/transfer/TransferService.java +++ b/daxpay-single/daxpay-single-service/src/main/java/org/dromara/daxpay/service/service/trade/transfer/TransferService.java @@ -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); diff --git a/daxpay-single/daxpay-single-service/src/main/java/org/dromara/daxpay/service/service/trade/transfer/TransferSyncService.java b/daxpay-single/daxpay-single-service/src/main/java/org/dromara/daxpay/service/service/trade/transfer/TransferSyncService.java index 9da3cf53..10ebaf57 100644 --- a/daxpay-single/daxpay-single-service/src/main/java/org/dromara/daxpay/service/service/trade/transfer/TransferSyncService.java +++ b/daxpay-single/daxpay-single-service/src/main/java/org/dromara/daxpay/service/service/trade/transfer/TransferSyncService.java @@ -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) {