瀏覽代碼

mall + pay:
1. 重构支付回调的逻辑,将回调解析改成 PayOrderRespDTO,为后续轮询做铺垫
2. 调整退款单的表结构
3. 调整退款调用的实现

YunaiV 1 年之前
父節點
當前提交
518e89dc4b
共有 46 個文件被更改,包括 439 次插入502 次删除
  1. 8 0
      yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/validation/ValidationUtils.java
  2. 12 10
      yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/PayClient.java
  3. 0 29
      yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/dto/notify/PayNotifyReqDTO.java
  4. 0 38
      yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/dto/notify/PayOrderNotifyRespDTO.java
  5. 0 58
      yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/dto/notify/PayRefundNotifyRespDTO.java
  6. 55 0
      yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/dto/order/PayOrderRespDTO.java
  7. 6 4
      yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/dto/order/PayOrderUnifiedReqDTO.java
  8. 3 3
      yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/dto/order/PayOrderUnifiedRespDTO.java
  9. 40 0
      yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/dto/refund/PayRefundRespDTO.java
  10. 13 25
      yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/dto/refund/PayRefundUnifiedReqDTO.java
  11. 0 24
      yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/dto/refund/PayRefundUnifiedRespDTO.java
  12. 4 4
      yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/AbstractPayClient.java
  13. 57 49
      yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/alipay/AbstractAlipayPayClient.java
  14. 2 2
      yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/alipay/AlipayAppPayClient.java
  15. 2 2
      yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/alipay/AlipayBarPayClient.java
  16. 2 2
      yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/alipay/AlipayPcPayClient.java
  17. 2 2
      yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/alipay/AlipayQrPayClient.java
  18. 2 2
      yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/alipay/AlipayWapPayClient.java
  19. 18 17
      yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/weixin/AbstractWxPayClient.java
  20. 2 2
      yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/weixin/WxAppPayClient.java
  21. 11 8
      yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/weixin/WxBarPayClient.java
  22. 2 2
      yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/weixin/WxH5PayClient.java
  23. 7 7
      yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/weixin/WxNativePayClient.java
  24. 6 6
      yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/weixin/WxPubPayClient.java
  25. 13 1
      yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/enums/order/PayOrderStatusRespEnum.java
  26. 0 23
      yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/enums/refund/PayNotifyRefundStatusEnum.java
  27. 0 23
      yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/enums/refund/PayRefundRespEnum.java
  28. 28 0
      yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/enums/refund/PayRefundStatusRespEnum.java
  29. 2 2
      yudao-framework/yudao-spring-boot-starter-biz-pay/src/test/java/cn/iocoder/yudao/framework/pay/core/client/impl/PayClientFactoryImplIntegrationTest.java
  30. 2 4
      yudao-framework/yudao-spring-boot-starter-biz-pay/src/test/java/cn/iocoder/yudao/framework/pay/core/client/impl/alipay/AlipayQrPayClientTest.java
  31. 8 4
      yudao-module-pay/yudao-module-pay-api/src/main/java/cn/iocoder/yudao/module/pay/enums/refund/PayRefundStatusEnum.java
  32. 7 9
      yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/controller/admin/notify/PayNotifyController.java
  33. 0 3
      yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/controller/admin/refund/vo/PayRefundExcelVO.java
  34. 3 4
      yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/convert/refund/PayRefundConvert.java
  35. 2 1
      yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/dal/dataobject/order/PayOrderDO.java
  36. 6 5
      yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/dal/dataobject/order/PayOrderExtensionDO.java
  37. 35 48
      yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/dal/dataobject/refund/PayRefundDO.java
  38. 3 1
      yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/dal/mysql/refund/PayRefundMapper.java
  39. 14 11
      yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/service/channel/PayChannelServiceImpl.java
  40. 2 4
      yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/service/order/PayOrderService.java
  41. 26 19
      yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/service/order/PayOrderServiceImpl.java
  42. 3 5
      yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/service/refund/PayRefundService.java
  43. 24 28
      yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/service/refund/PayRefundServiceImpl.java
  44. 0 4
      yudao-module-pay/yudao-module-pay-biz/src/test/java/cn/iocoder/yudao/module/pay/service/app/PayAppServiceTest.java
  45. 2 2
      yudao-module-pay/yudao-module-pay-biz/src/test/java/cn/iocoder/yudao/module/pay/service/refund/PayRefundServiceTest.java
  46. 5 5
      yudao-ui-admin/src/api/pay/channel.js

+ 8 - 0
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<ConstraintViolation<Object>> constraintViolations = validator.validate(object, groups);
         if (CollUtil.isNotEmpty(constraintViolations)) {

+ 12 - 10
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<String, String> params, String body) {
         throw new UnsupportedOperationException("未实现 parseNotify 方法!");
     }
 

+ 0 - 29
yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/dto/notify/PayNotifyReqDTO.java

@@ -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<String,String> params;
-
-}

