瀏覽代碼

mall + pay:
1. 优化 PayClient 支付逻辑,返回业务失败 errorCode + errorMsg 错误码

YunaiV 1 年之前
父節點
當前提交
6f475f8c85
共有 29 個文件被更改,包括 361 次插入292 次删除
  1. 1 2
      yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/PayClient.java
  2. 89 7
      yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/dto/order/PayOrderRespDTO.java
  3. 0 1
      yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/dto/order/PayOrderUnifiedReqDTO.java
  4. 0 38
      yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/dto/order/PayOrderUnifiedRespDTO.java
  5. 29 15
      yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/AbstractPayClient.java
  6. 35 46
      yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/alipay/AbstractAlipayPayClient.java
  7. 7 4
      yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/alipay/AlipayAppPayClient.java
  8. 8 4
      yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/alipay/AlipayBarPayClient.java
  9. 7 4
      yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/alipay/AlipayPcPayClient.java
  10. 7 4
      yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/alipay/AlipayQrPayClient.java
  11. 7 5
      yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/alipay/AlipayWapPayClient.java
  12. 51 76
      yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/weixin/AbstractWxPayClient.java
  13. 3 3
      yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/weixin/WxAppPayClient.java
  14. 11 19
      yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/weixin/WxBarPayClient.java
  15. 3 3
      yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/weixin/WxH5PayClient.java
  16. 7 7
      yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/weixin/WxNativePayClient.java
  17. 8 8
      yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/weixin/WxPubPayClient.java
  18. 0 16
      yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/enums/PayFrameworkErrorCodeConstants.java
  19. 1 2
      yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/enums/order/PayOrderDisplayModeEnum.java
  20. 10 0
      yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/enums/order/PayOrderStatusRespEnum.java
  21. 1 1
      yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/aftersale/TradeAfterSaleServiceImpl.java
  22. 2 2
      yudao-module-pay/yudao-module-pay-api/src/main/java/cn/iocoder/yudao/module/pay/api/refund/PayRefundApi.java
  23. 1 0
      yudao-module-pay/yudao-module-pay-api/src/main/java/cn/iocoder/yudao/module/pay/enums/ErrorCodeConstants.java
  24. 2 2
      yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/api/refund/PayRefundApiImpl.java
  25. 2 2
      yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/convert/order/PayOrderConvert.java
  26. 13 3
      yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/dal/dataobject/order/PayOrderExtensionDO.java
  27. 3 2
      yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/dal/dataobject/refund/PayRefundDO.java
  28. 2 2
      yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/service/demo/PayDemoOrderServiceImpl.java
  29. 51 14
      yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/service/order/PayOrderServiceImpl.java

+ 1 - 2
yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/PayClient.java

@@ -2,7 +2,6 @@ package cn.iocoder.yudao.framework.pay.core.client;
 
 
 import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderRespDTO;
 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.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.PayRefundRespDTO;
 import cn.iocoder.yudao.framework.pay.core.client.dto.refund.PayRefundUnifiedReqDTO;
 import cn.iocoder.yudao.framework.pay.core.client.dto.refund.PayRefundUnifiedReqDTO;
 
 
