From 518e89dc4b192cbfd965ec1b4fb204b3a8f3a796 Mon Sep 17 00:00:00 2001 From: YunaiV Date: Sat, 15 Jul 2023 10:35:41 +0800 Subject: [PATCH] =?UTF-8?q?mall=20+=20pay=EF=BC=9A=201.=20=E9=87=8D?= =?UTF-8?q?=E6=9E=84=E6=94=AF=E4=BB=98=E5=9B=9E=E8=B0=83=E7=9A=84=E9=80=BB?= =?UTF-8?q?=E8=BE=91=EF=BC=8C=E5=B0=86=E5=9B=9E=E8=B0=83=E8=A7=A3=E6=9E=90?= =?UTF-8?q?=E6=94=B9=E6=88=90=20PayOrderRespDTO=EF=BC=8C=E4=B8=BA=E5=90=8E?= =?UTF-8?q?=E7=BB=AD=E8=BD=AE=E8=AF=A2=E5=81=9A=E9=93=BA=E5=9E=AB=202.=20?= =?UTF-8?q?=E8=B0=83=E6=95=B4=E9=80=80=E6=AC=BE=E5=8D=95=E7=9A=84=E8=A1=A8?= =?UTF-8?q?=E7=BB=93=E6=9E=84=203.=20=E8=B0=83=E6=95=B4=E9=80=80=E6=AC=BE?= =?UTF-8?q?=E8=B0=83=E7=94=A8=E7=9A=84=E5=AE=9E=E7=8E=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../util/validation/ValidationUtils.java | 8 ++ .../framework/pay/core/client/PayClient.java | 22 ++-- .../client/dto/notify/PayNotifyReqDTO.java | 29 ----- .../dto/notify/PayOrderNotifyRespDTO.java | 38 ------- .../dto/notify/PayRefundNotifyRespDTO.java | 58 ---------- .../client/dto/order/PayOrderRespDTO.java | 55 +++++++++ .../dto/order/PayOrderUnifiedReqDTO.java | 10 +- .../dto/order/PayOrderUnifiedRespDTO.java | 6 +- .../client/dto/refund/PayRefundRespDTO.java | 40 +++++++ .../dto/refund/PayRefundUnifiedReqDTO.java | 38 +++---- .../dto/refund/PayRefundUnifiedRespDTO.java | 24 ---- .../core/client/impl/AbstractPayClient.java | 8 +- .../impl/alipay/AbstractAlipayPayClient.java | 104 ++++++++++-------- .../impl/alipay/AlipayAppPayClient.java | 4 +- .../impl/alipay/AlipayBarPayClient.java | 4 +- .../client/impl/alipay/AlipayPcPayClient.java | 4 +- .../client/impl/alipay/AlipayQrPayClient.java | 4 +- .../impl/alipay/AlipayWapPayClient.java | 4 +- .../impl/weixin/AbstractWxPayClient.java | 35 +++--- .../client/impl/weixin/WxAppPayClient.java | 4 +- .../client/impl/weixin/WxBarPayClient.java | 19 ++-- .../client/impl/weixin/WxH5PayClient.java | 4 +- .../client/impl/weixin/WxNativePayClient.java | 14 +-- .../client/impl/weixin/WxPubPayClient.java | 12 +- .../enums/order/PayOrderStatusRespEnum.java | 14 ++- .../refund/PayNotifyRefundStatusEnum.java | 23 ---- .../core/enums/refund/PayRefundRespEnum.java | 23 ---- .../enums/refund/PayRefundStatusRespEnum.java | 28 +++++ .../PayClientFactoryImplIntegrationTest.java | 4 +- .../impl/alipay/AlipayQrPayClientTest.java | 6 +- .../pay/enums/refund/PayRefundStatusEnum.java | 12 +- .../admin/notify/PayNotifyController.java | 16 ++- .../admin/refund/vo/PayRefundExcelVO.java | 3 - .../pay/convert/refund/PayRefundConvert.java | 7 +- .../pay/dal/dataobject/order/PayOrderDO.java | 3 +- .../dataobject/order/PayOrderExtensionDO.java | 11 +- .../dal/dataobject/refund/PayRefundDO.java | 81 ++++++-------- .../pay/dal/mysql/refund/PayRefundMapper.java | 4 +- .../channel/PayChannelServiceImpl.java | 23 ++-- .../pay/service/order/PayOrderService.java | 6 +- .../service/order/PayOrderServiceImpl.java | 45 ++++---- .../pay/service/refund/PayRefundService.java | 8 +- .../service/refund/PayRefundServiceImpl.java | 52 ++++----- .../pay/service/app/PayAppServiceTest.java | 4 - .../service/refund/PayRefundServiceTest.java | 4 +- yudao-ui-admin/src/api/pay/channel.js | 10 +- 46 files changed, 436 insertions(+), 499 deletions(-) delete mode 100644 yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/dto/notify/PayNotifyReqDTO.java delete mode 100644 yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/dto/notify/PayOrderNotifyRespDTO.java delete mode 100644 yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/dto/notify/PayRefundNotifyRespDTO.java create mode 100644 yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/dto/order/PayOrderRespDTO.java create mode 100644 yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/dto/refund/PayRefundRespDTO.java delete mode 100644 yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/dto/refund/PayRefundUnifiedRespDTO.java delete mode 100644 yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/enums/refund/PayNotifyRefundStatusEnum.java delete mode 100644 yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/enums/refund/PayRefundRespEnum.java create mode 100644 yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/enums/refund/PayRefundStatusRespEnum.java diff --git a/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/validation/ValidationUtils.java b/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/validation/ValidationUtils.java index 7e6f89d5a..436a9e669 100644 --- a/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/validation/ValidationUtils.java +++ b/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/validation/ValidationUtils.java @@ -1,10 +1,12 @@ package cn.iocoder.yudao.framework.common.util.validation; import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.lang.Assert; import org.springframework.util.StringUtils; import javax.validation.ConstraintViolation; import javax.validation.ConstraintViolationException; +import javax.validation.Validation; import javax.validation.Validator; import java.util.Set; import java.util.regex.Pattern; @@ -37,6 +39,12 @@ public class ValidationUtils { && PATTERN_XML_NCNAME.matcher(str).matches(); } + public static void validate(Object object, Class... groups) { + Validator validator = Validation.buildDefaultValidatorFactory().getValidator(); + Assert.notNull(validator); + validate(validator, object, groups); + } + public static void validate(Validator validator, Object object, Class... groups) { Set> constraintViolations = validator.validate(object, groups); if (CollUtil.isNotEmpty(constraintViolations)) { diff --git a/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/PayClient.java b/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/PayClient.java index 6db0a0040..0d0139000 100644 --- a/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/PayClient.java +++ b/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/PayClient.java @@ -1,12 +1,12 @@ package cn.iocoder.yudao.framework.pay.core.client; -import cn.iocoder.yudao.framework.pay.core.client.dto.notify.PayNotifyReqDTO; -import cn.iocoder.yudao.framework.pay.core.client.dto.notify.PayOrderNotifyRespDTO; -import cn.iocoder.yudao.framework.pay.core.client.dto.notify.PayRefundNotifyRespDTO; +import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderRespDTO; import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderUnifiedReqDTO; import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderUnifiedRespDTO; +import cn.iocoder.yudao.framework.pay.core.client.dto.refund.PayRefundRespDTO; import cn.iocoder.yudao.framework.pay.core.client.dto.refund.PayRefundUnifiedReqDTO; -import cn.iocoder.yudao.framework.pay.core.client.dto.refund.PayRefundUnifiedRespDTO; + +import java.util.Map; /** * 支付客户端,用于对接各支付渠道的 SDK,实现发起支付、退款等功能 @@ -32,20 +32,22 @@ public interface PayClient { /** * 调用支付渠道,进行退款 + * * @param reqDTO 统一退款请求信息 - * @return 各支付渠道的统一返回结果 + * @return 退款信息 */ - PayRefundUnifiedRespDTO unifiedRefund(PayRefundUnifiedReqDTO reqDTO); + PayRefundRespDTO unifiedRefund(PayRefundUnifiedReqDTO reqDTO); /** * 解析回调数据 * - * @param rawNotify 通知内容 + * @param params HTTP 回调接口 content type 为 application/x-www-form-urlencoded 的所有参数 + * @param body HTTP 回调接口的 request body * @return 回调对象 - * 1. {@link PayRefundNotifyRespDTO} 退款通知 - * 2. {@link PayOrderNotifyRespDTO} 支付通知 + * 1. {@link PayRefundRespDTO} 退款通知 + * 2. {@link PayOrderRespDTO} 支付通知 */ - default Object parseNotify(PayNotifyReqDTO rawNotify) { + default Object parseNotify(Map params, String body) { throw new UnsupportedOperationException("未实现 parseNotify 方法!"); } diff --git a/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/dto/notify/PayNotifyReqDTO.java b/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/dto/notify/PayNotifyReqDTO.java deleted file mode 100644 index 65d0d01ad..000000000 --- a/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/dto/notify/PayNotifyReqDTO.java +++ /dev/null @@ -1,29 +0,0 @@ -package cn.iocoder.yudao.framework.pay.core.client.dto.notify; - -import lombok.Builder; -import lombok.Data; -import lombok.ToString; - -import java.util.Map; - - -/** - * 支付订单,退款订单回调,渠道的统一通知请求数据 - */ -@Data -@ToString -@Builder -public class PayNotifyReqDTO { - - - /** - * HTTP 回调接口的 request body - */ - private String body; - - /** - * HTTP 回调接口 content type 为 application/x-www-form-urlencoded 的所有参数 - */ - private Map params; - -} diff --git a/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/dto/notify/PayOrderNotifyRespDTO.java b/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/dto/notify/PayOrderNotifyRespDTO.java deleted file mode 100644 index 239ddda23..000000000 --- a/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/dto/notify/PayOrderNotifyRespDTO.java +++ /dev/null @@ -1,38 +0,0 @@ -package cn.iocoder.yudao.framework.pay.core.client.dto.notify; - -import lombok.AllArgsConstructor; -import lombok.Builder; -import lombok.Data; -import lombok.NoArgsConstructor; - -import java.time.LocalDateTime; - -/** - * 支付通知 Response DTO - * - * @author 芋道源码 - */ -@Data -@Builder -@NoArgsConstructor -@AllArgsConstructor -public class PayOrderNotifyRespDTO { - - /** - * 支付订单号(支付模块的) - */ - private String orderExtensionNo; - /** - * 支付渠道编号 - */ - private String channelOrderNo; - /** - * 支付渠道用户编号 - */ - private String channelUserId; - /** - * 支付成功时间 - */ - private LocalDateTime successTime; - -} diff --git a/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/dto/notify/PayRefundNotifyRespDTO.java b/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/dto/notify/PayRefundNotifyRespDTO.java deleted file mode 100644 index 9c05b3ac0..000000000 --- a/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/dto/notify/PayRefundNotifyRespDTO.java +++ /dev/null @@ -1,58 +0,0 @@ -package cn.iocoder.yudao.framework.pay.core.client.dto.notify; - -import cn.iocoder.yudao.framework.pay.core.enums.refund.PayNotifyRefundStatusEnum; -import lombok.Builder; -import lombok.Data; -import lombok.ToString; - -import java.time.LocalDateTime; - -/** - * 从渠道返回数据中解析得到的支付退款通知的Notify DTO - * - * @author jason - */ -@Data -@ToString -@Builder -public class PayRefundNotifyRespDTO { - - /** - * 支付渠道编号 - */ - private String channelOrderNo; - - /** - * 交易订单号,根据规则生成 - * 调用支付渠道时,使用该字段作为对接的订单号。 - * 1. 调用微信支付 https://api.mch.weixin.qq.com/pay/unifiedorder 时,使用该字段作为 out_trade_no - * 2. 调用支付宝 https://opendocs.alipay.com/apis 时,使用该字段作为 out_trade_no - * 这里对应 pay_extension 里面的 no - * 例如说,P202110132239124200055 - */ - private String tradeNo; - - /** - * https://api.mch.weixin.qq.com/v3/refund/domestic/refunds 中的 out_refund_no - * https://opendocs.alipay.com/apis alipay.trade.refund 中的 out_request_no - * 退款请求号。 - * 标识一次退款请求,需要保证在交易号下唯一,如需部分退款,则此参数必传。 - * 注:针对同一次退款请求,如果调用接口失败或异常了,重试时需要保证退款请求号不能变更, - * 防止该笔交易重复退款。支付宝会保证同样的退款请求号多次请求只会退一次。 - * 退款单请求号,根据规则生成 - * - * 例如说,RR202109181134287570000 - */ - private String reqNo; - - /** - * 退款是否成功 - */ - private PayNotifyRefundStatusEnum status; - - /** - * 退款成功时间 - */ - private LocalDateTime refundSuccessTime; - -} diff --git a/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/dto/order/PayOrderRespDTO.java b/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/dto/order/PayOrderRespDTO.java new file mode 100644 index 000000000..3f98cf841 --- /dev/null +++ b/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/dto/order/PayOrderRespDTO.java @@ -0,0 +1,55 @@ +package cn.iocoder.yudao.framework.pay.core.client.dto.order; + +import cn.iocoder.yudao.framework.pay.core.enums.order.PayOrderStatusRespEnum; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.time.LocalDateTime; + +/** + * 渠道支付订单 Response DTO + * + * @author 芋道源码 + */ +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class PayOrderRespDTO { + + /** + * 支付状态 + * + * 枚举:{@link PayOrderStatusRespEnum} + */ + private Integer status; + + /** + * 外部订单号 + * + * 对应 PayOrderExtensionDO 的 no 字段 + */ + private String outTradeNo; + + /** + * 支付渠道编号 + */ + private String channelOrderNo; + /** + * 支付渠道用户编号 + */ + private String channelUserId; + + /** + * 支付成功时间 + */ + private LocalDateTime successTime; + + /** + * 原始的异步通知结果 + */ + private Object rawData; + +} diff --git a/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/dto/order/PayOrderUnifiedReqDTO.java b/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/dto/order/PayOrderUnifiedReqDTO.java index ba31b08ad..bd92df13a 100644 --- a/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/dto/order/PayOrderUnifiedReqDTO.java +++ b/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/dto/order/PayOrderUnifiedReqDTO.java @@ -28,10 +28,12 @@ public class PayOrderUnifiedReqDTO { // ========== 商户相关字段 ========== /** - * 商户订单编号 + * 外部订单号 + * + * 对应 PayOrderExtensionDO 的 no 字段 */ - @NotEmpty(message = "商户订单编号不能为空") - private String merchantOrderId; + @NotEmpty(message = "外部订单编号不能为空") + private String outTradeNo; /** * 商品标题 */ @@ -63,7 +65,7 @@ public class PayOrderUnifiedReqDTO { */ @NotNull(message = "支付金额不能为空") @DecimalMin(value = "0", inclusive = false, message = "支付金额必须大于零") - private Integer amount; + private Integer price; /** * 支付过期时间 diff --git a/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/dto/order/PayOrderUnifiedRespDTO.java b/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/dto/order/PayOrderUnifiedRespDTO.java index 441b4ece1..7a282b4eb 100644 --- a/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/dto/order/PayOrderUnifiedRespDTO.java +++ b/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/dto/order/PayOrderUnifiedRespDTO.java @@ -1,6 +1,5 @@ package cn.iocoder.yudao.framework.pay.core.client.dto.order; -import cn.iocoder.yudao.framework.pay.core.client.dto.notify.PayOrderNotifyRespDTO; import cn.iocoder.yudao.framework.pay.core.enums.order.PayOrderDisplayModeEnum; import lombok.Data; @@ -24,11 +23,12 @@ public class PayOrderUnifiedRespDTO { private String displayContent; /** - * 同步的通知信息 + * 渠道支付订单 * + * 只有在订单直接支付成功时,才会进行返回。 * 目前只有 bar 条码支付才会出现,它是支付发起时,直接返回是否支付成功的,而其它支付还是异步通知 */ - private PayOrderNotifyRespDTO notify; + private PayOrderRespDTO order; public PayOrderUnifiedRespDTO(String displayMode, String displayContent) { this.displayMode = displayMode; diff --git a/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/dto/refund/PayRefundRespDTO.java b/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/dto/refund/PayRefundRespDTO.java new file mode 100644 index 000000000..5268eddc4 --- /dev/null +++ b/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/dto/refund/PayRefundRespDTO.java @@ -0,0 +1,40 @@ +package cn.iocoder.yudao.framework.pay.core.client.dto.refund; + +import cn.iocoder.yudao.framework.pay.core.enums.refund.PayRefundStatusRespEnum; +import lombok.Data; + +import java.time.LocalDateTime; + +/** + * 渠道退款订单 Response DTO + * + * @author jason + */ +@Data +public class PayRefundRespDTO { + + /** + * 退款状态 + * + * 枚举 {@link PayRefundStatusRespEnum} + */ + private Integer status; + + /** + * 渠道退款单号 + * + * 对应 PayRefundDO.channelRefundNo 字段 + */ + private String channelRefundNo; + + /** + * 退款成功时间 + */ + private LocalDateTime successTime; + + /** + * 原始的异步通知结果 + */ + private Object rawData; + +} diff --git a/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/dto/refund/PayRefundUnifiedReqDTO.java b/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/dto/refund/PayRefundUnifiedReqDTO.java index e19a04aa0..d6f66ab63 100644 --- a/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/dto/refund/PayRefundUnifiedReqDTO.java +++ b/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/dto/refund/PayRefundUnifiedReqDTO.java @@ -24,33 +24,20 @@ import javax.validation.constraints.NotNull; public class PayRefundUnifiedReqDTO { /** - * 用户 IP + * 外部订单号 + * + * 对应 PayOrderExtensionDO 的 no 字段 */ - private String userIp; - - // TODO @jason:这个是否为非必传字段呀,只需要传递 payTradeNo 字段即可。尽可能精简 - /** - * https://api.mch.weixin.qq.com/v3/refund/domestic/refunds 中的 transaction_id - * https://opendocs.alipay.com/apis alipay.trade.refund 中的 trade_no - * 渠道订单号 - */ - private String channelOrderNo; + @NotEmpty(message = "外部订单编号不能为空") + private String outTradeNo; /** - * https://api.mch.weixin.qq.com/v3/refund/domestic/refunds 中的 out_trade_no - * https://opendocs.alipay.com/apis alipay.trade.refund 中的 out_trade_no - * 支付交易号 {PayOrderExtensionDO no字段} 和 渠道订单号 不能同时为空 + * 外部退款号 + * + * 对应 PayRefundDO 的 no 字段 */ - private String payTradeNo; - - /** - * https://api.mch.weixin.qq.com/v3/refund/domestic/refunds 中的 out_refund_no - * https://opendocs.alipay.com/apis alipay.trade.refund 中的 out_trade_no - * 退款请求单号 同一退款请求单号多次请求只退一笔。 - * 使用 商户的退款单号。{PayRefundDO 字段 merchantRefundNo} - */ - @NotEmpty(message = "退款请求单号") - private String merchantRefundId; + @NotEmpty(message = "退款请求单号不能为空") + private String outRefundNo; /** * 退款原因 @@ -63,11 +50,12 @@ public class PayRefundUnifiedReqDTO { */ @NotNull(message = "退款金额不能为空") @DecimalMin(value = "0", inclusive = false, message = "支付金额必须大于零") - private Integer amount; + private Integer price; /** - * 退款结果 notify 回调地址, 支付宝退款不需要回调地址, 微信需要 + * 退款结果的 notify 回调地址 */ + @NotEmpty(message = "支付结果的回调地址不能为空") @URL(message = "支付结果的 notify 回调地址必须是 URL 格式") private String notifyUrl; diff --git a/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/dto/refund/PayRefundUnifiedRespDTO.java b/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/dto/refund/PayRefundUnifiedRespDTO.java deleted file mode 100644 index 104dc2bbe..000000000 --- a/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/dto/refund/PayRefundUnifiedRespDTO.java +++ /dev/null @@ -1,24 +0,0 @@ -package cn.iocoder.yudao.framework.pay.core.client.dto.refund; - -import lombok.AllArgsConstructor; -import lombok.Builder; -import lombok.Data; -import lombok.NoArgsConstructor; -import lombok.experimental.Accessors; -/** - * 统一退款 Response DTO - * - * @author jason - */ -@Accessors(chain = true) -@Builder -@NoArgsConstructor -@AllArgsConstructor -@Data -public class PayRefundUnifiedRespDTO { - - /** - * 渠道退款单编号 - */ - private String channelRefundId; -} diff --git a/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/AbstractPayClient.java b/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/AbstractPayClient.java index 0225739ca..f25bb9881 100644 --- a/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/AbstractPayClient.java +++ b/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/AbstractPayClient.java @@ -5,8 +5,8 @@ import cn.iocoder.yudao.framework.pay.core.client.PayClient; import cn.iocoder.yudao.framework.pay.core.client.PayClientConfig; import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderUnifiedReqDTO; import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderUnifiedRespDTO; +import cn.iocoder.yudao.framework.pay.core.client.dto.refund.PayRefundRespDTO; import cn.iocoder.yudao.framework.pay.core.client.dto.refund.PayRefundUnifiedReqDTO; -import cn.iocoder.yudao.framework.pay.core.client.dto.refund.PayRefundUnifiedRespDTO; import cn.iocoder.yudao.framework.pay.core.client.exception.PayException; import lombok.extern.slf4j.Slf4j; @@ -92,10 +92,10 @@ public abstract class AbstractPayClient implemen throws Throwable; @Override - public PayRefundUnifiedRespDTO unifiedRefund(PayRefundUnifiedReqDTO reqDTO) { + public PayRefundRespDTO unifiedRefund(PayRefundUnifiedReqDTO reqDTO) { Validation.buildDefaultValidatorFactory().getValidator().validate(reqDTO); // 执行统一退款 - PayRefundUnifiedRespDTO resp; + PayRefundRespDTO resp; try { resp = doUnifiedRefund(reqDTO); } catch (ServiceException ex) { @@ -109,7 +109,7 @@ public abstract class AbstractPayClient implemen return resp; } - protected abstract PayRefundUnifiedRespDTO doUnifiedRefund(PayRefundUnifiedReqDTO reqDTO) throws Throwable; + protected abstract PayRefundRespDTO doUnifiedRefund(PayRefundUnifiedReqDTO reqDTO) throws Throwable; // ========== 各种工具方法 ========== diff --git a/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/alipay/AbstractAlipayPayClient.java b/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/alipay/AbstractAlipayPayClient.java index 60472fefb..1da5f2631 100644 --- a/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/alipay/AbstractAlipayPayClient.java +++ b/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/alipay/AbstractAlipayPayClient.java @@ -2,16 +2,19 @@ package cn.iocoder.yudao.framework.pay.core.client.impl.alipay; import cn.hutool.core.bean.BeanUtil; import cn.hutool.core.date.LocalDateTimeUtil; +import cn.hutool.core.lang.Assert; +import cn.hutool.core.util.StrUtil; import cn.hutool.http.HttpUtil; import cn.iocoder.yudao.framework.common.util.json.JsonUtils; -import cn.iocoder.yudao.framework.pay.core.client.dto.notify.PayNotifyReqDTO; -import cn.iocoder.yudao.framework.pay.core.client.dto.notify.PayOrderNotifyRespDTO; -import cn.iocoder.yudao.framework.pay.core.client.dto.notify.PayRefundNotifyRespDTO; +import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderRespDTO; +import cn.iocoder.yudao.framework.pay.core.client.dto.refund.PayRefundRespDTO; import cn.iocoder.yudao.framework.pay.core.client.dto.refund.PayRefundUnifiedReqDTO; -import cn.iocoder.yudao.framework.pay.core.client.dto.refund.PayRefundUnifiedRespDTO; import cn.iocoder.yudao.framework.pay.core.client.impl.AbstractPayClient; -import cn.iocoder.yudao.framework.pay.core.enums.refund.PayNotifyRefundStatusEnum; -import com.alipay.api.*; +import cn.iocoder.yudao.framework.pay.core.enums.order.PayOrderStatusRespEnum; +import com.alipay.api.AlipayApiException; +import com.alipay.api.AlipayConfig; +import com.alipay.api.AlipayResponse; +import com.alipay.api.DefaultAlipayClient; import com.alipay.api.domain.AlipayTradeRefundModel; import com.alipay.api.internal.util.AlipaySignature; import com.alipay.api.request.AlipayTradeRefundRequest; @@ -22,6 +25,8 @@ import lombok.extern.slf4j.Slf4j; import java.nio.charset.StandardCharsets; import java.time.LocalDateTime; import java.util.Map; +import java.util.Objects; +import java.util.function.Supplier; import static cn.hutool.core.date.DatePattern.NORM_DATETIME_FORMATTER; import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception0; @@ -52,69 +57,72 @@ public abstract class AbstractAlipayPayClient extends AbstractPayClient params, String body) { + // 补充说明:支付宝退款时,没有回调,这点和微信支付是不同的。并且,退款分成部分退款、和全部退款。 + // ① 部分退款:是会有回调,但是它回调的是订单状态的同步回调,不是退款订单的回调 + // ② 全部退款:Wap 支付有订单状态的同步回调,但是 PC/扫码又没有 + // 所以,这里在解析时,即使是退款导致的订单状态同步,我们也忽略不做为“退款同步”,而是订单的回调。 + // 实际上,支付宝退款只要发起成功,就可以认为退款成功,不需要等待回调。 + // 1. 校验回调数据 - String body = rawNotify.getBody(); - Map params = rawNotify.getParams(); Map bodyObj = HttpUtil.decodeParamMap(body, StandardCharsets.UTF_8); AlipaySignature.rsaCheckV1(bodyObj, config.getAlipayPublicKey(), - StandardCharsets.UTF_8.name(), "RSA2"); + StandardCharsets.UTF_8.name(), config.getSignType()); - // 2.1 退款的情况 - if (bodyObj.containsKey("refund_fee")) { - return PayRefundNotifyRespDTO.builder().channelOrderNo(bodyObj.get("trade_no")) - .tradeNo(bodyObj.get("out_trade_no")).reqNo(bodyObj.get("out_biz_no")) - .status(PayNotifyRefundStatusEnum.SUCCESS) - .refundSuccessTime(parseTime(params.get("gmt_refund"))) - .build(); - } - // 2.2 支付的情况 - return PayOrderNotifyRespDTO.builder() - .orderExtensionNo(bodyObj.get("out_trade_no")) + // 2. 解析订单的状态 + String tradeStatus = bodyObj.get("trade_status"); + PayOrderStatusRespEnum status = Objects.equals("WAIT_BUYER_PAY", tradeStatus) ? PayOrderStatusRespEnum.WAITING + : Objects.equals("TRADE_SUCCESS", tradeStatus) ? PayOrderStatusRespEnum.SUCCESS + : Objects.equals("TRADE_CLOSED", tradeStatus) ? PayOrderStatusRespEnum.CLOSED : null; + Assert.notNull(status, (Supplier) () -> { + throw new IllegalArgumentException(StrUtil.format("body({}) 的 trade_status 不正确", body)); + }); + return PayOrderRespDTO.builder() + .status(Objects.requireNonNull(status).getStatus()) + .outTradeNo(bodyObj.get("out_trade_no")) .channelOrderNo(bodyObj.get("trade_no")) .channelUserId(bodyObj.get("seller_id")) - .successTime(parseTime(params.get("notify_time"))) + .successTime(parseTime(params.get("gmt_payment"))) + .rawData(body) .build(); } diff --git a/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/alipay/AlipayAppPayClient.java b/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/alipay/AlipayAppPayClient.java index 19fd8b072..9ea80fa9b 100644 --- a/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/alipay/AlipayAppPayClient.java +++ b/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/alipay/AlipayAppPayClient.java @@ -31,10 +31,10 @@ public class AlipayAppPayClient extends AbstractAlipayPayClient { // 1.1 构建 AlipayTradeAppPayModel 请求 AlipayTradeAppPayModel model = new AlipayTradeAppPayModel(); // ① 通用的参数 - model.setOutTradeNo(reqDTO.getMerchantOrderId()); + model.setOutTradeNo(reqDTO.getOutTradeNo()); model.setSubject(reqDTO.getSubject()); model.setBody(reqDTO.getBody()); - model.setTotalAmount(formatAmount(reqDTO.getAmount())); + model.setTotalAmount(formatAmount(reqDTO.getPrice())); model.setProductCode(" QUICK_MSECURITY_PAY"); // 销售产品码:无线快捷支付产品 // ② 个性化的参数【无】 // ③ 支付宝扫码支付只有一种展示 diff --git a/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/alipay/AlipayBarPayClient.java b/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/alipay/AlipayBarPayClient.java index 42579fb8e..1f94f2a56 100644 --- a/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/alipay/AlipayBarPayClient.java +++ b/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/alipay/AlipayBarPayClient.java @@ -39,10 +39,10 @@ public class AlipayBarPayClient extends AbstractAlipayPayClient { // 1.1 构建 AlipayTradePayModel 请求 AlipayTradePayModel model = new AlipayTradePayModel(); // ① 通用的参数 - model.setOutTradeNo(reqDTO.getMerchantOrderId()); + model.setOutTradeNo(reqDTO.getOutTradeNo()); model.setSubject(reqDTO.getSubject()); model.setBody(reqDTO.getBody()); - model.setTotalAmount(formatAmount(reqDTO.getAmount())); + model.setTotalAmount(formatAmount(reqDTO.getPrice())); model.setScene("bar_code"); // 当面付条码支付场景 // ② 个性化的参数 model.setAuthCode(authCode); diff --git a/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/alipay/AlipayPcPayClient.java b/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/alipay/AlipayPcPayClient.java index d1a7eef14..02ae7c98c 100644 --- a/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/alipay/AlipayPcPayClient.java +++ b/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/alipay/AlipayPcPayClient.java @@ -33,10 +33,10 @@ public class AlipayPcPayClient extends AbstractAlipayPayClient { // 1.1 构建 AlipayTradePagePayModel 请求 AlipayTradePagePayModel model = new AlipayTradePagePayModel(); // ① 通用的参数 - model.setOutTradeNo(reqDTO.getMerchantOrderId()); + model.setOutTradeNo(reqDTO.getOutTradeNo()); model.setSubject(reqDTO.getSubject()); model.setBody(reqDTO.getBody()); - model.setTotalAmount(formatAmount(reqDTO.getAmount())); + model.setTotalAmount(formatAmount(reqDTO.getPrice())); model.setTimeExpire(formatTime(reqDTO.getExpireTime())); model.setProductCode("FAST_INSTANT_TRADE_PAY"); // 销售产品码. 目前 PC 支付场景下仅支持 FAST_INSTANT_TRADE_PAY // ② 个性化的参数 diff --git a/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/alipay/AlipayQrPayClient.java b/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/alipay/AlipayQrPayClient.java index 27b2ba5c4..f2deee58a 100644 --- a/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/alipay/AlipayQrPayClient.java +++ b/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/alipay/AlipayQrPayClient.java @@ -29,10 +29,10 @@ public class AlipayQrPayClient extends AbstractAlipayPayClient { // 1.1 构建 AlipayTradePrecreateModel 请求 AlipayTradePrecreateModel model = new AlipayTradePrecreateModel(); // ① 通用的参数 - model.setOutTradeNo(reqDTO.getMerchantOrderId()); + model.setOutTradeNo(reqDTO.getOutTradeNo()); model.setSubject(reqDTO.getSubject()); model.setBody(reqDTO.getBody()); - model.setTotalAmount(formatAmount(reqDTO.getAmount())); + model.setTotalAmount(formatAmount(reqDTO.getPrice())); model.setProductCode("FACE_TO_FACE_PAYMENT"); // 销售产品码. 目前扫码支付场景下仅支持 FACE_TO_FACE_PAYMENT // ② 个性化的参数【无】 // ③ 支付宝扫码支付只有一种展示,考虑到前端可能希望二维码扫描后,手机打开 diff --git a/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/alipay/AlipayWapPayClient.java b/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/alipay/AlipayWapPayClient.java index 1db837b69..d3acde46a 100644 --- a/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/alipay/AlipayWapPayClient.java +++ b/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/alipay/AlipayWapPayClient.java @@ -30,10 +30,10 @@ public class AlipayWapPayClient extends AbstractAlipayPayClient { // 1.1 构建 AlipayTradeWapPayModel 请求 AlipayTradeWapPayModel model = new AlipayTradeWapPayModel(); // ① 通用的参数 - model.setOutTradeNo(reqDTO.getMerchantOrderId()); + model.setOutTradeNo(reqDTO.getOutTradeNo()); model.setSubject(reqDTO.getSubject()); model.setBody(reqDTO.getBody()); - model.setTotalAmount(formatAmount(reqDTO.getAmount())); + model.setTotalAmount(formatAmount(reqDTO.getPrice())); model.setProductCode("QUICK_WAP_PAY"); // 销售产品码. 目前 Wap 支付场景下仅支持 QUICK_WAP_PAY // ② 个性化的参数【无】 // ③ 支付宝 Wap 支付只有一种展示:URL diff --git a/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/weixin/AbstractWxPayClient.java b/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/weixin/AbstractWxPayClient.java index fa5a793d5..49314f135 100644 --- a/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/weixin/AbstractWxPayClient.java +++ b/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/weixin/AbstractWxPayClient.java @@ -6,8 +6,7 @@ import cn.hutool.core.date.TemporalAccessorUtil; import cn.hutool.core.lang.Assert; import cn.hutool.core.util.StrUtil; import cn.iocoder.yudao.framework.common.util.io.FileUtils; -import cn.iocoder.yudao.framework.pay.core.client.dto.notify.PayNotifyReqDTO; -import cn.iocoder.yudao.framework.pay.core.client.dto.notify.PayOrderNotifyRespDTO; +import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderRespDTO; import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderUnifiedReqDTO; import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderUnifiedRespDTO; import cn.iocoder.yudao.framework.pay.core.client.impl.AbstractPayClient; @@ -22,11 +21,13 @@ import lombok.extern.slf4j.Slf4j; import java.time.LocalDateTime; import java.time.ZoneId; +import java.util.Map; import java.util.Objects; import static cn.hutool.core.date.DatePattern.PURE_DATETIME_PATTERN; import static cn.hutool.core.date.DatePattern.UTC_WITH_XXX_OFFSET_PATTERN; -import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.*; +import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; +import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.invalidParamException; import static cn.iocoder.yudao.framework.common.util.json.JsonUtils.toJsonString; /** @@ -103,47 +104,47 @@ public abstract class AbstractWxPayClient extends AbstractPayClient params, String body) { + log.info("[parseNotify][微信支付回调 data 数据: {}]", body); try { // 微信支付 v2 回调结果处理 switch (config.getApiVersion()) { case WxPayClientConfig.API_VERSION_V2: - return parseOrderNotifyV2(rawNotify); + return parseOrderNotifyV2(body); case WxPayClientConfig.API_VERSION_V3: - return parseOrderNotifyV3(rawNotify); + return parseOrderNotifyV3(body); default: throw new IllegalArgumentException(String.format("未知的 API 版本(%s)", config.getApiVersion())); } } catch (WxPayException e) { - log.error("[parseNotify][rawNotify({}) 解析失败]", toJsonString(rawNotify), e); + log.error("[parseNotify][params({}) body({}) 解析失败]", params, body, e); // throw buildPayException(e); throw new RuntimeException(e); // TODO 芋艿:缺一个异常翻译 } } - private PayOrderNotifyRespDTO parseOrderNotifyV2(PayNotifyReqDTO data) throws WxPayException { - WxPayOrderNotifyResult notifyResult = client.parseOrderNotifyResult(data.getBody()); + private PayOrderRespDTO parseOrderNotifyV2(String body) throws WxPayException { + WxPayOrderNotifyResult notifyResult = client.parseOrderNotifyResult(body); Assert.isTrue(Objects.equals(notifyResult.getResultCode(), "SUCCESS"), "支付结果非 SUCCESS"); // 转换结果 - return PayOrderNotifyRespDTO + return PayOrderRespDTO .builder() - .orderExtensionNo(notifyResult.getOutTradeNo()) + .outTradeNo(notifyResult.getOutTradeNo()) .channelOrderNo(notifyResult.getTransactionId()) .channelUserId(notifyResult.getOpenid()) .successTime(parseDateV2(notifyResult.getTimeEnd())) .build(); } - private PayOrderNotifyRespDTO parseOrderNotifyV3(PayNotifyReqDTO data) throws WxPayException { - WxPayOrderNotifyV3Result notifyResult = client.parseOrderNotifyV3Result(data.getBody(), null); + private PayOrderRespDTO parseOrderNotifyV3(String body) throws WxPayException { + WxPayOrderNotifyV3Result notifyResult = client.parseOrderNotifyV3Result(body, null); WxPayOrderNotifyV3Result.DecryptNotifyResult result = notifyResult.getResult(); // 转换结果 Assert.isTrue(Objects.equals(notifyResult.getResult().getTradeState(), "SUCCESS"), "支付结果非 SUCCESS"); - return PayOrderNotifyRespDTO.builder() - .orderExtensionNo(result.getOutTradeNo()) + return PayOrderRespDTO.builder() + .outTradeNo(result.getOutTradeNo()) .channelOrderNo(result.getTradeState()) .channelUserId(result.getPayer() != null ? result.getPayer().getOpenid() : null) .successTime(parseDateV3(result.getSuccessTime())) @@ -175,7 +176,7 @@ public abstract class AbstractWxPayClient extends AbstractPayClientJSAPI 支付 的 out_trade_no 字段 + * 2. 支付宝支付:对应 电脑网站支付 的 out_trade_no 字段 * * 例如说,P202110132239124200055 */ @@ -70,7 +71,7 @@ public class PayOrderExtensionDO extends BaseDO { /** * 支付渠道的额外参数 * - * 参见 https://www.pingxx.com/api/支付渠道%20extra%20参数说明.html + * 参见 参数说明 */ @TableField(typeHandler = JacksonTypeHandler.class) private Map channelExtras; diff --git a/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/dal/dataobject/refund/PayRefundDO.java b/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/dal/dataobject/refund/PayRefundDO.java index 8330f0075..898a13699 100644 --- a/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/dal/dataobject/refund/PayRefundDO.java +++ b/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/dal/dataobject/refund/PayRefundDO.java @@ -5,6 +5,7 @@ import cn.iocoder.yudao.framework.pay.core.enums.channel.PayChannelEnum; import cn.iocoder.yudao.module.pay.dal.dataobject.app.PayAppDO; import cn.iocoder.yudao.module.pay.dal.dataobject.channel.PayChannelDO; import cn.iocoder.yudao.module.pay.dal.dataobject.order.PayOrderDO; +import cn.iocoder.yudao.module.pay.enums.notify.PayNotifyStatusEnum; import cn.iocoder.yudao.module.pay.enums.refund.PayRefundStatusEnum; import cn.iocoder.yudao.module.pay.enums.refund.PayRefundTypeEnum; import com.baomidou.mybatisplus.annotation.KeySequence; @@ -37,6 +38,14 @@ public class PayRefundDO extends BaseDO { */ @TableId private Long id; + /** + * 外部退款号,根据规则生成 + * + * 调用支付渠道时,使用该字段作为对接的退款号: + * 1. 微信退款:对应 申请退款 的 out_refund_no 字段 + * 2. 支付宝退款:对应 的 out_request_no 字段 + */ + private String no; /** * 应用编号 @@ -63,47 +72,27 @@ public class PayRefundDO extends BaseDO { */ private Long orderId; - /** - * 交易订单号,根据规则生成 - * 调用支付渠道时,使用该字段作为对接的订单号。 - * 1. 调用微信支付 https://api.mch.weixin.qq.com/v3/refund/domestic/refunds 时,使用该字段作为 out_trade_no - * 2. 调用支付宝 https://opendocs.alipay.com/apis 时,使用该字段作为 out_trade_no - * 这里对应 pay_extension 里面的 no - * 例如说,P202110132239124200055 - */ - private String tradeNo; - // ========== 商户相关字段 ========== /** * 商户订单编号 + * + * 例如说,内部系统 A 的订单号,需要保证每个 PayAppDO 唯一 */ private String merchantOrderId; - /** - * 商户退款订单号, 由商户系统产生, 由他们保证唯一,不能为空,通知商户时会传该字段。 - * 例如说,内部系统 A 的退款订单号。需要保证每个 PayMerchantDO 唯一 - * 个商户退款订单,对应一条退款请求记录。可多次提交。 渠道保持幂等 - * 使用商户退款单,作为退款请求号 - * https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter3_1_9.shtml 中的 out_refund_no - * https://opendocs.alipay.com/apis alipay.trade.refund 中的 out_request_no - * 退款请求号。 - * 标识一次退款请求,需要保证在交易号下唯一,如需部分退款,则此参数必传。 - * 注:针对同一次退款请求,如果调用接口失败或异常了,重试时需要保证退款请求号不能变更, - * 防止该笔交易重复退款。支付宝会保证同样的退款请求号多次请求只会退一次。 - * 退款单请求号,根据规则生成 - * 例如说,R202109181134287570000 + * 商户退款订单号 + * + * 例如说,内部系统 A 的订单号,需要保证每个 PayAppDO 唯一 */ - // TODO @jason:merchantRefundNo =》merchantRefundOId - private String merchantRefundNo; - + private String merchantRefundId; /** * 异步通知地址 */ private String notifyUrl; - /** * 通知商户退款结果的回调状态 - * TODO 0 未发送 1 已发送 + * + * 枚举 {@link PayNotifyStatusEnum} */ private Integer notifyStatus; @@ -142,22 +131,27 @@ public class PayRefundDO extends BaseDO { // ========== 渠道相关字段 ========== /** - * 渠道订单号,pay_order 中的channel_order_no 对应 + * 渠道订单号 + * + * 冗余 {@link PayOrderDO#getChannelOrderNo()} */ private String channelOrderNo; /** - * 微信中的 refund_id - * https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter3_1_9.shtml - * 支付宝没有 - * 渠道退款单号,渠道返回 + * 渠道退款单号 + * + * 1. 微信退款:对应 申请退款 的 refund_id 字段 + * 2. 支付宝退款:没有字段 */ private String channelRefundNo; + /** + * 退款成功时间 + */ + private LocalDateTime successTime; /** * 调用渠道的错误码 */ private String channelErrorCode; - /** * 调用渠道报错时,错误信息 */ @@ -165,22 +159,15 @@ public class PayRefundDO extends BaseDO { /** * 支付渠道的额外参数 - * 参见 https://www.pingxx.com/api/Refunds%20退款概述.html + * + * 参见 参数说明 */ private String channelExtras; - /** - * TODO - * 退款失效时间 + * 支付渠道异步通知的内容 + * + * 在退款成功后,会记录回调的数据 */ - private LocalDateTime expireTime; - /** - * 退款成功时间 - */ - private LocalDateTime successTime; - /** - * 退款通知时间 - */ - private LocalDateTime notifyTime; + private String channelNotifyData; } diff --git a/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/dal/mysql/refund/PayRefundMapper.java b/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/dal/mysql/refund/PayRefundMapper.java index 35a6bd5a7..42bd00b81 100755 --- a/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/dal/mysql/refund/PayRefundMapper.java +++ b/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/dal/mysql/refund/PayRefundMapper.java @@ -45,8 +45,10 @@ public interface PayRefundMapper extends BaseMapperX { return selectOne("req_no", reqNo); } + // TODO 芋艿:要重构 default PayRefundDO selectByTradeNoAndMerchantRefundNo(String tradeNo, String merchantRefundNo){ - return selectOne("trade_no", tradeNo, "merchant_refund_no", merchantRefundNo); +// return selectOne("trade_no", tradeNo, "merchant_refund_no", merchantRefundNo); + return null; } } diff --git a/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/service/channel/PayChannelServiceImpl.java b/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/service/channel/PayChannelServiceImpl.java index 69adddc4d..45604c04c 100644 --- a/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/service/channel/PayChannelServiceImpl.java +++ b/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/service/channel/PayChannelServiceImpl.java @@ -83,17 +83,20 @@ public class PayChannelServiceImpl implements PayChannelService { */ @Scheduled(initialDelay = 60, fixedRate = 60, timeUnit = TimeUnit.SECONDS) public void refreshLocalCache() { - // 情况一:如果缓存里没有数据,则直接刷新缓存 - if (CollUtil.isEmpty(channelCache)) { - initLocalCache(); - return; - } + // 注意:忽略自动多租户,因为要全局初始化缓存 + TenantUtils.executeIgnore(() -> { + // 情况一:如果缓存里没有数据,则直接刷新缓存 + if (CollUtil.isEmpty(channelCache)) { + initLocalCache(); + return; + } - // 情况二,如果缓存里数据,则通过 updateTime 判断是否有数据变更,有变更则刷新缓存 - LocalDateTime maxTime = CollectionUtils.getMaxValue(channelCache, PayChannelDO::getUpdateTime); - if (channelMapper.selectCountByUpdateTimeGt(maxTime) > 0) { - initLocalCache(); - } + // 情况二,如果缓存里数据,则通过 updateTime 判断是否有数据变更,有变更则刷新缓存 + LocalDateTime maxTime = CollectionUtils.getMaxValue(channelCache, PayChannelDO::getUpdateTime); + if (channelMapper.selectCountByUpdateTimeGt(maxTime) > 0) { + initLocalCache(); + } + }); } @Override diff --git a/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/service/order/PayOrderService.java b/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/service/order/PayOrderService.java index 890bacd62..5f4c6aa21 100755 --- a/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/service/order/PayOrderService.java +++ b/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/service/order/PayOrderService.java @@ -2,8 +2,7 @@ package cn.iocoder.yudao.module.pay.service.order; import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils; -import cn.iocoder.yudao.framework.pay.core.client.dto.notify.PayNotifyReqDTO; -import cn.iocoder.yudao.framework.pay.core.client.dto.notify.PayOrderNotifyRespDTO; +import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderRespDTO; import cn.iocoder.yudao.module.pay.api.order.dto.PayOrderCreateReqDTO; import cn.iocoder.yudao.module.pay.controller.admin.order.vo.PayOrderExportReqVO; import cn.iocoder.yudao.module.pay.controller.admin.order.vo.PayOrderPageReqVO; @@ -103,8 +102,7 @@ public interface PayOrderService { * * @param channelId 渠道编号 * @param notify 通知 - * @param rawNotify 通知数据 */ - void notifyPayOrder(Long channelId, PayOrderNotifyRespDTO notify, PayNotifyReqDTO rawNotify); + void notifyPayOrder(Long channelId, PayOrderRespDTO notify); } diff --git a/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/service/order/PayOrderServiceImpl.java b/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/service/order/PayOrderServiceImpl.java index d27c52212..884b182df 100755 --- a/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/service/order/PayOrderServiceImpl.java +++ b/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/service/order/PayOrderServiceImpl.java @@ -9,10 +9,10 @@ import cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils; import cn.iocoder.yudao.framework.pay.config.PayProperties; import cn.iocoder.yudao.framework.pay.core.client.PayClient; import cn.iocoder.yudao.framework.pay.core.client.PayClientFactory; -import cn.iocoder.yudao.framework.pay.core.client.dto.notify.PayNotifyReqDTO; -import cn.iocoder.yudao.framework.pay.core.client.dto.notify.PayOrderNotifyRespDTO; +import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderRespDTO; import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderUnifiedReqDTO; import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderUnifiedRespDTO; +import cn.iocoder.yudao.framework.pay.core.enums.order.PayOrderStatusRespEnum; import cn.iocoder.yudao.framework.tenant.core.util.TenantUtils; import cn.iocoder.yudao.module.pay.api.order.dto.PayOrderCreateReqDTO; import cn.iocoder.yudao.module.pay.controller.admin.order.vo.PayOrderExportReqVO; @@ -148,17 +148,17 @@ public class PayOrderServiceImpl implements PayOrderService { // 3. 调用三方接口 PayOrderUnifiedReqDTO unifiedOrderReqDTO = PayOrderConvert.INSTANCE.convert2(reqVO, userIp) // 商户相关的字段 - .setMerchantOrderId(orderExtension.getNo()) // 注意,此处使用的是 PayOrderExtensionDO.no 属性! + .setOutTradeNo(orderExtension.getNo()) // 注意,此处使用的是 PayOrderExtensionDO.no 属性! .setSubject(order.getSubject()).setBody(order.getBody()) .setNotifyUrl(genChannelPayNotifyUrl(channel)) .setReturnUrl(reqVO.getReturnUrl()) // 订单相关字段 - .setAmount(order.getPrice()).setExpireTime(order.getExpireTime()); + .setPrice(order.getPrice()).setExpireTime(order.getExpireTime()); PayOrderUnifiedRespDTO unifiedOrderRespDTO = client.unifiedOrder(unifiedOrderReqDTO); // 4. 如果调用直接支付成功,则直接更新支付单状态为成功。例如说:付款码支付,免密支付时,就直接验证支付成功 - if (unifiedOrderRespDTO.getNotify() != null) { - notifyPayOrderSuccess(channel, unifiedOrderRespDTO.getNotify(), null); + if (unifiedOrderRespDTO.getOrder() != null) { + notifyPayOrder(channel, unifiedOrderRespDTO.getOrder()); // 此处需要读取最新的状态 order = orderMapper.selectById(order.getId()); } @@ -226,16 +226,26 @@ public class PayOrderServiceImpl implements PayOrderService { @Override @Transactional(rollbackFor = Exception.class) - public void notifyPayOrder(Long channelId, PayOrderNotifyRespDTO notify, PayNotifyReqDTO rawNotify) { + public void notifyPayOrder(Long channelId, PayOrderRespDTO notify) { // 校验支付渠道是否有效 PayChannelDO channel = channelService.validPayChannel(channelId); // 更新支付订单为已支付 - TenantUtils.execute(channel.getTenantId(), () -> notifyPayOrderSuccess(channel, notify, rawNotify)); + TenantUtils.execute(channel.getTenantId(), () -> notifyPayOrder(channel, notify)); } - private void notifyPayOrderSuccess(PayChannelDO channel, PayOrderNotifyRespDTO notify, PayNotifyReqDTO rawNotify) { + private void notifyPayOrder(PayChannelDO channel, PayOrderRespDTO notify) { + // 情况一:支付成功的回调 + if (PayOrderStatusRespEnum.isSuccess(notify.getStatus())) { + notifyPayOrderSuccess(channel, notify); + return; + } + // 情况二:非支付成功的回调,进行忽略 + log.info("[notifyPayOrder][非支付成功的回调({}),直接忽略]", toJsonString(notify)); + } + + private void notifyPayOrderSuccess(PayChannelDO channel, PayOrderRespDTO notify) { // 1. 更新 PayOrderExtensionDO 支付成功 - PayOrderExtensionDO orderExtension = updatePayOrderExtensionSuccess(notify.getOrderExtensionNo(), rawNotify); + PayOrderExtensionDO orderExtension = updatePayOrderExtensionSuccess(notify); // 2. 更新 PayOrderDO 支付成功 Pair order = updatePayOrderSuccess(channel, orderExtension, notify); if (order.getKey()) { // 如果之前已经成功回调,则直接返回,不用重复记录支付通知记录;例如说:支付平台重复回调 @@ -250,13 +260,12 @@ public class PayOrderServiceImpl implements PayOrderService { /** * 更新 PayOrderExtensionDO 支付成功 * - * @param no 支付订单号(支付模块) - * @param rawNotify 通知数据 + * @param notify 通知 * @return PayOrderExtensionDO 对象 */ - private PayOrderExtensionDO updatePayOrderExtensionSuccess(String no, PayNotifyReqDTO rawNotify) { + private PayOrderExtensionDO updatePayOrderExtensionSuccess(PayOrderRespDTO notify) { // 1. 查询 PayOrderExtensionDO - PayOrderExtensionDO orderExtension = orderExtensionMapper.selectByNo(no); + PayOrderExtensionDO orderExtension = orderExtensionMapper.selectByNo(notify.getOutTradeNo()); if (orderExtension == null) { throw exception(ErrorCodeConstants.PAY_ORDER_EXTENSION_NOT_FOUND); } @@ -269,10 +278,8 @@ public class PayOrderServiceImpl implements PayOrderService { } // 2. 更新 PayOrderExtensionDO - int updateCounts = orderExtensionMapper.updateByIdAndStatus(orderExtension.getId(), - PayOrderStatusEnum.WAITING.getStatus(), PayOrderExtensionDO.builder().id(orderExtension.getId()) - .status(PayOrderStatusEnum.SUCCESS.getStatus()) - .channelNotifyData(toJsonString(rawNotify)).build()); + int updateCounts = orderExtensionMapper.updateByIdAndStatus(orderExtension.getId(), PayOrderStatusEnum.WAITING.getStatus(), + PayOrderExtensionDO.builder().status(PayOrderStatusEnum.SUCCESS.getStatus()).channelNotifyData(toJsonString(notify)).build()); if (updateCounts == 0) { // 校验状态,必须是待支付 throw exception(ErrorCodeConstants.PAY_ORDER_EXTENSION_STATUS_IS_NOT_WAITING); } @@ -290,7 +297,7 @@ public class PayOrderServiceImpl implements PayOrderService { * value:PayOrderDO 对象 */ private Pair updatePayOrderSuccess(PayChannelDO channel, PayOrderExtensionDO orderExtension, - PayOrderNotifyRespDTO notify) { + PayOrderRespDTO notify) { // 1. 判断 PayOrderDO 是否处于待支付 PayOrderDO order = orderMapper.selectById(orderExtension.getOrderId()); if (order == null) { diff --git a/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/service/refund/PayRefundService.java b/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/service/refund/PayRefundService.java index 14f525582..f7485c486 100755 --- a/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/service/refund/PayRefundService.java +++ b/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/service/refund/PayRefundService.java @@ -1,11 +1,10 @@ package cn.iocoder.yudao.module.pay.service.refund; -import cn.iocoder.yudao.framework.pay.core.client.dto.notify.PayNotifyReqDTO; -import cn.iocoder.yudao.framework.pay.core.client.dto.notify.PayRefundNotifyRespDTO; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.pay.core.client.dto.refund.PayRefundRespDTO; import cn.iocoder.yudao.module.pay.api.refund.dto.PayRefundCreateReqDTO; import cn.iocoder.yudao.module.pay.controller.admin.refund.vo.PayRefundExportReqVO; import cn.iocoder.yudao.module.pay.controller.admin.refund.vo.PayRefundPageReqVO; -import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.module.pay.dal.dataobject.refund.PayRefundDO; import java.util.List; @@ -62,8 +61,7 @@ public interface PayRefundService { * * @param channelId 渠道编号 * @param notify 通知 - * @param rawNotify 通知数据 */ - void notifyPayRefund(Long channelId, PayRefundNotifyRespDTO notify, PayNotifyReqDTO rawNotify); + void notifyPayRefund(Long channelId, PayRefundRespDTO notify); } diff --git a/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/service/refund/PayRefundServiceImpl.java b/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/service/refund/PayRefundServiceImpl.java index a5f815bdd..0950fe471 100755 --- a/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/service/refund/PayRefundServiceImpl.java +++ b/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/service/refund/PayRefundServiceImpl.java @@ -7,10 +7,9 @@ import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.pay.config.PayProperties; import cn.iocoder.yudao.framework.pay.core.client.PayClient; import cn.iocoder.yudao.framework.pay.core.client.PayClientFactory; -import cn.iocoder.yudao.framework.pay.core.client.dto.notify.PayNotifyReqDTO; -import cn.iocoder.yudao.framework.pay.core.client.dto.notify.PayRefundNotifyRespDTO; +import cn.iocoder.yudao.framework.pay.core.client.dto.refund.PayRefundRespDTO; import cn.iocoder.yudao.framework.pay.core.client.dto.refund.PayRefundUnifiedReqDTO; -import cn.iocoder.yudao.framework.pay.core.enums.refund.PayNotifyRefundStatusEnum; +import cn.iocoder.yudao.framework.pay.core.enums.refund.PayRefundStatusRespEnum; import cn.iocoder.yudao.module.pay.api.refund.dto.PayRefundCreateReqDTO; import cn.iocoder.yudao.module.pay.controller.admin.refund.vo.PayRefundExportReqVO; import cn.iocoder.yudao.module.pay.controller.admin.refund.vo.PayRefundPageReqVO; @@ -39,14 +38,13 @@ import org.springframework.transaction.annotation.Transactional; import org.springframework.validation.annotation.Validated; import javax.annotation.Resource; -import java.time.LocalDateTime; import java.util.List; import java.util.Objects; /** * 退款订单 Service 实现类 * - * @author aquan + * @author jason */ @Service @Slf4j @@ -116,7 +114,7 @@ public class PayRefundServiceImpl implements PayRefundService { } // TODO 芋艿:待实现 - String merchantRefundId = RandomUtil.randomNumbers(16); + String merchantRefundId = "rrr" + RandomUtil.randomNumbers(16); // 校验退款的条件 validatePayRefund(reqDTO, order); @@ -128,12 +126,11 @@ public class PayRefundServiceImpl implements PayRefundService { PayOrderExtensionDO orderExtensionDO = orderExtensionService.getOrderExtension(order.getSuccessExtensionId()); PayRefundDO payRefundDO = refundMapper.selectByTradeNoAndMerchantRefundNo(orderExtensionDO.getNo(), merchantRefundId); // TODO 芋艿:需要优化 - if(Objects.nonNull(payRefundDO)){ + if (Objects.nonNull(payRefundDO)) { // 退款订单已经提交过。 //TODO 校验相同退款单的金额 // TODO @jason:咱要不封装一个 ObjectUtils.equalsAny - if (Objects.equals(PayRefundStatusEnum.SUCCESS.getStatus(), payRefundDO.getStatus()) - || Objects.equals(PayRefundStatusEnum.CLOSE.getStatus(), payRefundDO.getStatus())) { + if (Objects.equals(PayRefundStatusEnum.SUCCESS.getStatus(), payRefundDO.getStatus())) { //已成功退款 throw ServiceExceptionUtil.exception(ErrorCodeConstants.PAY_REFUND_SUCCEED); } @@ -147,14 +144,14 @@ public class PayRefundServiceImpl implements PayRefundService { .channelCode(order.getChannelCode()) .channelId(order.getChannelId()) .orderId(order.getId()) - .merchantRefundNo(merchantRefundId) // TODO 芋艿:需要优化 + .merchantRefundId(merchantRefundId) .notifyUrl(app.getRefundNotifyUrl()) .payPrice(order.getPrice()) .refundPrice(reqDTO.getPrice()) .userIp(reqDTO.getUserIp()) .merchantOrderId(order.getMerchantOrderId()) - .tradeNo(orderExtensionDO.getNo()) - .status(PayRefundStatusEnum.CREATE.getStatus()) + .no(orderExtensionDO.getNo()) + .status(PayRefundStatusEnum.WAITING.getStatus()) .reason(reqDTO.getReason()) .notifyStatus(PayOrderNotifyStatusEnum.NO.getStatus()) .type(refundType.getStatus()) @@ -163,11 +160,9 @@ public class PayRefundServiceImpl implements PayRefundService { } // TODO @jason:搞到 convert 里。一些额外的自动,手动 set 下; PayRefundUnifiedReqDTO unifiedReqDTO = new PayRefundUnifiedReqDTO(); - unifiedReqDTO.setUserIp(reqDTO.getUserIp()) - .setAmount(reqDTO.getPrice()) - .setChannelOrderNo(order.getChannelOrderNo()) - .setPayTradeNo(orderExtensionDO.getNo()) - .setMerchantRefundId(merchantRefundId) // TODO 芋艿:需要优化 + unifiedReqDTO.setPrice(reqDTO.getPrice()) + .setOutTradeNo(orderExtensionDO.getNo()) + .setOutRefundNo(merchantRefundId) // TODO 芋艿:需要优化 .setNotifyUrl(genChannelPayNotifyUrl(channel)) // TODO 芋艿:优化下 notifyUrl .setReason(reqDTO.getReason()); // 向渠道发起退款申请 @@ -191,24 +186,25 @@ public class PayRefundServiceImpl implements PayRefundService { @Override @Transactional(rollbackFor = Exception.class) - public void notifyPayRefund(Long channelId, PayRefundNotifyRespDTO notify, PayNotifyReqDTO rawNotify) { + public void notifyPayRefund(Long channelId, PayRefundRespDTO notify) { // 校验支付渠道是否有效 // TODO 芋艿:需要重构下这块的逻辑 PayChannelDO channel = channelService.validPayChannel(channelId); - if (Objects.equals(PayNotifyRefundStatusEnum.SUCCESS, notify.getStatus())){ + if (PayRefundStatusRespEnum.isSuccess(notify.getStatus())) { payRefundSuccess(notify); } else { - //TODO 支付异常, 支付宝似乎没有支付异常的通知。 // TODO @jason:那这里可以考虑打个 error logger @芋艿 微信是否存在支付异常通知 } } - private void payRefundSuccess(PayRefundNotifyRespDTO refundNotify) { + private void payRefundSuccess(PayRefundRespDTO refundNotify) { // 校验退款单存在 - PayRefundDO refundDO = refundMapper.selectByTradeNoAndMerchantRefundNo(refundNotify.getTradeNo(), - refundNotify.getReqNo()); + PayRefundDO refundDO = null; // TODO 芋艿:临时注释 +// PayRefundDO refundDO = refundMapper.selectByTradeNoAndMerchantRefundNo(refundNotify.getTradeNo(), +// refundNotify.getReqNo()); if (refundDO == null) { - log.error("[payRefundSuccess][不存在 seqNo 为{} 的支付退款单]", refundNotify.getReqNo()); + // TODO 芋艿:临时注释 +// log.error("[payRefundSuccess][不存在 seqNo 为{} 的支付退款单]", refundNotify.getReqNo()); throw ServiceExceptionUtil.exception(ErrorCodeConstants.PAY_REFUND_NOT_FOUND); } @@ -233,10 +229,10 @@ public class PayRefundServiceImpl implements PayRefundService { // 更新退款订单 PayRefundDO updateRefundDO = new PayRefundDO(); updateRefundDO.setId(refundDO.getId()) - .setSuccessTime(refundNotify.getRefundSuccessTime()) - .setChannelRefundNo(refundNotify.getChannelOrderNo()) - .setTradeNo(refundNotify.getTradeNo()) - .setNotifyTime(LocalDateTime.now()) + .setSuccessTime(refundNotify.getSuccessTime()) + // TODO 芋艿:如下两行,临时注释 +// .setChannelRefundNo(refundNotify.getChannelOrderNo()) +// .setNo(refundNotify.getTradeNo()) .setStatus(PayRefundStatusEnum.SUCCESS.getStatus()); refundMapper.updateById(updateRefundDO); diff --git a/yudao-module-pay/yudao-module-pay-biz/src/test/java/cn/iocoder/yudao/module/pay/service/app/PayAppServiceTest.java b/yudao-module-pay/yudao-module-pay-biz/src/test/java/cn/iocoder/yudao/module/pay/service/app/PayAppServiceTest.java index 81536aba3..e90b84685 100644 --- a/yudao-module-pay/yudao-module-pay-biz/src/test/java/cn/iocoder/yudao/module/pay/service/app/PayAppServiceTest.java +++ b/yudao-module-pay/yudao-module-pay-biz/src/test/java/cn/iocoder/yudao/module/pay/service/app/PayAppServiceTest.java @@ -3,9 +3,7 @@ package cn.iocoder.yudao.module.pay.service.app; import cn.hutool.core.util.RandomUtil; import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils; import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest; -import cn.iocoder.yudao.framework.test.core.util.RandomUtils; import cn.iocoder.yudao.module.pay.controller.admin.app.vo.PayAppCreateReqVO; import cn.iocoder.yudao.module.pay.controller.admin.app.vo.PayAppPageReqVO; import cn.iocoder.yudao.module.pay.controller.admin.app.vo.PayAppUpdateReqVO; @@ -18,8 +16,6 @@ import org.springframework.boot.test.mock.mockito.MockBean; import org.springframework.context.annotation.Import; import javax.annotation.Resource; -import java.time.LocalDateTime; -import java.util.Collections; import java.util.Map; import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.buildBetweenTime; diff --git a/yudao-module-pay/yudao-module-pay-biz/src/test/java/cn/iocoder/yudao/module/pay/service/refund/PayRefundServiceTest.java b/yudao-module-pay/yudao-module-pay-biz/src/test/java/cn/iocoder/yudao/module/pay/service/refund/PayRefundServiceTest.java index a3692a4e2..8365f1ab6 100755 --- a/yudao-module-pay/yudao-module-pay-biz/src/test/java/cn/iocoder/yudao/module/pay/service/refund/PayRefundServiceTest.java +++ b/yudao-module-pay/yudao-module-pay-biz/src/test/java/cn/iocoder/yudao/module/pay/service/refund/PayRefundServiceTest.java @@ -62,7 +62,7 @@ public class PayRefundServiceTest extends BaseDbUnitTest { o.setChannelId(1L); o.setChannelCode(PayChannelEnum.WX_PUB.getCode()); o.setOrderId(1L); - o.setTradeNo("OT0000001"); + o.setNo("OT0000001"); o.setMerchantOrderId("MOT0000001"); o.setMerchantRefundNo("MRF0000001"); o.setNotifyUrl("https://www.cancanzi.com"); @@ -127,7 +127,7 @@ public class PayRefundServiceTest extends BaseDbUnitTest { o.setChannelId(1L); o.setChannelCode(PayChannelEnum.WX_PUB.getCode()); o.setOrderId(1L); - o.setTradeNo("OT0000001"); + o.setNo("OT0000001"); o.setMerchantOrderId("MOT0000001"); o.setMerchantRefundNo("MRF0000001"); o.setNotifyUrl("https://www.cancanzi.com"); diff --git a/yudao-ui-admin/src/api/pay/channel.js b/yudao-ui-admin/src/api/pay/channel.js index 727fde092..3f06805b7 100644 --- a/yudao-ui-admin/src/api/pay/channel.js +++ b/yudao-ui-admin/src/api/pay/channel.js @@ -28,14 +28,14 @@ export function deleteChannel(id) { } // 获得支付渠道 -export function getChannel(appId,code) { +export function getChannel(appId, code) { return request({ - url: '/pay/channel/get-channel', + url: '/pay/channel/get', + method: 'get', params:{ - appId:appId, - code:code + appId, + code }, - method: 'get' }) }