+ 0 - 38
yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/dto/notify/PayOrderNotifyRespDTO.java

@@ -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;
-
-}

+ 0 - 58
yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/dto/notify/PayRefundNotifyRespDTO.java

@@ -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;
-
-}

+ 55 - 0
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;
+
+}

+ 6 - 4
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;
 
     /**
      * 支付过期时间

+ 3 - 3
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;

+ 40 - 0
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;
+
+}

+ 13 - 25
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;
+    @NotEmpty(message = "外部订单编号不能为空")
+    private String outTradeNo;
 
-    // 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
-     * 渠道订单号
+     * 外部退款号
+     *
+     * 对应 PayRefundDO 的 no 字段
      */
-    private String channelOrderNo;
-
-    /**
-     * 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字段} 和 渠道订单号 不能同时为空
-     */
-    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;
 

+ 0 - 24
yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/dto/refund/PayRefundUnifiedRespDTO.java

@@ -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;
-}

+ 4 - 4
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<Config extends PayClientConfig> 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<Config extends PayClientConfig> implemen
         return resp;
     }
 
-    protected abstract PayRefundUnifiedRespDTO doUnifiedRefund(PayRefundUnifiedReqDTO reqDTO) throws Throwable;
+    protected abstract PayRefundRespDTO doUnifiedRefund(PayRefundUnifiedReqDTO reqDTO) throws Throwable;
 
     // ========== 各种工具方法 ==========
 

+ 57 - 49
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<AlipayPa
 
     /**
      * 支付宝统一的退款接口 alipay.trade.refund
+     *
      * @param reqDTO 退款请求 request DTO
      * @return 退款请求 Response
      */
     @Override
-    protected PayRefundUnifiedRespDTO doUnifiedRefund(PayRefundUnifiedReqDTO reqDTO)  {
-        AlipayTradeRefundModel model=new AlipayTradeRefundModel();
-        model.setTradeNo(reqDTO.getChannelOrderNo());
-        model.setOutTradeNo(reqDTO.getPayTradeNo());
-
-        model.setOutRequestNo(reqDTO.getMerchantRefundId());
-        model.setRefundAmount(formatAmount(reqDTO.getAmount()));
+    protected PayRefundRespDTO doUnifiedRefund(PayRefundUnifiedReqDTO reqDTO)  {
+        // 1.1 构建 AlipayTradeRefundModel 请求
+        AlipayTradeRefundModel model = new AlipayTradeRefundModel();
+        model.setOutTradeNo(reqDTO.getOutTradeNo());
+        model.setOutRequestNo(reqDTO.getOutRefundNo());
+        model.setRefundAmount(formatAmount(reqDTO.getPrice()));
+//        model.setRefundAmount(formatAmount(reqDTO.getPrice() / 2));
         model.setRefundReason(reqDTO.getReason());
-
-        AlipayTradeRefundRequest refundRequest = new AlipayTradeRefundRequest();
-        refundRequest.setBizModel(model);
-        refundRequest.setNotifyUrl(reqDTO.getNotifyUrl());
-        refundRequest.setReturnUrl(reqDTO.getNotifyUrl());
+        // 1.2 构建 AlipayTradePayRequest 请求
+        AlipayTradeRefundRequest request = new AlipayTradeRefundRequest();
+        request.setBizModel(model);
         try {
-            AlipayTradeRefundResponse response =  client.execute(refundRequest);
-            log.info("[doUnifiedRefund][response({}) 发起退款 渠道返回", toJsonString(response));
+            // 2.1 执行请求
+            AlipayTradeRefundResponse response =  client.execute(request);
+            PayRefundRespDTO refund = new PayRefundRespDTO()
+                    .setRawData(response);
+            // 支付宝只要退款调用返回 success,就认为退款成功,不需要回调。具体可见 parseNotify 方法的说明。
+            // 另外,支付宝没有退款单号,所以不用设置
             if (response.isSuccess()) {
-                //退款导致触发的异步通知是发送到支付接口中设置的notify_url
-                //支付宝不返回退款单号,设置为空
-                PayRefundUnifiedRespDTO respDTO = new PayRefundUnifiedRespDTO();
-                respDTO.setChannelRefundId("");
-//                return PayCommonResult.build(response.getCode(), response.getMsg(), respDTO, codeMapping); TODO
-                return null;
+                refund.setStatus(PayOrderStatusRespEnum.SUCCESS.getStatus())
+                        .setSuccessTime(LocalDateTimeUtil.of(response.getGmtRefundPay()));
+                Assert.notNull(refund.getSuccessTime(), "退款成功时间不能为空");
+            } else {
+                refund.setStatus(PayOrderStatusRespEnum.CLOSED.getStatus());
             }
-            // 失败。需要抛出异常
-//            return PayCommonResult.build(response.getCode(), response.getMsg(), null, codeMapping); TODO
-            return null;
+            return refund;
         } catch (AlipayApiException e) {
-            // TODO 记录异常日志
-            log.error("[doUnifiedRefund][request({}) 发起退款失败,网络读超时,退款状态未知]", toJsonString(reqDTO), e);
-//            return PayCommonResult.build(e.getErrCode(), e.getErrMsg(), null, codeMapping); TODO
+            log.error("[doUnifiedRefund][request({}) 发起退款异常]", toJsonString(reqDTO), e);
             return null;
         }
     }
 
     @Override
     @SneakyThrows
-    public Object parseNotify(PayNotifyReqDTO rawNotify) {
+    public Object parseNotify(Map<String, String> params, String body) {
+        // 补充说明:支付宝退款时,没有回调,这点和微信支付是不同的。并且,退款分成部分退款、和全部退款。
+        // ① 部分退款:是会有回调,但是它回调的是订单状态的同步回调,不是退款订单的回调
+        // ② 全部退款:Wap 支付有订单状态的同步回调,但是 PC/扫码又没有
+        // 所以,这里在解析时,即使是退款导致的订单状态同步,我们也忽略不做为“退款同步”,而是订单的回调。
+        // 实际上,支付宝退款只要发起成功,就可以认为退款成功,不需要等待回调。
+
         // 1. 校验回调数据
-        String body = rawNotify.getBody();
-        Map<String, String> params = rawNotify.getParams();
         Map<String, String> bodyObj = HttpUtil.decodeParamMap(body, StandardCharsets.UTF_8);
         AlipaySignature.rsaCheckV1(bodyObj, config.getAlipayPublicKey(),
-                StandardCharsets.UTF_8.name(), "RSA2");
-
-        // 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"))
+                StandardCharsets.UTF_8.name(), config.getSignType());
+
+        // 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<Throwable>) () -> {
+            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();
     }
 

+ 2 - 2
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"); // 销售产品码:无线快捷支付产品
         // ② 个性化的参数【无】
         // ③ 支付宝扫码支付只有一种展示

+ 2 - 2
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);