@@ -30,7 +29,7 @@ public interface PayClient {
      * @param reqDTO 下单信息
      * @param reqDTO 下单信息
      * @return 各支付渠道的返回结果
      * @return 各支付渠道的返回结果
      */
      */
-    PayOrderUnifiedRespDTO unifiedOrder(PayOrderUnifiedReqDTO reqDTO);
+    PayOrderRespDTO unifiedOrder(PayOrderUnifiedReqDTO reqDTO);
 
 
     /**
     /**
      * 解析 order 回调数据
      * 解析 order 回调数据

+ 89 - 7
yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/dto/order/PayOrderRespDTO.java

@@ -1,10 +1,9 @@
 package cn.iocoder.yudao.framework.pay.core.client.dto.order;
 package cn.iocoder.yudao.framework.pay.core.client.dto.order;
 
 
+import cn.iocoder.yudao.framework.pay.core.client.exception.PayException;
+import cn.iocoder.yudao.framework.pay.core.enums.order.PayOrderDisplayModeEnum;
 import cn.iocoder.yudao.framework.pay.core.enums.order.PayOrderStatusRespEnum;
 import cn.iocoder.yudao.framework.pay.core.enums.order.PayOrderStatusRespEnum;
-import lombok.AllArgsConstructor;
-import lombok.Builder;
 import lombok.Data;
 import lombok.Data;
-import lombok.NoArgsConstructor;
 
 
 import java.time.LocalDateTime;
 import java.time.LocalDateTime;
 
 
@@ -14,9 +13,6 @@ import java.time.LocalDateTime;
  * @author 芋道源码
  * @author 芋道源码
  */
  */
 @Data
 @Data
-@Builder
-@NoArgsConstructor
-@AllArgsConstructor
 public class PayOrderRespDTO {
 public class PayOrderRespDTO {
 
 
     /**
     /**
@@ -48,8 +44,94 @@ public class PayOrderRespDTO {
     private LocalDateTime successTime;
     private LocalDateTime successTime;
 
 
     /**
     /**
-     * 原始的异步通知结果
+     * 原始的同步/异步通知结果
      */
      */
     private Object rawData;
     private Object rawData;
 
 
+    // ========== 主动发起支付时,会返回的字段 ==========
+
+    /**
+     * 展示模式
+     *
+     * 枚举 {@link PayOrderDisplayModeEnum} 类
+     */
+    private String displayMode;
+    /**
+     * 展示内容
+     */
+    private String displayContent;
+
+    /**
+     * 调用渠道的错误码
+     *
+     * 注意:这里返回的是业务异常,而是不系统异常。
+     * 如果是系统异常,则会抛出 {@link PayException}
+     */
+    private String channelErrorCode;
+    /**
+     * 调用渠道报错时,错误信息
+     */
+    private String channelErrorMsg;
+
+    public PayOrderRespDTO() {
+    }
+
+    /**
+     * 创建【WAITING】状态的订单返回
+     */
+    public PayOrderRespDTO(String displayMode, String displayContent,
+                           String outTradeNo, Object rawData) {
+        this.status = PayOrderStatusRespEnum.WAITING.getStatus();
+        this.displayMode = displayMode;
+        this.displayContent = displayContent;
+        // 相对通用的字段
+        this.outTradeNo = outTradeNo;
+        this.rawData = rawData;
+    }
+
+    /**
+     * 创建【SUCCESS】状态的订单返回
+     */
+    public PayOrderRespDTO(String channelOrderNo, String channelUserId, LocalDateTime successTime,
+                           String outTradeNo, Object rawData) {
+        this.status = PayOrderStatusRespEnum.SUCCESS.getStatus();
+        this.channelOrderNo = channelOrderNo;
+        this.channelUserId = channelUserId;
+        this.successTime = successTime;
+        // 相对通用的字段
+        this.outTradeNo = outTradeNo;
+        this.rawData = rawData;
+    }
+
+    /**
+     * 创建【SUCCESS】或【CLOSED】状态的订单返回,适合支付渠道回调时
+     */
+    public PayOrderRespDTO(Integer status, String channelOrderNo, String channelUserId, LocalDateTime successTime,
+                           String outTradeNo, Object rawData) {
+        this.status = status;
+        this.channelOrderNo = channelOrderNo;
+        this.channelUserId = channelUserId;
+        this.successTime = successTime;
+        // 相对通用的字段
+        this.outTradeNo = outTradeNo;
+        this.rawData = rawData;
+    }
+
+    /**
+     * 创建【CLOSED】状态的订单返回,适合调用支付渠道失败时
+     *
+     * 参数和 {@link #PayOrderRespDTO(String, String, String, Object)} 冲突,所以独立个方法出来
+     */
+    public static PayOrderRespDTO build(String channelErrorCode, String channelErrorMsg,
+                                        String outTradeNo, Object rawData) {
+        PayOrderRespDTO respDTO = new PayOrderRespDTO();
+        respDTO.status = PayOrderStatusRespEnum.CLOSED.getStatus();
+        respDTO.channelErrorCode = channelErrorCode;
+        respDTO.channelErrorMsg = channelErrorMsg;
+        // 相对通用的字段
+        respDTO.outTradeNo = outTradeNo;
+        respDTO.rawData = rawData;
+        return respDTO;
+    }
+
 }
 }

+ 0 - 1
yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/dto/order/PayOrderUnifiedReqDTO.java

@@ -43,7 +43,6 @@ public class PayOrderUnifiedReqDTO {
     /**
     /**
      * 商品描述信息
      * 商品描述信息
      */
      */
-    @NotEmpty(message = "商品描述信息不能为空")
     @Length(max = 128, message = "商品描述信息长度不能超过128")
     @Length(max = 128, message = "商品描述信息长度不能超过128")
     private String body;
     private String body;
     /**
     /**

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

@@ -1,38 +0,0 @@
-package cn.iocoder.yudao.framework.pay.core.client.dto.order;
-
-import cn.iocoder.yudao.framework.pay.core.enums.order.PayOrderDisplayModeEnum;
-import lombok.Data;
-
-/**
- * 统一下单 Response DTO
- *
- * @author 芋道源码
- */
-@Data
-public class PayOrderUnifiedRespDTO {
-
-    /**
-     * 展示模式
-     *
-     * 枚举 {@link PayOrderDisplayModeEnum} 类
-     */
-    private String displayMode;
-    /**
-     * 展示内容
-     */
-    private String displayContent;
-
-    /**
-     * 渠道支付订单
-     *
-     * 只有在订单直接支付成功时,才会进行返回。
-     * 目前只有 bar 条码支付才会出现,它是支付发起时,直接返回是否支付成功的,而其它支付还是异步通知
-     */
-    private PayOrderRespDTO order;
-
-    public PayOrderUnifiedRespDTO(String displayMode, String displayContent) {
-        this.displayMode = displayMode;
-        this.displayContent = displayContent;
-    }
-
-}

+ 29 - 15
yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/AbstractPayClient.java

@@ -1,16 +1,17 @@
 package cn.iocoder.yudao.framework.pay.core.client.impl;
 package cn.iocoder.yudao.framework.pay.core.client.impl;
 
 
 import cn.iocoder.yudao.framework.common.exception.ServiceException;
 import cn.iocoder.yudao.framework.common.exception.ServiceException;
+import cn.iocoder.yudao.framework.common.util.validation.ValidationUtils;
 import cn.iocoder.yudao.framework.pay.core.client.PayClient;
 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.PayClientConfig;
+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.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.PayRefundRespDTO;
 import cn.iocoder.yudao.framework.pay.core.client.dto.refund.PayRefundUnifiedReqDTO;
 import cn.iocoder.yudao.framework.pay.core.client.dto.refund.PayRefundUnifiedReqDTO;
 import cn.iocoder.yudao.framework.pay.core.client.exception.PayException;
 import cn.iocoder.yudao.framework.pay.core.client.exception.PayException;
 import lombok.extern.slf4j.Slf4j;
 import lombok.extern.slf4j.Slf4j;
 
 
-import javax.validation.Validation;
+import java.util.Map;
 
 
 import static cn.iocoder.yudao.framework.common.util.json.JsonUtils.toJsonString;
 import static cn.iocoder.yudao.framework.common.util.json.JsonUtils.toJsonString;
 
 
@@ -29,6 +30,7 @@ public abstract class AbstractPayClient<Config extends PayClientConfig> implemen
     /**
     /**
      * 渠道编码
      * 渠道编码
      */
      */
+    @SuppressWarnings("FieldCanBeLocal")
     private final String channelCode;
     private final String channelCode;
     /**
     /**
      * 支付配置
      * 支付配置
@@ -73,31 +75,42 @@ public abstract class AbstractPayClient<Config extends PayClientConfig> implemen
     // ============ 支付相关 ==========
     // ============ 支付相关 ==========
 
 
     @Override
     @Override
-    public final PayOrderUnifiedRespDTO unifiedOrder(PayOrderUnifiedReqDTO reqDTO) {
-        Validation.buildDefaultValidatorFactory().getValidator().validate(reqDTO);
+    public final PayOrderRespDTO unifiedOrder(PayOrderUnifiedReqDTO reqDTO) {
+        ValidationUtils.validate(reqDTO);
         // 执行统一下单
         // 执行统一下单
-        PayOrderUnifiedRespDTO resp;
+        PayOrderRespDTO resp;
         try {
         try {
             resp = doUnifiedOrder(reqDTO);
             resp = doUnifiedOrder(reqDTO);
-        } catch (ServiceException ex) {
-            // 业务异常,都是实现类已经翻译,所以直接抛出即可
-            throw ex;
         } catch (Throwable ex) {
         } catch (Throwable ex) {
             // 系统异常,则包装成 PayException 异常抛出
             // 系统异常,则包装成 PayException 异常抛出
-            log.error("[unifiedRefund][request({}) 发起支付异常]", toJsonString(reqDTO), ex);
-            throw buildException(ex);
+            log.error("[unifiedRefund][客户端({}) request({}) 发起支付异常]",
+                    getId(), toJsonString(reqDTO), ex);
+            throw buildPayException(ex);
         }
         }
         return resp;
         return resp;
     }
     }
 
 
-    protected abstract PayOrderUnifiedRespDTO doUnifiedOrder(PayOrderUnifiedReqDTO reqDTO)
+    protected abstract PayOrderRespDTO doUnifiedOrder(PayOrderUnifiedReqDTO reqDTO)
+            throws Throwable;
+
+    @Override
+    public PayOrderRespDTO parseOrderNotify(Map<String, String> params, String body) {
+        try {
+            return doParseOrderNotify(params, body);
+        } catch (Throwable ex) {
+            log.error("[parseOrderNotify][params({}) body({}) 解析失败]", params, body, ex);
+            throw buildPayException(ex);
+        }
+    }
+
+    protected abstract PayOrderRespDTO doParseOrderNotify(Map<String, String> params, String body)
             throws Throwable;
             throws Throwable;
 
 
     // ============ 退款相关 ==========
     // ============ 退款相关 ==========
 
 
     @Override
     @Override
     public PayRefundRespDTO unifiedRefund(PayRefundUnifiedReqDTO reqDTO) {
     public PayRefundRespDTO unifiedRefund(PayRefundUnifiedReqDTO reqDTO) {
-        Validation.buildDefaultValidatorFactory().getValidator().validate(reqDTO);
+        ValidationUtils.validate(reqDTO);
         // 执行统一退款
         // 执行统一退款
         PayRefundRespDTO resp;
         PayRefundRespDTO resp;
         try {
         try {
@@ -107,8 +120,9 @@ public abstract class AbstractPayClient<Config extends PayClientConfig> implemen
             throw ex;
             throw ex;
         } catch (Throwable ex) {
         } catch (Throwable ex) {
             // 系统异常,则包装成 PayException 异常抛出
             // 系统异常,则包装成 PayException 异常抛出
-            log.error("[unifiedRefund][request({}) 发起退款异常]", toJsonString(reqDTO), ex);
-            throw buildException(ex);
+            log.error("[unifiedRefund][客户端({}) request({}) 发起退款异常]",
+                    getId(), toJsonString(reqDTO), ex);
+            throw buildPayException(ex);
         }
         }
         return resp;
         return resp;
     }
     }
@@ -117,7 +131,7 @@ public abstract class AbstractPayClient<Config extends PayClientConfig> implemen
 
 
     // ========== 各种工具方法 ==========
     // ========== 各种工具方法 ==========
 
 
-    private PayException buildException(Throwable ex) {
+    private PayException buildPayException(Throwable ex) {
         if (ex instanceof PayException) {
         if (ex instanceof PayException) {
             return (PayException) ex;
             return (PayException) ex;
         }
         }

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

@@ -5,8 +5,8 @@ import cn.hutool.core.date.LocalDateTimeUtil;
 import cn.hutool.core.lang.Assert;
 import cn.hutool.core.lang.Assert;
 import cn.hutool.core.util.StrUtil;
 import cn.hutool.core.util.StrUtil;
 import cn.hutool.http.HttpUtil;
 import cn.hutool.http.HttpUtil;
-import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
 import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderRespDTO;
 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.refund.PayRefundRespDTO;
 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.PayRefundUnifiedReqDTO;
 import cn.iocoder.yudao.framework.pay.core.client.impl.AbstractPayClient;
 import cn.iocoder.yudao.framework.pay.core.client.impl.AbstractPayClient;
@@ -29,9 +29,7 @@ import java.util.Objects;
 import java.util.function.Supplier;
 import java.util.function.Supplier;
 
 
 import static cn.hutool.core.date.DatePattern.NORM_DATETIME_FORMATTER;
 import static cn.hutool.core.date.DatePattern.NORM_DATETIME_FORMATTER;
-import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception0;
 import static cn.iocoder.yudao.framework.common.util.json.JsonUtils.toJsonString;
 import static cn.iocoder.yudao.framework.common.util.json.JsonUtils.toJsonString;
-import static cn.iocoder.yudao.framework.pay.core.enums.PayFrameworkErrorCodeConstants.ORDER_UNIFIED_ERROR;
 
 
 /**
 /**
  * 支付宝抽象类,实现支付宝统一的接口、以及部分实现(退款)
  * 支付宝抽象类,实现支付宝统一的接口、以及部分实现(退款)
@@ -55,6 +53,40 @@ public abstract class AbstractAlipayPayClient extends AbstractPayClient<AlipayPa
         this.client = new DefaultAlipayClient(alipayConfig);
         this.client = new DefaultAlipayClient(alipayConfig);
     }
     }
 
 
+    // ============ 支付相关 ==========
+
+    /**
+     * 构造支付关闭的 {@link PayOrderRespDTO} 对象
+     *
+     * @return 支付关闭的 {@link PayOrderRespDTO} 对象
+     */
+    protected PayOrderRespDTO buildClosedPayOrderRespDTO(PayOrderUnifiedReqDTO reqDTO, AlipayResponse response) {
+        Assert.isFalse(response.isSuccess());
+        return PayOrderRespDTO.build(response.getSubCode(), response.getSubMsg(),
+                reqDTO.getOutTradeNo(), response);
+    }
+
+    @Override
+    public PayOrderRespDTO doParseOrderNotify(Map<String, String> params, String body) throws Throwable {
+        // 1. 校验回调数据
+        Map<String, String> bodyObj = HttpUtil.decodeParamMap(body, StandardCharsets.UTF_8);
+        AlipaySignature.rsaCheckV1(bodyObj, config.getAlipayPublicKey(),
+                StandardCharsets.UTF_8.name(), config.getSignType());
+
+        // 2. 解析订单的状态
+        String tradeStatus = bodyObj.get("trade_status");
+        Integer status = Objects.equals("WAIT_BUYER_PAY", tradeStatus) ? PayOrderStatusRespEnum.WAITING.getStatus()
+                : Objects.equals("TRADE_SUCCESS", tradeStatus) ? PayOrderStatusRespEnum.SUCCESS.getStatus()
+                : Objects.equals("TRADE_CLOSED", tradeStatus) ? PayOrderStatusRespEnum.CLOSED.getStatus() : null;
+        Assert.notNull(status, (Supplier<Throwable>) () -> {
+            throw new IllegalArgumentException(StrUtil.format("body({}) 的 trade_status 不正确", body));
+        });
+        return new PayOrderRespDTO(status, bodyObj.get("trade_no"), bodyObj.get("seller_id"), parseTime(params.get("gmt_payment")),
+                bodyObj.get("out_trade_no"), body);
+    }
+
+    // ============ 退款相关 ==========
+
     /**
     /**
      * 支付宝统一的退款接口 alipay.trade.refund
      * 支付宝统一的退款接口 alipay.trade.refund
      *
      *
@@ -95,32 +127,6 @@ public abstract class AbstractAlipayPayClient extends AbstractPayClient<AlipayPa
         }
         }
     }
     }
 
 
-    @Override
-    @SneakyThrows
-    public PayOrderRespDTO parseOrderNotify(Map<String, String> params, String body) {
-        // 1. 校验回调数据
-        Map<String, String> bodyObj = HttpUtil.decodeParamMap(body, StandardCharsets.UTF_8);
-        AlipaySignature.rsaCheckV1(bodyObj, config.getAlipayPublicKey(),
-                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("gmt_payment")))
-                .rawData(body)
-                .build();
-    }
-
     @Override
     @Override
     public PayRefundRespDTO parseRefundNotify(Map<String, String> params, String body) {
     public PayRefundRespDTO parseRefundNotify(Map<String, String> params, String body) {
         // 补充说明:支付宝退款时,没有回调,这点和微信支付是不同的。并且,退款分成部分退款、和全部退款。
         // 补充说明:支付宝退款时,没有回调,这点和微信支付是不同的。并且,退款分成部分退款、和全部退款。
@@ -145,21 +151,4 @@ public abstract class AbstractAlipayPayClient extends AbstractPayClient<AlipayPa
         return LocalDateTimeUtil.parse(str, NORM_DATETIME_FORMATTER);
         return LocalDateTimeUtil.parse(str, NORM_DATETIME_FORMATTER);
     }
     }
 
 
-    /**
-     * 校验支付宝统一下单的响应
-     *
-     * 如果校验不通过,则抛出 {@link cn.iocoder.yudao.framework.common.exception.ServiceException} 异常
-     *
-     * @param request 请求
-     * @param response 响应
-     */
-    protected void validateUnifiedOrderResponse(Object request, AlipayResponse response) {
-        if (response.isSuccess()) {
-            return;
-        }
-        log.error("[validateUnifiedOrderResponse][发起支付失败,request({}),response({})]",
-                JsonUtils.toJsonString(request), JsonUtils.toJsonString(response));
-        throw exception0(ORDER_UNIFIED_ERROR.getCode(), response.getSubMsg());
-    }
-
 }
 }

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

