Browse Source

mall + pay:
1. 完成支付宝的退款重构
2. 完成 demo 模块的退款接入

YunaiV 1 year ago
parent
commit
c44ace6011
23 changed files with 333 additions and 274 deletions
  1. 7 0
      yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/dto/refund/PayRefundRespDTO.java
  2. 1 1
      yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/alipay/AbstractAlipayPayClient.java
  3. 13 7
      yudao-module-pay/yudao-module-pay-api/src/main/java/cn/iocoder/yudao/module/pay/api/refund/dto/PayRefundCreateReqDTO.java
  4. 4 3
      yudao-module-pay/yudao-module-pay-api/src/main/java/cn/iocoder/yudao/module/pay/enums/ErrorCodeConstants.java
  5. 10 0
      yudao-module-pay/yudao-module-pay-api/src/main/java/cn/iocoder/yudao/module/pay/enums/order/PayOrderStatusEnum.java
  6. 1 1
      yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/api/order/PayOrderApiImpl.java
  7. 2 2
      yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/api/refund/PayRefundApiImpl.java
  8. 2 2
      yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/controller/admin/notify/PayNotifyController.java
  9. 1 1
      yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/controller/admin/order/PayOrderController.java
  10. 1 1
      yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/controller/app/order/AppPayOrderController.java
  11. 5 13
      yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/convert/refund/PayRefundConvert.java
  12. 3 3
      yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/dal/dataobject/order/PayOrderDO.java
  13. 0 13
      yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/dal/dataobject/refund/PayRefundDO.java
  14. 32 16
      yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/dal/mysql/refund/PayRefundMapper.java
  15. 3 3
      yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/enums/order/PayOrderRefundStatusEnum.java
  16. 8 3
      yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/service/demo/PayDemoOrderServiceImpl.java
  17. 1 0
      yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/service/order/PayOrderExtensionService.java
  18. 24 7
      yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/service/order/PayOrderService.java
  19. 62 32
      yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/service/order/PayOrderServiceImpl.java
  20. 1 1
      yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/service/refund/PayRefundService.java
  21. 132 139
      yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/service/refund/PayRefundServiceImpl.java
  22. 7 7
      yudao-module-pay/yudao-module-pay-biz/src/test/java/cn/iocoder/yudao/module/pay/service/order/PayOrderServiceTest.java
  23. 13 19
      yudao-module-pay/yudao-module-pay-biz/src/test/java/cn/iocoder/yudao/module/pay/service/refund/PayRefundServiceTest.java

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

@@ -20,6 +20,13 @@ public class PayRefundRespDTO {
      */
     private Integer status;
 
+    /**
+     * 外部退款号
+     *
+     * 对应 PayRefundDO 的 no 字段
+     */
+    private String outRefundNo;
+
     /**
      * 渠道退款单号
      *

+ 1 - 1
yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/alipay/AbstractAlipayPayClient.java

@@ -68,7 +68,6 @@ public abstract class AbstractAlipayPayClient extends AbstractPayClient<AlipayPa
         model.setOutTradeNo(reqDTO.getOutTradeNo());
         model.setOutRequestNo(reqDTO.getOutRefundNo());
         model.setRefundAmount(formatAmount(reqDTO.getPrice()));
-//        model.setRefundAmount(formatAmount(reqDTO.getPrice() / 2));
         model.setRefundReason(reqDTO.getReason());
         // 1.2 构建 AlipayTradePayRequest 请求
         AlipayTradeRefundRequest request = new AlipayTradeRefundRequest();
@@ -77,6 +76,7 @@ public abstract class AbstractAlipayPayClient extends AbstractPayClient<AlipayPa
             // 2.1 执行请求
             AlipayTradeRefundResponse response =  client.execute(request);
             PayRefundRespDTO refund = new PayRefundRespDTO()
+                    .setOutRefundNo(reqDTO.getOutRefundNo())
                     .setRawData(response);
             // 支付宝只要退款调用返回 success,就认为退款成功,不需要回调。具体可见 parseNotify 方法的说明。
             // 另外,支付宝没有退款单号,所以不用设置

+ 13 - 7
yudao-module-pay/yudao-module-pay-api/src/main/java/cn/iocoder/yudao/module/pay/api/refund/dto/PayRefundCreateReqDTO.java

@@ -27,26 +27,32 @@ public class PayRefundCreateReqDTO {
     private String userIp;
 
     // ========== 商户相关字段 ==========
+    /**
+     * 商户订单编号
+     */
+    @NotEmpty(message = "商户订单编号不能为空")
+    private String merchantOrderId;
+
+    /**
+     * 商户退款编号
+     */
+    @NotEmpty(message = "商户退款编号不能为空")
+    private String merchantRefundId;
 
     /**
      * 退款描述
      */
     @NotEmpty(message = "退款描述不能为空")
-    @Length(max = 128, message = "退款描述长度不能超过128")
+    @Length(max = 128, message = "退款描述长度不能超过 128")
     private String reason;
 
     // ========== 订单相关字段 ==========
 
-    /**
-     * 支付单号
-     */
-    @NotNull(message = "支付单号不能为空")
-    private Long payOrderId;
-
     /**
      * 退款金额,单位:分
      */
     @NotNull(message = "退款金额不能为空")
     @Min(value = 1, message = "退款金额必须大于零")
     private Integer price;
+
 }

+ 4 - 3
yudao-module-pay/yudao-module-pay-api/src/main/java/cn/iocoder/yudao/module/pay/enums/ErrorCodeConstants.java

@@ -33,11 +33,12 @@ public interface ErrorCodeConstants {
     ErrorCode PAY_ORDER_EXTENSION_STATUS_IS_NOT_WAITING = new ErrorCode(1007003001, "支付交易拓展单不处于待支付");
 
     // ========== 支付模块(退款) 1007006000 ==========
-    ErrorCode PAY_PRICE_PRICE_EXCEED = new ErrorCode(1007006000, "退款金额超过订单可退款金额");
+    ErrorCode PAY_REFUND_PRICE_EXCEED = new ErrorCode(1007006000, "退款金额超过订单可退款金额");
     ErrorCode PAY_REFUND_ALL_REFUNDED = new ErrorCode(1007006001, "订单已经全额退款");
-    ErrorCode PAY_REFUND_CHN_ORDER_NO_IS_NULL = new ErrorCode(1007006002, "该订单的渠道订单为空");
-    ErrorCode PAY_REFUND_SUCCEED = new ErrorCode(1007006003, "已经退款成功");
+    ErrorCode PAY_REFUND_HAS_REFUNDING = new ErrorCode(1007006002, "已经有退款在处理中");
+    ErrorCode PAY_REFUND_EXISTS = new ErrorCode(1007006003, "已经存在退款单");
     ErrorCode PAY_REFUND_NOT_FOUND = new ErrorCode(1007006004, "支付退款单不存在");
+    ErrorCode PAY_REFUND_STATUS_IS_NOT_WAITING = new ErrorCode(1007006005, "支付退款单不处于待退款");
 
     // ========== 示例订单 1007900000 ==========
     ErrorCode PAY_DEMO_ORDER_NOT_FOUND = new ErrorCode(1007900000, "示例订单不存在");

+ 10 - 0
yudao-module-pay/yudao-module-pay-api/src/main/java/cn/iocoder/yudao/module/pay/enums/order/PayOrderStatusEnum.java

@@ -38,4 +38,14 @@ public enum PayOrderStatusEnum implements IntArrayValuable {
         return Objects.equals(status, SUCCESS.getStatus());
     }
 
+    /**
+     * 判断是否支付关闭
+     *
+     * @param status 状态
+     * @return 是否支付关闭
+     */
+    public static boolean isClosed(Integer status) {
+        return Objects.equals(status, CLOSED.getStatus());
+    }
+
 }

+ 1 - 1
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/api/order/PayOrderApiImpl.java