+ 2 - 2
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
         // ② 个性化的参数

+ 2 - 2
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
         // ② 个性化的参数【无】
         // ③ 支付宝扫码支付只有一种展示,考虑到前端可能希望二维码扫描后,手机打开

+ 2 - 2
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

+ 18 - 17
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<WxPayClientC
 
 
     @Override
-    public Object parseNotify(PayNotifyReqDTO rawNotify) {
-        log.info("[parseNotify][微信支付回调 data 数据: {}]", rawNotify.getBody());
+    public Object parseNotify(Map<String, String> 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 AbstractPayClient<WxPayClientC
         if (Objects.equals(e.getReturnCode(), "FAIL")) {
             throw exception(PayFrameworkErrorCodeConstants.ORDER_UNIFIED_ERROR, e.getReturnMsg());
         }
-        // 情况三:系统异常,这里暂时不打,交给上层的 AbstractPayClient 统一打
+        // 情况三:系统异常,这里暂时不打,交给上层的 AbstractPayClient 统一打日志
         return e;
     }
 

+ 2 - 2
yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/weixin/WxAppPayClient.java

@@ -2,8 +2,8 @@ package cn.iocoder.yudao.framework.pay.core.client.impl.weixin;
 
 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.enums.channel.PayChannelEnum;
 import com.github.binarywang.wxpay.constant.WxPayConstants;
 import com.github.binarywang.wxpay.exception.WxPayException;
@@ -21,7 +21,7 @@ public class WxAppPayClient extends AbstractWxPayClient {
     }
 
     @Override
-    protected PayRefundUnifiedRespDTO doUnifiedRefund(PayRefundUnifiedReqDTO reqDTO) throws Throwable {
+    protected PayRefundRespDTO doUnifiedRefund(PayRefundUnifiedReqDTO reqDTO) throws Throwable {
         return null;
     }
 

+ 11 - 8
yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/weixin/WxBarPayClient.java

@@ -5,13 +5,14 @@ import cn.hutool.core.thread.ThreadUtil;
 import cn.hutool.core.util.StrUtil;
 import cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils;
 import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
-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.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.enums.channel.PayChannelEnum;
 import cn.iocoder.yudao.framework.pay.core.enums.order.PayOrderDisplayModeEnum;
+import cn.iocoder.yudao.framework.pay.core.enums.order.PayOrderStatusRespEnum;
 import com.github.binarywang.wxpay.bean.request.WxPayMicropayRequest;
 import com.github.binarywang.wxpay.bean.result.WxPayMicropayResult;
 import com.github.binarywang.wxpay.constant.WxPayConstants;
@@ -57,10 +58,10 @@ public class WxBarPayClient extends AbstractWxPayClient {
         }
         // 构建 WxPayMicropayRequest 对象
         WxPayMicropayRequest request = WxPayMicropayRequest.newBuilder()
-                .outTradeNo(reqDTO.getMerchantOrderId())
+                .outTradeNo(reqDTO.getOutTradeNo())
                 .body(reqDTO.getSubject())
                 .detail(reqDTO.getBody())
-                .totalFee(reqDTO.getAmount()) // 单位分
+                .totalFee(reqDTO.getPrice()) // 单位分
                 .timeExpire(formatDateV2(expireTime))
                 .spbillCreateIp(reqDTO.getUserIp())
                 .authCode(getAuthCode(reqDTO))
@@ -70,15 +71,17 @@ public class WxBarPayClient extends AbstractWxPayClient {
             try {
                 WxPayMicropayResult response = client.micropay(request);
                 // 支付成功(例如说,用户输入了密码)
-                PayOrderNotifyRespDTO notify = PayOrderNotifyRespDTO.builder()
-                        .orderExtensionNo(response.getOutTradeNo())
+                PayOrderRespDTO order = PayOrderRespDTO.builder()
+                        .status(PayOrderStatusRespEnum.SUCCESS.getStatus())
+                        .outTradeNo(response.getOutTradeNo())
                         .channelOrderNo(response.getTransactionId())
                         .channelUserId(response.getOpenid())
                         .successTime(parseDateV2(response.getTimeEnd()))
+                        .rawData(response)
                         .build();
                 return new PayOrderUnifiedRespDTO(PayOrderDisplayModeEnum.BAR_CODE.getMode(),
                         JsonUtils.toJsonString(response))
-                        .setNotify(notify);
+                        .setOrder(order);
             } catch (WxPayException ex) {
                 // 如果不满足这 3 种任一的,则直接抛出 WxPayException 异常,不仅需处理
                 // 1. SYSTEMERROR:接口返回错误:请立即调用被扫订单结果查询API,查询当前订单状态,并根据订单的状态决定下一步的操作。
@@ -102,7 +105,7 @@ public class WxBarPayClient extends AbstractWxPayClient {
     }
 
     @Override
-    protected PayRefundUnifiedRespDTO doUnifiedRefund(PayRefundUnifiedReqDTO reqDTO) {
+    protected PayRefundRespDTO doUnifiedRefund(PayRefundUnifiedReqDTO reqDTO) {
         return null;
     }
 

+ 2 - 2
yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/weixin/WxH5PayClient.java

@@ -2,8 +2,8 @@ package cn.iocoder.yudao.framework.pay.core.client.impl.weixin;
 
 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.enums.channel.PayChannelEnum;
 import com.github.binarywang.wxpay.constant.WxPayConstants;
 import com.github.binarywang.wxpay.exception.WxPayException;
@@ -21,7 +21,7 @@ public class WxH5PayClient extends AbstractWxPayClient {
     }
 
     @Override
-    protected PayRefundUnifiedRespDTO doUnifiedRefund(PayRefundUnifiedReqDTO reqDTO) throws Throwable {
+    protected PayRefundRespDTO doUnifiedRefund(PayRefundUnifiedReqDTO reqDTO) throws Throwable {
         return null;
     }
 

+ 7 - 7
yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/weixin/WxNativePayClient.java

@@ -2,8 +2,8 @@ package cn.iocoder.yudao.framework.pay.core.client.impl.weixin;
 
 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.enums.channel.PayChannelEnum;
 import cn.iocoder.yudao.framework.pay.core.enums.order.PayOrderDisplayModeEnum;
 import com.github.binarywang.wxpay.bean.order.WxPayNativeOrderResult;
@@ -37,11 +37,11 @@ public class WxNativePayClient extends AbstractWxPayClient {
     protected PayOrderUnifiedRespDTO doUnifiedOrderV2(PayOrderUnifiedReqDTO reqDTO) throws WxPayException {
         // 构建 WxPayUnifiedOrderRequest 对象
         WxPayUnifiedOrderRequest request = WxPayUnifiedOrderRequest.newBuilder()
-                .outTradeNo(reqDTO.getMerchantOrderId())
+                .outTradeNo(reqDTO.getOutTradeNo())
                 .body(reqDTO.getSubject())
                 .detail(reqDTO.getBody())
-                .totalFee(reqDTO.getAmount()) // 单位分
-                .productId(reqDTO.getMerchantOrderId())
+                .totalFee(reqDTO.getPrice()) // 单位分
+                .productId(reqDTO.getOutTradeNo())
                 .timeExpire(formatDateV2(reqDTO.getExpireTime()))
                 .spbillCreateIp(reqDTO.getUserIp())
                 .notifyUrl(reqDTO.getNotifyUrl())
@@ -58,9 +58,9 @@ public class WxNativePayClient extends AbstractWxPayClient {
     protected PayOrderUnifiedRespDTO doUnifiedOrderV3(PayOrderUnifiedReqDTO reqDTO) throws WxPayException {
         // 构建 WxPayUnifiedOrderRequest 对象
         WxPayUnifiedOrderV3Request request = new WxPayUnifiedOrderV3Request();
-        request.setOutTradeNo(reqDTO.getMerchantOrderId());
+        request.setOutTradeNo(reqDTO.getOutTradeNo());
         request.setDescription(reqDTO.getBody());
-        request.setAmount(new WxPayUnifiedOrderV3Request.Amount().setTotal(reqDTO.getAmount())); // 单位分
+        request.setAmount(new WxPayUnifiedOrderV3Request.Amount().setTotal(reqDTO.getPrice())); // 单位分
         request.setTimeExpire(formatDateV3(reqDTO.getExpireTime()));
         request.setSceneInfo(new WxPayUnifiedOrderV3Request.SceneInfo().setPayerClientIp(reqDTO.getUserIp()));
         request.setNotifyUrl(reqDTO.getNotifyUrl());
@@ -73,7 +73,7 @@ public class WxNativePayClient extends AbstractWxPayClient {
     }
 
     @Override
-    protected PayRefundUnifiedRespDTO doUnifiedRefund(PayRefundUnifiedReqDTO reqDTO) throws Throwable {
+    protected PayRefundRespDTO doUnifiedRefund(PayRefundUnifiedReqDTO reqDTO) throws Throwable {
         return null;
     }
 

+ 6 - 6
yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/weixin/WxPubPayClient.java

@@ -5,8 +5,8 @@ import cn.hutool.core.util.StrUtil;
 import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
 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.enums.channel.PayChannelEnum;
 import cn.iocoder.yudao.framework.pay.core.enums.order.PayOrderDisplayModeEnum;
 import com.github.binarywang.wxpay.bean.order.WxPayMpOrderResult;
@@ -47,10 +47,10 @@ public class WxPubPayClient extends AbstractWxPayClient {
     protected PayOrderUnifiedRespDTO doUnifiedOrderV2(PayOrderUnifiedReqDTO reqDTO) throws WxPayException {
         // 构建 WxPayUnifiedOrderRequest 对象
         WxPayUnifiedOrderRequest request = WxPayUnifiedOrderRequest.newBuilder()
-                .outTradeNo(reqDTO.getMerchantOrderId())
+                .outTradeNo(reqDTO.getOutTradeNo())
                 .body(reqDTO.getSubject())
                 .detail(reqDTO.getBody())
-                .totalFee(reqDTO.getAmount()) // 单位分
+                .totalFee(reqDTO.getPrice()) // 单位分
                 .timeExpire(formatDateV2(reqDTO.getExpireTime()))
                 .spbillCreateIp(reqDTO.getUserIp())
                 .openid(getOpenid(reqDTO))
@@ -68,9 +68,9 @@ public class WxPubPayClient extends AbstractWxPayClient {
     protected PayOrderUnifiedRespDTO doUnifiedOrderV3(PayOrderUnifiedReqDTO reqDTO) throws WxPayException {
         // 构建 WxPayUnifiedOrderRequest 对象
         WxPayUnifiedOrderV3Request request = new WxPayUnifiedOrderV3Request();
-        request.setOutTradeNo(reqDTO.getMerchantOrderId());
+        request.setOutTradeNo(reqDTO.getOutTradeNo());
         request.setDescription(reqDTO.getSubject());
-        request.setAmount(new WxPayUnifiedOrderV3Request.Amount().setTotal(reqDTO.getAmount())); // 单位分
+        request.setAmount(new WxPayUnifiedOrderV3Request.Amount().setTotal(reqDTO.getPrice())); // 单位分
         request.setTimeExpire(formatDateV3(reqDTO.getExpireTime()));
         request.setPayer(new WxPayUnifiedOrderV3Request.Payer().setOpenid(getOpenid(reqDTO)));
         request.setSceneInfo(new WxPayUnifiedOrderV3Request.SceneInfo().setPayerClientIp(reqDTO.getUserIp()));
@@ -84,7 +84,7 @@ public class WxPubPayClient extends AbstractWxPayClient {
     }
 
     @Override
-    protected PayRefundUnifiedRespDTO doUnifiedRefund(PayRefundUnifiedReqDTO reqDTO) {
+    protected PayRefundRespDTO doUnifiedRefund(PayRefundUnifiedReqDTO reqDTO) {
         // TODO 需要实现
         throw new UnsupportedOperationException();
     }

+ 13 - 1
yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/enums/order/PayOrderStatusRespEnum.java

@@ -3,10 +3,12 @@ package cn.iocoder.yudao.framework.pay.core.enums.order;
 import lombok.AllArgsConstructor;
 import lombok.Getter;
 
+import java.util.Objects;
+
 /**
  * 渠道的支付状态枚举
  *
- * @author 遇到源码
+ * @author 芋道源码
  */
 @Getter
 @AllArgsConstructor
@@ -20,4 +22,14 @@ public enum PayOrderStatusRespEnum {
     private final Integer status;
     private final String name;
 
+    /**
+     * 判断是否支付成功
+     *
+     * @param status 状态
+     * @return 是否支付成功
+     */
+    public static boolean isSuccess(Integer status) {
+        return Objects.equals(status, SUCCESS.getStatus());
+    }
+
 }

+ 0 - 23
yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/enums/refund/PayNotifyRefundStatusEnum.java

@@ -1,23 +0,0 @@
-package cn.iocoder.yudao.framework.pay.core.enums.refund;
-
-// TODO 芋艿:看看能不能去掉
-/**
- * 退款通知, 统一的渠道退款状态
- *
- * @author  jason
- */
-public enum PayNotifyRefundStatusEnum {
-
-    /**
-     * 支付宝 中 全额退款 trade_status=TRADE_CLOSED, 部分退款 trade_status=TRADE_SUCCESS
-     * 退款成功
-     */
-    SUCCESS,
-
-    /**
-     * 支付宝退款通知没有这个状态
-     * 退款异常
-     */
-    ABNORMAL;
-
-}

+ 0 - 23
yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/enums/refund/PayRefundRespEnum.java

@@ -1,23 +0,0 @@
-package cn.iocoder.yudao.framework.pay.core.enums.refund;
-
-import lombok.AllArgsConstructor;
-import lombok.Getter;
-
-/**
- * 渠道的退款状态枚举
- *
- * @author jason
- */
-@Getter
-@AllArgsConstructor
-public enum PayRefundRespEnum {
-
-    SUCCESS(1, "退款成功"),
-    FAILURE(2, "退款失败"),
-    PROCESSING(3,"退款处理中"),
-    CLOSED(4, "退款关闭");
-
-    private final Integer status;
-    private final String name;
-
-}

+ 28 - 0
yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/enums/refund/PayRefundStatusRespEnum.java

@@ -0,0 +1,28 @@
+package cn.iocoder.yudao.framework.pay.core.enums.refund;
+
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+
+import java.util.Objects;
+
+/**
+ * 渠道的退款状态枚举
+ *
+ * @author jason
+ */
+@Getter
+@AllArgsConstructor
+public enum PayRefundStatusRespEnum {
+
+    WAITING(0, "未退款"),
+    SUCCESS(10, "退款成功"),
+    FAILURE(20, "退款失败");
+
+    private final Integer status;
+    private final String name;
+
+    public static boolean isSuccess(Integer status) {
+        return Objects.equals(status, SUCCESS.getStatus());
+    }
+
+}

+ 2 - 2
yudao-framework/yudao-spring-boot-starter-biz-pay/src/test/java/cn/iocoder/yudao/framework/pay/core/client/impl/PayClientFactoryImplIntegrationTest.java

@@ -121,10 +121,10 @@ public class PayClientFactoryImplIntegrationTest {
 
     private static PayOrderUnifiedReqDTO buildPayOrderUnifiedReqDTO() {
         PayOrderUnifiedReqDTO reqDTO = new PayOrderUnifiedReqDTO();
-        reqDTO.setAmount(123);
+        reqDTO.setPrice(123);
         reqDTO.setSubject("IPhone 13");
         reqDTO.setBody("biubiubiu");
-        reqDTO.setMerchantOrderId(String.valueOf(System.currentTimeMillis()));
+        reqDTO.setOutTradeNo(String.valueOf(System.currentTimeMillis()));
         reqDTO.setUserIp("127.0.0.1");
         reqDTO.setNotifyUrl("http://127.0.0.1:8080");
         return reqDTO;

+ 2 - 4
yudao-framework/yudao-spring-boot-starter-biz-pay/src/test/java/cn/iocoder/yudao/framework/pay/core/client/impl/alipay/AlipayQrPayClientTest.java

@@ -1,8 +1,6 @@
 package cn.iocoder.yudao.framework.pay.core.client.impl.alipay;
 import cn.hutool.core.util.ReflectUtil;
-import cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeConstants;
 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.test.core.ut.BaseMockitoUnitTest;
 import com.alipay.api.AlipayApiException;
 import com.alipay.api.DefaultAlipayClient;
@@ -74,8 +72,8 @@ public class AlipayQrPayClientTest extends BaseMockitoUnitTest {
         // 这里,设置可以直接随机整个对象。
         Long shopOrderId = System.currentTimeMillis();
         PayOrderUnifiedReqDTO reqDTO=new PayOrderUnifiedReqDTO();
-        reqDTO.setMerchantOrderId(String.valueOf(System.currentTimeMillis()));
-        reqDTO.setAmount(1);
+        reqDTO.setOutTradeNo(String.valueOf(System.currentTimeMillis()));
+        reqDTO.setPrice(1);
         reqDTO.setBody("内容:" + shopOrderId);
         reqDTO.setSubject("标题:"+shopOrderId);
         String notify="http://niubi.natapp1.cc/api/pay/order/notify";

+ 8 - 4
yudao-module-pay/yudao-module-pay-api/src/main/java/cn/iocoder/yudao/module/pay/enums/refund/PayRefundStatusEnum.java

@@ -5,14 +5,18 @@ import lombok.Getter;
 
 import java.util.Objects;
 
+/**
+ * 渠道的退款状态枚举
+ *
+ * @author 芋道源码
+ */
 @Getter
 @AllArgsConstructor
 public enum PayRefundStatusEnum {
 
-    CREATE(0, "退款订单生成"),
-    SUCCESS(1, "退款成功"),
-    FAILURE(2, "退款失败"),
-    CLOSE(99, "退款关闭");
+    WAITING(0, "未退款"),
+    SUCCESS(10, "退款成功"),
+    FAILURE(20, "退款失败");
 
     private final Integer status;
     private final String name;

+ 7 - 9
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/controller/admin/notify/PayNotifyController.java

@@ -3,9 +3,8 @@ package cn.iocoder.yudao.module.pay.controller.admin.notify;
 import cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog;
 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.notify.PayRefundNotifyRespDTO;
+import cn.iocoder.yudao.framework.pay.core.client.dto.refund.PayRefundRespDTO;
+import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderRespDTO;
 import cn.iocoder.yudao.module.pay.service.order.PayOrderService;
 import cn.iocoder.yudao.module.pay.service.refund.PayRefundService;
 import io.swagger.v3.oas.annotations.Operation;
@@ -61,18 +60,17 @@ public class PayNotifyController {
         }
 
         // 2. 解析通知数据
-        PayNotifyReqDTO rawNotify = PayNotifyReqDTO.builder().params(params).body(body).build();
-        Object notify = payClient.parseNotify(rawNotify);
+        Object notify = payClient.parseNotify(params, body);
 
         // 3. 处理通知
         // 3.1:退款通知
-        if (notify instanceof PayRefundNotifyRespDTO) {
-            refundService.notifyPayRefund(channelId, (PayRefundNotifyRespDTO) notify, rawNotify);
+        if (notify instanceof PayRefundRespDTO) {
+            refundService.notifyPayRefund(channelId, (PayRefundRespDTO) notify);
             return "success";
         }
         // 3.2:支付通知
-        if (notify instanceof PayOrderNotifyRespDTO) {
-            orderService.notifyPayOrder(channelId, (PayOrderNotifyRespDTO) notify, rawNotify);
+        if (notify instanceof PayOrderRespDTO) {
+            orderService.notifyPayOrder(channelId, (PayOrderRespDTO) notify);
             return "success";
         }
         throw new UnsupportedOperationException("未知通知:" + toJsonString(notify));

+ 0 - 3
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/controller/admin/refund/vo/PayRefundExcelVO.java

@@ -76,9 +76,6 @@ public class PayRefundExcelVO {
     @ExcelProperty("退款成功时间")
     private LocalDateTime successTime;
 
-    @ExcelProperty("退款通知时间")
-    private LocalDateTime notifyTime;
-
     @ExcelProperty("退款失效时间")
     private LocalDateTime expireTime;
 

+ 3 - 4
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/convert/refund/PayRefundConvert.java

@@ -60,9 +60,10 @@ public interface PayRefundConvert {
         PayRefundExcelVO payRefundExcelVO = new PayRefundExcelVO();
 
         payRefundExcelVO.setId(bean.getId());
-        payRefundExcelVO.setTradeNo(bean.getTradeNo());
+        payRefundExcelVO.setTradeNo(bean.getNo());
         payRefundExcelVO.setMerchantOrderId(bean.getMerchantOrderId());
-        payRefundExcelVO.setMerchantRefundNo(bean.getMerchantRefundNo());
+        // TODO 芋艿:晚点在改
+//        payRefundExcelVO.setMerchantRefundNo(bean.getMerchantRefundNo());
         payRefundExcelVO.setNotifyUrl(bean.getNotifyUrl());
         payRefundExcelVO.setNotifyStatus(bean.getNotifyStatus());
         payRefundExcelVO.setStatus(bean.getStatus());
@@ -71,9 +72,7 @@ public interface PayRefundConvert {
         payRefundExcelVO.setUserIp(bean.getUserIp());
         payRefundExcelVO.setChannelOrderNo(bean.getChannelOrderNo());
         payRefundExcelVO.setChannelRefundNo(bean.getChannelRefundNo());
-        payRefundExcelVO.setExpireTime(bean.getExpireTime());
         payRefundExcelVO.setSuccessTime(bean.getSuccessTime());
-        payRefundExcelVO.setNotifyTime(bean.getNotifyTime());
         payRefundExcelVO.setCreateTime(bean.getCreateTime());
 
         BigDecimal multiple = new BigDecimal(100);

+ 2 - 1
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/dal/dataobject/order/PayOrderDO.java

@@ -55,7 +55,8 @@ public class PayOrderDO extends BaseDO {
 
     /**
      * 商户订单编号
-     * 例如说,内部系统 A 的订单号。需要保证每个 PayMerchantDO 唯一
+     *
+     * 例如说,内部系统 A 的订单号,需要保证每个 PayAppDO 唯一
      */
     private String merchantOrderId;
     /**

+ 6 - 5
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/dal/dataobject/order/PayOrderExtensionDO.java

@@ -32,10 +32,11 @@ public class PayOrderExtensionDO extends BaseDO {
      */
     private Long id;
     /**
-     * 支付订单号,根据规则生成
-     * 调用支付渠道时,使用该字段作为对接的订单号。
-     * 1. 调用微信支付 https://api.mch.weixin.qq.com/pay/unifiedorder 时,使用该字段作为 out_trade_no
-     * 2. 调用支付宝 https://opendocs.alipay.com/apis 时,使用该字段作为 out_trade_no
+     * 外部订单号,根据规则生成
+     *
+     * 调用支付渠道时,使用该字段作为对接的订单号:
+     * 1. 微信支付:对应 <a href="https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter3_1_1.shtml">JSAPI 支付</a> 的 out_trade_no 字段
+     * 2. 支付宝支付:对应 <a href="https://opendocs.alipay.com/open/270/105898">电脑网站支付</a> 的 out_trade_no 字段
      *
      * 例如说,P202110132239124200055
      */
@@ -70,7 +71,7 @@ public class PayOrderExtensionDO extends BaseDO {
     /**
      * 支付渠道的额外参数
      *
-     * 参见 https://www.pingxx.com/api/支付渠道%20extra%20参数说明.html
+     * 参见 <a href="https://www.pingxx.com/api/支付渠道%20extra%20参数说明.html">参数说明</>
      */
     @TableField(typeHandler = JacksonTypeHandler.class)
     private Map<String, String> channelExtras;

+ 35 - 48
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. 微信退款:对应 <a href="https://pay.weixin.qq.com/wiki/doc/api/micropay.php?chapter=9_4">申请退款</a> 的 out_refund_no 字段
+     * 2. 支付宝退款:对应 <a href="https://opendocs.alipay.com/open/02e7go"统一收单交易退款接口></a> 的 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
-     */
-    // TODO @jason:merchantRefundNo =》merchantRefundOId
-    private String merchantRefundNo;
-
+     * 商户退款订单号
+     *
+     * 例如说,内部系统 A 的订单号,需要保证每个 PayAppDO 唯一
+     */
+    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. 微信退款:对应 <a href="https://pay.weixin.qq.com/wiki/doc/api/micropay.php?chapter=9_4">申请退款</a> 的 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
+     *
+     * 参见 <a href="https://www.pingxx.com/api/支付渠道%20extra%20参数说明.html">参数说明</>
      */
     private String channelExtras;
-
     /**
-     * TODO
-     * 退款失效时间
-     */
-    private LocalDateTime expireTime;
-    /**
-     * 退款成功时间
-     */
-    private LocalDateTime successTime;
-    /**
-     * 退款通知时间
+     * 支付渠道异步通知的内容
+     *
+     * 在退款成功后,会记录回调的数据
      */
-    private LocalDateTime notifyTime;
+    private String channelNotifyData;
 
 }

+ 3 - 1
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<PayRefundDO> {
         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;
     }
 
 }

+ 14 - 11
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;
-        }
-
-        // 情况二,如果缓存里数据,则通过 updateTime 判断是否有数据变更,有变更则刷新缓存
-        LocalDateTime maxTime = CollectionUtils.getMaxValue(channelCache, PayChannelDO::getUpdateTime);
-        if (channelMapper.selectCountByUpdateTimeGt(maxTime) > 0) {
-            initLocalCache();
-        }
+        // 注意:忽略自动多租户,因为要全局初始化缓存
+        TenantUtils.executeIgnore(() -> {
+            // 情况一:如果缓存里没有数据,则直接刷新缓存
+            if (CollUtil.isEmpty(channelCache)) {
+                initLocalCache();
+                return;
+            }
+
+            // 情况二,如果缓存里数据,则通过 updateTime 判断是否有数据变更,有变更则刷新缓存
+            LocalDateTime maxTime = CollectionUtils.getMaxValue(channelCache, PayChannelDO::getUpdateTime);
+            if (channelMapper.selectCountByUpdateTimeGt(maxTime) > 0) {
+                initLocalCache();
+            }
+        });
     }
 
     @Override

+ 2 - 4
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);
 
 }

+ 26 - 19
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<Boolean, PayOrderDO> 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<Boolean, PayOrderDO> updatePayOrderSuccess(PayChannelDO channel, PayOrderExtensionDO orderExtension,
-                                                            PayOrderNotifyRespDTO notify) {
+                                                            PayOrderRespDTO notify) {
         // 1. 判断 PayOrderDO 是否处于待支付
         PayOrderDO order = orderMapper.selectById(orderExtension.getOrderId());
         if (order == null) {

+ 3 - 5
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);
 
 }

+ 24 - 28
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);
 

+ 0 - 4
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;

+ 2 - 2
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");

+ 5 - 5
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'
   })
 }