@@ -1,7 +1,7 @@
 package cn.iocoder.yudao.framework.pay.core.client.impl.alipay;
 package cn.iocoder.yudao.framework.pay.core.client.impl.alipay;
 
 
+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.PayOrderUnifiedReqDTO;
-import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderUnifiedRespDTO;
 import cn.iocoder.yudao.framework.pay.core.enums.channel.PayChannelEnum;
 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.PayOrderDisplayModeEnum;
 import com.alipay.api.AlipayApiException;
 import com.alipay.api.AlipayApiException;
@@ -27,7 +27,7 @@ public class AlipayAppPayClient extends AbstractAlipayPayClient {
     }
     }
 
 
     @Override
     @Override
-    public PayOrderUnifiedRespDTO doUnifiedOrder(PayOrderUnifiedReqDTO reqDTO) throws AlipayApiException {
+    public PayOrderRespDTO doUnifiedOrder(PayOrderUnifiedReqDTO reqDTO) throws AlipayApiException {
         // 1.1 构建 AlipayTradeAppPayModel 请求
         // 1.1 构建 AlipayTradeAppPayModel 请求
         AlipayTradeAppPayModel model = new AlipayTradeAppPayModel();
         AlipayTradeAppPayModel model = new AlipayTradeAppPayModel();
         // ① 通用的参数
         // ① 通用的参数
@@ -49,8 +49,11 @@ public class AlipayAppPayClient extends AbstractAlipayPayClient {
         // 2.1 执行请求
         // 2.1 执行请求
         AlipayTradeAppPayResponse response = client.execute(request);
         AlipayTradeAppPayResponse response = client.execute(request);
         // 2.2 处理结果
         // 2.2 处理结果
-        validateUnifiedOrderResponse(request, response);
-        return new PayOrderUnifiedRespDTO(displayMode, "");
+        if (!response.isSuccess()) {
+            return buildClosedPayOrderRespDTO(reqDTO, response);
+        }
+        return new PayOrderRespDTO(displayMode, "",
+                reqDTO.getOutTradeNo(), response);
     }
     }
 
 
 }
 }

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

@@ -2,8 +2,8 @@ package cn.iocoder.yudao.framework.pay.core.client.impl.alipay;
 
 
 import cn.hutool.core.map.MapUtil;
 import cn.hutool.core.map.MapUtil;
 import cn.hutool.core.util.StrUtil;
 import cn.hutool.core.util.StrUtil;
+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.PayOrderUnifiedReqDTO;
-import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderUnifiedRespDTO;
 import cn.iocoder.yudao.framework.pay.core.enums.channel.PayChannelEnum;
 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.PayOrderDisplayModeEnum;
 import com.alipay.api.AlipayApiException;
 import com.alipay.api.AlipayApiException;
@@ -30,7 +30,7 @@ public class AlipayBarPayClient extends AbstractAlipayPayClient {
     }
     }
 
 
     @Override
     @Override
-    public PayOrderUnifiedRespDTO doUnifiedOrder(PayOrderUnifiedReqDTO reqDTO) throws AlipayApiException {
+    public PayOrderRespDTO doUnifiedOrder(PayOrderUnifiedReqDTO reqDTO) throws AlipayApiException {
         String authCode = MapUtil.getStr(reqDTO.getChannelExtras(), "auth_code");
         String authCode = MapUtil.getStr(reqDTO.getChannelExtras(), "auth_code");
         if (StrUtil.isEmpty(authCode)) {
         if (StrUtil.isEmpty(authCode)) {
             throw exception0(BAD_REQUEST.getCode(), "条形码不能为空");
             throw exception0(BAD_REQUEST.getCode(), "条形码不能为空");
@@ -55,11 +55,15 @@ public class AlipayBarPayClient extends AbstractAlipayPayClient {
         request.setNotifyUrl(reqDTO.getNotifyUrl());
         request.setNotifyUrl(reqDTO.getNotifyUrl());
         request.setReturnUrl(reqDTO.getReturnUrl());
         request.setReturnUrl(reqDTO.getReturnUrl());
 
 
+        // TODO 芋艿:各种边界的处理
         // 2.1 执行请求
         // 2.1 执行请求
         AlipayTradePayResponse response = client.execute(request);
         AlipayTradePayResponse response = client.execute(request);
         // 2.2 处理结果
         // 2.2 处理结果
-        validateUnifiedOrderResponse(request, response);
-        return new PayOrderUnifiedRespDTO(displayMode, "");
+        if (!response.isSuccess()) {
+            return buildClosedPayOrderRespDTO(reqDTO, response);
+        }
+        return new PayOrderRespDTO(displayMode, "",
+                reqDTO.getOutTradeNo(), response);
     }
     }
 
 
 }
 }

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

@@ -2,8 +2,8 @@ package cn.iocoder.yudao.framework.pay.core.client.impl.alipay;
 
 
 import cn.hutool.core.util.ObjectUtil;
 import cn.hutool.core.util.ObjectUtil;
 import cn.hutool.http.Method;
 import cn.hutool.http.Method;
+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.PayOrderUnifiedReqDTO;
-import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderUnifiedRespDTO;
 import cn.iocoder.yudao.framework.pay.core.enums.channel.PayChannelEnum;
 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.PayOrderDisplayModeEnum;
 import com.alipay.api.AlipayApiException;
 import com.alipay.api.AlipayApiException;
@@ -29,7 +29,7 @@ public class AlipayPcPayClient extends AbstractAlipayPayClient {
     }
     }
 
 
     @Override
     @Override
-    public PayOrderUnifiedRespDTO doUnifiedOrder(PayOrderUnifiedReqDTO reqDTO) throws AlipayApiException {
+    public PayOrderRespDTO doUnifiedOrder(PayOrderUnifiedReqDTO reqDTO) throws AlipayApiException {
         // 1.1 构建 AlipayTradePagePayModel 请求
         // 1.1 构建 AlipayTradePagePayModel 请求
         AlipayTradePagePayModel model = new AlipayTradePagePayModel();
         AlipayTradePagePayModel model = new AlipayTradePagePayModel();
         // ① 通用的参数
         // ① 通用的参数
@@ -60,8 +60,11 @@ public class AlipayPcPayClient extends AbstractAlipayPayClient {
             response = client.pageExecute(request, Method.GET.name());
             response = client.pageExecute(request, Method.GET.name());
         }
         }
         // 2.2 处理结果
         // 2.2 处理结果
-        validateUnifiedOrderResponse(request, response);
-        return new PayOrderUnifiedRespDTO(displayMode, response.getBody());
+        if (!response.isSuccess()) {
+            return buildClosedPayOrderRespDTO(reqDTO, response);
+        }
+        return new PayOrderRespDTO(displayMode, response.getBody(),
+                reqDTO.getOutTradeNo(), response);
     }
     }
 
 
 }
 }

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

@@ -1,7 +1,7 @@
 package cn.iocoder.yudao.framework.pay.core.client.impl.alipay;
 package cn.iocoder.yudao.framework.pay.core.client.impl.alipay;
 
 
+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.PayOrderUnifiedReqDTO;
-import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderUnifiedRespDTO;
 import cn.iocoder.yudao.framework.pay.core.enums.channel.PayChannelEnum;
 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.PayOrderDisplayModeEnum;
 import com.alipay.api.AlipayApiException;
 import com.alipay.api.AlipayApiException;
@@ -25,7 +25,7 @@ public class AlipayQrPayClient extends AbstractAlipayPayClient {
     }
     }
 
 
     @Override
     @Override
-    public PayOrderUnifiedRespDTO doUnifiedOrder(PayOrderUnifiedReqDTO reqDTO) throws AlipayApiException {
+    public PayOrderRespDTO doUnifiedOrder(PayOrderUnifiedReqDTO reqDTO) throws AlipayApiException {
         // 1.1 构建 AlipayTradePrecreateModel 请求
         // 1.1 构建 AlipayTradePrecreateModel 请求
         AlipayTradePrecreateModel model = new AlipayTradePrecreateModel();
         AlipayTradePrecreateModel model = new AlipayTradePrecreateModel();
         // ① 通用的参数
         // ① 通用的参数
@@ -47,8 +47,11 @@ public class AlipayQrPayClient extends AbstractAlipayPayClient {
         // 2.1 执行请求
         // 2.1 执行请求
         AlipayTradePrecreateResponse response = client.execute(request);
         AlipayTradePrecreateResponse response = client.execute(request);
         // 2.2 处理结果
         // 2.2 处理结果
-        validateUnifiedOrderResponse(request, response);
-        return new PayOrderUnifiedRespDTO(displayMode, response.getQrCode());
+        if (!response.isSuccess()) {
+            return buildClosedPayOrderRespDTO(reqDTO, response);
+        }
+        return new PayOrderRespDTO(displayMode, response.getQrCode(),
+                reqDTO.getOutTradeNo(), response);
     }
     }
 
 
 }
 }

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

@@ -1,8 +1,8 @@
 package cn.iocoder.yudao.framework.pay.core.client.impl.alipay;
 package cn.iocoder.yudao.framework.pay.core.client.impl.alipay;
 
 
 import cn.hutool.http.Method;
 import cn.hutool.http.Method;
+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.PayOrderUnifiedReqDTO;
-import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderUnifiedRespDTO;
 import cn.iocoder.yudao.framework.pay.core.enums.channel.PayChannelEnum;
 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.PayOrderDisplayModeEnum;
 import com.alipay.api.AlipayApiException;
 import com.alipay.api.AlipayApiException;
@@ -26,7 +26,7 @@ public class AlipayWapPayClient extends AbstractAlipayPayClient {
     }
     }
 
 
     @Override
     @Override
-    public PayOrderUnifiedRespDTO doUnifiedOrder(PayOrderUnifiedReqDTO reqDTO) throws AlipayApiException {
+    public PayOrderRespDTO doUnifiedOrder(PayOrderUnifiedReqDTO reqDTO) throws AlipayApiException {
         // 1.1 构建 AlipayTradeWapPayModel 请求
         // 1.1 构建 AlipayTradeWapPayModel 请求
         AlipayTradeWapPayModel model = new AlipayTradeWapPayModel();
         AlipayTradeWapPayModel model = new AlipayTradeWapPayModel();
         // ① 通用的参数
         // ① 通用的参数
@@ -48,10 +48,12 @@ public class AlipayWapPayClient extends AbstractAlipayPayClient {
 
 
         // 2.1 执行请求
         // 2.1 执行请求
         AlipayTradeWapPayResponse response = client.pageExecute(request, Method.GET.name());
         AlipayTradeWapPayResponse response = client.pageExecute(request, Method.GET.name());
-
         // 2.2 处理结果
         // 2.2 处理结果
-        validateUnifiedOrderResponse(request, response);
-        return new PayOrderUnifiedRespDTO(displayMode, response.getBody());
+        if (!response.isSuccess()) {
+            return buildClosedPayOrderRespDTO(reqDTO, response);
+        }
+        return new PayOrderRespDTO(displayMode, response.getBody(),
+                reqDTO.getOutTradeNo(), response);
     }
     }
 
 
 }
 }

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

