diff --git a/_doc/Task.md b/_doc/Task.md index f9d91f36..aa6a7704 100644 --- a/_doc/Task.md +++ b/_doc/Task.md @@ -95,9 +95,13 @@ - [x] 修改退款补偿处理, 更改为退款粒度为整个退款单, 要不全部成功, 要不全部失败 - [x] 支付通道对出现疑似退款的订单进行**报错提醒**, 通过退款同步进行补偿 - 2024-01-31: - - [ ] 微信退款同步策略 - - [ ] 退款操作支持重试 -2.0.1 版本内容 + - [x] 微信退款同步策略 + - [x] 支付和退款同步时, 填充完成时间和网关订单号 + - [ ] 退款操作支持重试 +- 2024-02-01: +- + +- 2.0.1 版本内容 - [ ] 支付流程也改为先落库后支付情况, 避免极端情况导致掉单 - [ ] 完善各种同步支付方式 - [ ] 增加聚合支付功能支持 diff --git a/daxpay-single/daxpay-single-admin/src/main/java/cn/bootx/platform/daxpay/admin/controller/record/PayRepairRecordController.java b/daxpay-single/daxpay-single-admin/src/main/java/cn/bootx/platform/daxpay/admin/controller/record/PayRepairRecordController.java index f9f72488..19a9a599 100644 --- a/daxpay-single/daxpay-single-admin/src/main/java/cn/bootx/platform/daxpay/admin/controller/record/PayRepairRecordController.java +++ b/daxpay-single/daxpay-single-admin/src/main/java/cn/bootx/platform/daxpay/admin/controller/record/PayRepairRecordController.java @@ -34,8 +34,8 @@ public class PayRepairRecordController { @Operation(summary = "查询单条") @GetMapping("/findById") - public ResResult findById(Long paymentId){ - return Res.ok(service.findById(paymentId)); + public ResResult findById(Long id){ + return Res.ok(service.findById(id)); } } diff --git a/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/code/AliPayCode.java b/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/code/AliPayCode.java index 200a97cb..865791c4 100644 --- a/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/code/AliPayCode.java +++ b/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/code/AliPayCode.java @@ -79,6 +79,9 @@ public interface AliPayCode { /** 退款成功 */ String REFUND_SUCCESS = "REFUND_SUCCESS"; + // 参数 + /** 返回退款时间 */ + String GMT_REFUND_PAY = "gmt_refund_pay"; // 错误提示 /** 交易不存在 */ diff --git a/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/code/WeChatPayCode.java b/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/code/WeChatPayCode.java index 093a755b..0047ef22 100644 --- a/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/code/WeChatPayCode.java +++ b/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/code/WeChatPayCode.java @@ -66,23 +66,32 @@ public interface WeChatPayCode { /** 交易状态 */ String TRADE_STATE = "trade_state"; + // 回调参数 /** 支付金额 */ String TOTAL_FEE = "total_fee"; /** 微信退款单号 */ - String REFUND_ID = "refund_id"; + String CALLBACK_REFUND_ID = "refund_id"; /** 商户退款单号 */ - String OUT_REFUND_NO = "out_refund_no"; + String CALLBACK_OUT_REFUND_NO = "out_refund_no"; /** 退款状态 */ - String REFUND_STATUS = "refund_status"; + String CALLBACK_REFUND_STATUS = "refund_status"; /** 申请退款金额 */ - String REFUND_FEE = "refund_fee"; + String CALLBACK_REFUND_FEE = "refund_fee"; /** 退款成功时间 yyyyMMddHHmmss */ - String SUCCESS_TIME = "success_time"; + String CALLBACK_SUCCESS_TIME = "success_time"; + + // 退款信息查询参数 + /** 退款成功时间 yyyy-MM-dd HH:mm:ss */ + String REFUND_SUCCESS_TIME = "refund_success_time_0"; + /** 网关退款订单号 */ + String REFUND_ID = "refund_id_0"; + /** 退款成状态 */ + String REFUND_STATUS = "refund_status_0"; /** 当前返回退款笔数 */ String REFUND_COUNT = "refund_count"; @@ -116,7 +125,7 @@ public interface WeChatPayCode { String PAY_USERPAYING = "USERPAYING"; /** 退款成功 */ - String REFUND_USERPAYING = "SUCCESS"; + String REFUND_SUCCESS = "SUCCESS"; /** 退款异常 */ String REFUND_CHANGE = "CHANGE"; @@ -124,6 +133,9 @@ public interface WeChatPayCode { /** 退款关闭 */ String REFUND_REFUNDCLOSE = "REFUNDCLOSE"; + /** 退款处理中 */ + String REFUND_PROCESSING = "PROCESSING"; + /** 支付失败(刷卡支付) */ String TRADE_PAYERROR = "PAYERROR"; diff --git a/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/common/context/PaySyncLocal.java b/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/common/context/PaySyncLocal.java index e7e8d24f..b1cb924d 100644 --- a/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/common/context/PaySyncLocal.java +++ b/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/common/context/PaySyncLocal.java @@ -14,13 +14,5 @@ import java.time.LocalDateTime; @Accessors(chain = true) public class PaySyncLocal { - /** - * 第三方支付网关生成的订单号, 用与将记录关联起来 - * 1. 如付款码支付直接成功时会出现 - */ - private String gatewayOrderNo; - - /** 支付完成时间(通常用于接收异步支付返回的时间) */ - private LocalDateTime payTime; } diff --git a/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/channel/alipay/service/AliPayRefundService.java b/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/channel/alipay/service/AliPayRefundService.java index 6d648564..afdfb494 100644 --- a/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/channel/alipay/service/AliPayRefundService.java +++ b/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/channel/alipay/service/AliPayRefundService.java @@ -49,9 +49,8 @@ public class AliPayRefundService { } // 接口返回fund_change=Y为退款成功,fund_change=N或无此字段值返回时需通过退款查询接口进一步确认退款状态 if (response.getFundChange().equals("Y")){ - // TODO 测试退款同步 -// refundInfo.setStatus(PayRefundStatusEnum.SUCCESS) -// .setGatewayOrderNo(response.getTradeNo()); + refundInfo.setStatus(PayRefundStatusEnum.SUCCESS) + .setGatewayOrderNo(response.getTradeNo()); } refundInfo.setStatus(PayRefundStatusEnum.PROGRESS) .setGatewayOrderNo(response.getTradeNo()); diff --git a/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/channel/alipay/service/AliPaySyncService.java b/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/channel/alipay/service/AliPaySyncService.java index bc698bc0..4d772546 100644 --- a/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/channel/alipay/service/AliPaySyncService.java +++ b/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/channel/alipay/service/AliPaySyncService.java @@ -4,6 +4,7 @@ import cn.bootx.platform.common.core.util.LocalDateTimeUtil; import cn.bootx.platform.daxpay.code.PayRefundSyncStatusEnum; import cn.bootx.platform.daxpay.code.PaySyncStatusEnum; import cn.bootx.platform.daxpay.service.code.AliPayCode; +import cn.bootx.platform.daxpay.service.common.context.PaySyncLocal; import cn.bootx.platform.daxpay.service.common.local.PaymentContextLocal; import cn.bootx.platform.daxpay.service.core.order.pay.entity.PayOrder; import cn.bootx.platform.daxpay.service.core.order.refund.entity.PayRefundOrder; @@ -21,8 +22,11 @@ import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; import java.time.LocalDateTime; +import java.util.Collections; import java.util.Objects; +import static cn.bootx.platform.daxpay.service.code.AliPayCode.GMT_REFUND_PAY; + /** * 支付宝同步 * @@ -34,7 +38,7 @@ import java.util.Objects; @RequiredArgsConstructor public class AliPaySyncService { /** - * 与支付宝网关同步状态, 退款状态会 + * 与支付宝网关同步状态, 退款状态有 * 1 远程支付成功 * 2 交易创建,等待买家付款 * 3 超时关闭 @@ -42,6 +46,7 @@ public class AliPaySyncService { * 5 查询失败 */ public PayGatewaySyncResult syncPayStatus(PayOrder payOrder) { + PaySyncLocal paySyncLocal = PaymentContextLocal.get().getPaySyncInfo(); PayGatewaySyncResult syncResult = new PayGatewaySyncResult().setSyncStatus(PaySyncStatusEnum.FAIL); // 查询 try { @@ -58,13 +63,13 @@ public class AliPaySyncService { syncResult.setErrorMsg(response.getSubMsg()); return syncResult; } + // 设置网关订单号 + syncResult.setGatewayOrderNo(response.getTradeNo()); // 支付完成 TODO 部分退款也在这个地方, 但无法进行区分, 需要借助对账进行处理 if (Objects.equals(tradeStatus, AliPayCode.NOTIFY_TRADE_SUCCESS) || Objects.equals(tradeStatus, AliPayCode.NOTIFY_TRADE_FINISHED)) { - PaymentContextLocal.get().getPaySyncInfo().setGatewayOrderNo(response.getTradeNo()); // 支付完成时间 LocalDateTime payTime = LocalDateTimeUtil.of(response.getSendPayDate()); - PaymentContextLocal.get().getPaySyncInfo().setPayTime(payTime); - return syncResult.setSyncStatus(PaySyncStatusEnum.PAY_SUCCESS); + return syncResult.setSyncStatus(PaySyncStatusEnum.PAY_SUCCESS).setPayTime(payTime); } // 待支付 if (Objects.equals(tradeStatus, AliPayCode.NOTIFY_WAIT_BUYER_PAY)) { @@ -78,7 +83,6 @@ public class AliPaySyncService { } else { return syncResult.setSyncStatus(PaySyncStatusEnum.CLOSED); } - } // 支付宝支付后, 客户未进行操作将不会创建出订单, 所以交易不存在约等于未查询订单 if (Objects.equals(response.getSubCode(), AliPayCode.ACQ_TRADE_NOT_EXIST)) { @@ -103,6 +107,8 @@ public class AliPaySyncService { queryModel.setOutRequestNo(String.valueOf(refundOrder.getId())); // 商户订单号 queryModel.setOutTradeNo(String.valueOf(refundOrder.getPaymentId())); + // 设置返回退款完成时间 + queryModel.setQueryOptions(Collections.singletonList(GMT_REFUND_PAY)); AlipayTradeFastpayRefundQueryResponse response = AliPayApi.tradeRefundQueryToResponse(queryModel); syncResult.setSyncInfo(JSONUtil.toJsonStr(response)); // 失败 @@ -113,9 +119,12 @@ public class AliPaySyncService { return syncResult; } String tradeStatus = response.getRefundStatus(); + // 设置网关订单号 + syncResult.setGatewayOrderNo(response.getTradeNo()); // 成功 if (Objects.equals(tradeStatus, AliPayCode.REFUND_SUCCESS)){ - return syncResult.setSyncStatus(PayRefundSyncStatusEnum.SUCCESS); + LocalDateTime localDateTime = LocalDateTimeUtil.of(response.getGmtRefundPay()); + return syncResult.setRefundTime(localDateTime).setSyncStatus(PayRefundSyncStatusEnum.SUCCESS); } else { return syncResult.setSyncStatus(PayRefundSyncStatusEnum.FAIL).setErrorMsg("支付宝网关反正退款未成功"); } diff --git a/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/channel/wechat/service/WeChatPayCallbackService.java b/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/channel/wechat/service/WeChatPayCallbackService.java index f901c23f..e985c675 100644 --- a/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/channel/wechat/service/WeChatPayCallbackService.java +++ b/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/channel/wechat/service/WeChatPayCallbackService.java @@ -118,18 +118,19 @@ public class WeChatPayCallbackService extends AbsCallbackStrategy { callbackParam = WxPayKit.xmlToMap(decryptData); callbackInfo.setCallbackParam(callbackParam); // 网关订单号 - callbackInfo.setGatewayOrderNo(callbackParam.get(REFUND_ID)); + callbackInfo.setGatewayOrderNo(callbackParam.get(CALLBACK_REFUND_ID)); // 退款订单Id - callbackInfo.setOrderId(Long.valueOf(callbackParam.get(OUT_REFUND_NO))); + callbackInfo.setOrderId(Long.valueOf(callbackParam.get(CALLBACK_OUT_REFUND_NO))); // 退款金额 - callbackInfo.setAmount(callbackParam.get(REFUND_FEE)); + callbackInfo.setAmount(callbackParam.get(CALLBACK_REFUND_FEE)); // 交易状态 - PayStatusEnum payStatus = Objects.equals(callbackParam.get(REFUND_STATUS), REFUND_USERPAYING) ? PayStatusEnum.SUCCESS : PayStatusEnum.FAIL; + PayStatusEnum payStatus = Objects.equals(callbackParam.get(CALLBACK_REFUND_STATUS), REFUND_SUCCESS) + ? PayStatusEnum.SUCCESS : PayStatusEnum.FAIL; callbackInfo.setGatewayStatus(payStatus.getCode()); // 退款时间 - String timeEnd = callbackParam.get(SUCCESS_TIME); + String timeEnd = callbackParam.get(CALLBACK_SUCCESS_TIME); if (StrUtil.isNotBlank(timeEnd)) { LocalDateTime time = LocalDateTimeUtil.parse(timeEnd, DatePattern.NORM_DATETIME_PATTERN); callbackInfo.setFinishTime(time); diff --git a/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/channel/wechat/service/WeChatPaySyncService.java b/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/channel/wechat/service/WeChatPaySyncService.java index 2cb7bb56..047ee133 100644 --- a/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/channel/wechat/service/WeChatPaySyncService.java +++ b/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/channel/wechat/service/WeChatPaySyncService.java @@ -1,17 +1,11 @@ package cn.bootx.platform.daxpay.service.core.channel.wechat.service; import cn.bootx.platform.common.core.util.LocalDateTimeUtil; -import cn.bootx.platform.daxpay.code.PayChannelEnum; import cn.bootx.platform.daxpay.code.PayRefundSyncStatusEnum; import cn.bootx.platform.daxpay.code.PaySyncStatusEnum; -import cn.bootx.platform.daxpay.exception.pay.PayFailureException; import cn.bootx.platform.daxpay.service.code.WeChatPayCode; -import cn.bootx.platform.daxpay.service.common.local.PaymentContextLocal; import cn.bootx.platform.daxpay.service.core.channel.wechat.entity.WeChatPayConfig; -import cn.bootx.platform.daxpay.service.core.order.pay.dao.PayChannelOrderManager; import cn.bootx.platform.daxpay.service.core.order.pay.entity.PayOrder; -import cn.bootx.platform.daxpay.service.core.order.refund.dao.PayRefundChannelOrderManager; -import cn.bootx.platform.daxpay.service.core.order.refund.entity.PayRefundChannelOrder; import cn.bootx.platform.daxpay.service.core.order.refund.entity.PayRefundOrder; import cn.bootx.platform.daxpay.service.core.payment.sync.result.PayGatewaySyncResult; import cn.bootx.platform.daxpay.service.core.payment.sync.result.RefundGatewaySyncResult; @@ -30,6 +24,8 @@ import java.time.LocalDateTime; import java.util.Map; import java.util.Objects; +import static cn.bootx.platform.daxpay.service.code.WeChatPayCode.TRANSACTION_ID; + /** * 微信支付同步服务 * @@ -40,11 +36,9 @@ import java.util.Objects; @Service @RequiredArgsConstructor public class WeChatPaySyncService { - private final PayChannelOrderManager payChannelOrderManager; - private final PayRefundChannelOrderManager refundChannelOrderManager; /** - * 同步查询 + * 支付信息查询 */ public PayGatewaySyncResult syncPayStatus(PayOrder order, WeChatPayConfig weChatPayConfig) { PayGatewaySyncResult syncResult = new PayGatewaySyncResult().setSyncStatus(PaySyncStatusEnum.FAIL); @@ -70,14 +64,16 @@ public class WeChatPaySyncService { log.warn("疑似未查询到订单:{}", result); return syncResult.setSyncStatus(PaySyncStatusEnum.NOT_FOUND); } + + // 设置微信支付网关订单号 + syncResult.setGatewayOrderNo(result.get(TRANSACTION_ID)); // 查询到订单的状态 String tradeStatus = result.get(WeChatPayCode.TRADE_STATE); // 支付完成 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); - PaymentContextLocal.get().getPaySyncInfo().setPayTime(time); - return syncResult.setSyncStatus(PaySyncStatusEnum.PAY_SUCCESS); + return syncResult.setPayTime(time).setSyncStatus(PaySyncStatusEnum.PAY_SUCCESS); } // 待支付 if (Objects.equals(tradeStatus, WeChatPayCode.PAY_NOTPAY) @@ -97,7 +93,7 @@ public class WeChatPaySyncService { } } catch (RuntimeException e) { - log.error("查询订单失败:", e); + log.error("查询支付订单失败:", e); syncResult.setErrorMsg(e.getMessage()); } return syncResult; @@ -107,26 +103,41 @@ public class WeChatPaySyncService { * 退款信息查询 */ public RefundGatewaySyncResult syncRefundStatus(PayRefundOrder refundOrder, WeChatPayConfig weChatPayConfig){ - PayRefundChannelOrder orderChannel = refundChannelOrderManager.findByRefundIdAndChannel(refundOrder.getId(), PayChannelEnum.WECHAT.getCode()) - .orElseThrow(() -> new PayFailureException("支付订单通道信息不存在")); - + RefundGatewaySyncResult syncResult = new RefundGatewaySyncResult(); Map params = RefundQueryModel.builder() .appid(weChatPayConfig.getWxAppId()) .mch_id(weChatPayConfig.getWxMchId()) .nonce_str(WxPayKit.generateStr()) + // 使用退款单号查询, 只返回当前这条, 如果使用支付订单号查询, .out_refund_no(String.valueOf(refundOrder.getId())) .build() .createSign(weChatPayConfig.getApiKeyV2(), SignType.HMACSHA256); - String xmlResult = WxPayApi.orderRefundQuery(false, params); - Map result = WxPayKit.xmlToMap(xmlResult); - // TODO 处理退款同步的情况 + try { + String xmlResult = WxPayApi.orderRefundQuery(false, params); + Map result = WxPayKit.xmlToMap(xmlResult); + syncResult.setSyncInfo(JSONUtil.toJsonStr(result)); - - Integer refundFee = Integer.valueOf(result.get(WeChatPayCode.REFUND_FEE)); - if (Objects.equals(refundFee, orderChannel.getAmount())){ - return new RefundGatewaySyncResult().setSyncStatus(PayRefundSyncStatusEnum.REFUNDING); + // 设置微信支付网关订单号 + syncResult.setGatewayOrderNo(result.get(WeChatPayCode.REFUND_ID)); + // 状态 + String tradeStatus = result.get(WeChatPayCode.REFUND_STATUS); + // 退款成功 + if (Objects.equals(tradeStatus, WeChatPayCode.REFUND_SUCCESS)) { + String timeEnd = result.get(WeChatPayCode.REFUND_SUCCESS_TIME); + LocalDateTime time = LocalDateTimeUtil.parse(timeEnd, DatePattern.NORM_DATETIME_PATTERN); + return syncResult.setRefundTime(time).setSyncStatus(PayRefundSyncStatusEnum.SUCCESS); + } + // 退款中 + if (Objects.equals(tradeStatus, WeChatPayCode.REFUND_PROCESSING)) { + return syncResult.setSyncStatus(PayRefundSyncStatusEnum.REFUNDING); + } + return syncResult.setSyncStatus(PayRefundSyncStatusEnum.FAIL); + } catch (Exception e) { + log.error("查询退款订单失败:", e); + syncResult.setSyncStatus(PayRefundSyncStatusEnum.REFUNDING).setErrorMsg(e.getMessage()); } - return new RefundGatewaySyncResult().setSyncInfo(JSONUtil.toJsonStr(result)); + return syncResult; + } } diff --git a/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/notice/service/PayNoticeService.java b/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/notice/service/PayNoticeService.java index f520b25f..2e518d45 100644 --- a/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/notice/service/PayNoticeService.java +++ b/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/notice/service/PayNoticeService.java @@ -9,9 +9,9 @@ import cn.bootx.platform.daxpay.service.core.notice.result.PayChannelResult; import cn.bootx.platform.daxpay.service.core.notice.result.PayNoticeResult; import cn.bootx.platform.daxpay.service.core.order.pay.dao.PayChannelOrderManager; import cn.bootx.platform.daxpay.service.core.order.pay.dao.PayOrderExtraManager; -import cn.bootx.platform.daxpay.service.core.order.pay.dao.PayOrderManager; import cn.bootx.platform.daxpay.service.core.order.pay.entity.PayOrder; import cn.bootx.platform.daxpay.service.core.order.pay.entity.PayOrderExtra; +import cn.bootx.platform.daxpay.service.core.order.pay.service.PayOrderQueryService; import cn.bootx.platform.daxpay.util.PaySignUtil; import cn.hutool.core.util.StrUtil; import cn.hutool.http.ContentType; @@ -35,7 +35,7 @@ import java.util.stream.Collectors; @Service @RequiredArgsConstructor public class PayNoticeService { - private final PayOrderManager payOrderManager; + private final PayOrderQueryService payOrderQueryService; private final PayOrderExtraManager payOrderExtraManager; private final PayChannelOrderManager payChannelOrderManager; @@ -49,7 +49,7 @@ public class PayNoticeService { PlatformLocal platform = PaymentContextLocal.get().getPlatformInfo(); // 首先判断接口是开启了通知回调功能 if (apiInfo.isNotice()){ - PayOrder payOrder = payOrderManager.findById(paymentId).orElseThrow(DataNotExistException::new); + PayOrder payOrder = payOrderQueryService.findById(paymentId).orElseThrow(DataNotExistException::new); // 判断是否是同步支付, 并且配置不进行消息通知 if (!payOrder.isAsyncPay() && apiInfo.isOnlyAsyncNotice()){ return; diff --git a/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/order/pay/dao/PayOrderManager.java b/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/order/pay/dao/PayOrderManager.java index 67629e13..0a614c5f 100644 --- a/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/order/pay/dao/PayOrderManager.java +++ b/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/order/pay/dao/PayOrderManager.java @@ -6,18 +6,18 @@ import cn.bootx.platform.common.mybatisplus.util.MpUtil; import cn.bootx.platform.common.query.generator.QueryGenerator; import cn.bootx.platform.daxpay.service.core.order.pay.entity.PayOrder; import cn.bootx.platform.daxpay.service.param.order.PayOrderQuery; -import cn.hutool.core.text.NamingCase; 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; -import java.util.Objects; import java.util.Optional; /** * 支付订单 + * 注意: 增删改需要使用 PayOrderQueryService 服务类, 不可以直接使用此dao, 因为订单超时任务需要处理 + * * @author xxm * @since 2023/12/18 */ diff --git a/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/order/refund/entity/PayRefundChannelOrder.java b/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/order/refund/entity/PayRefundChannelOrder.java index 6dae3849..56fa29bb 100644 --- a/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/order/refund/entity/PayRefundChannelOrder.java +++ b/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/order/refund/entity/PayRefundChannelOrder.java @@ -7,6 +7,8 @@ import cn.bootx.platform.daxpay.service.core.order.refund.convert.RefundOrderCha import cn.bootx.platform.daxpay.service.dto.order.refund.RefundChannelOrderDto; import cn.bootx.table.modify.annotation.DbColumn; import cn.bootx.table.modify.annotation.DbTable; +import com.baomidou.mybatisplus.annotation.FieldStrategy; +import com.baomidou.mybatisplus.annotation.TableField; import com.baomidou.mybatisplus.annotation.TableName; import lombok.Data; import lombok.EqualsAndHashCode; @@ -44,6 +46,10 @@ public class PayRefundChannelOrder extends MpCreateEntity implements EntityBaseF @DbColumn(comment = "退款金额") private Integer amount; + @DbColumn(comment = "剩余可退余额") + @TableField(updateStrategy = FieldStrategy.NEVER) + private Integer refundableAmount; + /** * 退款状态 * @see PayRefundStatusEnum @@ -51,7 +57,7 @@ public class PayRefundChannelOrder extends MpCreateEntity implements EntityBaseF @DbColumn(comment = "退款状态") private String status; - @DbColumn(comment = "退款时间") + @DbColumn(comment = "退款完成时间") private LocalDateTime refundTime; /** diff --git a/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/order/refund/entity/PayRefundOrder.java b/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/order/refund/entity/PayRefundOrder.java index acc6b7e8..1ccd4888 100644 --- a/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/order/refund/entity/PayRefundOrder.java +++ b/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/order/refund/entity/PayRefundOrder.java @@ -90,7 +90,7 @@ public class PayRefundOrder extends MpBaseEntity implements EntityBaseFunction

new RuntimeException("支付单不存在")); // 关联支付通道支付单 Map payChannelOrderMap = payChannelOrderManager.findAllByPaymentId(refundOrder.getPaymentId()) @@ -127,7 +130,7 @@ public class RefundRepairService { .collect(Collectors.toList()); // 更新订单和退款相关订单 - payOrderManager.updateById(payOrder); + payOrderService.updateById(payOrder); refundOrderManager.updateById(refundOrder); payChannelOrderManager.updateAllById(payChannelOrders); refundChannelOrderManager.updateAllById(refundChannelOrders); @@ -183,7 +186,7 @@ public class RefundRepairService { // 更新订单和退款相关订单 payChannelOrderManager.updateAllById(payChannelOrders); - payOrderManager.updateById(payOrder); + payOrderService.updateById(payOrder); refundOrderManager.updateById(refundOrder); refundChannelOrderManager.updateAllById(refundChannelOrders); return repairResult; diff --git a/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/payment/sync/result/PayGatewaySyncResult.java b/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/payment/sync/result/PayGatewaySyncResult.java index f76b0c0a..d363ff7f 100644 --- a/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/payment/sync/result/PayGatewaySyncResult.java +++ b/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/payment/sync/result/PayGatewaySyncResult.java @@ -4,6 +4,8 @@ import cn.bootx.platform.daxpay.code.PaySyncStatusEnum; import lombok.Data; import lombok.experimental.Accessors; +import java.time.LocalDateTime; + import static cn.bootx.platform.daxpay.code.PaySyncStatusEnum.FAIL; /** @@ -22,6 +24,14 @@ public class PayGatewaySyncResult { */ private PaySyncStatusEnum syncStatus = FAIL; + /** + * 第三方支付网关生成的订单号, 用与将记录关联起来 + */ + private String gatewayOrderNo; + + /** 支付完成时间(通常用于接收异步支付返回的时间) */ + private LocalDateTime payTime; + /** 同步时网关返回的对象, 序列化为json字符串 */ private String syncInfo; diff --git a/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/payment/sync/result/RefundGatewaySyncResult.java b/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/payment/sync/result/RefundGatewaySyncResult.java index f75277f2..fd546bb4 100644 --- a/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/payment/sync/result/RefundGatewaySyncResult.java +++ b/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/payment/sync/result/RefundGatewaySyncResult.java @@ -4,7 +4,9 @@ import cn.bootx.platform.daxpay.code.PayRefundSyncStatusEnum; import lombok.Data; import lombok.experimental.Accessors; -import static cn.bootx.platform.daxpay.code.PayRefundSyncStatusEnum.FAIL; +import java.time.LocalDateTime; + +import static cn.bootx.platform.daxpay.code.PayRefundSyncStatusEnum.REFUNDING; /** * 支付退款同步结果 @@ -16,14 +18,22 @@ import static cn.bootx.platform.daxpay.code.PayRefundSyncStatusEnum.FAIL; public class RefundGatewaySyncResult { /** - * 支付网关订单状态 + * 支付网关订单状态, 默认为退款中 * @see PayRefundSyncStatusEnum */ - private PayRefundSyncStatusEnum syncStatus = FAIL; + private PayRefundSyncStatusEnum syncStatus = REFUNDING; /** 同步时网关返回的对象, 序列化为json字符串 */ private String syncInfo; + /** + * 第三方支付网关生成的订单号, 用与将记录关联起来 + */ + private String gatewayOrderNo; + + /** 退款完成时间(通常用于接收网关返回的时间) */ + private LocalDateTime refundTime; + /** 错误提示码 */ private String errorCode; diff --git a/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/payment/sync/service/PayRefundSyncService.java b/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/payment/sync/service/PayRefundSyncService.java index 1536c0a8..d888b1c4 100644 --- a/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/payment/sync/service/PayRefundSyncService.java +++ b/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/payment/sync/service/PayRefundSyncService.java @@ -2,6 +2,7 @@ package cn.bootx.platform.daxpay.service.core.payment.sync.service; import cn.bootx.platform.common.core.exception.BizException; import cn.bootx.platform.common.core.exception.RepetitiveOperationException; +import cn.bootx.platform.daxpay.code.PayRefundStatusEnum; import cn.bootx.platform.daxpay.code.PayRefundSyncStatusEnum; import cn.bootx.platform.daxpay.exception.pay.PayFailureException; import cn.bootx.platform.daxpay.param.pay.RefundSyncParam; @@ -53,19 +54,23 @@ public class PayRefundSyncService { @Transactional(propagation = Propagation.REQUIRES_NEW, rollbackFor = Exception.class) public SyncResult sync(RefundSyncParam param){ // 先获取退款单 - PayRefundOrder requestOrder; + PayRefundOrder refundOrder; if (Objects.nonNull(param.getRefundId())){ - requestOrder = refundOrderManager.findById(param.getRefundId()) + refundOrder = refundOrderManager.findById(param.getRefundId()) .orElseThrow(() -> new PayFailureException("未查询到退款订单")); } else { - requestOrder = refundOrderManager.findByRefundNo(param.getRefundNo()) + refundOrder = refundOrderManager.findByRefundNo(param.getRefundNo()) .orElseThrow(() -> new PayFailureException("未查询到退款订单")); } // 如果不是异步支付, 直接返回返回 - if (!requestOrder.isAsyncPay()){ + if (!refundOrder.isAsyncPay()){ return new SyncResult().setSuccess(false).setRepair(false).setErrorMsg("订单没有异步通道的退款,不需要同步"); } - return this.syncRefundOrder(requestOrder); + // 如果订单已经关闭, 直接返回失败 + if (Objects.equals(refundOrder.getStatus(), PayRefundStatusEnum.CLOSE.getCode())){ + return new SyncResult().setSuccess(false).setRepair(false).setErrorMsg("订单已经关闭,不需要同步"); + } + return this.syncRefundOrder(refundOrder); } /** @@ -82,6 +87,8 @@ public class PayRefundSyncService { // 获取支付同步策略类 AbsRefundSyncStrategy syncPayStrategy = RefundSyncStrategyFactory.create(refundOrder.getAsyncChannel()); syncPayStrategy.initRefundParam(refundOrder); + // 同步前处理, 主要预防请求过于迅速 + syncPayStrategy.doBeforeHandler(); // 执行操作, 获取支付网关同步的结果 RefundGatewaySyncResult syncResult = syncPayStrategy.doSyncStatus(); @@ -90,12 +97,23 @@ public class PayRefundSyncService { // 同步失败, 返回失败响应, 同时记录失败的日志 return new SyncResult().setErrorMsg(syncResult.getErrorMsg()); } + // 支付订单的网关订单号是否一致, 不一致进行更新 + if (!Objects.equals(syncResult.getGatewayOrderNo(), refundOrder.getGatewayOrderNo())){ + refundOrder.setGatewayOrderNo(syncResult.getGatewayOrderNo()); + refundOrderManager.updateById(refundOrder); + } // 判断网关状态是否和支付单一致, 同时特定情况下更新网关同步状态 boolean statusSync = this.checkSyncStatus(syncResult, refundOrder); RefundRepairResult repairResult = new RefundRepairResult(); try { // 状态不一致,执行退款单修复逻辑 if (!statusSync) { + // 如果没有支付来源, 设置支付来源为同步 + RepairLocal repairInfo = PaymentContextLocal.get().getRepairInfo(); + if (Objects.isNull(repairInfo.getSource())){ + repairInfo.setSource(PayRepairSourceEnum.SYNC); + } + repairInfo.setFinishTime(syncResult.getRefundTime()); repairResult = this.repairHandler(syncResult, refundOrder); } } catch (PayFailureException e) { @@ -119,11 +137,29 @@ public class PayRefundSyncService { /** * 检查状态是否一致 + * @see PayRefundSyncStatusEnum 同步返回类型 + * @see PayRefundStatusEnum 退款单状态 */ private boolean checkSyncStatus(RefundGatewaySyncResult syncResult, PayRefundOrder order){ PayRefundSyncStatusEnum syncStatus = syncResult.getSyncStatus(); String orderStatus = order.getStatus(); - return Objects.equals(orderStatus, syncStatus.getCode()); + // 退款完成 + if (Objects.equals(syncStatus, PayRefundSyncStatusEnum.SUCCESS)&& + Objects.equals(orderStatus, PayRefundStatusEnum.SUCCESS.getCode())) { + return true; + } + + // 退款失败 + if (Objects.equals(syncStatus, PayRefundSyncStatusEnum.FAIL)&& + Objects.equals(orderStatus, PayRefundStatusEnum.FAIL.getCode())) { + return true; + } + // 退款中 + if (Objects.equals(syncStatus, PayRefundSyncStatusEnum.REFUNDING)&& + Objects.equals(orderStatus, PayRefundStatusEnum.PROGRESS.getCode())) { + return true; + } + return false; } /** @@ -131,11 +167,6 @@ public class PayRefundSyncService { */ private RefundRepairResult repairHandler(RefundGatewaySyncResult syncResult, PayRefundOrder order){ PayRefundSyncStatusEnum syncStatusEnum = syncResult.getSyncStatus(); - // 如果没有支付来源, 设置支付来源为同步 - RepairLocal repairInfo = PaymentContextLocal.get().getRepairInfo(); - if (Objects.isNull(repairInfo.getSource())){ - repairInfo.setSource(PayRepairSourceEnum.SYNC); - } RefundRepairResult repair = new RefundRepairResult(); // 对支付网关同步的结果进行处理 switch (syncStatusEnum) { @@ -158,21 +189,20 @@ public class PayRefundSyncService { return repair; } - - /** * 保存同步记录 - * @param payOrder 支付单 + * @param refundOrder 支付单 * @param syncResult 同步结果 * @param repair 是否修复 * @param errorMsg 错误信息 */ - private void saveRecord(PayRefundOrder payOrder, RefundGatewaySyncResult syncResult, boolean repair, Long repairOrderId, String errorMsg){ + private void saveRecord(PayRefundOrder refundOrder, RefundGatewaySyncResult syncResult, boolean repair, Long repairOrderId, String errorMsg){ PaySyncRecord paySyncRecord = new PaySyncRecord() - .setOrderId(payOrder.getId()) - .setOrderNo(payOrder.getBusinessNo()) + .setOrderId(refundOrder.getId()) + .setOrderNo(refundOrder.getRefundNo()) .setSyncType(PaymentTypeEnum.REFUND.getCode()) - .setAsyncChannel(payOrder.getAsyncChannel()) + .setAsyncChannel(refundOrder.getAsyncChannel()) + .setGatewayOrderNo(syncResult.getGatewayOrderNo()) .setSyncInfo(syncResult.getSyncInfo()) .setGatewayStatus(syncResult.getSyncStatus().getCode()) .setRepairOrder(repair) diff --git a/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/payment/sync/service/PaySyncService.java b/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/payment/sync/service/PaySyncService.java index 39d85494..c996a81f 100644 --- a/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/payment/sync/service/PaySyncService.java +++ b/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/payment/sync/service/PaySyncService.java @@ -15,6 +15,7 @@ import cn.bootx.platform.daxpay.service.common.context.RepairLocal; import cn.bootx.platform.daxpay.service.common.local.PaymentContextLocal; import cn.bootx.platform.daxpay.service.core.order.pay.entity.PayOrder; import cn.bootx.platform.daxpay.service.core.order.pay.service.PayOrderQueryService; +import cn.bootx.platform.daxpay.service.core.order.pay.service.PayOrderService; import cn.bootx.platform.daxpay.service.core.payment.repair.result.PayRepairResult; import cn.bootx.platform.daxpay.service.core.payment.repair.service.PayRepairService; import cn.bootx.platform.daxpay.service.core.payment.sync.factory.PaySyncStrategyFactory; @@ -49,6 +50,8 @@ import static cn.bootx.platform.daxpay.code.PaySyncStatusEnum.*; public class PaySyncService { private final PayOrderQueryService payOrderQueryService; + private final PayOrderService payOrderService; + private final PaySyncRecordService paySyncRecordService; private final PayRepairService repairService; @@ -79,8 +82,8 @@ public class PaySyncService { /** * 同步支付状态, 开启一个新的事务, 不受外部抛出异常的影响 * 1. 如果状态一致, 不进行处理 - * 2. 如果状态不一致, 调用修复逻辑进行修复 - * todo 需要进行异常处理, 现在会有 Transaction rolled back because it has been marked as rollback-only 问题 + * 2. 如果状态不一致, 调用修复逻辑进行修复, 更新状态和完成时间 + * 3. 会更新关联网关订单号 */ @Transactional(propagation = Propagation.REQUIRES_NEW, rollbackFor = Exception.class) public SyncResult syncPayOrder(PayOrder payOrder) { @@ -102,13 +105,24 @@ public class PaySyncService { this.saveRecord(payOrder, syncResult, false, null, syncResult.getErrorMsg()); return new SyncResult().setErrorMsg(syncResult.getErrorMsg()); } - + // 支付订单的网关订单号是否一致, 不一致进行更新 + if (!Objects.equals(syncResult.getGatewayOrderNo(), payOrder.getGatewayOrderNo())){ + payOrder.setGatewayOrderNo(syncResult.getGatewayOrderNo()); + payOrderService.updateById(payOrder); + } // 判断网关状态是否和支付单一致, 同时特定情况下更新网关同步状态 boolean statusSync = this.checkAndAdjustSyncStatus(syncResult,payOrder); PayRepairResult repairResult = new PayRepairResult(); try { // 状态不一致,执行支付单修复逻辑 if (!statusSync){ + // 如果没有修复触发来源, 设置修复触发来源为同步 + RepairLocal repairInfo = PaymentContextLocal.get().getRepairInfo(); + if (Objects.isNull(repairInfo.getSource())){ + repairInfo.setSource(PayRepairSourceEnum.SYNC); + } + // 设置支付单完成时间 + repairInfo.setFinishTime(syncResult.getPayTime()); repairResult = this.repairHandler(syncResult, payOrder); } } catch (PayFailureException e) { @@ -178,11 +192,6 @@ public class PaySyncService { */ private PayRepairResult repairHandler(PayGatewaySyncResult syncResult, PayOrder payOrder){ PaySyncStatusEnum syncStatusEnum = syncResult.getSyncStatus(); - // 如果没有支付来源, 设置支付来源为同步 - RepairLocal repairInfo = PaymentContextLocal.get().getRepairInfo(); - if (Objects.isNull(repairInfo.getSource())){ - repairInfo.setSource(PayRepairSourceEnum.SYNC); - } PayRepairResult repair = new PayRepairResult(); // 对支付网关同步的结果进行处理 switch (syncStatusEnum) { diff --git a/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/record/sync/entity/PaySyncRecord.java b/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/record/sync/entity/PaySyncRecord.java index f23e01fe..a9974a55 100644 --- a/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/record/sync/entity/PaySyncRecord.java +++ b/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/record/sync/entity/PaySyncRecord.java @@ -3,6 +3,7 @@ package cn.bootx.platform.daxpay.service.core.record.sync.entity; import cn.bootx.platform.common.core.function.EntityBaseFunction; import cn.bootx.platform.common.mybatisplus.base.MpCreateEntity; import cn.bootx.platform.daxpay.code.PayChannelEnum; +import cn.bootx.platform.daxpay.code.PayRefundSyncStatusEnum; import cn.bootx.platform.daxpay.code.PaySyncStatusEnum; import cn.bootx.platform.daxpay.service.code.PaymentTypeEnum; import cn.bootx.platform.daxpay.service.core.record.sync.convert.PaySyncRecordConvert; @@ -36,6 +37,10 @@ public class PaySyncRecord extends MpCreateEntity implements EntityBaseFunction< @DbColumn(comment = "本地业务号") private String orderNo; + /** 网关订单号 */ + @DbColumn(comment = "网关订单号") + private String gatewayOrderNo; + /** * 同步类型 支付/退款 * @see PaymentTypeEnum @@ -43,14 +48,11 @@ public class PaySyncRecord extends MpCreateEntity implements EntityBaseFunction< @DbColumn(comment = "同步类型") private String syncType; - @DbColumn(comment = "同步的异步通道") - private String syncChannel; - /** - * 同步通道 + * 同步的异步通道 * @see PayChannelEnum#getCode() */ - @DbColumn(comment = "同步通道") + @DbColumn(comment = "同步的异步通道") private String asyncChannel; /** 网关返回的同步消息 */ @@ -61,6 +63,7 @@ public class PaySyncRecord extends MpCreateEntity implements EntityBaseFunction< /** * 网关返回状态 * @see PaySyncStatusEnum + * @see PayRefundSyncStatusEnum */ @DbColumn(comment = "网关返回状态") private String gatewayStatus; diff --git a/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/dto/order/refund/RefundChannelOrderDto.java b/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/dto/order/refund/RefundChannelOrderDto.java index 5b76bafb..87613acf 100644 --- a/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/dto/order/refund/RefundChannelOrderDto.java +++ b/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/dto/order/refund/RefundChannelOrderDto.java @@ -38,6 +38,9 @@ public class RefundChannelOrderDto extends BaseDto { @Schema(description = "退款金额") private Integer amount; + @Schema(description = "剩余可退余额") + private Integer refundableAmount; + /** * 退款状态 * @see PayRefundStatusEnum diff --git a/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/dto/record/sync/PaySyncRecordDto.java b/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/dto/record/sync/PaySyncRecordDto.java index 309a6932..bded7af5 100644 --- a/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/dto/record/sync/PaySyncRecordDto.java +++ b/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/dto/record/sync/PaySyncRecordDto.java @@ -2,6 +2,7 @@ package cn.bootx.platform.daxpay.service.dto.record.sync; import cn.bootx.platform.common.core.rest.dto.BaseDto; import cn.bootx.platform.daxpay.code.PayChannelEnum; +import cn.bootx.platform.daxpay.code.PayRefundSyncStatusEnum; import cn.bootx.platform.daxpay.code.PaySyncStatusEnum; import cn.bootx.table.modify.mysql.annotation.DbMySqlFieldType; import cn.bootx.table.modify.mysql.constants.MySqlFieldTypeEnum; @@ -21,19 +22,27 @@ import lombok.experimental.Accessors; @Schema(title = "支付同步订单") public class PaySyncRecordDto extends BaseDto { - /** 支付记录id */ - @Schema(description = "支付记录id") - private Long paymentId; + /** 本地订单ID */ + @Schema(description = "本地订单ID") + private Long orderId; - /** 业务号 */ - @Schema(description = "业务号") - private String businessNo; + /** 本地业务号 */ + @Schema(description = "本地业务号") + private String orderNo; + + /** 网关订单号 */ + @Schema(description = "网关订单号") + private String gatewayOrderNo; + + /** 同步类型 */ + @Schema(description = "同步类型") + private String syncType; /** - * 同步通道 + * 同步的异步通道 * @see PayChannelEnum#getCode() */ - @Schema(description = "同步通道") + @Schema(description = "同步的异步通道") private String asyncChannel; /** 通知消息 */ @@ -44,6 +53,7 @@ public class PaySyncRecordDto extends BaseDto { /** * 网关返回状态 * @see PaySyncStatusEnum + * @see PayRefundSyncStatusEnum */ @Schema(description = "网关返回状态") private String gatewayStatus; diff --git a/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/func/AbsRefundRepairStrategy.java b/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/func/AbsRefundRepairStrategy.java index 5998997d..abd0c4b9 100644 --- a/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/func/AbsRefundRepairStrategy.java +++ b/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/func/AbsRefundRepairStrategy.java @@ -72,6 +72,8 @@ public abstract class AbsRefundRepairStrategy implements PayStrategy{ payChannelOrder.setStatus(PayStatusEnum.PARTIAL_REFUND.getCode()); } + // 如果失败, 可退余额设置为null + refundChannelOrder.setRefundableAmount(null); refundChannelOrder.setStatus(PayRefundStatusEnum.CLOSE.getCode()); } diff --git a/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/func/AbsRefundStrategy.java b/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/func/AbsRefundStrategy.java index 2af3234f..ca04ad93 100644 --- a/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/func/AbsRefundStrategy.java +++ b/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/func/AbsRefundStrategy.java @@ -76,8 +76,7 @@ public abstract class AbsRefundStrategy implements PayStrategy{ */ public void doSuccessHandler() { // 更新退款订单数据状态 - this.refundChannelOrder.setStatus(PayRefundStatusEnum.SUCCESS.getCode()) - .setRefundTime(LocalDateTime.now()); + this.refundChannelOrder.setStatus(PayRefundStatusEnum.SUCCESS.getCode()).setRefundTime(LocalDateTime.now()); // 支付通道订单可退余额 int refundableBalance = this.getPayChannelOrder().getRefundableBalance() - this.refundChannelOrder.getAmount(); @@ -91,11 +90,14 @@ public abstract class AbsRefundStrategy implements PayStrategy{ * 生成通道退款订单对象 */ public void generateChannelOrder() { + int refundableAmount = this.getPayChannelOrder().getRefundableBalance() - this.getRefundChannelParam().getAmount(); + this.refundChannelOrder = new PayRefundChannelOrder() .setPayChannelId(this.getPayChannelOrder().getId()) .setAsync(this.getPayChannelOrder().isAsync()) .setChannel(this.getPayChannelOrder().getChannel()) .setOrderAmount(this.getPayChannelOrder().getAmount()) + .setRefundableAmount(refundableAmount) .setAmount(this.getRefundChannelParam().getAmount()); }