From bf135eec40f000ad917969fcbe18ba5edba842e0 Mon Sep 17 00:00:00 2001 From: DaxPay Date: Fri, 26 Apr 2024 18:04:45 +0800 Subject: [PATCH] =?UTF-8?q?ref=20=E5=9B=9E=E8=B0=83=E7=9B=B8=E5=85=B3?= =?UTF-8?q?=E9=80=BB=E8=BE=91=E8=B0=83=E6=95=B4,=20=E5=8E=BB=E9=99=A4?= =?UTF-8?q?=E7=BB=A7=E6=89=BF=E5=85=B3=E7=B3=BB.=20=E8=A1=A5=E5=85=85?= =?UTF-8?q?=E7=AD=BE=E5=90=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- _doc/Task.md | 8 +- .../order/RefundOrderController.java | 14 ++- .../exception/pay/PayFailureException.java | 2 +- .../daxpay/result/pay/SyncResult.java | 4 +- .../gateway/controller/UniPayController.java | 7 +- .../service/common/context/CallbackLocal.java | 5 + .../alipay/service/AliPayCallbackService.java | 72 +++++++++++---- .../service/UnionPayCallbackService.java | 71 +++++++++++--- .../service/WeChatPayCallbackService.java | 70 +++++++++++--- .../convert/RefundOrderChannelConvert.java | 15 --- .../refund/convert/RefundOrderConvert.java | 4 + .../order/refund/entity/RefundOrderExtra.java | 9 +- .../service/RefundOrderQueryService.java | 13 ++- .../close/service/PayCloseService.java | 17 ++-- .../common/service/PaymentSignService.java | 2 +- .../payment/pay/service/PayAssistService.java | 13 ++- .../core/payment/pay/service/PayService.java | 4 +- .../payment/refund/service/RefundService.java | 59 ++++++++---- .../payment/sync/service/PaySyncService.java | 37 +++++--- .../sync/service/RefundSyncService.java | 2 +- .../service/PayCallbackRecordService.java | 17 +++- .../dto/order/pay/PayOrderDetailDto.java | 9 ++ .../service/dto/order/pay/PayOrderDto.java | 2 +- .../order/refund/RefundChannelOrderDto.java | 48 ---------- .../dto/order/refund/RefundOrderDto.java | 2 +- .../dto/order/refund/RefundOrderExtraDto.java | 47 ++++++++++ .../service/func/AbsCallbackStrategy.java | 92 +------------------ .../exception/PaymentExceptionHandler.java | 36 ++++++++ .../service/param/order/PayOrderQuery.java | 2 +- .../param/reconcile/ReconcileOrderQuery.java | 4 +- 30 files changed, 420 insertions(+), 267 deletions(-) delete mode 100644 daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/order/refund/convert/RefundOrderChannelConvert.java delete mode 100644 daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/dto/order/refund/RefundChannelOrderDto.java create mode 100644 daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/dto/order/refund/RefundOrderExtraDto.java create mode 100644 daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/handler/exception/PaymentExceptionHandler.java diff --git a/_doc/Task.md b/_doc/Task.md index 6f5d526e8..3da2aea32 100644 --- a/_doc/Task.md +++ b/_doc/Task.md @@ -1,9 +1,9 @@ ## 单商户 2.0.6: 去除组合支付, 一系列代码优化 - [x] 删除现金支付和储值卡支付方式 -- [ ] 支付订单 +- [x] 支付订单 - [x] 支付改造 - - [ ] 回调改造, 不再使用继承类, 简化逻辑 + - [x] 回调改造, 不再使用继承类, 简化逻辑 - [x] 发送通知改造 - [x] 去除组合支付和场景,降低逻辑的的复杂度 - [x] 退款场景 @@ -15,8 +15,10 @@ - [x] 三方支付外部订单号规则优化: 支付P、退款R、分账A,根据环境加前缀:DEV_、DEMO_、PRE_ - [ ] 资金流水优化 - [ ] 金额显示统一使用元 -- [ ] 响应数据和通知数据全部都进行签名 +- - [ ] 数据加密方式改为类型处理器模式 +- [ ] 优化 +- [ ] 增加支付错误Rest处理器, 统一支付错误返回数据格式 2.0.7: 分账完善 - [ ] 支持分账组分账和自己传接收方进行分账 diff --git a/daxpay-single/daxpay-single-admin/src/main/java/cn/bootx/platform/daxpay/admin/controller/order/RefundOrderController.java b/daxpay-single/daxpay-single-admin/src/main/java/cn/bootx/platform/daxpay/admin/controller/order/RefundOrderController.java index fb928429f..c02f7277b 100644 --- a/daxpay-single/daxpay-single-admin/src/main/java/cn/bootx/platform/daxpay/admin/controller/order/RefundOrderController.java +++ b/daxpay-single/daxpay-single-admin/src/main/java/cn/bootx/platform/daxpay/admin/controller/order/RefundOrderController.java @@ -11,6 +11,7 @@ import cn.bootx.platform.daxpay.service.core.order.refund.service.RefundOrderSer import cn.bootx.platform.daxpay.service.core.payment.refund.service.RefundService; import cn.bootx.platform.daxpay.service.core.payment.sync.service.RefundSyncService; import cn.bootx.platform.daxpay.service.dto.order.refund.RefundOrderDto; +import cn.bootx.platform.daxpay.service.dto.order.refund.RefundOrderExtraDto; import cn.bootx.platform.daxpay.service.param.order.PayOrderRefundParam; import cn.bootx.platform.daxpay.service.param.order.RefundOrderQuery; import io.swagger.v3.oas.annotations.Operation; @@ -31,7 +32,6 @@ public class RefundOrderController { private final RefundOrderService refundOrderService; private final RefundOrderQueryService refundOrderQueryService; private final RefundSyncService refundSyncService; - private final RefundService refundService; @Operation(summary = "分页查询") @GetMapping("/page") @@ -45,6 +45,11 @@ public class RefundOrderController { return Res.ok(refundOrderQueryService.findById(id)); } + @Operation(summary = "查询扩展信息") + @GetMapping("/findExtraById") + public ResResult findExtraById(Long id){ + return Res.ok(refundOrderQueryService.findExtraById(id)); + } @Operation(summary = "手动发起退款") @PostMapping("/refund") @@ -53,13 +58,6 @@ public class RefundOrderController { return Res.ok(); } - @Operation(summary = "重新发起退款") - @PostMapping("/resetRefund") - public ResResult resetRefund(String bizRefundNo){ - PayOrderRefundParam param = new PayOrderRefundParam(); - return Res.ok(); - } - @Operation(summary = "退款同步") @PostMapping("/syncById") public ResResult syncById(String refundNo){ diff --git a/daxpay-single/daxpay-single-core/src/main/java/cn/bootx/platform/daxpay/exception/pay/PayFailureException.java b/daxpay-single/daxpay-single-core/src/main/java/cn/bootx/platform/daxpay/exception/pay/PayFailureException.java index 281f3bd33..45d4d05be 100644 --- a/daxpay-single/daxpay-single-core/src/main/java/cn/bootx/platform/daxpay/exception/pay/PayFailureException.java +++ b/daxpay-single/daxpay-single-core/src/main/java/cn/bootx/platform/daxpay/exception/pay/PayFailureException.java @@ -4,7 +4,7 @@ import cn.bootx.platform.common.core.exception.BizException; import cn.bootx.platform.daxpay.code.DaxPayErrorCode; /** - * 付款错误 + * 支付错误 * * @author xxm * @since 2020/12/8 diff --git a/daxpay-single/daxpay-single-core/src/main/java/cn/bootx/platform/daxpay/result/pay/SyncResult.java b/daxpay-single/daxpay-single-core/src/main/java/cn/bootx/platform/daxpay/result/pay/SyncResult.java index b79d34d63..b8c27acda 100644 --- a/daxpay-single/daxpay-single-core/src/main/java/cn/bootx/platform/daxpay/result/pay/SyncResult.java +++ b/daxpay-single/daxpay-single-core/src/main/java/cn/bootx/platform/daxpay/result/pay/SyncResult.java @@ -32,9 +32,9 @@ public class SyncResult extends CommonResult { private String gatewayStatus = FAIL.getCode(); @Schema(description = "是否进行了修复") - private boolean repair; + private Boolean repair; @Schema(description = "修复号") - private String repairOrderNo; + private String repairNo; } diff --git a/daxpay-single/daxpay-single-gateway/src/main/java/cn/bootx/platform/daxpay/gateway/controller/UniPayController.java b/daxpay-single/daxpay-single-gateway/src/main/java/cn/bootx/platform/daxpay/gateway/controller/UniPayController.java index 870ab9240..349af298e 100644 --- a/daxpay-single/daxpay-single-gateway/src/main/java/cn/bootx/platform/daxpay/gateway/controller/UniPayController.java +++ b/daxpay-single/daxpay-single-gateway/src/main/java/cn/bootx/platform/daxpay/gateway/controller/UniPayController.java @@ -1,6 +1,5 @@ package cn.bootx.platform.daxpay.gateway.controller; -import cn.bootx.platform.common.core.annotation.CountTime; import cn.bootx.platform.common.core.annotation.IgnoreAuth; import cn.bootx.platform.daxpay.code.PaymentApiCode; import cn.bootx.platform.daxpay.param.payment.allocation.AllocationFinishParam; @@ -12,6 +11,7 @@ import cn.bootx.platform.daxpay.param.payment.refund.RefundParam; import cn.bootx.platform.daxpay.param.payment.refund.RefundSyncParam; import cn.bootx.platform.daxpay.result.DaxResult; import cn.bootx.platform.daxpay.result.allocation.AllocationResult; +import cn.bootx.platform.daxpay.result.pay.PayCloseResult; import cn.bootx.platform.daxpay.result.pay.PayResult; import cn.bootx.platform.daxpay.result.pay.RefundResult; import cn.bootx.platform.daxpay.result.pay.SyncResult; @@ -60,9 +60,8 @@ public class UniPayController { @PaymentApi(PaymentApiCode.CLOSE) @Operation(summary = "支付关闭接口") @PostMapping("/close") - public DaxResult close(@RequestBody PayCloseParam param){ - payCloseService.close(param); - return DaxRes.ok(); + public DaxResult close(@RequestBody PayCloseParam param){ + return DaxRes.ok(payCloseService.close(param)); } @PaymentApi(PaymentApiCode.REFUND) diff --git a/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/common/context/CallbackLocal.java b/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/common/context/CallbackLocal.java index f3e3c324e..12b6b6b53 100644 --- a/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/common/context/CallbackLocal.java +++ b/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/common/context/CallbackLocal.java @@ -31,6 +31,11 @@ public class CallbackLocal { */ private String outTradeNo; + /** + * 通道 + */ + private String channel; + /** * 三方支付系统返回状态 * @see PayStatusEnum 支付状态 diff --git a/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/channel/alipay/service/AliPayCallbackService.java b/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/channel/alipay/service/AliPayCallbackService.java index dac9d2cd3..40d41cd10 100644 --- a/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/channel/alipay/service/AliPayCallbackService.java +++ b/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/channel/alipay/service/AliPayCallbackService.java @@ -4,11 +4,15 @@ import cn.bootx.platform.common.core.util.CertUtil; import cn.bootx.platform.common.core.util.LocalDateTimeUtil; import cn.bootx.platform.daxpay.code.PayChannelEnum; import cn.bootx.platform.daxpay.code.PayStatusEnum; +import cn.bootx.platform.daxpay.service.code.PayCallbackStatusEnum; +import cn.bootx.platform.daxpay.service.code.PayRepairSourceEnum; import cn.bootx.platform.daxpay.service.code.PaymentTypeEnum; import cn.bootx.platform.daxpay.service.common.context.CallbackLocal; import cn.bootx.platform.daxpay.service.common.local.PaymentContextLocal; import cn.bootx.platform.daxpay.service.core.channel.alipay.entity.AliPayConfig; -import cn.bootx.platform.daxpay.service.func.AbsCallbackStrategy; +import cn.bootx.platform.daxpay.service.core.payment.callback.service.PayCallbackService; +import cn.bootx.platform.daxpay.service.core.payment.callback.service.RefundCallbackService; +import cn.bootx.platform.daxpay.service.core.record.callback.service.PayCallbackRecordService; import cn.hutool.core.date.DatePattern; import cn.hutool.core.util.CharsetUtil; import cn.hutool.core.util.StrUtil; @@ -16,11 +20,11 @@ import cn.hutool.json.JSONUtil; import com.alipay.api.AlipayApiException; import com.alipay.api.AlipayConstants; import com.alipay.api.internal.util.AlipaySignature; +import lombok.RequiredArgsConstructor; import lombok.SneakyThrows; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; -import javax.annotation.Resource; import java.time.LocalDateTime; import java.util.Map; import java.util.Objects; @@ -35,25 +39,65 @@ import static cn.bootx.platform.daxpay.service.code.AliPayCode.*; */ @Slf4j @Service -public class AliPayCallbackService extends AbsCallbackStrategy { +@RequiredArgsConstructor +public class AliPayCallbackService { - @Resource - private AliPayConfigService aliasConfigService; + private final AliPayConfigService aliPayConfigService; + + private final PayCallbackRecordService callbackService; + + private final PayCallbackService payCallbackService; + + private final RefundCallbackService refundCallbackService; /** - * 策略标识 + * 回调处理入口 */ - @Override - public PayChannelEnum getChannel() { - return PayChannelEnum.ALI; + public String callback(Map params){ + CallbackLocal callbackInfo = PaymentContextLocal.get().getCallbackInfo(); + try { + // 将参数写入到上下文中 + callbackInfo.getCallbackParam().putAll(params); + + // 判断并保存回调类型 + PaymentTypeEnum callbackType = this.getCallbackType(); + callbackInfo.setCallbackType(callbackType) + .setChannel(PayChannelEnum.ALI.getCode()); + + // 验证消息 + if (!this.verifyNotify()) { + callbackInfo.setCallbackStatus(PayCallbackStatusEnum.FAIL).setMsg("验证信息格式不通过"); + // 消息有问题, 保存记录并返回 + callbackService.saveCallbackRecord(); + return null; + } + // 提前设置订单修复的来源 + PaymentContextLocal.get().getRepairInfo().setSource(PayRepairSourceEnum.CALLBACK); + + if (callbackType == PaymentTypeEnum.PAY){ + // 解析支付数据并放处理 + this.resolvePayData(); + payCallbackService.payCallback(); + } else { + // 解析退款数据并放处理 + this.resolveRefundData(); + refundCallbackService.refundCallback(); + } + callbackService.saveCallbackRecord(); + return this.getReturnMsg(); + } catch (Exception e) { + log.error("回调处理失败", e); + callbackInfo.setCallbackStatus(PayCallbackStatusEnum.FAIL).setMsg("回调处理失败: "+e.getMessage()); + callbackService.saveCallbackRecord(); + throw e; + } } /** * 验证信息格式是否合法 */ @SneakyThrows - @Override public boolean verifyNotify() { Map params =PaymentContextLocal.get().getCallbackInfo().getCallbackParam(); String callReq = JSONUtil.toJsonStr(params); @@ -63,7 +107,7 @@ public class AliPayCallbackService extends AbsCallbackStrategy { log.error("支付宝回调报文appId为空"); return false; } - AliPayConfig alipayConfig = aliasConfigService.getConfig(); + AliPayConfig alipayConfig = aliPayConfigService.getConfig(); if (Objects.isNull(alipayConfig)) { log.error("支付宝支付配置不存在"); return false; @@ -84,7 +128,6 @@ public class AliPayCallbackService extends AbsCallbackStrategy { /** * 解析支付数据并放到上下文中 */ - @Override public void resolvePayData() { CallbackLocal callback = PaymentContextLocal.get().getCallbackInfo(); Map callbackParam = callback.getCallbackParam(); @@ -112,7 +155,6 @@ public class AliPayCallbackService extends AbsCallbackStrategy { * 解析退款回调数据并放到上下文中 * 注意: 支付宝退款没有网关订单号, 网关订单号是支付单的 */ - @Override public void resolveRefundData() { CallbackLocal callback = PaymentContextLocal.get().getCallbackInfo(); Map callbackParam = callback.getCallbackParam(); @@ -138,7 +180,6 @@ public class AliPayCallbackService extends AbsCallbackStrategy { * * @see PaymentTypeEnum */ - @Override public PaymentTypeEnum getCallbackType() { CallbackLocal callback = PaymentContextLocal.get().getCallbackInfo(); Map callbackParam = callback.getCallbackParam(); @@ -152,9 +193,8 @@ public class AliPayCallbackService extends AbsCallbackStrategy { } /** - * 返回响应结果 + * 返回值 */ - @Override public String getReturnMsg() { return "success"; } diff --git a/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/channel/union/service/UnionPayCallbackService.java b/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/channel/union/service/UnionPayCallbackService.java index a465f4504..cfeb662e0 100644 --- a/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/channel/union/service/UnionPayCallbackService.java +++ b/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/channel/union/service/UnionPayCallbackService.java @@ -4,11 +4,16 @@ import cn.bootx.platform.common.core.util.LocalDateTimeUtil; import cn.bootx.platform.daxpay.code.PayChannelEnum; import cn.bootx.platform.daxpay.code.PayStatusEnum; import cn.bootx.platform.daxpay.code.RefundStatusEnum; +import cn.bootx.platform.daxpay.service.code.PayCallbackStatusEnum; +import cn.bootx.platform.daxpay.service.code.PayRepairSourceEnum; import cn.bootx.platform.daxpay.service.code.PaymentTypeEnum; import cn.bootx.platform.daxpay.service.code.UnionPayCode; import cn.bootx.platform.daxpay.service.common.context.CallbackLocal; import cn.bootx.platform.daxpay.service.common.local.PaymentContextLocal; import cn.bootx.platform.daxpay.service.core.channel.union.entity.UnionPayConfig; +import cn.bootx.platform.daxpay.service.core.payment.callback.service.PayCallbackService; +import cn.bootx.platform.daxpay.service.core.payment.callback.service.RefundCallbackService; +import cn.bootx.platform.daxpay.service.core.record.callback.service.PayCallbackRecordService; import cn.bootx.platform.daxpay.service.func.AbsCallbackStrategy; import cn.bootx.platform.daxpay.service.sdk.union.api.UnionPayKit; import cn.hutool.core.date.DatePattern; @@ -35,15 +40,62 @@ import static cn.bootx.platform.daxpay.service.code.UnionPayCode.*; @Slf4j @Service @RequiredArgsConstructor -public class UnionPayCallbackService extends AbsCallbackStrategy { +public class UnionPayCallbackService { - @Resource - private UnionPayConfigService unionPayConfigService; + private final UnionPayConfigService unionPayConfigService; + + private final PayCallbackRecordService callbackService; + + private final PayCallbackService payCallbackService; + + private final RefundCallbackService refundCallbackService; + + /** + * 回调处理入口 + */ + public String callback(Map params){ + CallbackLocal callbackInfo = PaymentContextLocal.get().getCallbackInfo(); + try { + // 将参数写入到上下文中 + callbackInfo.getCallbackParam().putAll(params); + + // 判断并保存回调类型 + PaymentTypeEnum callbackType = this.getCallbackType(); + callbackInfo.setCallbackType(callbackType) + .setChannel(PayChannelEnum.ALI.getCode()); + + // 验证消息 + if (!this.verifyNotify()) { + callbackInfo.setCallbackStatus(PayCallbackStatusEnum.FAIL).setMsg("验证信息格式不通过"); + // 消息有问题, 保存记录并返回 + callbackService.saveCallbackRecord(); + return null; + } + // 提前设置订单修复的来源 + PaymentContextLocal.get().getRepairInfo().setSource(PayRepairSourceEnum.CALLBACK); + + if (callbackType == PaymentTypeEnum.PAY){ + // 解析支付数据并放处理 + this.resolvePayData(); + payCallbackService.payCallback(); + } else { + // 解析退款数据并放处理 + this.resolveRefundData(); + refundCallbackService.refundCallback(); + } + callbackService.saveCallbackRecord(); + return this.getReturnMsg(); + } catch (Exception e) { + log.error("回调处理失败", e); + callbackInfo.setCallbackStatus(PayCallbackStatusEnum.FAIL).setMsg("回调处理失败: "+e.getMessage()); + callbackService.saveCallbackRecord(); + throw e; + } + } /** * 验证信息格式 */ - @Override public boolean verifyNotify() { CallbackLocal callbackInfo = PaymentContextLocal.get().getCallbackInfo(); Map params = callbackInfo.getCallbackParam(); @@ -64,7 +116,6 @@ public class UnionPayCallbackService extends AbsCallbackStrategy { * * @see PaymentTypeEnum */ - @Override public PaymentTypeEnum getCallbackType() { CallbackLocal callbackInfo = PaymentContextLocal.get().getCallbackInfo(); Map params = callbackInfo.getCallbackParam(); @@ -79,7 +130,6 @@ public class UnionPayCallbackService extends AbsCallbackStrategy { /** * 解析支付回调数据并放到上下文中 */ - @Override public void resolvePayData() { CallbackLocal callbackInfo = PaymentContextLocal.get().getCallbackInfo(); Map callbackParam = callbackInfo.getCallbackParam(); @@ -107,7 +157,6 @@ public class UnionPayCallbackService extends AbsCallbackStrategy { /** * 解析退款回调数据并放到上下文中 */ - @Override public void resolveRefundData() { // 云闪付需要延迟半秒再进行处理, 不然会出现业务未处理完, 但回调已经到达的情况 ThreadUtil.sleep(100); @@ -139,16 +188,8 @@ public class UnionPayCallbackService extends AbsCallbackStrategy { /** * 返回响应结果 */ - @Override public String getReturnMsg() { return "success"; } - /** - * 策略标识 - */ - @Override - public PayChannelEnum getChannel() { - return PayChannelEnum.UNION_PAY; - } } 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 443ecac23..48ab8e863 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 @@ -4,16 +4,23 @@ import cn.bootx.platform.common.core.util.LocalDateTimeUtil; import cn.bootx.platform.daxpay.code.PayChannelEnum; import cn.bootx.platform.daxpay.code.PayStatusEnum; import cn.bootx.platform.daxpay.code.RefundStatusEnum; +import cn.bootx.platform.daxpay.service.code.PayCallbackStatusEnum; +import cn.bootx.platform.daxpay.service.code.PayRepairSourceEnum; import cn.bootx.platform.daxpay.service.code.PaymentTypeEnum; import cn.bootx.platform.daxpay.service.common.context.CallbackLocal; import cn.bootx.platform.daxpay.service.common.local.PaymentContextLocal; +import cn.bootx.platform.daxpay.service.core.channel.alipay.service.AliPayConfigService; import cn.bootx.platform.daxpay.service.core.channel.wechat.entity.WeChatPayConfig; +import cn.bootx.platform.daxpay.service.core.payment.callback.service.PayCallbackService; +import cn.bootx.platform.daxpay.service.core.payment.callback.service.RefundCallbackService; +import cn.bootx.platform.daxpay.service.core.record.callback.service.PayCallbackRecordService; import cn.bootx.platform.daxpay.service.func.AbsCallbackStrategy; import cn.hutool.core.date.DatePattern; import cn.hutool.core.util.StrUtil; import cn.hutool.json.JSONUtil; import com.ijpay.core.enums.SignType; import com.ijpay.core.kit.WxPayKit; +import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; @@ -34,22 +41,59 @@ import static cn.bootx.platform.daxpay.service.code.WeChatPayCode.*; */ @Slf4j @Service -public class WeChatPayCallbackService extends AbsCallbackStrategy { - @Resource - private WeChatPayConfigService weChatPayConfigService; +@RequiredArgsConstructor +public class WeChatPayCallbackService { + private final WeChatPayConfigService weChatPayConfigService; + + private final PayCallbackRecordService callbackService; + + private final PayCallbackService payCallbackService; + + private final RefundCallbackService refundCallbackService; /** - * 策略标识 + * 回调处理入口 */ - @Override - public PayChannelEnum getChannel() { - return PayChannelEnum.WECHAT; + public String callback(Map params){ + CallbackLocal callbackInfo = PaymentContextLocal.get().getCallbackInfo(); + try { + // 将参数写入到上下文中 + callbackInfo.getCallbackParam().putAll(params); + + // 判断并保存回调类型 + PaymentTypeEnum callbackType = this.getCallbackType(); + callbackInfo.setCallbackType(callbackType) + .setChannel(PayChannelEnum.ALI.getCode()); + + // 验证消息 + if (!this.verifyNotify()) { + callbackInfo.setCallbackStatus(PayCallbackStatusEnum.FAIL).setMsg("验证信息格式不通过"); + // 消息有问题, 保存记录并返回 + callbackService.saveCallbackRecord(); + return null; + } + // 提前设置订单修复的来源 + PaymentContextLocal.get().getRepairInfo().setSource(PayRepairSourceEnum.CALLBACK); + + if (callbackType == PaymentTypeEnum.PAY){ + // 解析支付数据并放处理 + this.resolvePayData(); + payCallbackService.payCallback(); + } else { + // 解析退款数据并放处理 + this.resolveRefundData(); + refundCallbackService.refundCallback(); + } + callbackService.saveCallbackRecord(); + return this.getReturnMsg(); + } catch (Exception e) { + log.error("回调处理失败", e); + callbackInfo.setCallbackStatus(PayCallbackStatusEnum.FAIL).setMsg("回调处理失败: "+e.getMessage()); + callbackService.saveCallbackRecord(); + throw e; + } } - /** - * 验证回调消息 - */ - @Override public boolean verifyNotify() { CallbackLocal callbackInfo = PaymentContextLocal.get().getCallbackInfo(); Map params = callbackInfo.getCallbackParam(); @@ -79,7 +123,6 @@ public class WeChatPayCallbackService extends AbsCallbackStrategy { /** * 解析支付数据放到上下文中 */ - @Override public void resolvePayData() { CallbackLocal callbackInfo = PaymentContextLocal.get().getCallbackInfo(); Map callbackParam = callbackInfo.getCallbackParam(); @@ -105,7 +148,6 @@ public class WeChatPayCallbackService extends AbsCallbackStrategy { /** * 解析退款回调数据并放到上下文中, 微信退款通知返回的数据需要进行解密 */ - @Override public void resolveRefundData() { CallbackLocal callbackInfo = PaymentContextLocal.get().getCallbackInfo(); Map callbackParam = callbackInfo.getCallbackParam(); @@ -145,7 +187,6 @@ public class WeChatPayCallbackService extends AbsCallbackStrategy { * * @see PaymentTypeEnum */ - @Override public PaymentTypeEnum getCallbackType() { CallbackLocal callbackInfo = PaymentContextLocal.get().getCallbackInfo(); Map callbackParam = callbackInfo.getCallbackParam(); @@ -159,7 +200,6 @@ public class WeChatPayCallbackService extends AbsCallbackStrategy { /** * 返回响应结果 */ - @Override public String getReturnMsg() { Map xml = new HashMap<>(4); xml.put(RETURN_CODE, "SUCCESS"); diff --git a/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/order/refund/convert/RefundOrderChannelConvert.java b/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/order/refund/convert/RefundOrderChannelConvert.java deleted file mode 100644 index 6c9d7f2ac..000000000 --- a/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/order/refund/convert/RefundOrderChannelConvert.java +++ /dev/null @@ -1,15 +0,0 @@ -package cn.bootx.platform.daxpay.service.core.order.refund.convert; - -import org.mapstruct.Mapper; -import org.mapstruct.factory.Mappers; - -/** - * - * @author xxm - * @since 2024/1/17 - */ -@Mapper -public interface RefundOrderChannelConvert { - RefundOrderChannelConvert CONVERT = Mappers.getMapper(RefundOrderChannelConvert.class); - -} diff --git a/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/order/refund/convert/RefundOrderConvert.java b/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/order/refund/convert/RefundOrderConvert.java index cb6d51ae5..c2a2523b2 100644 --- a/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/order/refund/convert/RefundOrderConvert.java +++ b/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/order/refund/convert/RefundOrderConvert.java @@ -2,7 +2,9 @@ package cn.bootx.platform.daxpay.service.core.order.refund.convert; import cn.bootx.platform.daxpay.result.order.RefundOrderResult; import cn.bootx.platform.daxpay.service.core.order.refund.entity.RefundOrder; +import cn.bootx.platform.daxpay.service.core.order.refund.entity.RefundOrderExtra; import cn.bootx.platform.daxpay.service.dto.order.refund.RefundOrderDto; +import cn.bootx.platform.daxpay.service.dto.order.refund.RefundOrderExtraDto; import org.mapstruct.Mapper; import org.mapstruct.factory.Mappers; @@ -17,6 +19,8 @@ public interface RefundOrderConvert { RefundOrderDto convert(RefundOrder in); + RefundOrderExtraDto convert(RefundOrderExtra in); + RefundOrderResult convertResult(RefundOrder in); diff --git a/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/order/refund/entity/RefundOrderExtra.java b/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/order/refund/entity/RefundOrderExtra.java index cd272ede3..f84173f6b 100644 --- a/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/order/refund/entity/RefundOrderExtra.java +++ b/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/order/refund/entity/RefundOrderExtra.java @@ -1,9 +1,12 @@ package cn.bootx.platform.daxpay.service.core.order.refund.entity; +import cn.bootx.platform.common.core.function.EntityBaseFunction; import cn.bootx.platform.common.mybatisplus.base.MpBaseEntity; import cn.bootx.platform.daxpay.param.channel.AliPayParam; import cn.bootx.platform.daxpay.param.channel.WalletPayParam; import cn.bootx.platform.daxpay.param.channel.WeChatPayParam; +import cn.bootx.platform.daxpay.service.core.order.refund.convert.RefundOrderConvert; +import cn.bootx.platform.daxpay.service.dto.order.refund.RefundOrderExtraDto; import cn.bootx.table.modify.annotation.DbColumn; import cn.bootx.table.modify.annotation.DbTable; import com.baomidou.mybatisplus.annotation.FieldStrategy; @@ -25,7 +28,7 @@ import java.time.LocalDateTime; @Accessors(chain = true) @TableName("pay_refund_order_extra") @DbTable(comment = "退款订单扩展信息") -public class RefundOrderExtra extends MpBaseEntity { +public class RefundOrderExtra extends MpBaseEntity implements EntityBaseFunction { /** 异步通知地址 */ @DbColumn(comment = "异步通知地址") @@ -54,4 +57,8 @@ public class RefundOrderExtra extends MpBaseEntity { @DbColumn(comment = "支付终端ip") private String clientIp; + @Override + public RefundOrderExtraDto toDto() { + return RefundOrderConvert.CONVERT.convert(this); + } } diff --git a/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/order/refund/service/RefundOrderQueryService.java b/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/order/refund/service/RefundOrderQueryService.java index 70ac163e6..f1117c886 100644 --- a/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/order/refund/service/RefundOrderQueryService.java +++ b/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/order/refund/service/RefundOrderQueryService.java @@ -8,9 +8,12 @@ import cn.bootx.platform.common.mybatisplus.util.MpUtil; import cn.bootx.platform.daxpay.param.payment.refund.QueryRefundParam; import cn.bootx.platform.daxpay.result.order.RefundOrderResult; import cn.bootx.platform.daxpay.service.core.order.refund.convert.RefundOrderConvert; +import cn.bootx.platform.daxpay.service.core.order.refund.dao.RefundOrderExtraManager; import cn.bootx.platform.daxpay.service.core.order.refund.dao.RefundOrderManager; import cn.bootx.platform.daxpay.service.core.order.refund.entity.RefundOrder; +import cn.bootx.platform.daxpay.service.core.order.refund.entity.RefundOrderExtra; import cn.bootx.platform.daxpay.service.dto.order.refund.RefundOrderDto; +import cn.bootx.platform.daxpay.service.dto.order.refund.RefundOrderExtraDto; import cn.bootx.platform.daxpay.service.param.order.RefundOrderQuery; import cn.hutool.core.util.StrUtil; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; @@ -31,7 +34,7 @@ import java.util.Optional; @RequiredArgsConstructor public class RefundOrderQueryService { private final RefundOrderManager refundOrderManager; - + private final RefundOrderExtraManager refundOrderExtraManager; /** * 分页查询 @@ -49,6 +52,14 @@ public class RefundOrderQueryService { .orElseThrow(() -> new DataNotExistException("退款订单不存在")); } + /** + * 根据id查询扩展信息 + */ + public RefundOrderExtraDto findExtraById(Long id) { + return refundOrderExtraManager.findById(id).map(RefundOrderExtra::toDto) + .orElseThrow(() -> new DataNotExistException("退款订单扩展信息不存在")); + } + /** * 根据退款号和商户退款号查询 */ diff --git a/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/payment/close/service/PayCloseService.java b/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/payment/close/service/PayCloseService.java index f6cc7c609..49c5f6980 100644 --- a/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/payment/close/service/PayCloseService.java +++ b/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/payment/close/service/PayCloseService.java @@ -6,6 +6,7 @@ import cn.bootx.platform.daxpay.code.PayStatusEnum; import cn.bootx.platform.daxpay.exception.pay.PayFailureException; import cn.bootx.platform.daxpay.param.payment.pay.PayCloseParam; import cn.bootx.platform.daxpay.result.pay.PayCloseResult; +import cn.bootx.platform.daxpay.result.pay.PayResult; import cn.bootx.platform.daxpay.service.common.context.PlatformLocal; import cn.bootx.platform.daxpay.service.common.local.PaymentContextLocal; import cn.bootx.platform.daxpay.service.core.order.pay.entity.PayOrder; @@ -65,12 +66,12 @@ public class PayCloseService { * 关闭支付记录 */ private PayCloseResult close(PayOrder payOrder) { + PayCloseResult result = new PayCloseResult(); try { // 状态检查, 只有支付中可以进行取消支付 if (!Objects.equals(payOrder.getStatus(), PayStatusEnum.PROGRESS.getCode())) { throw new PayFailureException("订单不是支付中, 无法进行关闭订单"); } - AbsPayCloseStrategy strategy = PayCloseStrategyFactory.create(payOrder.getChannel()); // 设置支付订单 strategy.setOrder(payOrder); @@ -78,15 +79,18 @@ public class PayCloseService { strategy.doBeforeCloseHandler(); // 执行关闭策略 strategy.doCloseHandler(); + // 成功处理 this.successHandler(payOrder); + // 签名 + this.sign(result); // 返回结果 - return this.sign(new PayCloseResult()); + return result; } catch (Exception e) { // 记录关闭失败的记录 this.saveRecord(payOrder, false, e.getMessage()); - PayCloseResult payCloseResult = new PayCloseResult(); - payCloseResult.setCode("1").setMsg(e.getMessage()); - return this.sign(payCloseResult); + result.setCode("1").setMsg(e.getMessage()); + this.sign(result); + return result; } } @@ -123,7 +127,7 @@ public class PayCloseService { /** * 对返回结果进行签名 */ - private PayCloseResult sign(PayCloseResult result){ + private void sign(PayCloseResult result){ PlatformLocal platformInfo = PaymentContextLocal.get() .getPlatformInfo(); String signType = platformInfo.getSignType(); @@ -134,6 +138,5 @@ public class PayCloseService { } else { throw new PayFailureException("未获取到签名方式,请检查"); } - return result; } } diff --git a/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/payment/common/service/PaymentSignService.java b/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/payment/common/service/PaymentSignService.java index b08a161f8..0e957ba0e 100644 --- a/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/payment/common/service/PaymentSignService.java +++ b/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/payment/common/service/PaymentSignService.java @@ -26,7 +26,7 @@ public class PaymentSignService { private final PaymentAssistService paymentAssistService;; /** - * 签名 + * 入参签名校验 */ public void verifySign(PaymentCommonParam param) { // 先触发上下文的初始化 diff --git a/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/payment/pay/service/PayAssistService.java b/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/payment/pay/service/PayAssistService.java index 8097a7c11..84e130de8 100644 --- a/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/payment/pay/service/PayAssistService.java +++ b/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/payment/pay/service/PayAssistService.java @@ -245,11 +245,19 @@ public class PayAssistService { payResult.setStatus(payOrder.getStatus()); // 设置支付参数 - PayLocal asyncPayInfo = PaymentContextLocal.get().getPayInfo();; + PayLocal asyncPayInfo = PaymentContextLocal.get() + .getPayInfo(); if (StrUtil.isNotBlank(asyncPayInfo.getPayBody())) { payResult.setPayBody(asyncPayInfo.getPayBody()); } - + // 签名 + this.sign(payResult); + return payResult; + } + /** + * 对返回 + */ + public void sign(PayResult payResult) { // 进行签名 PlatformLocal platformInfo = PaymentContextLocal.get() .getPlatformInfo(); @@ -261,6 +269,5 @@ public class PayAssistService { } else { throw new PayFailureException("未获取到签名方式,请检查"); } - return payResult; } } diff --git a/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/payment/pay/service/PayService.java b/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/payment/pay/service/PayService.java index c8bb352c2..65ea1926c 100644 --- a/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/payment/pay/service/PayService.java +++ b/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/payment/pay/service/PayService.java @@ -86,7 +86,7 @@ public class PayService { } /** - * 首次支付 + * 首次支付 无事务 * 拆分为多阶段,1. 保存订单记录信息 2 调起支付 3. 支付成功后操作 */ public PayResult firstPay(PayParam payParam){ @@ -104,6 +104,7 @@ public class PayService { payStrategy.doPayHandler(); } catch (Exception e) { payOrder.setErrorMsg(e.getMessage()); + // 这个方法没有事务, 所以可以正常更新 payOrderService.updateById(payOrder); throw e; } @@ -182,7 +183,6 @@ public class PayService { payOrderService.updateById(payOrder); // 如果支付完成 发送通知 if (Objects.equals(payOrder.getStatus(), SUCCESS.getCode())){ - // 查询通道订单 clientNoticeService.registerPayNotice(payOrder, payOrderExtra); } return payAssistService.buildResult(payOrder); diff --git a/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/payment/refund/service/RefundService.java b/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/payment/refund/service/RefundService.java index 0a5ef980f..b89336574 100644 --- a/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/payment/refund/service/RefundService.java +++ b/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/payment/refund/service/RefundService.java @@ -2,6 +2,7 @@ package cn.bootx.platform.daxpay.service.core.payment.refund.service; import cn.bootx.platform.common.core.exception.DataNotExistException; import cn.bootx.platform.common.core.exception.RepetitiveOperationException; +import cn.bootx.platform.common.core.util.CollUtil; import cn.bootx.platform.common.core.util.ValidationUtil; import cn.bootx.platform.daxpay.code.PayStatusEnum; import cn.bootx.platform.daxpay.code.RefundStatusEnum; @@ -13,12 +14,15 @@ 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.order.refund.dao.RefundOrderExtraManager; import cn.bootx.platform.daxpay.service.core.order.refund.dao.RefundOrderManager; import cn.bootx.platform.daxpay.service.core.order.refund.entity.RefundOrder; +import cn.bootx.platform.daxpay.service.core.order.refund.entity.RefundOrderExtra; import cn.bootx.platform.daxpay.service.core.payment.notice.service.ClientNoticeService; import cn.bootx.platform.daxpay.service.core.payment.refund.factory.RefundStrategyFactory; import cn.bootx.platform.daxpay.service.func.AbsRefundStrategy; import cn.hutool.extra.spring.SpringUtil; +import cn.hutool.json.JSONUtil; import com.baomidou.lock.LockInfo; import com.baomidou.lock.LockTemplate; import lombok.RequiredArgsConstructor; @@ -52,26 +56,31 @@ public class RefundService { private final PayOrderQueryService payOrderQueryService; private final LockTemplate lockTemplate; + private final RefundOrderExtraManager refundOrderExtraManager; /** * 分支付通道进行退款 - * 1. 创建退款订单和通道订单并保存(单独事务) + * 1. 创建退款订单(单独事务) * 2. 调用API发起退款(异步退款) * 3. 根据API返回信息更新退款订单信息 */ public RefundResult refund(RefundParam param){ + RefundResult result = new RefundResult() + .setRefundNo(param.getBizRefundNo()) + .setBizRefundNo(param.getBizRefundNo()); // 参数校验 ValidationUtil.validateParam(param); // 加锁 LockInfo lock = lockTemplate.lock("payment:refund:" + param.getBizRefundNo(),10000,200); if (Objects.isNull(lock)){ - throw new RepetitiveOperationException("退款处理中,请勿重复操作"); + result.setMsg("退款处理中,请勿重复操作"); + return result; } try { // 判断是否是首次发起退款 Optional refund = refundOrderManager.findByBizRefundNo(param.getBizRefundNo()); if (refund.isPresent()){ - return this.repeatRefund(refund.get()); + return this.repeatRefund(refund.get(),param); } else { return this.firstRefund(param); } @@ -90,26 +99,24 @@ public class RefundService { .orElseThrow(() -> new DataNotExistException("支付订单不存在")); // 检查退款参数 refundAssistService.checkAndParam(param, payOrder); - - // ----------------------------- 发起退款操作 -------------------------------------------- - // 通过退款参数获取退款策略 AbsRefundStrategy refundStrategy = RefundStrategyFactory.create(payOrder.getChannel()); // 设置支付订单数据 refundStrategy.setPayOrder(payOrder); // 进行退款前预处理 refundStrategy.doBeforeRefundHandler(); - // 退款操作的预处理, 使用独立的新事物进行发起, 返回创建成功的退款订单, 成功后才可以进行下一阶段的操作 + // 退款操作的预处理, 对支付订单进行预扣款, 返回创建成功的退款订单, 成功后才可以进行下一阶段的操作 RefundOrder refundOrder = SpringUtil.getBean(this.getClass()).preRefundMethod(param, payOrder); try { - // 3.2 执行退款策略 + // 执行退款策略 refundStrategy.doRefundHandler(); } catch (Exception e) { - // 5. 失败处理, 所有记录都会回滚, 可以调用重新 + // 记录处理失败状态 PaymentContextLocal.get().getRefundInfo().setStatus(RefundStatusEnum.FAIL); - // 记录退款失败的记录 + // 更新退款失败的记录 refundAssistService.updateOrderByError(refundOrder); + return refundAssistService.buildResult(refundOrder); } SpringUtil.getBean(this.getClass()).successHandler(refundOrder, payOrder); return refundAssistService.buildResult(refundOrder); @@ -133,31 +140,37 @@ public class RefundService { /** * 重新发起退款处理 * 1. 查出相关退款订单 - * 2. 构建退款策略, 发起退款 + * 2. 更新退款扩展参数 + * 3. 构建退款策略, 发起退款 */ - public RefundResult repeatRefund(RefundOrder refundOrder){ + public RefundResult repeatRefund(RefundOrder refundOrder, RefundParam param){ // 退款失败才可以重新发起退款, 重新发起退款 if (!Objects.equals(refundOrder.getStatus(), RefundStatusEnum.FAIL.getCode())){ throw new PayFailureException("只有失败状态的才可以重新发起退款"); } - // 获取支付订单 PayOrder payOrder = payOrderQueryService.findByBizOrOrderNo(refundOrder.getOrderNo(), refundOrder.getBizOrderNo()) .orElseThrow(() -> new DataNotExistException("支付订单不存在")); + RefundOrderExtra refundOrderExtra = refundOrderExtraManager.findById(refundOrder.getId()) + .orElseThrow(() -> new DataNotExistException("退款订单扩展信息不存在")); AbsRefundStrategy refundStrategy = RefundStrategyFactory.create(refundOrder.getRefundNo()); // 设置退款订单对象 refundStrategy.setRefundOrder(refundOrder); // 退款前准备操作 refundStrategy.doBeforeRefundHandler(); + // 进行发起退款前的操作, 更新扩展记录信息 + this.updateExtra(refundOrderExtra, param); try { // 执行退款策略 refundStrategy.doRefundHandler(); } catch (Exception e) { - // 失败处理, 所有记录都会回滚, 可以调用退款重试 + // 记录处理失败状态 PaymentContextLocal.get().getRefundInfo().setStatus(RefundStatusEnum.FAIL); // 记录退款失败的记录 refundAssistService.updateOrderByError(refundOrder); + // 返回错误响应对象 + return refundAssistService.buildResult(refundOrder); } // 退款发起成功处理 SpringUtil.getBean(this.getClass()).successHandler(refundOrder, payOrder); @@ -165,7 +178,21 @@ public class RefundService { } /** - * 成功处理, 更新退款订单, 退款通道订单, 支付订单, 支付通道订单 + * 更新退款订单扩展信息 + */ + private void updateExtra(RefundOrderExtra refundOrderExtra, RefundParam param){ + refundOrderExtra.setAttach(param.getAttach()) + .setClientIp(param.getClientIp()) + .setNotifyUrl(param.getNotifyUrl()) + .setReqTime(param.getReqTime()); + if (CollUtil.isNotEmpty(param.getExtraParam())){ + refundOrderExtra.setExtraParam(JSONUtil.toJsonStr(param.getExtraParam())); + } + refundOrderExtraManager.updateById(refundOrderExtra); + } + + /** + * 成功处理, 更新退款订单, 支付订单, */ @Transactional(rollbackFor = Exception.class) public void successHandler(RefundOrder refundOrder, PayOrder payOrder) { @@ -182,7 +209,7 @@ public class RefundService { } payOrderService.updateById(payOrder); - // 更新退款订单和相关通道订单 + // 更新退款订单 refundAssistService.updateOrder(refundOrder); // 发送通知 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 e55626dc2..18a26b32a 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 @@ -4,14 +4,17 @@ import cn.bootx.platform.common.core.exception.BizException; import cn.bootx.platform.common.core.exception.RepetitiveOperationException; import cn.bootx.platform.common.core.util.LocalDateTimeUtil; import cn.bootx.platform.daxpay.code.PayChannelEnum; +import cn.bootx.platform.daxpay.code.PaySignTypeEnum; import cn.bootx.platform.daxpay.code.PayStatusEnum; import cn.bootx.platform.daxpay.code.PaySyncStatusEnum; import cn.bootx.platform.daxpay.exception.pay.PayFailureException; import cn.bootx.platform.daxpay.param.payment.pay.PaySyncParam; +import cn.bootx.platform.daxpay.result.CommonResult; import cn.bootx.platform.daxpay.result.pay.SyncResult; import cn.bootx.platform.daxpay.service.code.PayRepairSourceEnum; import cn.bootx.platform.daxpay.service.code.PayRepairWayEnum; import cn.bootx.platform.daxpay.service.code.PaymentTypeEnum; +import cn.bootx.platform.daxpay.service.common.context.PlatformLocal; 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; @@ -24,6 +27,7 @@ import cn.bootx.platform.daxpay.service.core.payment.sync.result.PaySyncResult; import cn.bootx.platform.daxpay.service.core.record.sync.entity.PaySyncRecord; import cn.bootx.platform.daxpay.service.core.record.sync.service.PaySyncRecordService; import cn.bootx.platform.daxpay.service.func.AbsPaySyncStrategy; +import cn.bootx.platform.daxpay.util.PaySignUtil; import com.baomidou.lock.LockInfo; import com.baomidou.lock.LockTemplate; import lombok.RequiredArgsConstructor; @@ -64,21 +68,16 @@ public class PaySyncService { */ @Transactional(propagation = Propagation.REQUIRES_NEW, rollbackFor = Exception.class) public SyncResult sync(PaySyncParam param) { - PayOrder payOrder = null; - if (Objects.nonNull(param.getOrderNo())){ - payOrder = payOrderQueryService.findByOrderNo(param.getOrderNo()) - .orElseThrow(() -> new PayFailureException("未查询到支付订单")); - } - if (Objects.isNull(payOrder)){ - payOrder = payOrderQueryService.findByBizOrderNo(param.getBizOrderNo()) - .orElseThrow(() -> new PayFailureException("未查询到支付订单")); - } + SyncResult syncResult = new SyncResult(); + PayOrder payOrder = payOrderQueryService.findByBizOrOrderNo(param.getBizOrderNo(), param.getOrderNo()) + .orElseThrow(() -> new BizException("支付订单不存在")); // 钱包支付直接返回结果 if (PayChannelEnum.WALLET.getCode().equals(payOrder.getChannel())){ throw new PayFailureException("订单没有异步支付方式,不需要同步"); } // 执行订单同步逻辑 - return this.syncPayOrder(payOrder); + syncResult = this.syncPayOrder(payOrder); + return syncResult; } /** * 同步支付状态, 开启一个新的事务, 不受外部抛出异常的影响 @@ -138,7 +137,7 @@ public class PaySyncService { return new SyncResult() .setGatewayStatus(syncResult.getSyncStatus().getCode()) .setRepair(!statusSync) - .setRepairOrderNo(repairResult.getRepairNo()); + .setRepairNo(repairResult.getRepairNo()); } finally { lockTemplate.releaseLock(lock); } @@ -260,4 +259,20 @@ public class PaySyncService { .setClientIp(PaymentContextLocal.get().getRequestInfo().getClientIp()); paySyncRecordService.saveRecord(paySyncRecord); } + + /** + * 签名 + */ + private void sign(CommonResult result){ + PlatformLocal platformInfo = PaymentContextLocal.get() + .getPlatformInfo(); + String signType = platformInfo.getSignType(); + if (Objects.equals(PaySignTypeEnum.HMAC_SHA256.getCode(), signType)){ + result.setSign(PaySignUtil.hmacSha256Sign(result, platformInfo.getSignSecret())); + } else if (Objects.equals(PaySignTypeEnum.MD5.getCode(), signType)){ + result.setSign(PaySignUtil.md5Sign(result, platformInfo.getSignSecret())); + } else { + throw new PayFailureException("未获取到签名方式,请检查"); + } + } } diff --git a/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/payment/sync/service/RefundSyncService.java b/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/payment/sync/service/RefundSyncService.java index b4cfc1105..fd76704a8 100644 --- a/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/payment/sync/service/RefundSyncService.java +++ b/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/payment/sync/service/RefundSyncService.java @@ -121,7 +121,7 @@ public class RefundSyncService { return new SyncResult() .setGatewayStatus(syncResult.getSyncStatus().getCode()) .setRepair(!statusSync) - .setRepairOrderNo(repairResult.getRepairNo()); + .setRepairNo(repairResult.getRepairNo()); } finally { lockTemplate.releaseLock(lock); } diff --git a/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/record/callback/service/PayCallbackRecordService.java b/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/record/callback/service/PayCallbackRecordService.java index 220dcb989..3c0ab9e53 100644 --- a/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/record/callback/service/PayCallbackRecordService.java +++ b/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/record/callback/service/PayCallbackRecordService.java @@ -4,10 +4,13 @@ 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.bootx.platform.daxpay.service.common.context.CallbackLocal; +import cn.bootx.platform.daxpay.service.common.local.PaymentContextLocal; import cn.bootx.platform.daxpay.service.core.record.callback.dao.PayCallbackRecordManager; import cn.bootx.platform.daxpay.service.core.record.callback.entity.PayCallbackRecord; import cn.bootx.platform.daxpay.service.dto.record.callback.PayCallbackRecordDto; import cn.bootx.platform.daxpay.service.param.record.PayCallbackRecordQuery; +import cn.hutool.json.JSONUtil; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; @@ -43,7 +46,17 @@ public class PayCallbackRecordService { * 保存回调记录 */ @Transactional(propagation = Propagation.REQUIRES_NEW, rollbackFor = Exception.class) - public void save(PayCallbackRecord record) { - callbackRecordManager.save(record); + public void saveCallbackRecord() { + CallbackLocal callbackInfo = PaymentContextLocal.get().getCallbackInfo(); + PayCallbackRecord payNotifyRecord = new PayCallbackRecord() + .setTradeNo(callbackInfo.getTradeNo()) + .setOutTradeNo(callbackInfo.getOutTradeNo()) + .setChannel(callbackInfo.getChannel()) + .setNotifyInfo(JSONUtil.toJsonStr(callbackInfo.getCallbackParam())) + .setCallbackType(callbackInfo.getCallbackType().getCode()) + .setRepairOrderNo(callbackInfo.getRepairNo()) + .setStatus(callbackInfo.getCallbackStatus().getCode()) + .setMsg(callbackInfo.getMsg()); + callbackRecordManager.save(payNotifyRecord); } } diff --git a/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/dto/order/pay/PayOrderDetailDto.java b/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/dto/order/pay/PayOrderDetailDto.java index 50805634c..a0e6b654a 100644 --- a/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/dto/order/pay/PayOrderDetailDto.java +++ b/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/dto/order/pay/PayOrderDetailDto.java @@ -1,11 +1,20 @@ package cn.bootx.platform.daxpay.service.dto.order.pay; +import io.swagger.v3.oas.annotations.media.Schema; import lombok.Data; import java.util.List; +/** + * 支付订单和扩展信息 + * @author xxm + * @since 2024/4/26 + */ @Data +@Schema(title = "支付订单和扩展信息") public class PayOrderDetailDto { + @Schema(description = "支付订单") private PayOrderDto payOrder; + @Schema(description = "支付订单扩展信息") private PayOrderExtraDto payOrderExtra; } diff --git a/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/dto/order/pay/PayOrderDto.java b/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/dto/order/pay/PayOrderDto.java index c4884110a..6cbf52d8e 100644 --- a/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/dto/order/pay/PayOrderDto.java +++ b/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/dto/order/pay/PayOrderDto.java @@ -47,7 +47,7 @@ public class PayOrderDto extends BaseDto { /** * 异步支付通道 - * @see PayChannelEnum#ASYNC_TYPE_CODE + * @see PayChannelEnum */ @Schema(description = "异步支付通道") private String asyncChannel; 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 deleted file mode 100644 index 97acf396f..000000000 --- a/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/dto/order/refund/RefundChannelOrderDto.java +++ /dev/null @@ -1,48 +0,0 @@ -package cn.bootx.platform.daxpay.service.dto.order.refund; - -import cn.bootx.platform.common.core.rest.dto.BaseDto; -import cn.bootx.platform.daxpay.code.RefundStatusEnum; -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; -import lombok.EqualsAndHashCode; -import lombok.experimental.Accessors; - -/** - * 支付退款通道订单 - * @author xxm - * @since 2024/1/17 - */ -@EqualsAndHashCode(callSuper = true) -@Data -@Accessors(chain = true) -@Schema(title = "支付退款通道订单") -public class RefundChannelOrderDto extends BaseDto { - - @Schema(description = "关联退款id") - private Long refundId; - - @Schema(description = "通道") - private String channel; - - @Schema(description = "通道支付单id") - private Long payChannelId; - - @Schema(description = "异步支付方式") - private boolean async; - - @Schema(description = "订单金额") - private Integer orderAmount; - - @Schema(description = "退款金额") - private Integer amount; - - @Schema(description = "剩余可退余额") - private Integer refundableAmount; - - /** - * 退款状态 - * @see RefundStatusEnum - */ - @Schema(description = "退款状态") - private String status; -} diff --git a/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/dto/order/refund/RefundOrderDto.java b/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/dto/order/refund/RefundOrderDto.java index af619c398..4a0b0c0ad 100644 --- a/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/dto/order/refund/RefundOrderDto.java +++ b/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/dto/order/refund/RefundOrderDto.java @@ -41,7 +41,7 @@ public class RefundOrderDto extends BaseDto { /** * 异步通道 - * @see PayChannelEnum#ASYNC_TYPE_CODE + * @see PayChannelEnum */ @Schema(description = "异步通道") private String asyncChannel; diff --git a/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/dto/order/refund/RefundOrderExtraDto.java b/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/dto/order/refund/RefundOrderExtraDto.java new file mode 100644 index 000000000..699c7abe3 --- /dev/null +++ b/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/dto/order/refund/RefundOrderExtraDto.java @@ -0,0 +1,47 @@ +package cn.bootx.platform.daxpay.service.dto.order.refund; + +import cn.bootx.platform.common.core.rest.dto.BaseDto; +import cn.bootx.platform.daxpay.param.channel.AliPayParam; +import cn.bootx.platform.daxpay.param.channel.WalletPayParam; +import cn.bootx.platform.daxpay.param.channel.WeChatPayParam; +import cn.bootx.table.modify.annotation.DbColumn; +import com.baomidou.mybatisplus.annotation.FieldStrategy; +import com.baomidou.mybatisplus.annotation.TableField; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.experimental.Accessors; + +import java.time.LocalDateTime; + +/** + * 退款订单扩展信息 + * @author xxm + * @since 2024/4/26 + */ +@EqualsAndHashCode(callSuper = true) +@Data +@Accessors(chain = true) +@Schema(title = "退款订单扩展信息") +public class RefundOrderExtraDto extends BaseDto { + + /** 异步通知地址 */ + private String notifyUrl; + + /** 商户扩展参数,回调时会原样返回, 以最后一次为准 */ + private String attach; + + /** + * 附加参数 以最后一次为准 + * @see AliPayParam + * @see WeChatPayParam + * @see WalletPayParam + */ + private String extraParam; + + /** 请求时间,时间戳转时间 */ + private LocalDateTime reqTime; + + /** 终端ip */ + private String clientIp; +} diff --git a/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/func/AbsCallbackStrategy.java b/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/func/AbsCallbackStrategy.java index 6fef0e140..d8157bdda 100644 --- a/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/func/AbsCallbackStrategy.java +++ b/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/func/AbsCallbackStrategy.java @@ -22,96 +22,6 @@ import java.util.Map; * @since 2021/6/21 */ @Slf4j +@Deprecated public abstract class AbsCallbackStrategy implements PayStrategy { - @Resource - private PayCallbackRecordService callbackRecordService; - @Resource - private PayCallbackService payCallbackService; - @Resource - private RefundCallbackService refundCallbackService; - - /** - * 回调处理入口 - */ - public String callback(Map params) { - CallbackLocal callbackInfo = PaymentContextLocal.get().getCallbackInfo(); - try { - // 将参数写入到上下文中 - callbackInfo.getCallbackParam().putAll(params); - - // 判断并保存回调类型 - PaymentTypeEnum callbackType = this.getCallbackType(); - callbackInfo.setCallbackType(callbackType); - - // 验证消息 - if (!this.verifyNotify()) { - callbackInfo.setCallbackStatus(PayCallbackStatusEnum.FAIL).setMsg("验证信息格式不通过"); - // 消息有问题, 保存记录并返回 - this.saveCallbackRecord(); - return null; - } - // 提前设置订单修复的来源 - PaymentContextLocal.get().getRepairInfo().setSource(PayRepairSourceEnum.CALLBACK); - - if (callbackType == PaymentTypeEnum.PAY){ - // 解析支付数据并放处理 - this.resolvePayData(); - payCallbackService.payCallback(); - } else { - // 解析退款数据并放处理 - this.resolveRefundData(); - refundCallbackService.refundCallback(); - } - this.saveCallbackRecord(); - return this.getReturnMsg(); - } catch (Exception e) { - log.error("回调处理失败", e); - callbackInfo.setCallbackStatus(PayCallbackStatusEnum.FAIL).setMsg("回调处理失败: "+e.getMessage()); - this.saveCallbackRecord(); - throw e; - } - } - - /** - * 验证信息格式 - */ - public abstract boolean verifyNotify(); - - /** - * 判断类型 支付回调/退款回调 - * @see PaymentTypeEnum - */ - public abstract PaymentTypeEnum getCallbackType(); - - /** - * 解析支付回调数据并放到上下文中 - */ - public abstract void resolvePayData(); - - /** - * 解析退款回调数据并放到上下文中 - */ - public abstract void resolveRefundData(); - - /** - * 返回响应结果 - */ - public abstract String getReturnMsg(); - - /** - * 保存回调记录 - */ - public void saveCallbackRecord() { - CallbackLocal callbackInfo = PaymentContextLocal.get().getCallbackInfo(); - PayCallbackRecord payNotifyRecord = new PayCallbackRecord() - .setTradeNo(callbackInfo.getTradeNo()) - .setOutTradeNo(callbackInfo.getOutTradeNo()) - .setChannel(this.getChannel().getCode()) - .setNotifyInfo(JSONUtil.toJsonStr(callbackInfo.getCallbackParam())) - .setCallbackType(callbackInfo.getCallbackType().getCode()) - .setRepairOrderNo(callbackInfo.getRepairNo()) - .setStatus(callbackInfo.getCallbackStatus().getCode()) - .setMsg(callbackInfo.getMsg()); - callbackRecordService.save(payNotifyRecord); - } } diff --git a/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/handler/exception/PaymentExceptionHandler.java b/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/handler/exception/PaymentExceptionHandler.java new file mode 100644 index 000000000..09bc51930 --- /dev/null +++ b/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/handler/exception/PaymentExceptionHandler.java @@ -0,0 +1,36 @@ +package cn.bootx.platform.daxpay.service.handler.exception; + +import cn.bootx.platform.common.core.exception.BizException; +import cn.bootx.platform.common.core.rest.Res; +import cn.bootx.platform.common.core.rest.ResResult; +import cn.bootx.platform.daxpay.exception.pay.PayFailureException; +import cn.bootx.platform.daxpay.result.CommonResult; +import lombok.extern.slf4j.Slf4j; +import org.slf4j.MDC; +import org.springframework.core.Ordered; +import org.springframework.core.annotation.Order; +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.bind.annotation.RestControllerAdvice; + +/** + * 过滤SaTokenException,需要运行在 RestExceptionHandler 之前 + * + * @author xxm + * @since 2021/8/5 + */ +@Order(Ordered.LOWEST_PRECEDENCE - 2) +@Slf4j +@RestControllerAdvice +public class PaymentExceptionHandler { + + /** + * 支付异常 + */ + @ExceptionHandler({PayFailureException.class}) + public ResResult handleBusinessException(PayFailureException ex) { + log.info(ex.getMessage(), ex); + CommonResult commonResult = new CommonResult(); + commonResult.setMsg(ex.getMessage()); + return Res.ok(commonResult); + } +} diff --git a/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/param/order/PayOrderQuery.java b/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/param/order/PayOrderQuery.java index ae1a9a4f3..1988d6852 100644 --- a/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/param/order/PayOrderQuery.java +++ b/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/param/order/PayOrderQuery.java @@ -47,7 +47,7 @@ public class PayOrderQuery extends QueryOrder { /** * 异步支付通道 - * @see PayChannelEnum#ASYNC_TYPE_CODE + * @see PayChannelEnum */ @QueryParam(type = QueryParam.CompareTypeEnum.EQ) @Schema(description = "异步支付通道") diff --git a/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/param/reconcile/ReconcileOrderQuery.java b/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/param/reconcile/ReconcileOrderQuery.java index c851b56ec..f2636e57c 100644 --- a/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/param/reconcile/ReconcileOrderQuery.java +++ b/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/param/reconcile/ReconcileOrderQuery.java @@ -1,9 +1,11 @@ package cn.bootx.platform.daxpay.service.param.reconcile; import cn.bootx.platform.common.core.annotation.QueryParam; +import cn.hutool.core.date.DatePattern; import io.swagger.v3.oas.annotations.media.Schema; import lombok.Data; import lombok.experimental.Accessors; +import org.springframework.format.annotation.DateTimeFormat; import java.time.LocalDate; @@ -22,7 +24,7 @@ public class ReconcileOrderQuery { private String batchNo; @Schema(description = "日期") - @DateTimeFormat(pattern = "yyyy-MM-dd") + @DateTimeFormat(pattern = DatePattern.NORM_DATE_PATTERN) private LocalDate date; @Schema(description = "通道")