@@ -8,11 +8,9 @@ import cn.hutool.core.util.StrUtil;
 import cn.iocoder.yudao.framework.common.util.io.FileUtils;
 import cn.iocoder.yudao.framework.common.util.io.FileUtils;
 import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderRespDTO;
 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.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.PayRefundRespDTO;
 import cn.iocoder.yudao.framework.pay.core.client.dto.refund.PayRefundUnifiedReqDTO;
 import cn.iocoder.yudao.framework.pay.core.client.dto.refund.PayRefundUnifiedReqDTO;
 import cn.iocoder.yudao.framework.pay.core.client.impl.AbstractPayClient;
 import cn.iocoder.yudao.framework.pay.core.client.impl.AbstractPayClient;
-import cn.iocoder.yudao.framework.pay.core.enums.PayFrameworkErrorCodeConstants;
 import cn.iocoder.yudao.framework.pay.core.enums.order.PayOrderStatusRespEnum;
 import cn.iocoder.yudao.framework.pay.core.enums.order.PayOrderStatusRespEnum;
 import cn.iocoder.yudao.framework.pay.core.enums.refund.PayRefundStatusRespEnum;
 import cn.iocoder.yudao.framework.pay.core.enums.refund.PayRefundStatusRespEnum;
 import com.github.binarywang.wxpay.bean.notify.WxPayOrderNotifyResult;
 import com.github.binarywang.wxpay.bean.notify.WxPayOrderNotifyResult;
@@ -35,9 +33,6 @@ import java.util.Map;
 import java.util.Objects;
 import java.util.Objects;
 
 
 import static cn.hutool.core.date.DatePattern.*;
 import static cn.hutool.core.date.DatePattern.*;
-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;
 import static cn.iocoder.yudao.framework.pay.core.client.impl.weixin.WxPayClientConfig.API_VERSION_V2;
 import static cn.iocoder.yudao.framework.pay.core.client.impl.weixin.WxPayClientConfig.API_VERSION_V2;
 
 
 /**
 /**
@@ -83,7 +78,7 @@ public abstract class AbstractWxPayClient extends AbstractPayClient<WxPayClientC
     // ============ 支付相关 ==========
     // ============ 支付相关 ==========
 
 
     @Override
     @Override
-    protected PayOrderUnifiedRespDTO doUnifiedOrder(PayOrderUnifiedReqDTO reqDTO) throws Exception {
+    protected PayOrderRespDTO doUnifiedOrder(PayOrderUnifiedReqDTO reqDTO) throws Exception {
         try {
         try {
             switch (config.getApiVersion()) {
             switch (config.getApiVersion()) {
                 case API_VERSION_V2:
                 case API_VERSION_V2:
@@ -94,8 +89,10 @@ public abstract class AbstractWxPayClient extends AbstractPayClient<WxPayClientC
                     throw new IllegalArgumentException(String.format("未知的 API 版本(%s)", config.getApiVersion()));
                     throw new IllegalArgumentException(String.format("未知的 API 版本(%s)", config.getApiVersion()));
             }
             }
         } catch (WxPayException e) {
         } catch (WxPayException e) {
-            // todo 芋艿:异常的处理;
-            throw buildUnifiedOrderException(reqDTO, e);
+            String errorCode = getErrorCode(e);
+            String errorMessage = getErrorMessage(e);
+            return PayOrderRespDTO.build(errorCode, errorMessage,
+                    reqDTO.getOutTradeNo(), e.getXmlString());
         }
         }
     }
     }
 
 
@@ -105,8 +102,8 @@ public abstract class AbstractWxPayClient extends AbstractPayClient<WxPayClientC
      * @param reqDTO 下单信息
      * @param reqDTO 下单信息
      * @return 各支付渠道的返回结果
      * @return 各支付渠道的返回结果
      */
      */
-    protected abstract PayOrderUnifiedRespDTO doUnifiedOrderV2(PayOrderUnifiedReqDTO reqDTO)
-            throws WxPayException;
+    protected abstract PayOrderRespDTO doUnifiedOrderV2(PayOrderUnifiedReqDTO reqDTO)
+            throws Exception;
 
 
     /**
     /**
      * 【V3】调用支付渠道,统一下单
      * 【V3】调用支付渠道,统一下单
@@ -114,57 +111,42 @@ public abstract class AbstractWxPayClient extends AbstractPayClient<WxPayClientC
      * @param reqDTO 下单信息
      * @param reqDTO 下单信息
      * @return 各支付渠道的返回结果
      * @return 各支付渠道的返回结果
      */
      */
-    protected abstract PayOrderUnifiedRespDTO doUnifiedOrderV3(PayOrderUnifiedReqDTO reqDTO)
+    protected abstract PayOrderRespDTO doUnifiedOrderV3(PayOrderUnifiedReqDTO reqDTO)
             throws WxPayException;
             throws WxPayException;
 
 
     @Override
     @Override
-    public PayOrderRespDTO parseOrderNotify(Map<String, String> params, String body) {
-        try {
-            // 微信支付 v2 回调结果处理
-            switch (config.getApiVersion()) {
-                case API_VERSION_V2:
-                    return parseOrderNotifyV2(body);
-                case WxPayClientConfig.API_VERSION_V3:
-                    return parseOrderNotifyV3(body);
-                default:
-                    throw new IllegalArgumentException(String.format("未知的 API 版本(%s)", config.getApiVersion()));
-            }
-        } catch (WxPayException e) {
-            log.error("[parseNotify][params({}) body({}) 解析失败]", params, body, e);
-//            throw buildPayException(e);
-            throw new RuntimeException(e);
-            // TODO 芋艿:缺一个异常翻译
+    public PayOrderRespDTO doParseOrderNotify(Map<String, String> params, String body) throws WxPayException {
+        // 微信支付 v2 回调结果处理
+        switch (config.getApiVersion()) {
+            case API_VERSION_V2:
+                return doParseOrderNotifyV2(body);
+            case WxPayClientConfig.API_VERSION_V3:
+                return doParseOrderNotifyV3(body);
+            default:
+                throw new IllegalArgumentException(String.format("未知的 API 版本(%s)", config.getApiVersion()));
         }
         }
     }
     }
 
 
-    private PayOrderRespDTO parseOrderNotifyV2(String body) throws WxPayException {
+    private PayOrderRespDTO doParseOrderNotifyV2(String body) throws WxPayException {
         // 1. 解析回调
         // 1. 解析回调
         WxPayOrderNotifyResult response = client.parseOrderNotifyResult(body);
         WxPayOrderNotifyResult response = client.parseOrderNotifyResult(body);
         // 2. 构建结果
         // 2. 构建结果
-        return PayOrderRespDTO.builder()
-                .outTradeNo(response.getOutTradeNo())
-                .channelOrderNo(response.getTransactionId())
-                .channelUserId(response.getOpenid())
-                .status(Objects.equals(response.getResultCode(), "SUCCESS") ?
-                        PayOrderStatusRespEnum.SUCCESS.getStatus() : PayOrderStatusRespEnum.CLOSED.getStatus())
-                .successTime(parseDateV2(response.getTimeEnd()))
-                .rawData(response)
-                .build();
+        Integer status = Objects.equals(response.getResultCode(), "SUCCESS") ?
+                PayOrderStatusRespEnum.SUCCESS.getStatus() : PayOrderStatusRespEnum.CLOSED.getStatus();
+        return new PayOrderRespDTO(status, response.getTransactionId(), response.getOpenid(), parseDateV2(response.getTimeEnd()),
+                response.getOutTradeNo(), body);
     }
     }
 
 
-    private PayOrderRespDTO parseOrderNotifyV3(String body) throws WxPayException {
+    private PayOrderRespDTO doParseOrderNotifyV3(String body) throws WxPayException {
         // 1. 解析回调
         // 1. 解析回调
         WxPayOrderNotifyV3Result response = client.parseOrderNotifyV3Result(body, null);
         WxPayOrderNotifyV3Result response = client.parseOrderNotifyV3Result(body, null);
-        WxPayOrderNotifyV3Result.DecryptNotifyResult responseResult = response.getResult();
+        WxPayOrderNotifyV3Result.DecryptNotifyResult result = response.getResult();
         // 2. 构建结果
         // 2. 构建结果
-        return PayOrderRespDTO.builder()
-                .outTradeNo(responseResult.getOutTradeNo())
-                .channelOrderNo(responseResult.getTradeState())
-                .channelUserId(responseResult.getPayer() != null ? responseResult.getPayer().getOpenid() : null)
-                .status(Objects.equals(responseResult.getTradeState(), "SUCCESS") ?
-                        PayOrderStatusRespEnum.SUCCESS.getStatus() : PayOrderStatusRespEnum.CLOSED.getStatus())
-                .successTime(parseDateV3(responseResult.getSuccessTime()))
-                .build();
+        Integer status = Objects.equals(result.getTradeState(), "SUCCESS") ?
+                PayOrderStatusRespEnum.SUCCESS.getStatus() : PayOrderStatusRespEnum.CLOSED.getStatus();
+        String openid = result.getPayer() != null ? result.getPayer().getOpenid() : null;
+        return new PayOrderRespDTO(status, result.getTransactionId(), openid, parseDateV3(result.getSuccessTime()),
+                result.getOutTradeNo(), body);
     }
     }
 
 
     // ============ 退款相关 ==========
     // ============ 退款相关 ==========
@@ -182,7 +164,8 @@ public abstract class AbstractWxPayClient extends AbstractPayClient<WxPayClientC
             }
             }
         } catch (WxPayException e) {
         } catch (WxPayException e) {
             // todo 芋艿:异常的处理;
             // todo 芋艿:异常的处理;
-            throw buildUnifiedOrderException(null, e);
+//            throw buildUnifiedOrderException(null, e);
+            return null;
         }
         }
     }
     }
 
 
@@ -254,7 +237,6 @@ public abstract class AbstractWxPayClient extends AbstractPayClient<WxPayClientC
             }
             }
         } catch (WxPayException e) {
         } catch (WxPayException e) {
             log.error("[parseNotify][params({}) body({}) 解析失败]", params, body, e);
             log.error("[parseNotify][params({}) body({}) 解析失败]", params, body, e);
-//            throw buildPayException(e);
             throw new RuntimeException(e);
             throw new RuntimeException(e);
             // TODO 芋艿:缺一个异常翻译
             // TODO 芋艿:缺一个异常翻译
         }
         }