@@ -22,7 +22,7 @@ public class PayOrderApiImpl implements PayOrderApi {
 
     @Override
     public Long createOrder(PayOrderCreateReqDTO reqDTO) {
-        return payOrderService.createPayOrder(reqDTO);
+        return payOrderService.createOrder(reqDTO);
     }
 
     @Override

+ 2 - 2
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/api/refund/PayRefundApiImpl.java

@@ -2,6 +2,7 @@ package cn.iocoder.yudao.module.pay.api.refund;
 
 import cn.iocoder.yudao.module.pay.api.refund.dto.PayRefundCreateReqDTO;
 import cn.iocoder.yudao.module.pay.api.refund.dto.PayRefundRespDTO;
+import cn.iocoder.yudao.module.pay.convert.refund.PayRefundConvert;
 import cn.iocoder.yudao.module.pay.service.refund.PayRefundService;
 import org.springframework.stereotype.Service;
 import org.springframework.validation.annotation.Validated;
@@ -27,8 +28,7 @@ public class PayRefundApiImpl implements PayRefundApi {
 
     @Override
     public PayRefundRespDTO getPayRefund(Long id) {
-        // TODO 芋艿:暂未实现
-        return null;
+        return PayRefundConvert.INSTANCE.convert02(payRefundService.getRefund(id));
     }
 
 }

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

@@ -65,12 +65,12 @@ public class PayNotifyController {
         // 3. 处理通知
         // 3.1:退款通知
         if (notify instanceof PayRefundRespDTO) {
-            refundService.notifyPayRefund(channelId, (PayRefundRespDTO) notify);
+            refundService.notifyRefund(channelId, (PayRefundRespDTO) notify);
             return "success";
         }
         // 3.2:支付通知
         if (notify instanceof PayOrderRespDTO) {
-            orderService.notifyPayOrder(channelId, (PayOrderRespDTO) notify);
+            orderService.notifyOrder(channelId, (PayOrderRespDTO) notify);
             return "success";
         }
         throw new UnsupportedOperationException("未知通知:" + toJsonString(notify));

+ 1 - 1
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/controller/admin/order/PayOrderController.java

@@ -86,7 +86,7 @@ public class PayOrderController {
     @PostMapping("/submit")
     @Operation(summary = "提交支付订单")
     public CommonResult<PayOrderSubmitRespVO> submitPayOrder(@RequestBody PayOrderSubmitReqVO reqVO) {
-        PayOrderSubmitRespVO respVO = payOrderService.submitPayOrder(reqVO, getClientIP());
+        PayOrderSubmitRespVO respVO = payOrderService.submitOrder(reqVO, getClientIP());
         return success(respVO);
     }
 

+ 1 - 1
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/controller/app/order/AppPayOrderController.java

@@ -40,7 +40,7 @@ public class AppPayOrderController {
     @PostMapping("/submit")
     @Operation(summary = "提交支付订单")
     public CommonResult<AppPayOrderSubmitRespVO> submitPayOrder(@RequestBody AppPayOrderSubmitReqVO reqVO) {
-        PayOrderSubmitRespVO respVO = payOrderService.submitPayOrder(reqVO, getClientIP());
+        PayOrderSubmitRespVO respVO = payOrderService.submitOrder(reqVO, getClientIP());
         return success(PayOrderConvert.INSTANCE.convert3(respVO));
     }
 

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

@@ -1,12 +1,11 @@
 package cn.iocoder.yudao.module.pay.convert.refund;
 
 import cn.iocoder.yudao.framework.common.pojo.PageResult;
+import cn.iocoder.yudao.module.pay.api.refund.dto.PayRefundCreateReqDTO;
+import cn.iocoder.yudao.module.pay.api.refund.dto.PayRefundRespDTO;
 import cn.iocoder.yudao.module.pay.controller.admin.refund.vo.*;
-import cn.iocoder.yudao.module.pay.dal.dataobject.order.PayOrderDO;
 import cn.iocoder.yudao.module.pay.dal.dataobject.refund.PayRefundDO;
 import org.mapstruct.Mapper;
-import org.mapstruct.Mapping;
-import org.mapstruct.Mappings;
 import org.mapstruct.factory.Mappers;
 
 import java.math.BigDecimal;
@@ -44,8 +43,6 @@ public interface PayRefundConvert {
 
     PageResult<PayRefundRespVO> convertPage(PageResult<PayRefundDO> page);
 
-    List<PayRefundExcelVO> convertList02(List<PayRefundDO> list);
-
     /**
      * 退款订单DO 转 导出excel VO
      *
@@ -67,7 +64,6 @@ public interface PayRefundConvert {
         payRefundExcelVO.setNotifyUrl(bean.getNotifyUrl());
         payRefundExcelVO.setNotifyStatus(bean.getNotifyStatus());
         payRefundExcelVO.setStatus(bean.getStatus());
-        payRefundExcelVO.setType(bean.getType());
         payRefundExcelVO.setReason(bean.getReason());
         payRefundExcelVO.setUserIp(bean.getUserIp());
         payRefundExcelVO.setChannelOrderNo(bean.getChannelOrderNo());
@@ -84,12 +80,8 @@ public interface PayRefundConvert {
         return payRefundExcelVO;
     }
 
-    //TODO 太多需要处理了, 暂时不用
-    @Mappings(value = {
-            @Mapping(source = "price", target = "payPrice"),
-            @Mapping(source = "id", target = "orderId"),
-            @Mapping(target = "status",ignore = true)
-    })
-    PayRefundDO convert(PayOrderDO orderDO);
+    PayRefundDO convert(PayRefundCreateReqDTO bean);
+
+    PayRefundRespDTO convert02(PayRefundDO bean);
 
 }

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

@@ -6,7 +6,7 @@ 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.enums.order.PayOrderNotifyStatusEnum;
 import cn.iocoder.yudao.module.pay.enums.order.PayOrderStatusEnum;
-import cn.iocoder.yudao.module.pay.enums.refund.PayRefundTypeEnum;
+import cn.iocoder.yudao.module.pay.enums.order.PayOrderRefundStatusEnum;
 import com.baomidou.mybatisplus.annotation.KeySequence;
 import com.baomidou.mybatisplus.annotation.TableName;
 import lombok.*;
@@ -127,7 +127,7 @@ public class PayOrderDO extends BaseDO {
     /**
      * 退款状态
      *
-     * 枚举 {@link PayRefundTypeEnum}
+     * 枚举 {@link PayOrderRefundStatusEnum}
      */
     private Integer refundStatus;
     /**
@@ -137,7 +137,7 @@ public class PayOrderDO extends BaseDO {
     /**
      * 退款总金额,单位:分
      */
-    private Long refundPrice;
+    private Integer refundPrice;
 
     // ========== 渠道相关字段 ==========
     /**

+ 0 - 13
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/dal/dataobject/refund/PayRefundDO.java

@@ -7,7 +7,6 @@ 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;
 import com.baomidou.mybatisplus.annotation.TableId;
 import com.baomidou.mybatisplus.annotation.TableName;
@@ -104,12 +103,6 @@ public class PayRefundDO extends BaseDO {
      */
     private Integer status;
 
-    /**
-     * 退款类型(部分退款,全部退款)
-     *
-     * 枚举 {@link PayRefundTypeEnum}
-     */
-    private Integer type;
     /**
      * 支付金额,单位:分
      */
@@ -157,12 +150,6 @@ public class PayRefundDO extends BaseDO {
      */
     private String channelErrorMsg;
 
-    /**
-     * 支付渠道的额外参数
-     *
-     * 参见 <a href="https://www.pingxx.com/api/支付渠道%20extra%20参数说明.html">参数说明</>
-     */
-    private String channelExtras;
     /**
      * 支付渠道异步通知的内容
      *

+ 32 - 16
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/dal/mysql/refund/PayRefundMapper.java

@@ -1,11 +1,13 @@
 package cn.iocoder.yudao.module.pay.dal.mysql.refund;
 
-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.framework.mybatis.core.mapper.BaseMapperX;
+import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
 import cn.iocoder.yudao.framework.mybatis.core.query.QueryWrapperX;
+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.module.pay.dal.dataobject.refund.PayRefundDO;
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 import org.apache.ibatis.annotations.Mapper;
 
 import java.util.List;
@@ -13,6 +15,34 @@ import java.util.List;
 @Mapper
 public interface PayRefundMapper extends BaseMapperX<PayRefundDO> {
 
+    default Long selectCountByAppId(Long appId) {
+        return selectCount(PayRefundDO::getAppId, appId);
+    }
+
+    default  PayRefundDO selectByAppIdAndMerchantRefundId(Long appId, String merchantRefundId) {
+        return selectOne(new LambdaQueryWrapperX<PayRefundDO>()
+                .eq(PayRefundDO::getAppId, appId)
+                .eq(PayRefundDO::getMerchantRefundId, merchantRefundId));
+    }
+
+    default Long selectCountByAppIdAndOrderId(Long appId, Long orderId, Integer status) {
+        return selectCount(new LambdaQueryWrapperX<PayRefundDO>()
+                .eq(PayRefundDO::getAppId, appId)
+                .eq(PayRefundDO::getOrderId, orderId)
+                .eq(PayRefundDO::getStatus, status));
+    }
+
+    default  PayRefundDO selectByAppIdAndNo(Long appId, String no) {
+        return selectOne(new LambdaQueryWrapperX<PayRefundDO>()
+                .eq(PayRefundDO::getAppId, appId)
+                .eq(PayRefundDO::getNo, no));
+    }
+
+    default int updateByIdAndStatus(Long id, Integer status, PayRefundDO update) {
+        return update(update, new LambdaQueryWrapper<PayRefundDO>()
+                .eq(PayRefundDO::getId, id).eq(PayRefundDO::getStatus, status));
+    }
+
     default PageResult<PayRefundDO> selectPage(PayRefundPageReqVO reqVO) {
         return selectPage(reqVO, new QueryWrapperX<PayRefundDO>()
                 .eqIfPresent("app_id", reqVO.getAppId())
@@ -37,18 +67,4 @@ public interface PayRefundMapper extends BaseMapperX<PayRefundDO> {
                 .orderByDesc("id"));
     }
 
-    default Long selectCountByApp(Long appId) {
-         return selectCount(PayRefundDO::getAppId, appId);
-    }
-
-    default PayRefundDO selectByReqNo(String reqNo) {
-        return selectOne("req_no", reqNo);
-    }
-
-    // TODO 芋艿:要重构
-    default  PayRefundDO selectByTradeNoAndMerchantRefundNo(String tradeNo, String merchantRefundNo){
-//        return selectOne("trade_no", tradeNo, "merchant_refund_no", merchantRefundNo);
-        return null;
-    }
-
 }

+ 3 - 3
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/enums/refund/PayRefundTypeEnum.java → yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/enums/order/PayOrderRefundStatusEnum.java

@@ -1,4 +1,4 @@
-package cn.iocoder.yudao.module.pay.enums.refund;
+package cn.iocoder.yudao.module.pay.enums.order;
 
 import cn.iocoder.yudao.framework.common.core.IntArrayValuable;
 import lombok.AllArgsConstructor;
@@ -11,10 +11,10 @@ import lombok.Getter;
  */
 @Getter
 @AllArgsConstructor
-public enum PayRefundTypeEnum implements IntArrayValuable {
+public enum PayOrderRefundStatusEnum implements IntArrayValuable {
 
     NO(0, "未退款"),
-    SOME(10, "部分退款"),
+    PART(10, "部分退款"),
     ALL(20, "全部退款")
     ;
 

+ 8 - 3
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/service/demo/PayDemoOrderServiceImpl.java

@@ -184,12 +184,17 @@ public class PayDemoOrderServiceImpl implements PayDemoOrderService {
         // 1. 校验订单是否可以退款
         PayDemoOrderDO order = validateDemoOrderCanRefund(id);
 
-        // 2.1 创建退款单
+        // 2.1 生成退款单号
+        // 一般来说,用户发起退款的时候,都会单独插入一个售后维权表,然后使用该表的 id 作为 refundId
+        // 这里我们是个简单的 demo,所以没有售后维权表,直接使用订单 id + "-refund" 来演示
+        String refundId = order.getId() + "-refund";
+        // 2.2 创建退款单
         Long payRefundId = payRefundApi.createPayRefund(new PayRefundCreateReqDTO()
                 .setAppId(PAY_APP_ID).setUserIp(getClientIP()) // 支付应用
-                .setPayOrderId(order.getPayOrderId()) // 支付单号
+                .setMerchantOrderId(String.valueOf(order.getId())) // 支付单号
+                .setMerchantRefundId(refundId)
                 .setReason("想退钱").setPrice(order.getPrice()));// 价格信息
-        // 2.2 更新退款单到 demo 订单
+        // 2.3 更新退款单到 demo 订单
         payDemoOrderMapper.updateById(new PayDemoOrderDO().setId(id)
                 .setPayRefundId(payRefundId).setRefundPrice(order.getPrice()));
     }

+ 1 - 0
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/service/order/PayOrderExtensionService.java

@@ -7,6 +7,7 @@ import java.util.Collection;
 import java.util.List;
 import java.util.Map;
 
+// TODO 芋艿:合并到 PayOrder 里;
 /**
  * 支付订单 Service 接口
  *

+ 24 - 7
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/service/order/PayOrderService.java

@@ -31,6 +31,15 @@ public interface PayOrderService {
      */
     PayOrderDO getOrder(Long id);
 
+    /**
+     * 获得支付订单
+     *
+     * @param appId 应用编号
+     * @param merchantOrderId 商户订单编号
+     * @return 支付订单
+     */
+    PayOrderDO getOrder(Long appId, String merchantOrderId);
+
     /**
      * 获得指定应用的订单数量
      *
@@ -70,11 +79,11 @@ public interface PayOrderService {
     /**
      * 根据订单 ID 集合获取订单商品名称Map集合
      *
-     * @param idList 订单 ID 集合
+     * @param ids 订单 ID 集合
      * @return 订单商品 map 集合
      */
-    default Map<Long, PayOrderDO> getOrderSubjectMap(Collection<Long> idList) {
-        List<PayOrderDO> list = getOrderSubjectList(idList);
+    default Map<Long, PayOrderDO> getOrderSubjectMap(Collection<Long> ids) {
+        List<PayOrderDO> list = getOrderSubjectList(ids);
         return CollectionUtils.convertMap(list, PayOrderDO::getId);
     }
 
@@ -84,7 +93,7 @@ public interface PayOrderService {
      * @param reqDTO 创建请求
      * @return 支付单编号
      */
-    Long createPayOrder(@Valid PayOrderCreateReqDTO reqDTO);
+    Long createOrder(@Valid PayOrderCreateReqDTO reqDTO);
 
     /**
      * 提交支付
@@ -94,8 +103,8 @@ public interface PayOrderService {
      * @param userIp 提交 IP
      * @return 提交结果
      */
-    PayOrderSubmitRespVO submitPayOrder(@Valid PayOrderSubmitReqVO reqVO,
-                                        @NotEmpty(message = "提交 IP 不能为空") String userIp);
+    PayOrderSubmitRespVO submitOrder(@Valid PayOrderSubmitReqVO reqVO,
+                                     @NotEmpty(message = "提交 IP 不能为空") String userIp);
 
     /**
      * 通知支付单成功
@@ -103,6 +112,14 @@ public interface PayOrderService {
      * @param channelId 渠道编号
      * @param notify    通知
      */
-    void notifyPayOrder(Long channelId, PayOrderRespDTO notify);
+    void notifyOrder(Long channelId, PayOrderRespDTO notify);
+
+    /**
+     * 更新支付订单的退款金额
+     *
+     * @param id 编号
+     * @param incrRefundPrice 增加的退款金额
+     */
+    void updateOrderRefundPrice(Long id, Integer incrRefundPrice);
 
 }

+ 62 - 32
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/service/order/PayOrderServiceImpl.java

@@ -26,11 +26,10 @@ import cn.iocoder.yudao.module.pay.dal.dataobject.order.PayOrderDO;
 import cn.iocoder.yudao.module.pay.dal.dataobject.order.PayOrderExtensionDO;
 import cn.iocoder.yudao.module.pay.dal.mysql.order.PayOrderExtensionMapper;
 import cn.iocoder.yudao.module.pay.dal.mysql.order.PayOrderMapper;
-import cn.iocoder.yudao.module.pay.enums.ErrorCodeConstants;
 import cn.iocoder.yudao.module.pay.enums.notify.PayNotifyTypeEnum;
 import cn.iocoder.yudao.module.pay.enums.order.PayOrderNotifyStatusEnum;
+import cn.iocoder.yudao.module.pay.enums.order.PayOrderRefundStatusEnum;
 import cn.iocoder.yudao.module.pay.enums.order.PayOrderStatusEnum;
-import cn.iocoder.yudao.module.pay.enums.refund.PayRefundTypeEnum;
 import cn.iocoder.yudao.module.pay.service.app.PayAppService;
 import cn.iocoder.yudao.module.pay.service.channel.PayChannelService;
 import cn.iocoder.yudao.module.pay.service.notify.PayNotifyService;
@@ -48,6 +47,7 @@ import java.util.Objects;
 
 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
 import static cn.iocoder.yudao.framework.common.util.json.JsonUtils.toJsonString;
+import static cn.iocoder.yudao.module.pay.enums.ErrorCodeConstants.*;
 
 /**
  * 支付订单 Service 实现类
@@ -82,6 +82,11 @@ public class PayOrderServiceImpl implements PayOrderService {
         return orderMapper.selectById(id);
     }
 
+    @Override
+    public PayOrderDO getOrder(Long appId, String merchantOrderId) {
+        return orderMapper.selectByAppIdAndMerchantOrderId(appId, merchantOrderId);
+    }
+
     @Override
     public Long getOrderCountByAppId(Long appId) {
         return orderMapper.selectCountByAppId(appId);
@@ -104,7 +109,7 @@ public class PayOrderServiceImpl implements PayOrderService {
     }
 
     @Override
-    public Long createPayOrder(PayOrderCreateReqDTO reqDTO) {
+    public Long createOrder(PayOrderCreateReqDTO reqDTO) {
         // 校验 App
         PayAppDO app = appService.validPayApp(reqDTO.getAppId());
 
@@ -112,7 +117,7 @@ public class PayOrderServiceImpl implements PayOrderService {
         PayOrderDO order = orderMapper.selectByAppIdAndMerchantOrderId(
                 reqDTO.getAppId(), reqDTO.getMerchantOrderId());
         if (order != null) {
-            log.warn("[createPayOrder][appId({}) merchantOrderId({}) 已经存在对应的支付单({})]", order.getAppId(),
+            log.warn("[createOrder][appId({}) merchantOrderId({}) 已经存在对应的支付单({})]", order.getAppId(),
                     order.getMerchantOrderId(), toJsonString(order)); // 理论来说,不会出现这个情况
             return order.getId();
         }
@@ -124,16 +129,16 @@ public class PayOrderServiceImpl implements PayOrderService {
                 // 订单相关字段
                 .setStatus(PayOrderStatusEnum.WAITING.getStatus())
                 // 退款相关字段
-                .setRefundStatus(PayRefundTypeEnum.NO.getStatus()).setRefundTimes(0).setRefundPrice(0L);
+                .setRefundStatus(PayOrderRefundStatusEnum.NO.getStatus()).setRefundTimes(0).setRefundPrice(0);
         orderMapper.insert(order);
         return order.getId();
     }
 
     @Override
     @Transactional(rollbackFor = Exception.class)
-    public PayOrderSubmitRespVO submitPayOrder(PayOrderSubmitReqVO reqVO, String userIp) {
+    public PayOrderSubmitRespVO submitOrder(PayOrderSubmitReqVO reqVO, String userIp) {
         // 1. 获得 PayOrderDO ,并校验其是否存在
-        PayOrderDO order = validatePayOrderCanSubmit(reqVO.getId());
+        PayOrderDO order = validateOrderCanSubmit(reqVO.getId());
         // 1.2 校验支付渠道是否有效
         PayChannelDO channel = validatePayChannelCanSubmit(order.getAppId(), reqVO.getChannelCode());
         PayClient client = payClientFactory.getPayClient(channel.getId());
@@ -167,16 +172,16 @@ public class PayOrderServiceImpl implements PayOrderService {
         return PayOrderConvert.INSTANCE.convert(order, unifiedOrderRespDTO);
     }
 
-    private PayOrderDO validatePayOrderCanSubmit(Long id) {
+    private PayOrderDO validateOrderCanSubmit(Long id) {
         PayOrderDO order = orderMapper.selectById(id);
         if (order == null) { // 是否存在
-            throw exception(ErrorCodeConstants.PAY_ORDER_NOT_FOUND);
+            throw exception(PAY_ORDER_NOT_FOUND);
         }
         if (!PayOrderStatusEnum.WAITING.getStatus().equals(order.getStatus())) { // 校验状态,必须是待支付
-            throw exception(ErrorCodeConstants.PAY_ORDER_STATUS_IS_NOT_WAITING);
+            throw exception(PAY_ORDER_STATUS_IS_NOT_WAITING);
         }
         if (LocalDateTimeUtils.beforeNow(order.getExpireTime())) { // 校验是否过期
-            throw exception(ErrorCodeConstants.PAY_ORDER_IS_EXPIRED);
+            throw exception(PAY_ORDER_IS_EXPIRED);
         }
         return order;
     }
@@ -184,14 +189,12 @@ public class PayOrderServiceImpl implements PayOrderService {
     private PayChannelDO validatePayChannelCanSubmit(Long appId, String channelCode) {
         // 校验 App
         appService.validPayApp(appId);
-
         // 校验支付渠道是否有效
         PayChannelDO channel = channelService.validPayChannel(appId, channelCode);
-        // 校验支付客户端是否正确初始化
         PayClient client = payClientFactory.getPayClient(channel.getId());
         if (client == null) {
             log.error("[validatePayChannelCanSubmit][渠道编号({}) 找不到对应的支付客户端]", channel.getId());
-            throw exception(ErrorCodeConstants.PAY_CHANNEL_CLIENT_NOT_FOUND);
+            throw exception(PAY_CHANNEL_CLIENT_NOT_FOUND);
         }
         return channel;
     }
@@ -226,28 +229,55 @@ public class PayOrderServiceImpl implements PayOrderService {
 
     @Override
     @Transactional(rollbackFor = Exception.class)
-    public void notifyPayOrder(Long channelId, PayOrderRespDTO notify) {
+    public void notifyOrder(Long channelId, PayOrderRespDTO notify) {
         // 校验支付渠道是否有效
         PayChannelDO channel = channelService.validPayChannel(channelId);
         // 更新支付订单为已支付
         TenantUtils.execute(channel.getTenantId(), () -> notifyPayOrder(channel, notify));
     }
 
+    @Override
+    public void updateOrderRefundPrice(Long id, Integer incrRefundPrice) {
+        PayOrderDO order = orderMapper.selectById(id);
+        if (order == null) {
+            throw exception(PAY_ORDER_NOT_FOUND);
+        }
+        if (!PayOrderStatusEnum.isSuccess(order.getStatus())) {
+            throw exception(PAY_REFUND_PRICE_EXCEED);
+        }
+        if (order.getRefundPrice() + incrRefundPrice > order.getPrice()) {
+            throw exception(PAY_REFUND_PRICE_EXCEED);
+        }
+
+        // 更新订单
+        PayOrderDO updateObj = new PayOrderDO()
+                .setRefundPrice(order.getRefundPrice() + incrRefundPrice)
+                .setRefundTimes(order.getRefundTimes() + 1);
+        if (Objects.equals(updateObj.getRefundPrice(), order.getPrice())) {
+            updateObj.setStatus(PayOrderStatusEnum.CLOSED.getStatus())
+                    .setRefundStatus(PayOrderRefundStatusEnum.ALL.getStatus());
+        } else {
+            updateObj.setStatus(PayOrderStatusEnum.CLOSED.getStatus())
+                    .setRefundStatus(PayOrderRefundStatusEnum.PART.getStatus());
+        }
+        orderMapper.updateByIdAndStatus(id, PayOrderStatusEnum.SUCCESS.getStatus(), updateObj);
+    }
+
     private void notifyPayOrder(PayChannelDO channel, PayOrderRespDTO notify) {
         // 情况一:支付成功的回调
         if (PayOrderStatusRespEnum.isSuccess(notify.getStatus())) {
-            notifyPayOrderSuccess(channel, notify);
+            notifyOrderSuccess(channel, notify);
             return;
         }
         // 情况二:非支付成功的回调,进行忽略
         log.info("[notifyPayOrder][非支付成功的回调({}),直接忽略]", toJsonString(notify));
     }
 
-    private void notifyPayOrderSuccess(PayChannelDO channel, PayOrderRespDTO notify) {
+    private void notifyOrderSuccess(PayChannelDO channel, PayOrderRespDTO notify) {
         // 1. 更新 PayOrderExtensionDO 支付成功
-        PayOrderExtensionDO orderExtension = updatePayOrderExtensionSuccess(notify);
+        PayOrderExtensionDO orderExtension = updateOrderExtensionSuccess(notify);
         // 2. 更新 PayOrderDO 支付成功
-        Pair<Boolean, PayOrderDO> order = updatePayOrderSuccess(channel, orderExtension, notify);
+        Pair<Boolean, PayOrderDO> order = updateOrderExtensionSuccess(channel, orderExtension, notify);
         if (order.getKey()) { // 如果之前已经成功回调,则直接返回,不用重复记录支付通知记录;例如说:支付平台重复回调
             return;
         }
@@ -263,27 +293,27 @@ public class PayOrderServiceImpl implements PayOrderService {
      * @param notify 通知
      * @return PayOrderExtensionDO 对象
      */
-    private PayOrderExtensionDO updatePayOrderExtensionSuccess(PayOrderRespDTO notify) {
+    private PayOrderExtensionDO updateOrderExtensionSuccess(PayOrderRespDTO notify) {
         // 1. 查询 PayOrderExtensionDO
         PayOrderExtensionDO orderExtension = orderExtensionMapper.selectByNo(notify.getOutTradeNo());
         if (orderExtension == null) {
-            throw exception(ErrorCodeConstants.PAY_ORDER_EXTENSION_NOT_FOUND);
+            throw exception(PAY_ORDER_EXTENSION_NOT_FOUND);
         }
         if (PayOrderStatusEnum.isSuccess(orderExtension.getStatus())) { // 如果已经是成功,直接返回,不用重复更新
-            log.info("[updatePayOrderSuccess][支付拓展单({}) 已经是已支付,无需更新为已支付]", orderExtension.getId());
+            log.info("[updateOrderExtensionSuccess][支付拓展单({}) 已经是已支付,无需更新为已支付]", orderExtension.getId());
             return orderExtension;
         }
         if (ObjectUtil.notEqual(orderExtension.getStatus(), PayOrderStatusEnum.WAITING.getStatus())) { // 校验状态,必须是待支付
-            throw exception(ErrorCodeConstants.PAY_ORDER_EXTENSION_STATUS_IS_NOT_WAITING);
+            throw exception(PAY_ORDER_EXTENSION_STATUS_IS_NOT_WAITING);
         }
 
         // 2. 更新 PayOrderExtensionDO
-        int updateCounts = orderExtensionMapper.updateByIdAndStatus(orderExtension.getId(), PayOrderStatusEnum.WAITING.getStatus(),
+        int updateCounts = orderExtensionMapper.updateByIdAndStatus(orderExtension.getId(), orderExtension.getStatus(),
                 PayOrderExtensionDO.builder().status(PayOrderStatusEnum.SUCCESS.getStatus()).channelNotifyData(toJsonString(notify)).build());
         if (updateCounts == 0) { // 校验状态,必须是待支付
-            throw exception(ErrorCodeConstants.PAY_ORDER_EXTENSION_STATUS_IS_NOT_WAITING);
+            throw exception(PAY_ORDER_EXTENSION_STATUS_IS_NOT_WAITING);
         }
-        log.info("[updatePayOrderSuccess][支付拓展单({}) 更新为已支付]", orderExtension.getId());
+        log.info("[updateOrderExtensionSuccess][支付拓展单({}) 更新为已支付]", orderExtension.getId());
         return orderExtension;
     }
 
@@ -296,20 +326,20 @@ public class PayOrderServiceImpl implements PayOrderService {
      * @return key:是否之前已经成功回调
      *         value:PayOrderDO 对象
      */
-    private Pair<Boolean, PayOrderDO> updatePayOrderSuccess(PayChannelDO channel, PayOrderExtensionDO orderExtension,
+    private Pair<Boolean, PayOrderDO> updateOrderExtensionSuccess(PayChannelDO channel, PayOrderExtensionDO orderExtension,
                                                             PayOrderRespDTO notify) {
         // 1. 判断 PayOrderDO 是否处于待支付
         PayOrderDO order = orderMapper.selectById(orderExtension.getOrderId());
         if (order == null) {
-            throw exception(ErrorCodeConstants.PAY_ORDER_NOT_FOUND);
+            throw exception(PAY_ORDER_NOT_FOUND);
         }
         if (PayOrderStatusEnum.isSuccess(order.getStatus()) // 如果已经是成功,直接返回,不用重复更新
                 && Objects.equals(order.getSuccessExtensionId(), orderExtension.getId())) {
-            log.info("[updatePayOrderSuccess][支付订单({}) 已经是已支付,无需更新为已支付]", order.getId());
+            log.info("[updateOrderExtensionSuccess][支付订单({}) 已经是已支付,无需更新为已支付]", order.getId());
             return Pair.of(true, order);
         }
         if (!PayOrderStatusEnum.WAITING.getStatus().equals(order.getStatus())) { // 校验状态,必须是待支付
-            throw exception(ErrorCodeConstants.PAY_ORDER_STATUS_IS_NOT_WAITING);
+            throw exception(PAY_ORDER_STATUS_IS_NOT_WAITING);
         }
 
         // 2. 更新 PayOrderDO
@@ -320,9 +350,9 @@ public class PayOrderServiceImpl implements PayOrderService {
                         .channelOrderNo(notify.getChannelOrderNo()).channelUserId(notify.getChannelUserId())
                         .notifyTime(LocalDateTime.now()).build());
         if (updateCounts == 0) { // 校验状态,必须是待支付
-            throw exception(ErrorCodeConstants.PAY_ORDER_STATUS_IS_NOT_WAITING);
+            throw exception(PAY_ORDER_STATUS_IS_NOT_WAITING);
         }
-        log.info("[updatePayOrderSuccess][支付订单({}) 更新为已支付]", order.getId());
+        log.info("[updateOrderExtensionSuccess][支付订单({}) 更新为已支付]", order.getId());
         return Pair.of(false, order);
     }
 

+ 1 - 1
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/service/refund/PayRefundService.java

@@ -62,6 +62,6 @@ public interface PayRefundService {
      * @param channelId  渠道编号
      * @param notify     通知
      */
-    void notifyPayRefund(Long channelId, PayRefundRespDTO notify);
+    void notifyRefund(Long channelId, PayRefundRespDTO notify);
 
 }

+ 132 - 139
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/service/refund/PayRefundServiceImpl.java

@@ -1,8 +1,7 @@
 package cn.iocoder.yudao.module.pay.service.refund;
 
+import cn.hutool.core.date.DateUtil;
 import cn.hutool.core.util.RandomUtil;
-import cn.hutool.core.util.StrUtil;
-import cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil;
 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;
@@ -10,22 +9,23 @@ import cn.iocoder.yudao.framework.pay.core.client.PayClientFactory;
 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.PayRefundStatusRespEnum;
+import cn.iocoder.yudao.framework.tenant.core.util.TenantUtils;
 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.module.pay.convert.refund.PayRefundConvert;
 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.dal.dataobject.order.PayOrderExtensionDO;
 import cn.iocoder.yudao.module.pay.dal.dataobject.refund.PayRefundDO;
-import cn.iocoder.yudao.module.pay.dal.mysql.order.PayOrderMapper;
 import cn.iocoder.yudao.module.pay.dal.mysql.refund.PayRefundMapper;
 import cn.iocoder.yudao.module.pay.enums.ErrorCodeConstants;
+import cn.iocoder.yudao.module.pay.enums.notify.PayNotifyStatusEnum;
 import cn.iocoder.yudao.module.pay.enums.notify.PayNotifyTypeEnum;
-import cn.iocoder.yudao.module.pay.enums.order.PayOrderNotifyStatusEnum;
+import cn.iocoder.yudao.module.pay.enums.order.PayOrderRefundStatusEnum;
 import cn.iocoder.yudao.module.pay.enums.order.PayOrderStatusEnum;
 import cn.iocoder.yudao.module.pay.enums.refund.PayRefundStatusEnum;
-import cn.iocoder.yudao.module.pay.enums.refund.PayRefundTypeEnum;
 import cn.iocoder.yudao.module.pay.service.app.PayAppService;
 import cn.iocoder.yudao.module.pay.service.channel.PayChannelService;
 import cn.iocoder.yudao.module.pay.service.notify.PayNotifyService;
@@ -38,8 +38,11 @@ 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;
+
+import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
+import static cn.iocoder.yudao.framework.common.util.json.JsonUtils.toJsonString;
 
 /**
  * 退款订单 Service 实现类
@@ -59,8 +62,6 @@ public class PayRefundServiceImpl implements PayRefundService {
 
     @Resource
     private PayRefundMapper refundMapper;
-    @Resource
-    private PayOrderMapper orderMapper; // TODO @jason:需要改成不直接操作 db;
 
     @Resource
     private PayOrderService orderService;
@@ -80,7 +81,7 @@ public class PayRefundServiceImpl implements PayRefundService {
 
     @Override
     public Long getRefundCountByAppId(Long appId) {
-        return refundMapper.selectCountByApp(appId);
+        return refundMapper.selectCountByAppId(appId);
     }
 
     @Override
@@ -96,82 +97,82 @@ public class PayRefundServiceImpl implements PayRefundService {
     @Override
     @Transactional(rollbackFor = Exception.class)
     public Long createPayRefund(PayRefundCreateReqDTO reqDTO) {
-        // 获得 PayOrderDO
-        PayOrderDO order = orderService.getOrder(reqDTO.getPayOrderId());
-        // 校验订单是否存在
-        if (Objects.isNull(order) ) {
-            throw ServiceExceptionUtil.exception(ErrorCodeConstants.PAY_ORDER_NOT_FOUND);
-        }
-        // 校验 App
-        PayAppDO app = appService.validPayApp(order.getAppId());
-        // 校验支付渠道是否有效
+        // 1.1 校验 App
+        PayAppDO app = appService.validPayApp(reqDTO.getAppId());
+        // 1.2 校验支付订单
+        PayOrderDO order = validatePayOrderCanRefund(reqDTO);
+        // 1.3 校验支付渠道是否有效
         PayChannelDO channel = channelService.validPayChannel(order.getChannelId());
-        // 校验支付客户端是否正确初始化
         PayClient client = payClientFactory.getPayClient(channel.getId());
         if (client == null) {
             log.error("[refund][渠道编号({}) 找不到对应的支付客户端]", channel.getId());
-            throw ServiceExceptionUtil.exception(ErrorCodeConstants.PAY_CHANNEL_CLIENT_NOT_FOUND);
+            throw exception(ErrorCodeConstants.PAY_CHANNEL_CLIENT_NOT_FOUND);
         }
-
-        // TODO 芋艿:待实现
-        String merchantRefundId = "rrr" + RandomUtil.randomNumbers(16);
-
-        // 校验退款的条件
-        validatePayRefund(reqDTO, order);
-        // 退款类型
-        PayRefundTypeEnum refundType = PayRefundTypeEnum.SOME;
-        if (Objects.equals(reqDTO.getPrice(), order.getPrice())) {
-            refundType = PayRefundTypeEnum.ALL;
+        // 1.4 校验退款订单是否已经存在
+        PayRefundDO refund = refundMapper.selectByAppIdAndMerchantRefundId(
+                app.getId(), reqDTO.getMerchantRefundId());
+        if (refund != null) {
+            throw exception(ErrorCodeConstants.PAY_REFUND_EXISTS);
         }
-        PayOrderExtensionDO orderExtensionDO = orderExtensionService.getOrderExtension(order.getSuccessExtensionId());
-        PayRefundDO payRefundDO = refundMapper.selectByTradeNoAndMerchantRefundNo(orderExtensionDO.getNo(),
-                merchantRefundId);  // TODO 芋艿:需要优化
-        if (Objects.nonNull(payRefundDO)) {
-            // 退款订单已经提交过。
-            //TODO 校验相同退款单的金额
-            // TODO @jason:咱要不封装一个 ObjectUtils.equalsAny
-            if (Objects.equals(PayRefundStatusEnum.SUCCESS.getStatus(), payRefundDO.getStatus())) {
-                //已成功退款
-                throw ServiceExceptionUtil.exception(ErrorCodeConstants.PAY_REFUND_SUCCEED);
-            }
-            //可以重复提交,保证 退款请求号 一致,由渠道保证幂等
-        } else {
-            // 成功,插入退款单 状态为生成.没有和渠道交互
-            // TODO @jason:搞到 convert 里。一些额外的自动,手动 set 下;
-            payRefundDO = PayRefundDO.builder()
-                    .appId(order.getAppId())
-                    .channelOrderNo(order.getChannelOrderNo())
-                    .channelCode(order.getChannelCode())
-                    .channelId(order.getChannelId())
-                    .orderId(order.getId())
-                    .merchantRefundId(merchantRefundId)
-                    .notifyUrl(app.getRefundNotifyUrl())
-                    .payPrice(order.getPrice())
-                    .refundPrice(reqDTO.getPrice())
-                    .userIp(reqDTO.getUserIp())
-                    .merchantOrderId(order.getMerchantOrderId())
-                    .no(orderExtensionDO.getNo())
-                    .status(PayRefundStatusEnum.WAITING.getStatus())
-                    .reason(reqDTO.getReason())
-                    .notifyStatus(PayOrderNotifyStatusEnum.NO.getStatus())
-                    .type(refundType.getStatus())
-                    .build();
-            refundMapper.insert(payRefundDO);
-        }
-        // TODO @jason:搞到 convert 里。一些额外的自动,手动 set 下;
+
+        // 2.1 插入退款单
+        refund = PayRefundConvert.INSTANCE.convert(reqDTO)
+                .setNo(generateRefundNo()).setOrderId(order.getId())
+                .setChannelId(order.getChannelId()).setChannelCode(order.getChannelCode())
+                // 商户相关的字段
+                .setNotifyUrl(app.getRefundNotifyUrl()).setNotifyStatus(PayNotifyStatusEnum.WAITING.getStatus())
+                // 渠道相关字段
+                .setChannelOrderNo(order.getChannelOrderNo())
+                // 退款相关字段
+                .setStatus(PayRefundStatusEnum.WAITING.getStatus())
+                .setPayPrice(order.getPrice()).setRefundPrice(reqDTO.getPrice());
+        refundMapper.insert(refund);
+        // 2.2 向渠道发起退款申请
+        PayOrderExtensionDO orderExtension = orderExtensionService.getOrderExtension(order.getSuccessExtensionId());
         PayRefundUnifiedReqDTO unifiedReqDTO = new PayRefundUnifiedReqDTO();
         unifiedReqDTO.setPrice(reqDTO.getPrice())
-                .setOutTradeNo(orderExtensionDO.getNo())
-                .setOutRefundNo(merchantRefundId)  // TODO 芋艿:需要优化
+                .setOutTradeNo(orderExtension.getNo())
+                .setOutRefundNo(refund.getNo())
                 .setNotifyUrl(genChannelPayNotifyUrl(channel)) // TODO 芋艿:优化下 notifyUrl
                 .setReason(reqDTO.getReason());
-        // 向渠道发起退款申请
-        client.unifiedRefund(unifiedReqDTO);
-        // 检查是否失败,失败抛出业务异常。
-        // TODO 渠道的异常记录。
-        // TODO @jason:可以先打个 warn log 哈;
+        PayRefundRespDTO refundRespDTO = client.unifiedRefund(unifiedReqDTO); // TODO 增加一个 channelErrorCode、channelErrorMsg 字段
+        // 2.3 处理退款返回
+        notifyRefund(channel, refundRespDTO);
+
         // 成功在 退款回调中处理
-        return payRefundDO.getId();
+        return refund.getId();
+    }
+
+    /**
+     * 校验支付订单是否可以退款
+     *
+     * @param reqDTO 退款申请信息
+     * @return 支付订单
+     */
+    private PayOrderDO validatePayOrderCanRefund(PayRefundCreateReqDTO reqDTO) {
+        PayOrderDO order = orderService.getOrder(reqDTO.getAppId(), reqDTO.getMerchantOrderId());
+        if (order == null) {
+            throw exception(ErrorCodeConstants.PAY_ORDER_NOT_FOUND);
+        }
+        // 校验状态,必须是支付状态
+        if (!PayOrderStatusEnum.SUCCESS.getStatus().equals(order.getStatus())) {
+            throw exception(ErrorCodeConstants.PAY_ORDER_STATUS_IS_NOT_SUCCESS);
+        }
+
+        // 是否已经全额退款
+        if (PayOrderRefundStatusEnum.ALL.getStatus().equals(order.getRefundStatus())) {
+            throw exception(ErrorCodeConstants.PAY_REFUND_ALL_REFUNDED);
+        }
+        // 校验金额 退款金额不能大于原定的金额
+        if (reqDTO.getPrice() + order.getRefundPrice() > order.getPrice()){
+            throw exception(ErrorCodeConstants.PAY_REFUND_PRICE_EXCEED);
+        }
+        // 是否有退款中的订单
+        if (refundMapper.selectCountByAppIdAndOrderId(reqDTO.getAppId(), order.getId(),
+                PayRefundStatusEnum.WAITING.getStatus()) > 0) {
+            throw exception(ErrorCodeConstants.PAY_REFUND_HAS_REFUNDING);
+        }
+        return order;
     }
 
     /**
@@ -184,88 +185,80 @@ public class PayRefundServiceImpl implements PayRefundService {
         return payProperties.getCallbackUrl() + "/" + channel.getId();
     }
 
+    private String generateRefundNo() {
+//    wx
+//    2014
+//    10
+//    27
+//    20
+//    09
+//    39
+//    5522657
+//    a690389285100
+        // 目前的算法
+        // 时间序列,年月日时分秒 14 位
+        // 纯随机,6 位 TODO 芋艿:此处估计是会有问题的,后续在调整
+        return DateUtil.format(LocalDateTime.now(), "yyyyMMddHHmmss") + // 时间序列
+                RandomUtil.randomInt(100000, 999999) // 随机。为什么是这个范围,因为偷懒
+                ;
+    }
+
     @Override
     @Transactional(rollbackFor = Exception.class)
-    public void notifyPayRefund(Long channelId, PayRefundRespDTO notify) {
+    public void notifyRefund(Long channelId, PayRefundRespDTO notify) {
+        // 校验支付渠道是否有效
+        channelService.validPayChannel(channelId);
+        // 通知结果
+
         // 校验支付渠道是否有效
-        // TODO 芋艿:需要重构下这块的逻辑
         PayChannelDO channel = channelService.validPayChannel(channelId);
+        // 更新退款订单
+        TenantUtils.execute(channel.getTenantId(), () -> notifyRefund(channel, notify));
+    }
+
+    private void notifyRefund(PayChannelDO channel, PayRefundRespDTO notify) {
         if (PayRefundStatusRespEnum.isSuccess(notify.getStatus())) {
-            payRefundSuccess(notify);
+            notifyRefundSuccess(channel, notify);
         } else {
-            // TODO @jason:那这里可以考虑打个 error logger @芋艿 微信是否存在支付异常通知
+            notifyRefundFailure(channel, notify);
         }
     }
 
-    private void payRefundSuccess(PayRefundRespDTO refundNotify) {
-        // 校验退款单存在
-        PayRefundDO refundDO = null; // TODO 芋艿:临时注释
-//        PayRefundDO refundDO = refundMapper.selectByTradeNoAndMerchantRefundNo(refundNotify.getTradeNo(),
-//                refundNotify.getReqNo());
-        if (refundDO == null) {
-            // TODO 芋艿:临时注释
-//            log.error("[payRefundSuccess][不存在 seqNo 为{} 的支付退款单]", refundNotify.getReqNo());
-            throw ServiceExceptionUtil.exception(ErrorCodeConstants.PAY_REFUND_NOT_FOUND);
+    private void notifyRefundSuccess(PayChannelDO channel, PayRefundRespDTO notify) {
+        // 1.1 查询 PayRefundDO
+        PayRefundDO refund = refundMapper.selectByAppIdAndNo(
+                channel.getAppId(), notify.getOutRefundNo());
+        if (refund == null) {
+            throw exception(ErrorCodeConstants.PAY_REFUND_NOT_FOUND);
+        }
+        if (PayRefundStatusEnum.isSuccess(refund.getStatus())) { // 如果已经是成功,直接返回,不用重复更新
+            return;
+        }
+        if (!PayRefundStatusEnum.WAITING.getStatus().equals(refund.getStatus())) {
+            throw exception(ErrorCodeConstants.PAY_REFUND_STATUS_IS_NOT_WAITING);
         }
 
-        // 得到已退金额
-        PayOrderDO payOrderDO = orderService.getOrder(refundDO.getOrderId());
-        Long refundedAmount = payOrderDO.getRefundPrice();
-
-        PayOrderStatusEnum orderStatus = PayOrderStatusEnum.SUCCESS;
-        if(Objects.equals(payOrderDO.getPrice(), refundedAmount+ refundDO.getRefundPrice())){
-            //支付金额  = 已退金额 + 本次退款金额。
-            orderStatus = PayOrderStatusEnum.CLOSED;
+        // 1.2 更新 PayRefundDO
+        PayRefundDO updateRefundObj = new PayRefundDO()
+                .setSuccessTime(notify.getSuccessTime())
+                .setChannelRefundNo(notify.getChannelRefundNo())
+                .setStatus(PayRefundStatusEnum.SUCCESS.getStatus())
+                .setChannelNotifyData(toJsonString(notify));
+        int updateCounts = refundMapper.updateByIdAndStatus(refund.getId(), refund.getStatus(), updateRefundObj);
+        if (updateCounts == 0) { // 校验状态,必须是等待状态
+            throw exception(ErrorCodeConstants.PAY_REFUND_STATUS_IS_NOT_WAITING);
         }
-        // 更新支付订单
-        PayOrderDO updateOrderDO = new PayOrderDO();
-        updateOrderDO.setId(refundDO.getOrderId())
-                .setRefundPrice(refundedAmount + refundDO.getRefundPrice())
-                .setStatus(orderStatus.getStatus())
-                .setRefundTimes(payOrderDO.getRefundTimes() + 1)
-                .setRefundStatus(refundDO.getType());
-        orderMapper.updateById(updateOrderDO);
 
-        // 更新退款订单
-        PayRefundDO updateRefundDO = new PayRefundDO();
-        updateRefundDO.setId(refundDO.getId())
-                .setSuccessTime(refundNotify.getSuccessTime())
-                // TODO 芋艿:如下两行,临时注释
-//                .setChannelRefundNo(refundNotify.getChannelOrderNo())
-//                .setNo(refundNotify.getTradeNo())
-                .setStatus(PayRefundStatusEnum.SUCCESS.getStatus());
-        refundMapper.updateById(updateRefundDO);
+        // 2. 更新订单
+        orderService.updateOrderRefundPrice(refund.getOrderId(), refund.getRefundPrice());
 
-        // 插入退款通知记录
-        // TODO 通知商户成功或者失败. 现在通知似乎没有实现, 只是回调
+        // 3. 插入退款通知记录
         notifyService.createPayNotifyTask(PayNotifyTaskCreateReqDTO.builder()
-                .type(PayNotifyTypeEnum.REFUND.getType()).dataId(refundDO.getId()).build());
+                .type(PayNotifyTypeEnum.REFUND.getType()).dataId(refund.getId()).build());
     }
 
-    /**
-     * 校验是否进行退款
-     *
-     * @param reqDTO 退款申请信息
-     * @param order 原始支付订单信息
-     */
-    private void validatePayRefund(PayRefundCreateReqDTO reqDTO, PayOrderDO order) {
-        // 校验状态,必须是支付状态
-        if (!PayOrderStatusEnum.SUCCESS.getStatus().equals(order.getStatus())) {
-            throw ServiceExceptionUtil.exception(ErrorCodeConstants.PAY_ORDER_STATUS_IS_NOT_SUCCESS);
-        }
-        // 是否已经全额退款
-        if (PayRefundTypeEnum.ALL.getStatus().equals(order.getRefundStatus())) {
-            throw ServiceExceptionUtil.exception(ErrorCodeConstants.PAY_REFUND_ALL_REFUNDED);
-        }
-        // 校验金额 退款金额不能大于 原定的金额
-        if (reqDTO.getPrice() + order.getRefundPrice() > order.getPrice()){
-            throw ServiceExceptionUtil.exception(ErrorCodeConstants.PAY_PRICE_PRICE_EXCEED);
-        }
-        // 校验渠道订单号
-        if (StrUtil.isEmpty(order.getChannelOrderNo())) {
-            throw ServiceExceptionUtil.exception(ErrorCodeConstants.PAY_REFUND_CHN_ORDER_NO_IS_NULL);
-        }
-        //TODO  退款的期限  退款次数的控制
+    private void notifyRefundFailure(PayChannelDO channel, PayRefundRespDTO notify) {
+        // TODO 芋艿:未实现
     }
 
 }

+ 7 - 7
yudao-module-pay/yudao-module-pay-biz/src/test/java/cn/iocoder/yudao/module/pay/service/order/PayOrderServiceTest.java

@@ -13,7 +13,7 @@ import cn.iocoder.yudao.module.pay.dal.dataobject.order.PayOrderDO;
 import cn.iocoder.yudao.module.pay.dal.mysql.order.PayOrderMapper;
 import cn.iocoder.yudao.module.pay.enums.order.PayOrderNotifyStatusEnum;
 import cn.iocoder.yudao.module.pay.enums.order.PayOrderStatusEnum;
-import cn.iocoder.yudao.module.pay.enums.refund.PayRefundTypeEnum;
+import cn.iocoder.yudao.module.pay.enums.order.PayOrderRefundStatusEnum;
 import cn.iocoder.yudao.module.pay.service.app.PayAppService;
 import cn.iocoder.yudao.module.pay.service.channel.PayChannelService;
 import cn.iocoder.yudao.module.pay.service.notify.PayNotifyService;
@@ -85,7 +85,7 @@ public class PayOrderServiceTest extends BaseDbUnitTest {
             o.setSuccessTime(LocalDateTime.of(2018, 1, 1, 10, 10, 2));
             o.setNotifyTime(LocalDateTime.of(2018, 1, 1, 10, 10, 15));
             o.setSuccessExtensionId(1L);
-            o.setRefundStatus(PayRefundTypeEnum.NO.getStatus());
+            o.setRefundStatus(PayOrderRefundStatusEnum.NO.getStatus());
             o.setRefundTimes(0);
             o.setRefundPrice(0L);
             o.setChannelUserId("1008611");
@@ -106,7 +106,7 @@ public class PayOrderServiceTest extends BaseDbUnitTest {
         // 测试 status 不匹配
         orderMapper.insert(cloneIgnoreId(dbOrder, o -> o.setStatus(PayOrderStatusEnum.CLOSED.getStatus())));
         // 测试 refundStatus 不匹配
-        orderMapper.insert(cloneIgnoreId(dbOrder, o -> o.setRefundStatus(PayRefundTypeEnum.ALL.getStatus())));
+        orderMapper.insert(cloneIgnoreId(dbOrder, o -> o.setRefundStatus(PayOrderRefundStatusEnum.ALL.getStatus())));
         // 测试 createTime 不匹配
         orderMapper.insert(cloneIgnoreId(dbOrder, o -> o.setCreateTime(LocalDateTime.of(2019, 1, 1, 10, 10,
                 1))));
@@ -118,7 +118,7 @@ public class PayOrderServiceTest extends BaseDbUnitTest {
         reqVO.setMerchantOrderId(merchantOrderId);
         reqVO.setNotifyStatus(PayOrderNotifyStatusEnum.SUCCESS.getStatus());
         reqVO.setStatus(PayOrderStatusEnum.SUCCESS.getStatus());
-        reqVO.setRefundStatus(PayRefundTypeEnum.NO.getStatus());
+        reqVO.setRefundStatus(PayOrderRefundStatusEnum.NO.getStatus());
         reqVO.setCreateTime((new LocalDateTime[]{LocalDateTime.of(2018, 1, 1, 10, 1, 0), LocalDateTime.of(2018, 1, 1, 10, 1, 0)}));
         // 调用
         PageResult<PayOrderDO> pageResult = orderService.getOrderPage(reqVO);
@@ -153,7 +153,7 @@ public class PayOrderServiceTest extends BaseDbUnitTest {
             o.setSuccessTime(LocalDateTime.of(2018, 1, 1, 10, 10, 2));
             o.setNotifyTime(LocalDateTime.of(2018, 1, 1, 10, 10, 15));
             o.setSuccessExtensionId(1L);
-            o.setRefundStatus(PayRefundTypeEnum.NO.getStatus());
+            o.setRefundStatus(PayOrderRefundStatusEnum.NO.getStatus());
             o.setRefundTimes(0);
             o.setRefundPrice(0L);
             o.setChannelUserId("1008611");
@@ -175,7 +175,7 @@ public class PayOrderServiceTest extends BaseDbUnitTest {
         // 测试 status 不匹配
         orderMapper.insert(cloneIgnoreId(dbOrder, o -> o.setStatus(PayOrderStatusEnum.CLOSED.getStatus())));
         // 测试 refundStatus 不匹配
-        orderMapper.insert(cloneIgnoreId(dbOrder, o -> o.setRefundStatus(PayRefundTypeEnum.ALL.getStatus())));
+        orderMapper.insert(cloneIgnoreId(dbOrder, o -> o.setRefundStatus(PayOrderRefundStatusEnum.ALL.getStatus())));
         // 测试 createTime 不匹配
         orderMapper.insert(cloneIgnoreId(dbOrder, o -> o.setCreateTime(LocalDateTime.of(2019, 1, 1, 10, 10,
                 1))));
@@ -187,7 +187,7 @@ public class PayOrderServiceTest extends BaseDbUnitTest {
         reqVO.setMerchantOrderId(merchantOrderId);
         reqVO.setNotifyStatus(PayOrderNotifyStatusEnum.SUCCESS.getStatus());
         reqVO.setStatus(PayOrderStatusEnum.SUCCESS.getStatus());
-        reqVO.setRefundStatus(PayRefundTypeEnum.NO.getStatus());
+        reqVO.setRefundStatus(PayOrderRefundStatusEnum.NO.getStatus());
         reqVO.setCreateTime((new LocalDateTime[]{LocalDateTime.of(2018, 1, 1, 10, 1, 0), LocalDateTime.of(2018, 1, 1, 10, 1, 0)}));
 
         // 调用

+ 13 - 19
yudao-module-pay/yudao-module-pay-biz/src/test/java/cn/iocoder/yudao/module/pay/service/refund/PayRefundServiceTest.java

@@ -11,7 +11,7 @@ import cn.iocoder.yudao.module.pay.dal.dataobject.refund.PayRefundDO;
 import cn.iocoder.yudao.module.pay.dal.mysql.refund.PayRefundMapper;
 import cn.iocoder.yudao.module.pay.enums.order.PayOrderNotifyStatusEnum;
 import cn.iocoder.yudao.module.pay.enums.refund.PayRefundStatusEnum;
-import cn.iocoder.yudao.module.pay.enums.refund.PayRefundTypeEnum;
+import cn.iocoder.yudao.module.pay.enums.order.PayOrderRefundStatusEnum;
 import cn.iocoder.yudao.module.pay.service.app.PayAppService;
 import cn.iocoder.yudao.module.pay.service.channel.PayChannelService;
 import cn.iocoder.yudao.module.pay.service.notify.PayNotifyService;
@@ -64,11 +64,11 @@ public class PayRefundServiceTest extends BaseDbUnitTest {
             o.setOrderId(1L);
             o.setNo("OT0000001");
             o.setMerchantOrderId("MOT0000001");
-            o.setMerchantRefundNo("MRF0000001");
+            o.setMerchantRefundId("MRF0000001");
             o.setNotifyUrl("https://www.cancanzi.com");
             o.setNotifyStatus(PayOrderNotifyStatusEnum.SUCCESS.getStatus());
             o.setStatus(PayRefundStatusEnum.SUCCESS.getStatus());
-            o.setType(PayRefundTypeEnum.SOME.getStatus());
+            o.setType(PayOrderRefundStatusEnum.PART.getStatus());
             o.setPayPrice(100);
             o.setRefundPrice(500);
             o.setReason("就是想退款了,你有意见吗");
@@ -77,10 +77,7 @@ public class PayRefundServiceTest extends BaseDbUnitTest {
             o.setChannelRefundNo("CHR0000001");
             o.setChannelErrorCode("");
             o.setChannelErrorMsg("");
-            o.setChannelExtras("");
-            o.setExpireTime(LocalDateTime.of(2021, 1, 1, 10, 10, 30));
             o.setSuccessTime(LocalDateTime.of(2021, 1, 1, 10, 10, 15));
-            o.setNotifyTime(LocalDateTime.of(2021, 1, 1, 10, 10, 20));
             o.setCreateTime(LocalDateTime.of(2021, 1, 1, 10, 10, 10));
             o.setUpdateTime(LocalDateTime.of(2021, 1, 1, 10, 10, 35));
         });
@@ -90,14 +87,14 @@ public class PayRefundServiceTest extends BaseDbUnitTest {
         // 测试 channelCode 不匹配
         refundMapper.insert(cloneIgnoreId(dbRefund, o -> o.setChannelCode(PayChannelEnum.ALIPAY_APP.getCode())));
         // 测试 merchantRefundNo 不匹配
-        refundMapper.insert(cloneIgnoreId(dbRefund, o -> o.setMerchantRefundNo("MRF1111112")));
+        refundMapper.insert(cloneIgnoreId(dbRefund, o -> o.setMerchantRefundId("MRF1111112")));
         // 测试 notifyStatus 不匹配
         refundMapper.insert(
                 cloneIgnoreId(dbRefund, o -> o.setNotifyStatus(PayOrderNotifyStatusEnum.FAILURE.getStatus())));
         // 测试 status 不匹配
-        refundMapper.insert(cloneIgnoreId(dbRefund, o -> o.setStatus(PayRefundStatusEnum.CLOSE.getStatus())));
+        refundMapper.insert(cloneIgnoreId(dbRefund, o -> o.setStatus(PayRefundStatusEnum.FAILURE.getStatus())));
         // 测试 type 不匹配
-        refundMapper.insert(cloneIgnoreId(dbRefund, o -> o.setType(PayRefundTypeEnum.ALL.getStatus())));
+        refundMapper.insert(cloneIgnoreId(dbRefund, o -> o.setType(PayOrderRefundStatusEnum.ALL.getStatus())));
         // 测试 createTime 不匹配
         refundMapper.insert(cloneIgnoreId(dbRefund, o ->
                 o.setCreateTime(LocalDateTime.of(2022, 1, 1, 10, 10, 10))));
@@ -108,7 +105,7 @@ public class PayRefundServiceTest extends BaseDbUnitTest {
         reqVO.setMerchantRefundNo("MRF0000001");
         reqVO.setNotifyStatus(PayOrderNotifyStatusEnum.SUCCESS.getStatus());
         reqVO.setStatus(PayRefundStatusEnum.SUCCESS.getStatus());
-        reqVO.setType(PayRefundTypeEnum.SOME.getStatus());
+        reqVO.setType(PayOrderRefundStatusEnum.PART.getStatus());
         reqVO.setCreateTime((new LocalDateTime[]{LocalDateTime.of(2021, 1, 1, 10, 10, 10), LocalDateTime.of(2021, 1, 1, 10, 10, 12)}));
 
         // 调用
@@ -129,11 +126,11 @@ public class PayRefundServiceTest extends BaseDbUnitTest {
             o.setOrderId(1L);
             o.setNo("OT0000001");
             o.setMerchantOrderId("MOT0000001");
-            o.setMerchantRefundNo("MRF0000001");
+            o.setMerchantRefundId("MRF0000001");
             o.setNotifyUrl("https://www.cancanzi.com");
             o.setNotifyStatus(PayOrderNotifyStatusEnum.SUCCESS.getStatus());
             o.setStatus(PayRefundStatusEnum.SUCCESS.getStatus());
-            o.setType(PayRefundTypeEnum.SOME.getStatus());
+            o.setType(PayOrderRefundStatusEnum.PART.getStatus());
             o.setPayPrice(100);
             o.setRefundPrice(500);
             o.setReason("就是想退款了,你有意见吗");
@@ -142,10 +139,7 @@ public class PayRefundServiceTest extends BaseDbUnitTest {
             o.setChannelRefundNo("CHR0000001");
             o.setChannelErrorCode("");
             o.setChannelErrorMsg("");
-            o.setChannelExtras("");
-            o.setExpireTime(LocalDateTime.of(2021, 1, 1, 10, 10, 30));
             o.setSuccessTime(LocalDateTime.of(2021, 1, 1, 10, 10, 15));
-            o.setNotifyTime(LocalDateTime.of(2021, 1, 1, 10, 10, 20));
             o.setCreateTime(LocalDateTime.of(2021, 1, 1, 10, 10, 10));
             o.setUpdateTime(LocalDateTime.of(2021, 1, 1, 10, 10, 35));
         });
@@ -155,14 +149,14 @@ public class PayRefundServiceTest extends BaseDbUnitTest {
         // 测试 channelCode 不匹配
         refundMapper.insert(cloneIgnoreId(dbRefund, o -> o.setChannelCode(PayChannelEnum.ALIPAY_APP.getCode())));
         // 测试 merchantRefundNo 不匹配
-        refundMapper.insert(cloneIgnoreId(dbRefund, o -> o.setMerchantRefundNo("MRF1111112")));
+        refundMapper.insert(cloneIgnoreId(dbRefund, o -> o.setMerchantRefundId("MRF1111112")));
         // 测试 notifyStatus 不匹配
         refundMapper.insert(
                 cloneIgnoreId(dbRefund, o -> o.setNotifyStatus(PayOrderNotifyStatusEnum.FAILURE.getStatus())));
         // 测试 status 不匹配
-        refundMapper.insert(cloneIgnoreId(dbRefund, o -> o.setStatus(PayRefundStatusEnum.CLOSE.getStatus())));
+        refundMapper.insert(cloneIgnoreId(dbRefund, o -> o.setStatus(PayRefundStatusEnum.FAILURE.getStatus())));
         // 测试 type 不匹配
-        refundMapper.insert(cloneIgnoreId(dbRefund, o -> o.setType(PayRefundTypeEnum.ALL.getStatus())));
+        refundMapper.insert(cloneIgnoreId(dbRefund, o -> o.setType(PayOrderRefundStatusEnum.ALL.getStatus())));
         // 测试 createTime 不匹配
         refundMapper.insert(cloneIgnoreId(dbRefund, o ->
                 o.setCreateTime(LocalDateTime.of(2022, 1, 1, 10, 10, 10))));
@@ -174,7 +168,7 @@ public class PayRefundServiceTest extends BaseDbUnitTest {
         reqVO.setMerchantRefundNo("MRF0000001");
         reqVO.setNotifyStatus(PayOrderNotifyStatusEnum.SUCCESS.getStatus());
         reqVO.setStatus(PayRefundStatusEnum.SUCCESS.getStatus());
-        reqVO.setType(PayRefundTypeEnum.SOME.getStatus());
+        reqVO.setType(PayOrderRefundStatusEnum.PART.getStatus());
         reqVO.setCreateTime((new LocalDateTime[]{LocalDateTime.of(2021, 1, 1, 10, 10, 10), LocalDateTime.of(2021, 1, 1, 10, 10, 12)}));
 
         // 调用