@@ -300,33 +282,6 @@ public abstract class AbstractWxPayClient extends AbstractPayClient<WxPayClientC
 
 
     // ========== 各种工具方法 ==========
     // ========== 各种工具方法 ==========
 
 
-    /**
-     * 构建统一下单的异常
-     *
-     * 目的:将参数不正确等异常,转换成 {@link cn.iocoder.yudao.framework.common.exception.ServiceException} 业务异常
-     *
-     * @param reqDTO 请求
-     * @param e 微信的支付异常
-     * @return 转换后的异常
-     *
-     */
-    static Exception buildUnifiedOrderException(PayOrderUnifiedReqDTO reqDTO, WxPayException e) {
-        // 情况一:业务结果为 FAIL
-        if (Objects.equals(e.getResultCode(), "FAIL")) {
-            log.error("[buildUnifiedOrderException][request({}) 发起支付失败]", toJsonString(reqDTO), e);
-            if (Objects.equals(e.getErrCode(), "PARAM_ERROR")) {
-                throw invalidParamException(e.getErrCodeDes());
-            }
-            throw exception(PayFrameworkErrorCodeConstants.ORDER_UNIFIED_ERROR, e.getErrCodeDes());
-        }
-        // 情况二:状态码结果为 FAIL
-        if (Objects.equals(e.getReturnCode(), "FAIL")) {
-            throw exception(PayFrameworkErrorCodeConstants.ORDER_UNIFIED_ERROR, e.getReturnMsg());
-        }
-        // 情况三:系统异常,这里暂时不打,交给上层的 AbstractPayClient 统一打日志
-        return e;
-    }
-
     static String formatDateV2(LocalDateTime time) {
     static String formatDateV2(LocalDateTime time) {
         return TemporalAccessorUtil.format(time.atZone(ZoneId.systemDefault()), PURE_DATETIME_PATTERN);
         return TemporalAccessorUtil.format(time.atZone(ZoneId.systemDefault()), PURE_DATETIME_PATTERN);
     }
     }
@@ -347,4 +302,24 @@ public abstract class AbstractWxPayClient extends AbstractPayClient<WxPayClientC
         return LocalDateTimeUtil.parse(time, UTC_WITH_XXX_OFFSET_PATTERN);
         return LocalDateTimeUtil.parse(time, UTC_WITH_XXX_OFFSET_PATTERN);
     }
     }
 
 
+    static String getErrorCode(WxPayException e) {
+        if (StrUtil.isNotEmpty(e.getErrCode())) {
+            return e.getErrCode();
+        }
+        if (StrUtil.isNotEmpty(e.getCustomErrorMsg())) {
+            return "CUSTOM_ERROR";
+        }
+        return e.getReturnCode();
+    }
+
+    static String getErrorMessage(WxPayException e) {
+        if (StrUtil.isNotEmpty(e.getErrCode())) {
+            return e.getErrCodeDes();
+        }
+        if (StrUtil.isNotEmpty(e.getCustomErrorMsg())) {
+            return e.getCustomErrorMsg();
+        }
+        return e.getReturnMsg();
+    }
+
 }
 }

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

@@ -1,7 +1,7 @@
 package cn.iocoder.yudao.framework.pay.core.client.impl.weixin;
 package cn.iocoder.yudao.framework.pay.core.client.impl.weixin;
 
 
+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.PayOrderUnifiedReqDTO;
-import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderUnifiedRespDTO;
 import cn.iocoder.yudao.framework.pay.core.enums.channel.PayChannelEnum;
 import cn.iocoder.yudao.framework.pay.core.enums.channel.PayChannelEnum;
 import com.github.binarywang.wxpay.constant.WxPayConstants;
 import com.github.binarywang.wxpay.constant.WxPayConstants;
 import com.github.binarywang.wxpay.exception.WxPayException;
 import com.github.binarywang.wxpay.exception.WxPayException;
@@ -19,12 +19,12 @@ public class WxAppPayClient extends AbstractWxPayClient {
     }
     }
 
 
     @Override
     @Override
-    protected PayOrderUnifiedRespDTO doUnifiedOrderV2(PayOrderUnifiedReqDTO reqDTO) throws WxPayException {
+    protected PayOrderRespDTO doUnifiedOrderV2(PayOrderUnifiedReqDTO reqDTO) throws WxPayException {
         return null;
         return null;
     }
     }
 
 
     @Override
     @Override
-    protected PayOrderUnifiedRespDTO doUnifiedOrderV3(PayOrderUnifiedReqDTO reqDTO) throws WxPayException {
+    protected PayOrderRespDTO doUnifiedOrderV3(PayOrderUnifiedReqDTO reqDTO) throws WxPayException {
         return null;
         return null;
     }
     }
 
 

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

@@ -4,13 +4,10 @@ import cn.hutool.core.map.MapUtil;
 import cn.hutool.core.thread.ThreadUtil;
 import cn.hutool.core.thread.ThreadUtil;
 import cn.hutool.core.util.StrUtil;
 import cn.hutool.core.util.StrUtil;
 import cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils;
 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.order.PayOrderRespDTO;
 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.PayOrderUnifiedReqDTO;
-import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderUnifiedRespDTO;
 import cn.iocoder.yudao.framework.pay.core.enums.channel.PayChannelEnum;
 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.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.request.WxPayMicropayRequest;
 import com.github.binarywang.wxpay.bean.result.WxPayMicropayResult;
 import com.github.binarywang.wxpay.bean.result.WxPayMicropayResult;
 import com.github.binarywang.wxpay.constant.WxPayConstants;
 import com.github.binarywang.wxpay.constant.WxPayConstants;
@@ -22,6 +19,7 @@ import java.time.LocalDateTime;
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.TimeUnit;
 
 
 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.invalidParamException;
 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.invalidParamException;
+import static cn.iocoder.yudao.framework.common.util.json.JsonUtils.toJsonString;
 
 
 /**
 /**
  * 微信支付【付款码支付】的 PayClient 实现类
  * 微信支付【付款码支付】的 PayClient 实现类
@@ -48,7 +46,7 @@ public class WxBarPayClient extends AbstractWxPayClient {
     }
     }
 
 
     @Override
     @Override
-    protected PayOrderUnifiedRespDTO doUnifiedOrderV2(PayOrderUnifiedReqDTO reqDTO) throws WxPayException {
+    protected PayOrderRespDTO doUnifiedOrderV2(PayOrderUnifiedReqDTO reqDTO) throws WxPayException {
         // 由于付款码需要不断轮询,所以需要在较短的时间完成支付
         // 由于付款码需要不断轮询,所以需要在较短的时间完成支付
         LocalDateTime expireTime = LocalDateTimeUtils.addTime(AUTH_CODE_EXPIRE);
         LocalDateTime expireTime = LocalDateTimeUtils.addTime(AUTH_CODE_EXPIRE);
         if (expireTime.isAfter(reqDTO.getExpireTime())) {
         if (expireTime.isAfter(reqDTO.getExpireTime())) {
@@ -65,22 +63,16 @@ public class WxBarPayClient extends AbstractWxPayClient {
                 .authCode(getAuthCode(reqDTO))
                 .authCode(getAuthCode(reqDTO))
                 .build();
                 .build();
         // 执行请求,重试直到失败(过期),或者成功
         // 执行请求,重试直到失败(过期),或者成功
+        WxPayException lastWxPayException = null;
         for (int i = 1; i < Byte.MAX_VALUE; i++) {
         for (int i = 1; i < Byte.MAX_VALUE; i++) {
             try {
             try {
                 WxPayMicropayResult response = client.micropay(request);
                 WxPayMicropayResult response = client.micropay(request);
-                // 支付成功(例如说,用户输入了密码)
-                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))
-                        .setOrder(order);
+                // 支付成功,例如说:1)用户输入了密码;2)
+                return new PayOrderRespDTO(response.getTransactionId(), response.getOpenid(), parseDateV2(response.getTimeEnd()),
+                        response.getOutTradeNo(), response)
+                        .setDisplayMode(PayOrderDisplayModeEnum.BAR_CODE.getMode());
             } catch (WxPayException ex) {
             } catch (WxPayException ex) {
+                lastWxPayException = ex;
                 // 如果不满足这 3 种任一的,则直接抛出 WxPayException 异常,不仅需处理
                 // 如果不满足这 3 种任一的,则直接抛出 WxPayException 异常,不仅需处理
                 // 1. SYSTEMERROR:接口返回错误:请立即调用被扫订单结果查询API,查询当前订单状态,并根据订单的状态决定下一步的操作。
                 // 1. SYSTEMERROR:接口返回错误:请立即调用被扫订单结果查询API,查询当前订单状态,并根据订单的状态决定下一步的操作。
                 // 2. USERPAYING:用户支付中,需要输入密码:等待 5 秒,然后调用被扫订单结果查询 API,查询当前订单的不同状态,决定下一步的操作。
                 // 2. USERPAYING:用户支付中,需要输入密码:等待 5 秒,然后调用被扫订单结果查询 API,查询当前订单的不同状态,决定下一步的操作。
@@ -90,15 +82,15 @@ public class WxBarPayClient extends AbstractWxPayClient {
                 }
                 }
                 // 等待 5 秒,继续下一轮重新发起支付
                 // 等待 5 秒,继续下一轮重新发起支付
                 log.info("[doUnifiedOrderV2][发起微信 Bar 支付第({})失败,等待下一轮重试,请求({}),响应({})]", i,
                 log.info("[doUnifiedOrderV2][发起微信 Bar 支付第({})失败,等待下一轮重试,请求({}),响应({})]", i,
-                        JsonUtils.toJsonString(request), ex.getMessage());
+                        toJsonString(request), ex.getMessage());
                 ThreadUtil.sleep(5, TimeUnit.SECONDS);
                 ThreadUtil.sleep(5, TimeUnit.SECONDS);
             }
             }
         }
         }
-        throw new IllegalStateException("微信 Bar 支付,重试多次失败");
+        throw lastWxPayException;
     }
     }
 
 
     @Override
     @Override
-    protected PayOrderUnifiedRespDTO doUnifiedOrderV3(PayOrderUnifiedReqDTO reqDTO) throws WxPayException {
+    protected PayOrderRespDTO doUnifiedOrderV3(PayOrderUnifiedReqDTO reqDTO) throws WxPayException {
         return doUnifiedOrderV2(reqDTO);
         return doUnifiedOrderV2(reqDTO);
     }
     }
 
 

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

@@ -1,7 +1,7 @@
 package cn.iocoder.yudao.framework.pay.core.client.impl.weixin;
 package cn.iocoder.yudao.framework.pay.core.client.impl.weixin;
 
 
+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.PayOrderUnifiedReqDTO;
-import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderUnifiedRespDTO;
 import cn.iocoder.yudao.framework.pay.core.enums.channel.PayChannelEnum;
 import cn.iocoder.yudao.framework.pay.core.enums.channel.PayChannelEnum;
 import com.github.binarywang.wxpay.constant.WxPayConstants;
 import com.github.binarywang.wxpay.constant.WxPayConstants;
 import com.github.binarywang.wxpay.exception.WxPayException;
 import com.github.binarywang.wxpay.exception.WxPayException;
@@ -19,12 +19,12 @@ public class WxH5PayClient extends AbstractWxPayClient {
     }
     }
 
 
     @Override
     @Override
-    protected PayOrderUnifiedRespDTO doUnifiedOrderV2(PayOrderUnifiedReqDTO reqDTO) throws WxPayException {
+    protected PayOrderRespDTO doUnifiedOrderV2(PayOrderUnifiedReqDTO reqDTO) throws WxPayException {
         return null;
         return null;
     }
     }
 
 
     @Override
     @Override
-    protected PayOrderUnifiedRespDTO doUnifiedOrderV3(PayOrderUnifiedReqDTO reqDTO) throws WxPayException {
+    protected PayOrderRespDTO doUnifiedOrderV3(PayOrderUnifiedReqDTO reqDTO) throws WxPayException {
         return null;
         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

@@ -1,7 +1,7 @@
 package cn.iocoder.yudao.framework.pay.core.client.impl.weixin;
 package cn.iocoder.yudao.framework.pay.core.client.impl.weixin;
 
 
+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.PayOrderUnifiedReqDTO;
-import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderUnifiedRespDTO;
 import cn.iocoder.yudao.framework.pay.core.enums.channel.PayChannelEnum;
 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.PayOrderDisplayModeEnum;
 import com.github.binarywang.wxpay.bean.order.WxPayNativeOrderResult;
 import com.github.binarywang.wxpay.bean.order.WxPayNativeOrderResult;
@@ -32,7 +32,7 @@ public class WxNativePayClient extends AbstractWxPayClient {
     }
     }
 
 
     @Override
     @Override
-    protected PayOrderUnifiedRespDTO doUnifiedOrderV2(PayOrderUnifiedReqDTO reqDTO) throws WxPayException {
+    protected PayOrderRespDTO doUnifiedOrderV2(PayOrderUnifiedReqDTO reqDTO) throws WxPayException {
         // 构建 WxPayUnifiedOrderRequest 对象
         // 构建 WxPayUnifiedOrderRequest 对象
         WxPayUnifiedOrderRequest request = WxPayUnifiedOrderRequest.newBuilder()
         WxPayUnifiedOrderRequest request = WxPayUnifiedOrderRequest.newBuilder()
                 .outTradeNo(reqDTO.getOutTradeNo())
                 .outTradeNo(reqDTO.getOutTradeNo())
@@ -48,12 +48,12 @@ public class WxNativePayClient extends AbstractWxPayClient {
         WxPayNativeOrderResult response = client.createOrder(request);
         WxPayNativeOrderResult response = client.createOrder(request);
 
 
         // 转换结果
         // 转换结果
-        return new PayOrderUnifiedRespDTO(PayOrderDisplayModeEnum.QR_CODE.getMode(),
-                response.getCodeUrl());
+        return new PayOrderRespDTO(PayOrderDisplayModeEnum.QR_CODE.getMode(), response.getCodeUrl(),
+                reqDTO.getOutTradeNo(), response);
     }
     }
 
 
     @Override
     @Override
-    protected PayOrderUnifiedRespDTO doUnifiedOrderV3(PayOrderUnifiedReqDTO reqDTO) throws WxPayException {
+    protected PayOrderRespDTO doUnifiedOrderV3(PayOrderUnifiedReqDTO reqDTO) throws WxPayException {
         // 构建 WxPayUnifiedOrderRequest 对象
         // 构建 WxPayUnifiedOrderRequest 对象
         WxPayUnifiedOrderV3Request request = new WxPayUnifiedOrderV3Request()
         WxPayUnifiedOrderV3Request request = new WxPayUnifiedOrderV3Request()
                 .setOutTradeNo(reqDTO.getOutTradeNo())
                 .setOutTradeNo(reqDTO.getOutTradeNo())
@@ -66,8 +66,8 @@ public class WxNativePayClient extends AbstractWxPayClient {
         String response = client.createOrderV3(TradeTypeEnum.NATIVE, request);
         String response = client.createOrderV3(TradeTypeEnum.NATIVE, request);
 
 
         // 转换结果
         // 转换结果
-        return new PayOrderUnifiedRespDTO(PayOrderDisplayModeEnum.QR_CODE.getMode(),
-                response);
+        return new PayOrderRespDTO(PayOrderDisplayModeEnum.QR_CODE.getMode(), response,
+                reqDTO.getOutTradeNo(), response);
     }
     }
 
 
 }
 }

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

@@ -2,9 +2,8 @@ package cn.iocoder.yudao.framework.pay.core.client.impl.weixin;
 
 
 import cn.hutool.core.map.MapUtil;
 import cn.hutool.core.map.MapUtil;
 import cn.hutool.core.util.StrUtil;
 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.PayOrderRespDTO;
 import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderUnifiedReqDTO;
 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.channel.PayChannelEnum;
 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.PayOrderDisplayModeEnum;
 import com.github.binarywang.wxpay.bean.order.WxPayMpOrderResult;
 import com.github.binarywang.wxpay.bean.order.WxPayMpOrderResult;
@@ -17,6 +16,7 @@ import com.github.binarywang.wxpay.exception.WxPayException;
 import lombok.extern.slf4j.Slf4j;
 import lombok.extern.slf4j.Slf4j;
 
 
 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.invalidParamException;
 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.invalidParamException;
+import static cn.iocoder.yudao.framework.common.util.json.JsonUtils.toJsonString;
 
 
 /**
 /**
  * 微信支付(公众号)的 PayClient 实现类
  * 微信支付(公众号)的 PayClient 实现类
@@ -42,7 +42,7 @@ public class WxPubPayClient extends AbstractWxPayClient {
     }
     }
 
 
     @Override
     @Override
-    protected PayOrderUnifiedRespDTO doUnifiedOrderV2(PayOrderUnifiedReqDTO reqDTO) throws WxPayException {
+    protected PayOrderRespDTO doUnifiedOrderV2(PayOrderUnifiedReqDTO reqDTO) throws WxPayException {
         // 构建 WxPayUnifiedOrderRequest 对象
         // 构建 WxPayUnifiedOrderRequest 对象
         WxPayUnifiedOrderRequest request = WxPayUnifiedOrderRequest.newBuilder()
         WxPayUnifiedOrderRequest request = WxPayUnifiedOrderRequest.newBuilder()
                 .outTradeNo(reqDTO.getOutTradeNo())
                 .outTradeNo(reqDTO.getOutTradeNo())
@@ -58,12 +58,12 @@ public class WxPubPayClient extends AbstractWxPayClient {
         WxPayMpOrderResult response = client.createOrder(request);
         WxPayMpOrderResult response = client.createOrder(request);
 
 
         // 转换结果
         // 转换结果
-        return new PayOrderUnifiedRespDTO(PayOrderDisplayModeEnum.CUSTOM.getMode(),
-                JsonUtils.toJsonString(response));
+        return new PayOrderRespDTO(PayOrderDisplayModeEnum.APP.getMode(), toJsonString(response),
+                reqDTO.getOutTradeNo(), response);
     }
     }
 
 
     @Override
     @Override
-    protected PayOrderUnifiedRespDTO doUnifiedOrderV3(PayOrderUnifiedReqDTO reqDTO) throws WxPayException {
+    protected PayOrderRespDTO doUnifiedOrderV3(PayOrderUnifiedReqDTO reqDTO) throws WxPayException {
         // 构建 WxPayUnifiedOrderRequest 对象
         // 构建 WxPayUnifiedOrderRequest 对象
         WxPayUnifiedOrderV3Request request = new WxPayUnifiedOrderV3Request();
         WxPayUnifiedOrderV3Request request = new WxPayUnifiedOrderV3Request();
         request.setOutTradeNo(reqDTO.getOutTradeNo());
         request.setOutTradeNo(reqDTO.getOutTradeNo());
@@ -77,8 +77,8 @@ public class WxPubPayClient extends AbstractWxPayClient {
         WxPayUnifiedOrderV3Result.JsapiResult response = client.createOrderV3(TradeTypeEnum.JSAPI, request);
         WxPayUnifiedOrderV3Result.JsapiResult response = client.createOrderV3(TradeTypeEnum.JSAPI, request);
 
 
         // 转换结果
         // 转换结果
-        return new PayOrderUnifiedRespDTO(PayOrderDisplayModeEnum.CUSTOM.getMode(),
-                JsonUtils.toJsonString(response));
+        return new PayOrderRespDTO(PayOrderDisplayModeEnum.APP.getMode(), toJsonString(response),
+                reqDTO.getOutTradeNo(), response);
     }
     }
 
 
     // ========== 各种工具方法 ==========
     // ========== 各种工具方法 ==========

+ 0 - 16
yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/enums/PayFrameworkErrorCodeConstants.java

@@ -1,16 +0,0 @@
-package cn.iocoder.yudao.framework.pay.core.enums;
-
-import cn.iocoder.yudao.framework.common.exception.ErrorCode;
-
-/**
- * 支付框架的错误码枚举
- *
- * 支付框架,使用 2-002-000-000 段
- *
- * @author 芋道源码
- */
-public interface PayFrameworkErrorCodeConstants {
-
-    ErrorCode ORDER_UNIFIED_ERROR = new ErrorCode(2002000000, "发起支付失败,原因:{}");
-
-}

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

@@ -18,8 +18,7 @@ public enum PayOrderDisplayModeEnum {
     QR_CODE("qr_code"), // 二维码的文字内容
     QR_CODE("qr_code"), // 二维码的文字内容
     QR_CODE_URL("qr_code_url"), // 二维码的图片链接
     QR_CODE_URL("qr_code_url"), // 二维码的图片链接
     BAR_CODE("bar_code"), // 条形码
     BAR_CODE("bar_code"), // 条形码
-    APP("app"), // 应用【目前暂时用不到】
-    CUSTOM("custom"), // 自定义:每种支付方式,做个性化处理;例如说,微信公众号支付时,调用 JSAPI 接口
+    APP("app"), // 应用:Android、iOS、微信小程序、微信公众号等,需要做自定义处理的
     ;
     ;
 
 
     /**
     /**

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

@@ -32,4 +32,14 @@ public enum PayOrderStatusRespEnum {
         return Objects.equals(status, SUCCESS.getStatus());
         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-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/aftersale/TradeAfterSaleServiceImpl.java

@@ -351,7 +351,7 @@ public class TradeAfterSaleServiceImpl implements TradeAfterSaleService, AfterSa
             public void afterCommit() {
             public void afterCommit() {
                 // 创建退款单
                 // 创建退款单
                 PayRefundCreateReqDTO createReqDTO = TradeAfterSaleConvert.INSTANCE.convert(userIp, afterSale, tradeOrderProperties);
                 PayRefundCreateReqDTO createReqDTO = TradeAfterSaleConvert.INSTANCE.convert(userIp, afterSale, tradeOrderProperties);
-                Long payRefundId = payRefundApi.createPayRefund(createReqDTO);
+                Long payRefundId = payRefundApi.createRefund(createReqDTO);
                 // 更新售后单的退款单号
                 // 更新售后单的退款单号
                 tradeAfterSaleMapper.updateById(new TradeAfterSaleDO().setId(afterSale.getId()).setPayRefundId(payRefundId));
                 tradeAfterSaleMapper.updateById(new TradeAfterSaleDO().setId(afterSale.getId()).setPayRefundId(payRefundId));
             }
             }

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

@@ -18,7 +18,7 @@ public interface PayRefundApi {
      * @param reqDTO 创建请求
      * @param reqDTO 创建请求
      * @return 退款单编号
      * @return 退款单编号
      */
      */
-    Long createPayRefund(@Valid PayRefundCreateReqDTO reqDTO);
+    Long createRefund(@Valid PayRefundCreateReqDTO reqDTO);
 
 
     /**
     /**
      * 获得退款单
      * 获得退款单
@@ -26,6 +26,6 @@ public interface PayRefundApi {
      * @param id 退款单编号
      * @param id 退款单编号
      * @return 退款单
      * @return 退款单
      */
      */
-    PayRefundRespDTO getPayRefund(Long id);
+    PayRefundRespDTO getRefund(Long id);
 
 
 }
 }

+ 1 - 0
yudao-module-pay/yudao-module-pay-api/src/main/java/cn/iocoder/yudao/module/pay/enums/ErrorCodeConstants.java

@@ -27,6 +27,7 @@ public interface ErrorCodeConstants {
     ErrorCode PAY_ORDER_STATUS_IS_NOT_WAITING = new ErrorCode(1007002001, "支付订单不处于待支付");
     ErrorCode PAY_ORDER_STATUS_IS_NOT_WAITING = new ErrorCode(1007002001, "支付订单不处于待支付");
     ErrorCode PAY_ORDER_STATUS_IS_NOT_SUCCESS = new ErrorCode(1007002002, "支付订单不处于已支付");
     ErrorCode PAY_ORDER_STATUS_IS_NOT_SUCCESS = new ErrorCode(1007002002, "支付订单不处于已支付");
     ErrorCode PAY_ORDER_IS_EXPIRED = new ErrorCode(1007002003, "支付订单已经过期");
     ErrorCode PAY_ORDER_IS_EXPIRED = new ErrorCode(1007002003, "支付订单已经过期");
+    ErrorCode PAY_ORDER_SUBMIT_CHANNEL_ERROR = new ErrorCode(1007002004, "发起支付报错,错误码:{},错误提示:{}");
 
 
     // ========== ORDER 模块(拓展单) 1007003000 ==========
     // ========== ORDER 模块(拓展单) 1007003000 ==========
     ErrorCode PAY_ORDER_EXTENSION_NOT_FOUND = new ErrorCode(1007003000, "支付交易拓展单不存在");
     ErrorCode PAY_ORDER_EXTENSION_NOT_FOUND = new ErrorCode(1007003000, "支付交易拓展单不存在");

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

@@ -22,12 +22,12 @@ public class PayRefundApiImpl implements PayRefundApi {
     private PayRefundService payRefundService;
     private PayRefundService payRefundService;
 
 
     @Override
     @Override
-    public Long createPayRefund(PayRefundCreateReqDTO reqDTO) {
+    public Long createRefund(PayRefundCreateReqDTO reqDTO) {
         return payRefundService.createPayRefund(reqDTO);
         return payRefundService.createPayRefund(reqDTO);
     }
     }
 
 
     @Override
     @Override
-    public PayRefundRespDTO getPayRefund(Long id) {
+    public PayRefundRespDTO getRefund(Long id) {
         return PayRefundConvert.INSTANCE.convert02(payRefundService.getRefund(id));
         return PayRefundConvert.INSTANCE.convert02(payRefundService.getRefund(id));
     }
     }
 
 

+ 2 - 2
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/convert/order/PayOrderConvert.java

@@ -2,7 +2,6 @@ package cn.iocoder.yudao.module.pay.convert.order;
 
 
 import cn.iocoder.yudao.framework.common.pojo.PageResult;
 import cn.iocoder.yudao.framework.common.pojo.PageResult;
 import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderUnifiedReqDTO;
 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.module.pay.api.order.dto.PayOrderCreateReqDTO;
 import cn.iocoder.yudao.module.pay.api.order.dto.PayOrderCreateReqDTO;
 import cn.iocoder.yudao.module.pay.api.order.dto.PayOrderRespDTO;
 import cn.iocoder.yudao.module.pay.api.order.dto.PayOrderRespDTO;
 import cn.iocoder.yudao.module.pay.controller.admin.order.vo.*;
 import cn.iocoder.yudao.module.pay.controller.admin.order.vo.*;
@@ -93,7 +92,8 @@ public interface PayOrderConvert {
 
 
     PayOrderUnifiedReqDTO convert2(PayOrderSubmitReqVO reqVO, String userIp);
     PayOrderUnifiedReqDTO convert2(PayOrderSubmitReqVO reqVO, String userIp);
 
 
-    PayOrderSubmitRespVO convert(PayOrderDO order, PayOrderUnifiedRespDTO unifiedRespDTO);
+    @Mapping(source = "order.status", target = "status")
+    PayOrderSubmitRespVO convert(PayOrderDO order, cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderRespDTO respDTO);
 
 
     AppPayOrderSubmitRespVO convert3(PayOrderSubmitRespVO bean);
     AppPayOrderSubmitRespVO convert3(PayOrderSubmitRespVO bean);
 
 

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

@@ -1,5 +1,6 @@
 package cn.iocoder.yudao.module.pay.dal.dataobject.order;
 package cn.iocoder.yudao.module.pay.dal.dataobject.order;
 
 
+import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderRespDTO;
 import cn.iocoder.yudao.module.pay.dal.dataobject.channel.PayChannelDO;
 import cn.iocoder.yudao.module.pay.dal.dataobject.channel.PayChannelDO;
 import cn.iocoder.yudao.module.pay.enums.order.PayOrderStatusEnum;
 import cn.iocoder.yudao.module.pay.enums.order.PayOrderStatusEnum;
 import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
 import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
@@ -65,7 +66,6 @@ public class PayOrderExtensionDO extends BaseDO {
      * 支付状态
      * 支付状态
      *
      *
      * 枚举 {@link PayOrderStatusEnum}
      * 枚举 {@link PayOrderStatusEnum}
-     * 注意,只包含上述枚举的 WAITING 和 SUCCESS
      */
      */
     private Integer status;
     private Integer status;
     /**
     /**
@@ -75,10 +75,20 @@ public class PayOrderExtensionDO extends BaseDO {
      */
      */
     @TableField(typeHandler = JacksonTypeHandler.class)
     @TableField(typeHandler = JacksonTypeHandler.class)
     private Map<String, String> channelExtras;
     private Map<String, String> channelExtras;
+
+    /**
+     * 调用渠道的错误码
+     */
+    private String channelErrorCode;
+    /**
+     * 调用渠道报错时,错误信息
+     */
+    private String channelErrorMsg;
+
     /**
     /**
-     * 支付渠道异步通知的内容
+     * 支付渠道的同步/异步通知的内容
      *
      *
-     * 在支持成功后,会记录回调的数据
+     * 对应 {@link PayOrderRespDTO#getRawData()}
      */
      */
     private String channelNotifyData;
     private String channelNotifyData;
 
 

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

@@ -1,6 +1,7 @@
 package cn.iocoder.yudao.module.pay.dal.dataobject.refund;
 package cn.iocoder.yudao.module.pay.dal.dataobject.refund;
 
 
 import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
 import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
+import cn.iocoder.yudao.framework.pay.core.client.dto.refund.PayRefundRespDTO;
 import cn.iocoder.yudao.framework.pay.core.enums.channel.PayChannelEnum;
 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.app.PayAppDO;
 import cn.iocoder.yudao.module.pay.dal.dataobject.channel.PayChannelDO;
 import cn.iocoder.yudao.module.pay.dal.dataobject.channel.PayChannelDO;
@@ -151,9 +152,9 @@ public class PayRefundDO extends BaseDO {
     private String channelErrorMsg;
     private String channelErrorMsg;
 
 
     /**
     /**
-     * 支付渠道异步通知的内容
+     * 支付渠道的同步/异步通知的内容
      *
      *
-     * 在退款成功后,会记录回调的数据
+     * 对应 {@link PayRefundRespDTO#getRawData()}
      */
      */
     private String channelNotifyData;
     private String channelNotifyData;
 
 

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

@@ -189,7 +189,7 @@ public class PayDemoOrderServiceImpl implements PayDemoOrderService {
         // 这里我们是个简单的 demo,所以没有售后维权表,直接使用订单 id + "-refund" 来演示
         // 这里我们是个简单的 demo,所以没有售后维权表,直接使用订单 id + "-refund" 来演示
         String refundId = order.getId() + "-refund";
         String refundId = order.getId() + "-refund";
         // 2.2 创建退款单
         // 2.2 创建退款单
-        Long payRefundId = payRefundApi.createPayRefund(new PayRefundCreateReqDTO()
+        Long payRefundId = payRefundApi.createRefund(new PayRefundCreateReqDTO()
                 .setAppId(PAY_APP_ID).setUserIp(getClientIP()) // 支付应用
                 .setAppId(PAY_APP_ID).setUserIp(getClientIP()) // 支付应用
                 .setMerchantOrderId(String.valueOf(order.getId())) // 支付单号
                 .setMerchantOrderId(String.valueOf(order.getId())) // 支付单号
                 .setMerchantRefundId(refundId)
                 .setMerchantRefundId(refundId)
@@ -239,7 +239,7 @@ public class PayDemoOrderServiceImpl implements PayDemoOrderService {
         }
         }
 
 
         // 2.1 校验退款订单
         // 2.1 校验退款订单
-        PayRefundRespDTO payRefund = payRefundApi.getPayRefund(payRefundId);
+        PayRefundRespDTO payRefund = payRefundApi.getRefund(payRefundId);
         if (payRefund == null) {
         if (payRefund == null) {
             throw exception(PAY_DEMO_ORDER_REFUND_FAIL_REFUND_NOT_FOUND);
             throw exception(PAY_DEMO_ORDER_REFUND_FAIL_REFUND_NOT_FOUND);
         }
         }

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

@@ -4,6 +4,7 @@ import cn.hutool.core.date.DateUtil;
 import cn.hutool.core.lang.Pair;
 import cn.hutool.core.lang.Pair;
 import cn.hutool.core.util.ObjectUtil;
 import cn.hutool.core.util.ObjectUtil;
 import cn.hutool.core.util.RandomUtil;
 import cn.hutool.core.util.RandomUtil;
+import cn.hutool.core.util.StrUtil;
 import cn.iocoder.yudao.framework.common.pojo.PageResult;
 import cn.iocoder.yudao.framework.common.pojo.PageResult;
 import cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils;
 import cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils;
 import cn.iocoder.yudao.framework.pay.config.PayProperties;
 import cn.iocoder.yudao.framework.pay.config.PayProperties;
@@ -11,7 +12,6 @@ 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.PayClientFactory;
 import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderRespDTO;
 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.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.pay.core.enums.order.PayOrderStatusRespEnum;
 import cn.iocoder.yudao.framework.tenant.core.util.TenantUtils;
 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.api.order.dto.PayOrderCreateReqDTO;
@@ -134,8 +134,7 @@ public class PayOrderServiceImpl implements PayOrderService {
         return order.getId();
         return order.getId();
     }
     }
 
 
-    @Override
-    @Transactional(rollbackFor = Exception.class)
+    @Override // 注意,这里不能添加事务注解,避免调用支付渠道失败时,将 PayOrderExtensionDO 回滚了
     public PayOrderSubmitRespVO submitOrder(PayOrderSubmitReqVO reqVO, String userIp) {
     public PayOrderSubmitRespVO submitOrder(PayOrderSubmitReqVO reqVO, String userIp) {
         // 1. 获得 PayOrderDO ,并校验其是否存在
         // 1. 获得 PayOrderDO ,并校验其是否存在
         PayOrderDO order = validateOrderCanSubmit(reqVO.getId());
         PayOrderDO order = validateOrderCanSubmit(reqVO.getId());
@@ -159,17 +158,20 @@ public class PayOrderServiceImpl implements PayOrderService {
                 .setReturnUrl(reqVO.getReturnUrl())
                 .setReturnUrl(reqVO.getReturnUrl())
                 // 订单相关字段
                 // 订单相关字段
                 .setPrice(order.getPrice()).setExpireTime(order.getExpireTime());
                 .setPrice(order.getPrice()).setExpireTime(order.getExpireTime());
-        PayOrderUnifiedRespDTO unifiedOrderRespDTO = client.unifiedOrder(unifiedOrderReqDTO);
+        PayOrderRespDTO unifiedOrderResp = client.unifiedOrder(unifiedOrderReqDTO);
 
 
         // 4. 如果调用直接支付成功,则直接更新支付单状态为成功。例如说:付款码支付,免密支付时,就直接验证支付成功
         // 4. 如果调用直接支付成功,则直接更新支付单状态为成功。例如说:付款码支付,免密支付时,就直接验证支付成功
-        if (unifiedOrderRespDTO.getOrder() != null) {
-            notifyPayOrder(channel, unifiedOrderRespDTO.getOrder());
+        if (unifiedOrderResp != null) {
+            notifyPayOrder(channel, unifiedOrderResp);
+            // 如有渠道错误码,则抛出业务异常,提示用户
+            if (StrUtil.isNotEmpty(unifiedOrderResp.getChannelErrorCode())) {
+                throw exception(PAY_ORDER_SUBMIT_CHANNEL_ERROR, unifiedOrderResp.getChannelErrorCode(),
+                        unifiedOrderResp.getChannelErrorMsg());
+            }
             // 此处需要读取最新的状态
             // 此处需要读取最新的状态
             order = orderMapper.selectById(order.getId());
             order = orderMapper.selectById(order.getId());
         }
         }
-
-        // 返回成功
-        return PayOrderConvert.INSTANCE.convert(order, unifiedOrderRespDTO);
+        return PayOrderConvert.INSTANCE.convert(order, unifiedOrderResp);
     }
     }
 
 
     private PayOrderDO validateOrderCanSubmit(Long id) {
     private PayOrderDO validateOrderCanSubmit(Long id) {
@@ -269,8 +271,10 @@ public class PayOrderServiceImpl implements PayOrderService {
             notifyOrderSuccess(channel, notify);
             notifyOrderSuccess(channel, notify);
             return;
             return;
         }
         }
-        // 情况二:非支付成功的回调,进行忽略
-        log.info("[notifyPayOrder][非支付成功的回调({}),直接忽略]", toJsonString(notify));
+        // 情况二:支付失败的回调
+        if (PayOrderStatusRespEnum.isClosed(notify.getStatus())) {
+            notifyOrderClosed(channel, notify);
+        }
     }
     }
 
 
     private void notifyOrderSuccess(PayChannelDO channel, PayOrderRespDTO notify) {
     private void notifyOrderSuccess(PayChannelDO channel, PayOrderRespDTO notify) {
@@ -300,7 +304,7 @@ public class PayOrderServiceImpl implements PayOrderService {
             throw exception(PAY_ORDER_EXTENSION_NOT_FOUND);
             throw exception(PAY_ORDER_EXTENSION_NOT_FOUND);
         }
         }
         if (PayOrderStatusEnum.isSuccess(orderExtension.getStatus())) { // 如果已经是成功,直接返回,不用重复更新
         if (PayOrderStatusEnum.isSuccess(orderExtension.getStatus())) { // 如果已经是成功,直接返回,不用重复更新
-            log.info("[updateOrderExtensionSuccess][支付拓展单({}) 已经是已支付,无需更新为已支付]", orderExtension.getId());
+            log.info("[updateOrderExtensionSuccess][支付拓展单({}) 已经是已支付,无需更新]", orderExtension.getId());
             return orderExtension;
             return orderExtension;
         }
         }
         if (ObjectUtil.notEqual(orderExtension.getStatus(), PayOrderStatusEnum.WAITING.getStatus())) { // 校验状态,必须是待支付
         if (ObjectUtil.notEqual(orderExtension.getStatus(), PayOrderStatusEnum.WAITING.getStatus())) { // 校验状态,必须是待支付
@@ -327,7 +331,7 @@ public class PayOrderServiceImpl implements PayOrderService {
      *         value:PayOrderDO 对象
      *         value:PayOrderDO 对象
      */
      */
     private Pair<Boolean, PayOrderDO> updateOrderExtensionSuccess(PayChannelDO channel, PayOrderExtensionDO orderExtension,
     private Pair<Boolean, PayOrderDO> updateOrderExtensionSuccess(PayChannelDO channel, PayOrderExtensionDO orderExtension,
-                                                            PayOrderRespDTO notify) {
+                                                                  PayOrderRespDTO notify) {
         // 1. 判断 PayOrderDO 是否处于待支付
         // 1. 判断 PayOrderDO 是否处于待支付
         PayOrderDO order = orderMapper.selectById(orderExtension.getOrderId());
         PayOrderDO order = orderMapper.selectById(orderExtension.getOrderId());
         if (order == null) {
         if (order == null) {
@@ -335,7 +339,7 @@ public class PayOrderServiceImpl implements PayOrderService {
         }
         }
         if (PayOrderStatusEnum.isSuccess(order.getStatus()) // 如果已经是成功,直接返回,不用重复更新
         if (PayOrderStatusEnum.isSuccess(order.getStatus()) // 如果已经是成功,直接返回,不用重复更新
                 && Objects.equals(order.getSuccessExtensionId(), orderExtension.getId())) {
                 && Objects.equals(order.getSuccessExtensionId(), orderExtension.getId())) {
-            log.info("[updateOrderExtensionSuccess][支付订单({}) 已经是已支付,无需更新为已支付]", order.getId());
+            log.info("[updateOrderExtensionSuccess][支付订单({}) 已经是已支付,无需更新]", order.getId());
             return Pair.of(true, order);
             return Pair.of(true, order);
         }
         }
         if (!PayOrderStatusEnum.WAITING.getStatus().equals(order.getStatus())) { // 校验状态,必须是待支付
         if (!PayOrderStatusEnum.WAITING.getStatus().equals(order.getStatus())) { // 校验状态,必须是待支付
@@ -356,4 +360,37 @@ public class PayOrderServiceImpl implements PayOrderService {
         return Pair.of(false, order);
         return Pair.of(false, order);
     }
     }
 
 
+    private void notifyOrderClosed(PayChannelDO channel, PayOrderRespDTO notify) {
+        updateOrderExtensionClosed(channel, notify);
+    }
+
+    private void updateOrderExtensionClosed(PayChannelDO channel, PayOrderRespDTO notify) {
+        // 1. 查询 PayOrderExtensionDO
+        PayOrderExtensionDO orderExtension = orderExtensionMapper.selectByNo(notify.getOutTradeNo());
+        if (orderExtension == null) {
+            throw exception(PAY_ORDER_EXTENSION_NOT_FOUND);
+        }
+        if (PayOrderStatusEnum.isClosed(orderExtension.getStatus())) { // 如果已经是关闭,直接返回,不用重复更新
+            log.info("[updateOrderExtensionClosed][支付拓展单({}) 已经是支付关闭,无需更新]", orderExtension.getId());
+            return;
+        }
+        // 一般出现先是支付成功,然后支付关闭,都是全部退款导致关闭的场景。这个情况,我们不更新支付拓展单,只通过退款流程,更新支付单
+        if (PayOrderStatusEnum.isSuccess(orderExtension.getStatus())) {
+            log.info("[updateOrderExtensionClosed][支付拓展单({}) 是已支付,无需更新为支付关闭]", orderExtension.getId());
+            return;
+        }
+        if (ObjectUtil.notEqual(orderExtension.getStatus(), PayOrderStatusEnum.WAITING.getStatus())) { // 校验状态,必须是待支付
+            throw exception(PAY_ORDER_EXTENSION_STATUS_IS_NOT_WAITING);
+        }
+
+        // 2. 更新 PayOrderExtensionDO
+        int updateCounts = orderExtensionMapper.updateByIdAndStatus(orderExtension.getId(), orderExtension.getStatus(),
+                PayOrderExtensionDO.builder().status(PayOrderStatusEnum.CLOSED.getStatus()).channelNotifyData(toJsonString(notify))
+                        .channelErrorCode(notify.getChannelErrorCode()).channelErrorMsg(notify.getChannelErrorMsg()).build());
+        if (updateCounts == 0) { // 校验状态,必须是待支付
+            throw exception(PAY_ORDER_EXTENSION_STATUS_IS_NOT_WAITING);
+        }
+        log.info("[updateOrderExtensionClosed][支付拓展单({}) 更新为支付关闭]", orderExtension.getId());
+    }
+
 }
 }