Commit 6f475f8c85cdad39c733579872b402b700139bb8

Authored by YunaiV
1 parent 7cf4c541

mall + pay:

1. 优化 PayClient 支付逻辑,返回业务失败 errorCode + errorMsg 错误码
Showing 29 changed files with 361 additions and 292 deletions
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; @@ -2,7 +2,6 @@ package cn.iocoder.yudao.framework.pay.core.client;
2 2
3 import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderRespDTO; 3 import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderRespDTO;
4 import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderUnifiedReqDTO; 4 import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderUnifiedReqDTO;
5 -import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderUnifiedRespDTO;  
6 import cn.iocoder.yudao.framework.pay.core.client.dto.refund.PayRefundRespDTO; 5 import cn.iocoder.yudao.framework.pay.core.client.dto.refund.PayRefundRespDTO;
7 import cn.iocoder.yudao.framework.pay.core.client.dto.refund.PayRefundUnifiedReqDTO; 6 import cn.iocoder.yudao.framework.pay.core.client.dto.refund.PayRefundUnifiedReqDTO;
8 7
@@ -30,7 +29,7 @@ public interface PayClient { @@ -30,7 +29,7 @@ public interface PayClient {
30 * @param reqDTO 下单信息 29 * @param reqDTO 下单信息
31 * @return 各支付渠道的返回结果 30 * @return 各支付渠道的返回结果
32 */ 31 */
33 - PayOrderUnifiedRespDTO unifiedOrder(PayOrderUnifiedReqDTO reqDTO); 32 + PayOrderRespDTO unifiedOrder(PayOrderUnifiedReqDTO reqDTO);
34 33
35 /** 34 /**
36 * 解析 order 回调数据 35 * 解析 order 回调数据
yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/dto/order/PayOrderRespDTO.java
1 package cn.iocoder.yudao.framework.pay.core.client.dto.order; 1 package cn.iocoder.yudao.framework.pay.core.client.dto.order;
2 2
  3 +import cn.iocoder.yudao.framework.pay.core.client.exception.PayException;
  4 +import cn.iocoder.yudao.framework.pay.core.enums.order.PayOrderDisplayModeEnum;
3 import cn.iocoder.yudao.framework.pay.core.enums.order.PayOrderStatusRespEnum; 5 import cn.iocoder.yudao.framework.pay.core.enums.order.PayOrderStatusRespEnum;
4 -import lombok.AllArgsConstructor;  
5 -import lombok.Builder;  
6 import lombok.Data; 6 import lombok.Data;
7 -import lombok.NoArgsConstructor;  
8 7
9 import java.time.LocalDateTime; 8 import java.time.LocalDateTime;
10 9
@@ -14,9 +13,6 @@ import java.time.LocalDateTime; @@ -14,9 +13,6 @@ import java.time.LocalDateTime;
14 * @author 芋道源码 13 * @author 芋道源码
15 */ 14 */
16 @Data 15 @Data
17 -@Builder  
18 -@NoArgsConstructor  
19 -@AllArgsConstructor  
20 public class PayOrderRespDTO { 16 public class PayOrderRespDTO {
21 17
22 /** 18 /**
@@ -48,8 +44,94 @@ public class PayOrderRespDTO { @@ -48,8 +44,94 @@ public class PayOrderRespDTO {
48 private LocalDateTime successTime; 44 private LocalDateTime successTime;
49 45
50 /** 46 /**
51 - * 原始的异步通知结果 47 + * 原始的同步/异步通知结果
52 */ 48 */
53 private Object rawData; 49 private Object rawData;
54 50
  51 + // ========== 主动发起支付时,会返回的字段 ==========
  52 +
  53 + /**
  54 + * 展示模式
  55 + *
  56 + * 枚举 {@link PayOrderDisplayModeEnum} 类
  57 + */
  58 + private String displayMode;
  59 + /**
  60 + * 展示内容
  61 + */
  62 + private String displayContent;
  63 +
  64 + /**
  65 + * 调用渠道的错误码
  66 + *
  67 + * 注意:这里返回的是业务异常,而是不系统异常。
  68 + * 如果是系统异常,则会抛出 {@link PayException}
  69 + */
  70 + private String channelErrorCode;
  71 + /**
  72 + * 调用渠道报错时,错误信息
  73 + */
  74 + private String channelErrorMsg;
  75 +
  76 + public PayOrderRespDTO() {
  77 + }
  78 +
  79 + /**
  80 + * 创建【WAITING】状态的订单返回
  81 + */
  82 + public PayOrderRespDTO(String displayMode, String displayContent,
  83 + String outTradeNo, Object rawData) {
  84 + this.status = PayOrderStatusRespEnum.WAITING.getStatus();
  85 + this.displayMode = displayMode;
  86 + this.displayContent = displayContent;
  87 + // 相对通用的字段
  88 + this.outTradeNo = outTradeNo;
  89 + this.rawData = rawData;
  90 + }
  91 +
  92 + /**
  93 + * 创建【SUCCESS】状态的订单返回
  94 + */
  95 + public PayOrderRespDTO(String channelOrderNo, String channelUserId, LocalDateTime successTime,
  96 + String outTradeNo, Object rawData) {
  97 + this.status = PayOrderStatusRespEnum.SUCCESS.getStatus();
  98 + this.channelOrderNo = channelOrderNo;
  99 + this.channelUserId = channelUserId;
  100 + this.successTime = successTime;
  101 + // 相对通用的字段
  102 + this.outTradeNo = outTradeNo;
  103 + this.rawData = rawData;
  104 + }
  105 +
  106 + /**
  107 + * 创建【SUCCESS】或【CLOSED】状态的订单返回,适合支付渠道回调时
  108 + */
  109 + public PayOrderRespDTO(Integer status, String channelOrderNo, String channelUserId, LocalDateTime successTime,
  110 + String outTradeNo, Object rawData) {
  111 + this.status = status;
  112 + this.channelOrderNo = channelOrderNo;
  113 + this.channelUserId = channelUserId;
  114 + this.successTime = successTime;
  115 + // 相对通用的字段
  116 + this.outTradeNo = outTradeNo;
  117 + this.rawData = rawData;
  118 + }
  119 +
  120 + /**
  121 + * 创建【CLOSED】状态的订单返回,适合调用支付渠道失败时
  122 + *
  123 + * 参数和 {@link #PayOrderRespDTO(String, String, String, Object)} 冲突,所以独立个方法出来
  124 + */
  125 + public static PayOrderRespDTO build(String channelErrorCode, String channelErrorMsg,
  126 + String outTradeNo, Object rawData) {
  127 + PayOrderRespDTO respDTO = new PayOrderRespDTO();
  128 + respDTO.status = PayOrderStatusRespEnum.CLOSED.getStatus();
  129 + respDTO.channelErrorCode = channelErrorCode;
  130 + respDTO.channelErrorMsg = channelErrorMsg;
  131 + // 相对通用的字段
  132 + respDTO.outTradeNo = outTradeNo;
  133 + respDTO.rawData = rawData;
  134 + return respDTO;
  135 + }
  136 +
55 } 137 }
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 { @@ -43,7 +43,6 @@ public class PayOrderUnifiedReqDTO {
43 /** 43 /**
44 * 商品描述信息 44 * 商品描述信息
45 */ 45 */
46 - @NotEmpty(message = "商品描述信息不能为空")  
47 @Length(max = 128, message = "商品描述信息长度不能超过128") 46 @Length(max = 128, message = "商品描述信息长度不能超过128")
48 private String body; 47 private String body;
49 /** 48 /**
yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/dto/order/PayOrderUnifiedRespDTO.java deleted
1 -package cn.iocoder.yudao.framework.pay.core.client.dto.order;  
2 -  
3 -import cn.iocoder.yudao.framework.pay.core.enums.order.PayOrderDisplayModeEnum;  
4 -import lombok.Data;  
5 -  
6 -/**  
7 - * 统一下单 Response DTO  
8 - *  
9 - * @author 芋道源码  
10 - */  
11 -@Data  
12 -public class PayOrderUnifiedRespDTO {  
13 -  
14 - /**  
15 - * 展示模式  
16 - *  
17 - * 枚举 {@link PayOrderDisplayModeEnum} 类  
18 - */  
19 - private String displayMode;  
20 - /**  
21 - * 展示内容  
22 - */  
23 - private String displayContent;  
24 -  
25 - /**  
26 - * 渠道支付订单  
27 - *  
28 - * 只有在订单直接支付成功时,才会进行返回。  
29 - * 目前只有 bar 条码支付才会出现,它是支付发起时,直接返回是否支付成功的,而其它支付还是异步通知  
30 - */  
31 - private PayOrderRespDTO order;  
32 -  
33 - public PayOrderUnifiedRespDTO(String displayMode, String displayContent) {  
34 - this.displayMode = displayMode;  
35 - this.displayContent = displayContent;  
36 - }  
37 -  
38 -}  
yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/AbstractPayClient.java
1 package cn.iocoder.yudao.framework.pay.core.client.impl; 1 package cn.iocoder.yudao.framework.pay.core.client.impl;
2 2
3 import cn.iocoder.yudao.framework.common.exception.ServiceException; 3 import cn.iocoder.yudao.framework.common.exception.ServiceException;
  4 +import cn.iocoder.yudao.framework.common.util.validation.ValidationUtils;
4 import cn.iocoder.yudao.framework.pay.core.client.PayClient; 5 import cn.iocoder.yudao.framework.pay.core.client.PayClient;
5 import cn.iocoder.yudao.framework.pay.core.client.PayClientConfig; 6 import cn.iocoder.yudao.framework.pay.core.client.PayClientConfig;
  7 +import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderRespDTO;
6 import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderUnifiedReqDTO; 8 import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderUnifiedReqDTO;
7 -import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderUnifiedRespDTO;  
8 import cn.iocoder.yudao.framework.pay.core.client.dto.refund.PayRefundRespDTO; 9 import cn.iocoder.yudao.framework.pay.core.client.dto.refund.PayRefundRespDTO;
9 import cn.iocoder.yudao.framework.pay.core.client.dto.refund.PayRefundUnifiedReqDTO; 10 import cn.iocoder.yudao.framework.pay.core.client.dto.refund.PayRefundUnifiedReqDTO;
10 import cn.iocoder.yudao.framework.pay.core.client.exception.PayException; 11 import cn.iocoder.yudao.framework.pay.core.client.exception.PayException;
11 import lombok.extern.slf4j.Slf4j; 12 import lombok.extern.slf4j.Slf4j;
12 13
13 -import javax.validation.Validation; 14 +import java.util.Map;
14 15
15 import static cn.iocoder.yudao.framework.common.util.json.JsonUtils.toJsonString; 16 import static cn.iocoder.yudao.framework.common.util.json.JsonUtils.toJsonString;
16 17
@@ -29,6 +30,7 @@ public abstract class AbstractPayClient<Config extends PayClientConfig> implemen @@ -29,6 +30,7 @@ public abstract class AbstractPayClient<Config extends PayClientConfig> implemen
29 /** 30 /**
30 * 渠道编码 31 * 渠道编码
31 */ 32 */
  33 + @SuppressWarnings("FieldCanBeLocal")
32 private final String channelCode; 34 private final String channelCode;
33 /** 35 /**
34 * 支付配置 36 * 支付配置
@@ -73,31 +75,42 @@ public abstract class AbstractPayClient<Config extends PayClientConfig> implemen @@ -73,31 +75,42 @@ public abstract class AbstractPayClient<Config extends PayClientConfig> implemen
73 // ============ 支付相关 ========== 75 // ============ 支付相关 ==========
74 76
75 @Override 77 @Override
76 - public final PayOrderUnifiedRespDTO unifiedOrder(PayOrderUnifiedReqDTO reqDTO) {  
77 - Validation.buildDefaultValidatorFactory().getValidator().validate(reqDTO); 78 + public final PayOrderRespDTO unifiedOrder(PayOrderUnifiedReqDTO reqDTO) {
  79 + ValidationUtils.validate(reqDTO);
78 // 执行统一下单 80 // 执行统一下单
79 - PayOrderUnifiedRespDTO resp; 81 + PayOrderRespDTO resp;
80 try { 82 try {
81 resp = doUnifiedOrder(reqDTO); 83 resp = doUnifiedOrder(reqDTO);
82 - } catch (ServiceException ex) {  
83 - // 业务异常,都是实现类已经翻译,所以直接抛出即可  
84 - throw ex;  
85 } catch (Throwable ex) { 84 } catch (Throwable ex) {
86 // 系统异常,则包装成 PayException 异常抛出 85 // 系统异常,则包装成 PayException 异常抛出
87 - log.error("[unifiedRefund][request({}) 发起支付异常]", toJsonString(reqDTO), ex);  
88 - throw buildException(ex); 86 + log.error("[unifiedRefund][客户端({}) request({}) 发起支付异常]",
  87 + getId(), toJsonString(reqDTO), ex);
  88 + throw buildPayException(ex);
89 } 89 }
90 return resp; 90 return resp;
91 } 91 }
92 92
93 - protected abstract PayOrderUnifiedRespDTO doUnifiedOrder(PayOrderUnifiedReqDTO reqDTO) 93 + protected abstract PayOrderRespDTO doUnifiedOrder(PayOrderUnifiedReqDTO reqDTO)
  94 + throws Throwable;
  95 +
  96 + @Override
  97 + public PayOrderRespDTO parseOrderNotify(Map<String, String> params, String body) {
  98 + try {
  99 + return doParseOrderNotify(params, body);
  100 + } catch (Throwable ex) {
  101 + log.error("[parseOrderNotify][params({}) body({}) 解析失败]", params, body, ex);
  102 + throw buildPayException(ex);
  103 + }
  104 + }
  105 +
  106 + protected abstract PayOrderRespDTO doParseOrderNotify(Map<String, String> params, String body)
94 throws Throwable; 107 throws Throwable;
95 108
96 // ============ 退款相关 ========== 109 // ============ 退款相关 ==========
97 110
98 @Override 111 @Override
99 public PayRefundRespDTO unifiedRefund(PayRefundUnifiedReqDTO reqDTO) { 112 public PayRefundRespDTO unifiedRefund(PayRefundUnifiedReqDTO reqDTO) {
100 - Validation.buildDefaultValidatorFactory().getValidator().validate(reqDTO); 113 + ValidationUtils.validate(reqDTO);
101 // 执行统一退款 114 // 执行统一退款
102 PayRefundRespDTO resp; 115 PayRefundRespDTO resp;
103 try { 116 try {
@@ -107,8 +120,9 @@ public abstract class AbstractPayClient&lt;Config extends PayClientConfig&gt; implemen @@ -107,8 +120,9 @@ public abstract class AbstractPayClient&lt;Config extends PayClientConfig&gt; implemen
107 throw ex; 120 throw ex;
108 } catch (Throwable ex) { 121 } catch (Throwable ex) {
109 // 系统异常,则包装成 PayException 异常抛出 122 // 系统异常,则包装成 PayException 异常抛出
110 - log.error("[unifiedRefund][request({}) 发起退款异常]", toJsonString(reqDTO), ex);  
111 - throw buildException(ex); 123 + log.error("[unifiedRefund][客户端({}) request({}) 发起退款异常]",
  124 + getId(), toJsonString(reqDTO), ex);
  125 + throw buildPayException(ex);
112 } 126 }
113 return resp; 127 return resp;
114 } 128 }
@@ -117,7 +131,7 @@ public abstract class AbstractPayClient&lt;Config extends PayClientConfig&gt; implemen @@ -117,7 +131,7 @@ public abstract class AbstractPayClient&lt;Config extends PayClientConfig&gt; implemen
117 131
118 // ========== 各种工具方法 ========== 132 // ========== 各种工具方法 ==========
119 133
120 - private PayException buildException(Throwable ex) { 134 + private PayException buildPayException(Throwable ex) {
121 if (ex instanceof PayException) { 135 if (ex instanceof PayException) {
122 return (PayException) ex; 136 return (PayException) ex;
123 } 137 }
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; @@ -5,8 +5,8 @@ import cn.hutool.core.date.LocalDateTimeUtil;
5 import cn.hutool.core.lang.Assert; 5 import cn.hutool.core.lang.Assert;
6 import cn.hutool.core.util.StrUtil; 6 import cn.hutool.core.util.StrUtil;
7 import cn.hutool.http.HttpUtil; 7 import cn.hutool.http.HttpUtil;
8 -import cn.iocoder.yudao.framework.common.util.json.JsonUtils;  
9 import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderRespDTO; 8 import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderRespDTO;
  9 +import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderUnifiedReqDTO;
10 import cn.iocoder.yudao.framework.pay.core.client.dto.refund.PayRefundRespDTO; 10 import cn.iocoder.yudao.framework.pay.core.client.dto.refund.PayRefundRespDTO;
11 import cn.iocoder.yudao.framework.pay.core.client.dto.refund.PayRefundUnifiedReqDTO; 11 import cn.iocoder.yudao.framework.pay.core.client.dto.refund.PayRefundUnifiedReqDTO;
12 import cn.iocoder.yudao.framework.pay.core.client.impl.AbstractPayClient; 12 import cn.iocoder.yudao.framework.pay.core.client.impl.AbstractPayClient;
@@ -29,9 +29,7 @@ import java.util.Objects; @@ -29,9 +29,7 @@ import java.util.Objects;
29 import java.util.function.Supplier; 29 import java.util.function.Supplier;
30 30
31 import static cn.hutool.core.date.DatePattern.NORM_DATETIME_FORMATTER; 31 import static cn.hutool.core.date.DatePattern.NORM_DATETIME_FORMATTER;
32 -import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception0;  
33 import static cn.iocoder.yudao.framework.common.util.json.JsonUtils.toJsonString; 32 import static cn.iocoder.yudao.framework.common.util.json.JsonUtils.toJsonString;
34 -import static cn.iocoder.yudao.framework.pay.core.enums.PayFrameworkErrorCodeConstants.ORDER_UNIFIED_ERROR;  
35 33
36 /** 34 /**
37 * 支付宝抽象类,实现支付宝统一的接口、以及部分实现(退款) 35 * 支付宝抽象类,实现支付宝统一的接口、以及部分实现(退款)
@@ -55,6 +53,40 @@ public abstract class AbstractAlipayPayClient extends AbstractPayClient&lt;AlipayPa @@ -55,6 +53,40 @@ public abstract class AbstractAlipayPayClient extends AbstractPayClient&lt;AlipayPa
55 this.client = new DefaultAlipayClient(alipayConfig); 53 this.client = new DefaultAlipayClient(alipayConfig);
56 } 54 }
57 55
  56 + // ============ 支付相关 ==========
  57 +
  58 + /**
  59 + * 构造支付关闭的 {@link PayOrderRespDTO} 对象
  60 + *
  61 + * @return 支付关闭的 {@link PayOrderRespDTO} 对象
  62 + */
  63 + protected PayOrderRespDTO buildClosedPayOrderRespDTO(PayOrderUnifiedReqDTO reqDTO, AlipayResponse response) {
  64 + Assert.isFalse(response.isSuccess());
  65 + return PayOrderRespDTO.build(response.getSubCode(), response.getSubMsg(),
  66 + reqDTO.getOutTradeNo(), response);
  67 + }
  68 +
  69 + @Override
  70 + public PayOrderRespDTO doParseOrderNotify(Map<String, String> params, String body) throws Throwable {
  71 + // 1. 校验回调数据
  72 + Map<String, String> bodyObj = HttpUtil.decodeParamMap(body, StandardCharsets.UTF_8);
  73 + AlipaySignature.rsaCheckV1(bodyObj, config.getAlipayPublicKey(),
  74 + StandardCharsets.UTF_8.name(), config.getSignType());
  75 +
  76 + // 2. 解析订单的状态
  77 + String tradeStatus = bodyObj.get("trade_status");
  78 + Integer status = Objects.equals("WAIT_BUYER_PAY", tradeStatus) ? PayOrderStatusRespEnum.WAITING.getStatus()
  79 + : Objects.equals("TRADE_SUCCESS", tradeStatus) ? PayOrderStatusRespEnum.SUCCESS.getStatus()
  80 + : Objects.equals("TRADE_CLOSED", tradeStatus) ? PayOrderStatusRespEnum.CLOSED.getStatus() : null;
  81 + Assert.notNull(status, (Supplier<Throwable>) () -> {
  82 + throw new IllegalArgumentException(StrUtil.format("body({}) 的 trade_status 不正确", body));
  83 + });
  84 + return new PayOrderRespDTO(status, bodyObj.get("trade_no"), bodyObj.get("seller_id"), parseTime(params.get("gmt_payment")),
  85 + bodyObj.get("out_trade_no"), body);
  86 + }
  87 +
  88 + // ============ 退款相关 ==========
  89 +
58 /** 90 /**
59 * 支付宝统一的退款接口 alipay.trade.refund 91 * 支付宝统一的退款接口 alipay.trade.refund
60 * 92 *
@@ -96,32 +128,6 @@ public abstract class AbstractAlipayPayClient extends AbstractPayClient&lt;AlipayPa @@ -96,32 +128,6 @@ public abstract class AbstractAlipayPayClient extends AbstractPayClient&lt;AlipayPa
96 } 128 }
97 129
98 @Override 130 @Override
99 - @SneakyThrows  
100 - public PayOrderRespDTO parseOrderNotify(Map<String, String> params, String body) {  
101 - // 1. 校验回调数据  
102 - Map<String, String> bodyObj = HttpUtil.decodeParamMap(body, StandardCharsets.UTF_8);  
103 - AlipaySignature.rsaCheckV1(bodyObj, config.getAlipayPublicKey(),  
104 - StandardCharsets.UTF_8.name(), config.getSignType());  
105 -  
106 - // 2. 解析订单的状态  
107 - String tradeStatus = bodyObj.get("trade_status");  
108 - PayOrderStatusRespEnum status = Objects.equals("WAIT_BUYER_PAY", tradeStatus) ? PayOrderStatusRespEnum.WAITING  
109 - : Objects.equals("TRADE_SUCCESS", tradeStatus) ? PayOrderStatusRespEnum.SUCCESS  
110 - : Objects.equals("TRADE_CLOSED", tradeStatus) ? PayOrderStatusRespEnum.CLOSED : null;  
111 - Assert.notNull(status, (Supplier<Throwable>) () -> {  
112 - throw new IllegalArgumentException(StrUtil.format("body({}) 的 trade_status 不正确", body));  
113 - });  
114 - return PayOrderRespDTO.builder()  
115 - .status(Objects.requireNonNull(status).getStatus())  
116 - .outTradeNo(bodyObj.get("out_trade_no"))  
117 - .channelOrderNo(bodyObj.get("trade_no"))  
118 - .channelUserId(bodyObj.get("seller_id"))  
119 - .successTime(parseTime(params.get("gmt_payment")))  
120 - .rawData(body)  
121 - .build();  
122 - }  
123 -  
124 - @Override  
125 public PayRefundRespDTO parseRefundNotify(Map<String, String> params, String body) { 131 public PayRefundRespDTO parseRefundNotify(Map<String, String> params, String body) {
126 // 补充说明:支付宝退款时,没有回调,这点和微信支付是不同的。并且,退款分成部分退款、和全部退款。 132 // 补充说明:支付宝退款时,没有回调,这点和微信支付是不同的。并且,退款分成部分退款、和全部退款。
127 // ① 部分退款:是会有回调,但是它回调的是订单状态的同步回调,不是退款订单的回调 133 // ① 部分退款:是会有回调,但是它回调的是订单状态的同步回调,不是退款订单的回调
@@ -145,21 +151,4 @@ public abstract class AbstractAlipayPayClient extends AbstractPayClient&lt;AlipayPa @@ -145,21 +151,4 @@ public abstract class AbstractAlipayPayClient extends AbstractPayClient&lt;AlipayPa
145 return LocalDateTimeUtil.parse(str, NORM_DATETIME_FORMATTER); 151 return LocalDateTimeUtil.parse(str, NORM_DATETIME_FORMATTER);
146 } 152 }
147 153
148 - /**  
149 - * 校验支付宝统一下单的响应  
150 - *  
151 - * 如果校验不通过,则抛出 {@link cn.iocoder.yudao.framework.common.exception.ServiceException} 异常  
152 - *  
153 - * @param request 请求  
154 - * @param response 响应  
155 - */  
156 - protected void validateUnifiedOrderResponse(Object request, AlipayResponse response) {  
157 - if (response.isSuccess()) {  
158 - return;  
159 - }  
160 - log.error("[validateUnifiedOrderResponse][发起支付失败,request({}),response({})]",  
161 - JsonUtils.toJsonString(request), JsonUtils.toJsonString(response));  
162 - throw exception0(ORDER_UNIFIED_ERROR.getCode(), response.getSubMsg());  
163 - }  
164 -  
165 } 154 }
yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/alipay/AlipayAppPayClient.java
1 package cn.iocoder.yudao.framework.pay.core.client.impl.alipay; 1 package cn.iocoder.yudao.framework.pay.core.client.impl.alipay;
2 2
  3 +import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderRespDTO;
3 import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderUnifiedReqDTO; 4 import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderUnifiedReqDTO;
4 -import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderUnifiedRespDTO;  
5 import cn.iocoder.yudao.framework.pay.core.enums.channel.PayChannelEnum; 5 import cn.iocoder.yudao.framework.pay.core.enums.channel.PayChannelEnum;
6 import cn.iocoder.yudao.framework.pay.core.enums.order.PayOrderDisplayModeEnum; 6 import cn.iocoder.yudao.framework.pay.core.enums.order.PayOrderDisplayModeEnum;
7 import com.alipay.api.AlipayApiException; 7 import com.alipay.api.AlipayApiException;
@@ -27,7 +27,7 @@ public class AlipayAppPayClient extends AbstractAlipayPayClient { @@ -27,7 +27,7 @@ public class AlipayAppPayClient extends AbstractAlipayPayClient {
27 } 27 }
28 28
29 @Override 29 @Override
30 - public PayOrderUnifiedRespDTO doUnifiedOrder(PayOrderUnifiedReqDTO reqDTO) throws AlipayApiException { 30 + public PayOrderRespDTO doUnifiedOrder(PayOrderUnifiedReqDTO reqDTO) throws AlipayApiException {
31 // 1.1 构建 AlipayTradeAppPayModel 请求 31 // 1.1 构建 AlipayTradeAppPayModel 请求
32 AlipayTradeAppPayModel model = new AlipayTradeAppPayModel(); 32 AlipayTradeAppPayModel model = new AlipayTradeAppPayModel();
33 // ① 通用的参数 33 // ① 通用的参数
@@ -49,8 +49,11 @@ public class AlipayAppPayClient extends AbstractAlipayPayClient { @@ -49,8 +49,11 @@ public class AlipayAppPayClient extends AbstractAlipayPayClient {
49 // 2.1 执行请求 49 // 2.1 执行请求
50 AlipayTradeAppPayResponse response = client.execute(request); 50 AlipayTradeAppPayResponse response = client.execute(request);
51 // 2.2 处理结果 51 // 2.2 处理结果
52 - validateUnifiedOrderResponse(request, response);  
53 - return new PayOrderUnifiedRespDTO(displayMode, ""); 52 + if (!response.isSuccess()) {
  53 + return buildClosedPayOrderRespDTO(reqDTO, response);
  54 + }
  55 + return new PayOrderRespDTO(displayMode, "",
  56 + reqDTO.getOutTradeNo(), response);
54 } 57 }
55 58
56 } 59 }
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; @@ -2,8 +2,8 @@ package cn.iocoder.yudao.framework.pay.core.client.impl.alipay;
2 2
3 import cn.hutool.core.map.MapUtil; 3 import cn.hutool.core.map.MapUtil;
4 import cn.hutool.core.util.StrUtil; 4 import cn.hutool.core.util.StrUtil;
  5 +import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderRespDTO;
5 import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderUnifiedReqDTO; 6 import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderUnifiedReqDTO;
6 -import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderUnifiedRespDTO;  
7 import cn.iocoder.yudao.framework.pay.core.enums.channel.PayChannelEnum; 7 import cn.iocoder.yudao.framework.pay.core.enums.channel.PayChannelEnum;
8 import cn.iocoder.yudao.framework.pay.core.enums.order.PayOrderDisplayModeEnum; 8 import cn.iocoder.yudao.framework.pay.core.enums.order.PayOrderDisplayModeEnum;
9 import com.alipay.api.AlipayApiException; 9 import com.alipay.api.AlipayApiException;
@@ -30,7 +30,7 @@ public class AlipayBarPayClient extends AbstractAlipayPayClient { @@ -30,7 +30,7 @@ public class AlipayBarPayClient extends AbstractAlipayPayClient {
30 } 30 }
31 31
32 @Override 32 @Override
33 - public PayOrderUnifiedRespDTO doUnifiedOrder(PayOrderUnifiedReqDTO reqDTO) throws AlipayApiException { 33 + public PayOrderRespDTO doUnifiedOrder(PayOrderUnifiedReqDTO reqDTO) throws AlipayApiException {
34 String authCode = MapUtil.getStr(reqDTO.getChannelExtras(), "auth_code"); 34 String authCode = MapUtil.getStr(reqDTO.getChannelExtras(), "auth_code");
35 if (StrUtil.isEmpty(authCode)) { 35 if (StrUtil.isEmpty(authCode)) {
36 throw exception0(BAD_REQUEST.getCode(), "条形码不能为空"); 36 throw exception0(BAD_REQUEST.getCode(), "条形码不能为空");
@@ -55,11 +55,15 @@ public class AlipayBarPayClient extends AbstractAlipayPayClient { @@ -55,11 +55,15 @@ public class AlipayBarPayClient extends AbstractAlipayPayClient {
55 request.setNotifyUrl(reqDTO.getNotifyUrl()); 55 request.setNotifyUrl(reqDTO.getNotifyUrl());
56 request.setReturnUrl(reqDTO.getReturnUrl()); 56 request.setReturnUrl(reqDTO.getReturnUrl());
57 57
  58 + // TODO 芋艿:各种边界的处理
58 // 2.1 执行请求 59 // 2.1 执行请求
59 AlipayTradePayResponse response = client.execute(request); 60 AlipayTradePayResponse response = client.execute(request);
60 // 2.2 处理结果 61 // 2.2 处理结果
61 - validateUnifiedOrderResponse(request, response);  
62 - return new PayOrderUnifiedRespDTO(displayMode, ""); 62 + if (!response.isSuccess()) {
  63 + return buildClosedPayOrderRespDTO(reqDTO, response);
  64 + }
  65 + return new PayOrderRespDTO(displayMode, "",
  66 + reqDTO.getOutTradeNo(), response);
63 } 67 }
64 68
65 } 69 }
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; @@ -2,8 +2,8 @@ package cn.iocoder.yudao.framework.pay.core.client.impl.alipay;
2 2
3 import cn.hutool.core.util.ObjectUtil; 3 import cn.hutool.core.util.ObjectUtil;
4 import cn.hutool.http.Method; 4 import cn.hutool.http.Method;
  5 +import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderRespDTO;
5 import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderUnifiedReqDTO; 6 import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderUnifiedReqDTO;
6 -import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderUnifiedRespDTO;  
7 import cn.iocoder.yudao.framework.pay.core.enums.channel.PayChannelEnum; 7 import cn.iocoder.yudao.framework.pay.core.enums.channel.PayChannelEnum;
8 import cn.iocoder.yudao.framework.pay.core.enums.order.PayOrderDisplayModeEnum; 8 import cn.iocoder.yudao.framework.pay.core.enums.order.PayOrderDisplayModeEnum;
9 import com.alipay.api.AlipayApiException; 9 import com.alipay.api.AlipayApiException;
@@ -29,7 +29,7 @@ public class AlipayPcPayClient extends AbstractAlipayPayClient { @@ -29,7 +29,7 @@ public class AlipayPcPayClient extends AbstractAlipayPayClient {
29 } 29 }
30 30
31 @Override 31 @Override
32 - public PayOrderUnifiedRespDTO doUnifiedOrder(PayOrderUnifiedReqDTO reqDTO) throws AlipayApiException { 32 + public PayOrderRespDTO doUnifiedOrder(PayOrderUnifiedReqDTO reqDTO) throws AlipayApiException {
33 // 1.1 构建 AlipayTradePagePayModel 请求 33 // 1.1 构建 AlipayTradePagePayModel 请求
34 AlipayTradePagePayModel model = new AlipayTradePagePayModel(); 34 AlipayTradePagePayModel model = new AlipayTradePagePayModel();
35 // ① 通用的参数 35 // ① 通用的参数
@@ -60,8 +60,11 @@ public class AlipayPcPayClient extends AbstractAlipayPayClient { @@ -60,8 +60,11 @@ public class AlipayPcPayClient extends AbstractAlipayPayClient {
60 response = client.pageExecute(request, Method.GET.name()); 60 response = client.pageExecute(request, Method.GET.name());
61 } 61 }
62 // 2.2 处理结果 62 // 2.2 处理结果
63 - validateUnifiedOrderResponse(request, response);  
64 - return new PayOrderUnifiedRespDTO(displayMode, response.getBody()); 63 + if (!response.isSuccess()) {
  64 + return buildClosedPayOrderRespDTO(reqDTO, response);
  65 + }
  66 + return new PayOrderRespDTO(displayMode, response.getBody(),
  67 + reqDTO.getOutTradeNo(), response);
65 } 68 }
66 69
67 } 70 }
yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/alipay/AlipayQrPayClient.java
1 package cn.iocoder.yudao.framework.pay.core.client.impl.alipay; 1 package cn.iocoder.yudao.framework.pay.core.client.impl.alipay;
2 2
  3 +import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderRespDTO;
3 import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderUnifiedReqDTO; 4 import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderUnifiedReqDTO;
4 -import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderUnifiedRespDTO;  
5 import cn.iocoder.yudao.framework.pay.core.enums.channel.PayChannelEnum; 5 import cn.iocoder.yudao.framework.pay.core.enums.channel.PayChannelEnum;
6 import cn.iocoder.yudao.framework.pay.core.enums.order.PayOrderDisplayModeEnum; 6 import cn.iocoder.yudao.framework.pay.core.enums.order.PayOrderDisplayModeEnum;
7 import com.alipay.api.AlipayApiException; 7 import com.alipay.api.AlipayApiException;
@@ -25,7 +25,7 @@ public class AlipayQrPayClient extends AbstractAlipayPayClient { @@ -25,7 +25,7 @@ public class AlipayQrPayClient extends AbstractAlipayPayClient {
25 } 25 }
26 26
27 @Override 27 @Override
28 - public PayOrderUnifiedRespDTO doUnifiedOrder(PayOrderUnifiedReqDTO reqDTO) throws AlipayApiException { 28 + public PayOrderRespDTO doUnifiedOrder(PayOrderUnifiedReqDTO reqDTO) throws AlipayApiException {
29 // 1.1 构建 AlipayTradePrecreateModel 请求 29 // 1.1 构建 AlipayTradePrecreateModel 请求
30 AlipayTradePrecreateModel model = new AlipayTradePrecreateModel(); 30 AlipayTradePrecreateModel model = new AlipayTradePrecreateModel();
31 // ① 通用的参数 31 // ① 通用的参数
@@ -47,8 +47,11 @@ public class AlipayQrPayClient extends AbstractAlipayPayClient { @@ -47,8 +47,11 @@ public class AlipayQrPayClient extends AbstractAlipayPayClient {
47 // 2.1 执行请求 47 // 2.1 执行请求
48 AlipayTradePrecreateResponse response = client.execute(request); 48 AlipayTradePrecreateResponse response = client.execute(request);
49 // 2.2 处理结果 49 // 2.2 处理结果
50 - validateUnifiedOrderResponse(request, response);  
51 - return new PayOrderUnifiedRespDTO(displayMode, response.getQrCode()); 50 + if (!response.isSuccess()) {
  51 + return buildClosedPayOrderRespDTO(reqDTO, response);
  52 + }
  53 + return new PayOrderRespDTO(displayMode, response.getQrCode(),
  54 + reqDTO.getOutTradeNo(), response);
52 } 55 }
53 56
54 } 57 }
yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/alipay/AlipayWapPayClient.java
1 package cn.iocoder.yudao.framework.pay.core.client.impl.alipay; 1 package cn.iocoder.yudao.framework.pay.core.client.impl.alipay;
2 2
3 import cn.hutool.http.Method; 3 import cn.hutool.http.Method;
  4 +import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderRespDTO;
4 import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderUnifiedReqDTO; 5 import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderUnifiedReqDTO;
5 -import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderUnifiedRespDTO;  
6 import cn.iocoder.yudao.framework.pay.core.enums.channel.PayChannelEnum; 6 import cn.iocoder.yudao.framework.pay.core.enums.channel.PayChannelEnum;
7 import cn.iocoder.yudao.framework.pay.core.enums.order.PayOrderDisplayModeEnum; 7 import cn.iocoder.yudao.framework.pay.core.enums.order.PayOrderDisplayModeEnum;
8 import com.alipay.api.AlipayApiException; 8 import com.alipay.api.AlipayApiException;
@@ -26,7 +26,7 @@ public class AlipayWapPayClient extends AbstractAlipayPayClient { @@ -26,7 +26,7 @@ public class AlipayWapPayClient extends AbstractAlipayPayClient {
26 } 26 }
27 27
28 @Override 28 @Override
29 - public PayOrderUnifiedRespDTO doUnifiedOrder(PayOrderUnifiedReqDTO reqDTO) throws AlipayApiException { 29 + public PayOrderRespDTO doUnifiedOrder(PayOrderUnifiedReqDTO reqDTO) throws AlipayApiException {
30 // 1.1 构建 AlipayTradeWapPayModel 请求 30 // 1.1 构建 AlipayTradeWapPayModel 请求
31 AlipayTradeWapPayModel model = new AlipayTradeWapPayModel(); 31 AlipayTradeWapPayModel model = new AlipayTradeWapPayModel();
32 // ① 通用的参数 32 // ① 通用的参数
@@ -48,10 +48,12 @@ public class AlipayWapPayClient extends AbstractAlipayPayClient { @@ -48,10 +48,12 @@ public class AlipayWapPayClient extends AbstractAlipayPayClient {
48 48
49 // 2.1 执行请求 49 // 2.1 执行请求
50 AlipayTradeWapPayResponse response = client.pageExecute(request, Method.GET.name()); 50 AlipayTradeWapPayResponse response = client.pageExecute(request, Method.GET.name());
51 -  
52 // 2.2 处理结果 51 // 2.2 处理结果
53 - validateUnifiedOrderResponse(request, response);  
54 - return new PayOrderUnifiedRespDTO(displayMode, response.getBody()); 52 + if (!response.isSuccess()) {
  53 + return buildClosedPayOrderRespDTO(reqDTO, response);
  54 + }
  55 + return new PayOrderRespDTO(displayMode, response.getBody(),
  56 + reqDTO.getOutTradeNo(), response);
55 } 57 }
56 58
57 } 59 }
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; @@ -8,11 +8,9 @@ import cn.hutool.core.util.StrUtil;
8 import cn.iocoder.yudao.framework.common.util.io.FileUtils; 8 import cn.iocoder.yudao.framework.common.util.io.FileUtils;
9 import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderRespDTO; 9 import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderRespDTO;
10 import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderUnifiedReqDTO; 10 import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderUnifiedReqDTO;
11 -import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderUnifiedRespDTO;  
12 import cn.iocoder.yudao.framework.pay.core.client.dto.refund.PayRefundRespDTO; 11 import cn.iocoder.yudao.framework.pay.core.client.dto.refund.PayRefundRespDTO;
13 import cn.iocoder.yudao.framework.pay.core.client.dto.refund.PayRefundUnifiedReqDTO; 12 import cn.iocoder.yudao.framework.pay.core.client.dto.refund.PayRefundUnifiedReqDTO;
14 import cn.iocoder.yudao.framework.pay.core.client.impl.AbstractPayClient; 13 import cn.iocoder.yudao.framework.pay.core.client.impl.AbstractPayClient;
15 -import cn.iocoder.yudao.framework.pay.core.enums.PayFrameworkErrorCodeConstants;  
16 import cn.iocoder.yudao.framework.pay.core.enums.order.PayOrderStatusRespEnum; 14 import cn.iocoder.yudao.framework.pay.core.enums.order.PayOrderStatusRespEnum;
17 import cn.iocoder.yudao.framework.pay.core.enums.refund.PayRefundStatusRespEnum; 15 import cn.iocoder.yudao.framework.pay.core.enums.refund.PayRefundStatusRespEnum;
18 import com.github.binarywang.wxpay.bean.notify.WxPayOrderNotifyResult; 16 import com.github.binarywang.wxpay.bean.notify.WxPayOrderNotifyResult;
@@ -35,9 +33,6 @@ import java.util.Map; @@ -35,9 +33,6 @@ import java.util.Map;
35 import java.util.Objects; 33 import java.util.Objects;
36 34
37 import static cn.hutool.core.date.DatePattern.*; 35 import static cn.hutool.core.date.DatePattern.*;
38 -import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;  
39 -import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.invalidParamException;  
40 -import static cn.iocoder.yudao.framework.common.util.json.JsonUtils.toJsonString;  
41 import static cn.iocoder.yudao.framework.pay.core.client.impl.weixin.WxPayClientConfig.API_VERSION_V2; 36 import static cn.iocoder.yudao.framework.pay.core.client.impl.weixin.WxPayClientConfig.API_VERSION_V2;
42 37
43 /** 38 /**
@@ -83,7 +78,7 @@ public abstract class AbstractWxPayClient extends AbstractPayClient&lt;WxPayClientC @@ -83,7 +78,7 @@ public abstract class AbstractWxPayClient extends AbstractPayClient&lt;WxPayClientC
83 // ============ 支付相关 ========== 78 // ============ 支付相关 ==========
84 79
85 @Override 80 @Override
86 - protected PayOrderUnifiedRespDTO doUnifiedOrder(PayOrderUnifiedReqDTO reqDTO) throws Exception { 81 + protected PayOrderRespDTO doUnifiedOrder(PayOrderUnifiedReqDTO reqDTO) throws Exception {
87 try { 82 try {
88 switch (config.getApiVersion()) { 83 switch (config.getApiVersion()) {
89 case API_VERSION_V2: 84 case API_VERSION_V2:
@@ -94,8 +89,10 @@ public abstract class AbstractWxPayClient extends AbstractPayClient&lt;WxPayClientC @@ -94,8 +89,10 @@ public abstract class AbstractWxPayClient extends AbstractPayClient&lt;WxPayClientC
94 throw new IllegalArgumentException(String.format("未知的 API 版本(%s)", config.getApiVersion())); 89 throw new IllegalArgumentException(String.format("未知的 API 版本(%s)", config.getApiVersion()));
95 } 90 }
96 } catch (WxPayException e) { 91 } catch (WxPayException e) {
97 - // todo 芋艿:异常的处理;  
98 - throw buildUnifiedOrderException(reqDTO, e); 92 + String errorCode = getErrorCode(e);
  93 + String errorMessage = getErrorMessage(e);
  94 + return PayOrderRespDTO.build(errorCode, errorMessage,
  95 + reqDTO.getOutTradeNo(), e.getXmlString());
99 } 96 }
100 } 97 }
101 98
@@ -105,8 +102,8 @@ public abstract class AbstractWxPayClient extends AbstractPayClient&lt;WxPayClientC @@ -105,8 +102,8 @@ public abstract class AbstractWxPayClient extends AbstractPayClient&lt;WxPayClientC
105 * @param reqDTO 下单信息 102 * @param reqDTO 下单信息
106 * @return 各支付渠道的返回结果 103 * @return 各支付渠道的返回结果
107 */ 104 */
108 - protected abstract PayOrderUnifiedRespDTO doUnifiedOrderV2(PayOrderUnifiedReqDTO reqDTO)  
109 - throws WxPayException; 105 + protected abstract PayOrderRespDTO doUnifiedOrderV2(PayOrderUnifiedReqDTO reqDTO)
  106 + throws Exception;
110 107
111 /** 108 /**
112 * 【V3】调用支付渠道,统一下单 109 * 【V3】调用支付渠道,统一下单
@@ -114,57 +111,42 @@ public abstract class AbstractWxPayClient extends AbstractPayClient&lt;WxPayClientC @@ -114,57 +111,42 @@ public abstract class AbstractWxPayClient extends AbstractPayClient&lt;WxPayClientC
114 * @param reqDTO 下单信息 111 * @param reqDTO 下单信息
115 * @return 各支付渠道的返回结果 112 * @return 各支付渠道的返回结果
116 */ 113 */
117 - protected abstract PayOrderUnifiedRespDTO doUnifiedOrderV3(PayOrderUnifiedReqDTO reqDTO) 114 + protected abstract PayOrderRespDTO doUnifiedOrderV3(PayOrderUnifiedReqDTO reqDTO)
118 throws WxPayException; 115 throws WxPayException;
119 116
120 @Override 117 @Override
121 - public PayOrderRespDTO parseOrderNotify(Map<String, String> params, String body) {  
122 - try {  
123 - // 微信支付 v2 回调结果处理  
124 - switch (config.getApiVersion()) {  
125 - case API_VERSION_V2:  
126 - return parseOrderNotifyV2(body);  
127 - case WxPayClientConfig.API_VERSION_V3:  
128 - return parseOrderNotifyV3(body);  
129 - default:  
130 - throw new IllegalArgumentException(String.format("未知的 API 版本(%s)", config.getApiVersion()));  
131 - }  
132 - } catch (WxPayException e) {  
133 - log.error("[parseNotify][params({}) body({}) 解析失败]", params, body, e);  
134 -// throw buildPayException(e);  
135 - throw new RuntimeException(e);  
136 - // TODO 芋艿:缺一个异常翻译 118 + public PayOrderRespDTO doParseOrderNotify(Map<String, String> params, String body) throws WxPayException {
  119 + // 微信支付 v2 回调结果处理
  120 + switch (config.getApiVersion()) {
  121 + case API_VERSION_V2:
  122 + return doParseOrderNotifyV2(body);
  123 + case WxPayClientConfig.API_VERSION_V3:
  124 + return doParseOrderNotifyV3(body);
  125 + default:
  126 + throw new IllegalArgumentException(String.format("未知的 API 版本(%s)", config.getApiVersion()));
137 } 127 }
138 } 128 }
139 129
140 - private PayOrderRespDTO parseOrderNotifyV2(String body) throws WxPayException { 130 + private PayOrderRespDTO doParseOrderNotifyV2(String body) throws WxPayException {
141 // 1. 解析回调 131 // 1. 解析回调
142 WxPayOrderNotifyResult response = client.parseOrderNotifyResult(body); 132 WxPayOrderNotifyResult response = client.parseOrderNotifyResult(body);
143 // 2. 构建结果 133 // 2. 构建结果
144 - return PayOrderRespDTO.builder()  
145 - .outTradeNo(response.getOutTradeNo())  
146 - .channelOrderNo(response.getTransactionId())  
147 - .channelUserId(response.getOpenid())  
148 - .status(Objects.equals(response.getResultCode(), "SUCCESS") ?  
149 - PayOrderStatusRespEnum.SUCCESS.getStatus() : PayOrderStatusRespEnum.CLOSED.getStatus())  
150 - .successTime(parseDateV2(response.getTimeEnd()))  
151 - .rawData(response)  
152 - .build(); 134 + Integer status = Objects.equals(response.getResultCode(), "SUCCESS") ?
  135 + PayOrderStatusRespEnum.SUCCESS.getStatus() : PayOrderStatusRespEnum.CLOSED.getStatus();
  136 + return new PayOrderRespDTO(status, response.getTransactionId(), response.getOpenid(), parseDateV2(response.getTimeEnd()),
  137 + response.getOutTradeNo(), body);
153 } 138 }
154 139
155 - private PayOrderRespDTO parseOrderNotifyV3(String body) throws WxPayException { 140 + private PayOrderRespDTO doParseOrderNotifyV3(String body) throws WxPayException {
156 // 1. 解析回调 141 // 1. 解析回调
157 WxPayOrderNotifyV3Result response = client.parseOrderNotifyV3Result(body, null); 142 WxPayOrderNotifyV3Result response = client.parseOrderNotifyV3Result(body, null);
158 - WxPayOrderNotifyV3Result.DecryptNotifyResult responseResult = response.getResult(); 143 + WxPayOrderNotifyV3Result.DecryptNotifyResult result = response.getResult();
159 // 2. 构建结果 144 // 2. 构建结果
160 - return PayOrderRespDTO.builder()  
161 - .outTradeNo(responseResult.getOutTradeNo())  
162 - .channelOrderNo(responseResult.getTradeState())  
163 - .channelUserId(responseResult.getPayer() != null ? responseResult.getPayer().getOpenid() : null)  
164 - .status(Objects.equals(responseResult.getTradeState(), "SUCCESS") ?  
165 - PayOrderStatusRespEnum.SUCCESS.getStatus() : PayOrderStatusRespEnum.CLOSED.getStatus())  
166 - .successTime(parseDateV3(responseResult.getSuccessTime()))  
167 - .build(); 145 + Integer status = Objects.equals(result.getTradeState(), "SUCCESS") ?
  146 + PayOrderStatusRespEnum.SUCCESS.getStatus() : PayOrderStatusRespEnum.CLOSED.getStatus();
  147 + String openid = result.getPayer() != null ? result.getPayer().getOpenid() : null;
  148 + return new PayOrderRespDTO(status, result.getTransactionId(), openid, parseDateV3(result.getSuccessTime()),
  149 + result.getOutTradeNo(), body);
168 } 150 }
169 151
170 // ============ 退款相关 ========== 152 // ============ 退款相关 ==========
@@ -182,7 +164,8 @@ public abstract class AbstractWxPayClient extends AbstractPayClient&lt;WxPayClientC @@ -182,7 +164,8 @@ public abstract class AbstractWxPayClient extends AbstractPayClient&lt;WxPayClientC
182 } 164 }
183 } catch (WxPayException e) { 165 } catch (WxPayException e) {
184 // todo 芋艿:异常的处理; 166 // todo 芋艿:异常的处理;
185 - throw buildUnifiedOrderException(null, e); 167 +// throw buildUnifiedOrderException(null, e);
  168 + return null;
186 } 169 }
187 } 170 }
188 171
@@ -254,7 +237,6 @@ public abstract class AbstractWxPayClient extends AbstractPayClient&lt;WxPayClientC @@ -254,7 +237,6 @@ public abstract class AbstractWxPayClient extends AbstractPayClient&lt;WxPayClientC
254 } 237 }
255 } catch (WxPayException e) { 238 } catch (WxPayException e) {
256 log.error("[parseNotify][params({}) body({}) 解析失败]", params, body, e); 239 log.error("[parseNotify][params({}) body({}) 解析失败]", params, body, e);
257 -// throw buildPayException(e);  
258 throw new RuntimeException(e); 240 throw new RuntimeException(e);
259 // TODO 芋艿:缺一个异常翻译 241 // TODO 芋艿:缺一个异常翻译
260 } 242 }
@@ -300,33 +282,6 @@ public abstract class AbstractWxPayClient extends AbstractPayClient&lt;WxPayClientC @@ -300,33 +282,6 @@ public abstract class AbstractWxPayClient extends AbstractPayClient&lt;WxPayClientC
300 282
301 // ========== 各种工具方法 ========== 283 // ========== 各种工具方法 ==========
302 284
303 - /**  
304 - * 构建统一下单的异常  
305 - *  
306 - * 目的:将参数不正确等异常,转换成 {@link cn.iocoder.yudao.framework.common.exception.ServiceException} 业务异常  
307 - *  
308 - * @param reqDTO 请求  
309 - * @param e 微信的支付异常  
310 - * @return 转换后的异常  
311 - *  
312 - */  
313 - static Exception buildUnifiedOrderException(PayOrderUnifiedReqDTO reqDTO, WxPayException e) {  
314 - // 情况一:业务结果为 FAIL  
315 - if (Objects.equals(e.getResultCode(), "FAIL")) {  
316 - log.error("[buildUnifiedOrderException][request({}) 发起支付失败]", toJsonString(reqDTO), e);  
317 - if (Objects.equals(e.getErrCode(), "PARAM_ERROR")) {  
318 - throw invalidParamException(e.getErrCodeDes());  
319 - }  
320 - throw exception(PayFrameworkErrorCodeConstants.ORDER_UNIFIED_ERROR, e.getErrCodeDes());  
321 - }  
322 - // 情况二:状态码结果为 FAIL  
323 - if (Objects.equals(e.getReturnCode(), "FAIL")) {  
324 - throw exception(PayFrameworkErrorCodeConstants.ORDER_UNIFIED_ERROR, e.getReturnMsg());  
325 - }  
326 - // 情况三:系统异常,这里暂时不打,交给上层的 AbstractPayClient 统一打日志  
327 - return e;  
328 - }  
329 -  
330 static String formatDateV2(LocalDateTime time) { 285 static String formatDateV2(LocalDateTime time) {
331 return TemporalAccessorUtil.format(time.atZone(ZoneId.systemDefault()), PURE_DATETIME_PATTERN); 286 return TemporalAccessorUtil.format(time.atZone(ZoneId.systemDefault()), PURE_DATETIME_PATTERN);
332 } 287 }
@@ -347,4 +302,24 @@ public abstract class AbstractWxPayClient extends AbstractPayClient&lt;WxPayClientC @@ -347,4 +302,24 @@ public abstract class AbstractWxPayClient extends AbstractPayClient&lt;WxPayClientC
347 return LocalDateTimeUtil.parse(time, UTC_WITH_XXX_OFFSET_PATTERN); 302 return LocalDateTimeUtil.parse(time, UTC_WITH_XXX_OFFSET_PATTERN);
348 } 303 }
349 304
  305 + static String getErrorCode(WxPayException e) {
  306 + if (StrUtil.isNotEmpty(e.getErrCode())) {
  307 + return e.getErrCode();
  308 + }
  309 + if (StrUtil.isNotEmpty(e.getCustomErrorMsg())) {
  310 + return "CUSTOM_ERROR";
  311 + }
  312 + return e.getReturnCode();
  313 + }
  314 +
  315 + static String getErrorMessage(WxPayException e) {
  316 + if (StrUtil.isNotEmpty(e.getErrCode())) {
  317 + return e.getErrCodeDes();
  318 + }
  319 + if (StrUtil.isNotEmpty(e.getCustomErrorMsg())) {
  320 + return e.getCustomErrorMsg();
  321 + }
  322 + return e.getReturnMsg();
  323 + }
  324 +
350 } 325 }
yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/weixin/WxAppPayClient.java
1 package cn.iocoder.yudao.framework.pay.core.client.impl.weixin; 1 package cn.iocoder.yudao.framework.pay.core.client.impl.weixin;
2 2
  3 +import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderRespDTO;
3 import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderUnifiedReqDTO; 4 import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderUnifiedReqDTO;
4 -import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderUnifiedRespDTO;  
5 import cn.iocoder.yudao.framework.pay.core.enums.channel.PayChannelEnum; 5 import cn.iocoder.yudao.framework.pay.core.enums.channel.PayChannelEnum;
6 import com.github.binarywang.wxpay.constant.WxPayConstants; 6 import com.github.binarywang.wxpay.constant.WxPayConstants;
7 import com.github.binarywang.wxpay.exception.WxPayException; 7 import com.github.binarywang.wxpay.exception.WxPayException;
@@ -19,12 +19,12 @@ public class WxAppPayClient extends AbstractWxPayClient { @@ -19,12 +19,12 @@ public class WxAppPayClient extends AbstractWxPayClient {
19 } 19 }
20 20
21 @Override 21 @Override
22 - protected PayOrderUnifiedRespDTO doUnifiedOrderV2(PayOrderUnifiedReqDTO reqDTO) throws WxPayException { 22 + protected PayOrderRespDTO doUnifiedOrderV2(PayOrderUnifiedReqDTO reqDTO) throws WxPayException {
23 return null; 23 return null;
24 } 24 }
25 25
26 @Override 26 @Override
27 - protected PayOrderUnifiedRespDTO doUnifiedOrderV3(PayOrderUnifiedReqDTO reqDTO) throws WxPayException { 27 + protected PayOrderRespDTO doUnifiedOrderV3(PayOrderUnifiedReqDTO reqDTO) throws WxPayException {
28 return null; 28 return null;
29 } 29 }
30 30
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; @@ -4,13 +4,10 @@ import cn.hutool.core.map.MapUtil;
4 import cn.hutool.core.thread.ThreadUtil; 4 import cn.hutool.core.thread.ThreadUtil;
5 import cn.hutool.core.util.StrUtil; 5 import cn.hutool.core.util.StrUtil;
6 import cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils; 6 import cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils;
7 -import cn.iocoder.yudao.framework.common.util.json.JsonUtils;  
8 import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderRespDTO; 7 import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderRespDTO;
9 import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderUnifiedReqDTO; 8 import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderUnifiedReqDTO;
10 -import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderUnifiedRespDTO;  
11 import cn.iocoder.yudao.framework.pay.core.enums.channel.PayChannelEnum; 9 import cn.iocoder.yudao.framework.pay.core.enums.channel.PayChannelEnum;
12 import cn.iocoder.yudao.framework.pay.core.enums.order.PayOrderDisplayModeEnum; 10 import cn.iocoder.yudao.framework.pay.core.enums.order.PayOrderDisplayModeEnum;
13 -import cn.iocoder.yudao.framework.pay.core.enums.order.PayOrderStatusRespEnum;  
14 import com.github.binarywang.wxpay.bean.request.WxPayMicropayRequest; 11 import com.github.binarywang.wxpay.bean.request.WxPayMicropayRequest;
15 import com.github.binarywang.wxpay.bean.result.WxPayMicropayResult; 12 import com.github.binarywang.wxpay.bean.result.WxPayMicropayResult;
16 import com.github.binarywang.wxpay.constant.WxPayConstants; 13 import com.github.binarywang.wxpay.constant.WxPayConstants;
@@ -22,6 +19,7 @@ import java.time.LocalDateTime; @@ -22,6 +19,7 @@ import java.time.LocalDateTime;
22 import java.util.concurrent.TimeUnit; 19 import java.util.concurrent.TimeUnit;
23 20
24 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.invalidParamException; 21 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.invalidParamException;
  22 +import static cn.iocoder.yudao.framework.common.util.json.JsonUtils.toJsonString;
25 23
26 /** 24 /**
27 * 微信支付【付款码支付】的 PayClient 实现类 25 * 微信支付【付款码支付】的 PayClient 实现类
@@ -48,7 +46,7 @@ public class WxBarPayClient extends AbstractWxPayClient { @@ -48,7 +46,7 @@ public class WxBarPayClient extends AbstractWxPayClient {
48 } 46 }
49 47
50 @Override 48 @Override
51 - protected PayOrderUnifiedRespDTO doUnifiedOrderV2(PayOrderUnifiedReqDTO reqDTO) throws WxPayException { 49 + protected PayOrderRespDTO doUnifiedOrderV2(PayOrderUnifiedReqDTO reqDTO) throws WxPayException {
52 // 由于付款码需要不断轮询,所以需要在较短的时间完成支付 50 // 由于付款码需要不断轮询,所以需要在较短的时间完成支付
53 LocalDateTime expireTime = LocalDateTimeUtils.addTime(AUTH_CODE_EXPIRE); 51 LocalDateTime expireTime = LocalDateTimeUtils.addTime(AUTH_CODE_EXPIRE);
54 if (expireTime.isAfter(reqDTO.getExpireTime())) { 52 if (expireTime.isAfter(reqDTO.getExpireTime())) {
@@ -65,22 +63,16 @@ public class WxBarPayClient extends AbstractWxPayClient { @@ -65,22 +63,16 @@ public class WxBarPayClient extends AbstractWxPayClient {
65 .authCode(getAuthCode(reqDTO)) 63 .authCode(getAuthCode(reqDTO))
66 .build(); 64 .build();
67 // 执行请求,重试直到失败(过期),或者成功 65 // 执行请求,重试直到失败(过期),或者成功
  66 + WxPayException lastWxPayException = null;
68 for (int i = 1; i < Byte.MAX_VALUE; i++) { 67 for (int i = 1; i < Byte.MAX_VALUE; i++) {
69 try { 68 try {
70 WxPayMicropayResult response = client.micropay(request); 69 WxPayMicropayResult response = client.micropay(request);
71 - // 支付成功(例如说,用户输入了密码)  
72 - PayOrderRespDTO order = PayOrderRespDTO.builder()  
73 - .status(PayOrderStatusRespEnum.SUCCESS.getStatus())  
74 - .outTradeNo(response.getOutTradeNo())  
75 - .channelOrderNo(response.getTransactionId())  
76 - .channelUserId(response.getOpenid())  
77 - .successTime(parseDateV2(response.getTimeEnd()))  
78 - .rawData(response)  
79 - .build();  
80 - return new PayOrderUnifiedRespDTO(PayOrderDisplayModeEnum.BAR_CODE.getMode(),  
81 - JsonUtils.toJsonString(response))  
82 - .setOrder(order); 70 + // 支付成功,例如说:1)用户输入了密码;2)
  71 + return new PayOrderRespDTO(response.getTransactionId(), response.getOpenid(), parseDateV2(response.getTimeEnd()),
  72 + response.getOutTradeNo(), response)
  73 + .setDisplayMode(PayOrderDisplayModeEnum.BAR_CODE.getMode());
83 } catch (WxPayException ex) { 74 } catch (WxPayException ex) {
  75 + lastWxPayException = ex;
84 // 如果不满足这 3 种任一的,则直接抛出 WxPayException 异常,不仅需处理 76 // 如果不满足这 3 种任一的,则直接抛出 WxPayException 异常,不仅需处理
85 // 1. SYSTEMERROR:接口返回错误:请立即调用被扫订单结果查询API,查询当前订单状态,并根据订单的状态决定下一步的操作。 77 // 1. SYSTEMERROR:接口返回错误:请立即调用被扫订单结果查询API,查询当前订单状态,并根据订单的状态决定下一步的操作。
86 // 2. USERPAYING:用户支付中,需要输入密码:等待 5 秒,然后调用被扫订单结果查询 API,查询当前订单的不同状态,决定下一步的操作。 78 // 2. USERPAYING:用户支付中,需要输入密码:等待 5 秒,然后调用被扫订单结果查询 API,查询当前订单的不同状态,决定下一步的操作。
@@ -90,15 +82,15 @@ public class WxBarPayClient extends AbstractWxPayClient { @@ -90,15 +82,15 @@ public class WxBarPayClient extends AbstractWxPayClient {
90 } 82 }
91 // 等待 5 秒,继续下一轮重新发起支付 83 // 等待 5 秒,继续下一轮重新发起支付
92 log.info("[doUnifiedOrderV2][发起微信 Bar 支付第({})失败,等待下一轮重试,请求({}),响应({})]", i, 84 log.info("[doUnifiedOrderV2][发起微信 Bar 支付第({})失败,等待下一轮重试,请求({}),响应({})]", i,
93 - JsonUtils.toJsonString(request), ex.getMessage()); 85 + toJsonString(request), ex.getMessage());
94 ThreadUtil.sleep(5, TimeUnit.SECONDS); 86 ThreadUtil.sleep(5, TimeUnit.SECONDS);
95 } 87 }
96 } 88 }
97 - throw new IllegalStateException("微信 Bar 支付,重试多次失败"); 89 + throw lastWxPayException;
98 } 90 }
99 91
100 @Override 92 @Override
101 - protected PayOrderUnifiedRespDTO doUnifiedOrderV3(PayOrderUnifiedReqDTO reqDTO) throws WxPayException { 93 + protected PayOrderRespDTO doUnifiedOrderV3(PayOrderUnifiedReqDTO reqDTO) throws WxPayException {
102 return doUnifiedOrderV2(reqDTO); 94 return doUnifiedOrderV2(reqDTO);
103 } 95 }
104 96
yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/weixin/WxH5PayClient.java
1 package cn.iocoder.yudao.framework.pay.core.client.impl.weixin; 1 package cn.iocoder.yudao.framework.pay.core.client.impl.weixin;
2 2
  3 +import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderRespDTO;
3 import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderUnifiedReqDTO; 4 import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderUnifiedReqDTO;
4 -import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderUnifiedRespDTO;  
5 import cn.iocoder.yudao.framework.pay.core.enums.channel.PayChannelEnum; 5 import cn.iocoder.yudao.framework.pay.core.enums.channel.PayChannelEnum;
6 import com.github.binarywang.wxpay.constant.WxPayConstants; 6 import com.github.binarywang.wxpay.constant.WxPayConstants;
7 import com.github.binarywang.wxpay.exception.WxPayException; 7 import com.github.binarywang.wxpay.exception.WxPayException;
@@ -19,12 +19,12 @@ public class WxH5PayClient extends AbstractWxPayClient { @@ -19,12 +19,12 @@ public class WxH5PayClient extends AbstractWxPayClient {
19 } 19 }
20 20
21 @Override 21 @Override
22 - protected PayOrderUnifiedRespDTO doUnifiedOrderV2(PayOrderUnifiedReqDTO reqDTO) throws WxPayException { 22 + protected PayOrderRespDTO doUnifiedOrderV2(PayOrderUnifiedReqDTO reqDTO) throws WxPayException {
23 return null; 23 return null;
24 } 24 }
25 25
26 @Override 26 @Override
27 - protected PayOrderUnifiedRespDTO doUnifiedOrderV3(PayOrderUnifiedReqDTO reqDTO) throws WxPayException { 27 + protected PayOrderRespDTO doUnifiedOrderV3(PayOrderUnifiedReqDTO reqDTO) throws WxPayException {
28 return null; 28 return null;
29 } 29 }
30 30
yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/weixin/WxNativePayClient.java
1 package cn.iocoder.yudao.framework.pay.core.client.impl.weixin; 1 package cn.iocoder.yudao.framework.pay.core.client.impl.weixin;
2 2
  3 +import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderRespDTO;
3 import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderUnifiedReqDTO; 4 import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderUnifiedReqDTO;
4 -import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderUnifiedRespDTO;  
5 import cn.iocoder.yudao.framework.pay.core.enums.channel.PayChannelEnum; 5 import cn.iocoder.yudao.framework.pay.core.enums.channel.PayChannelEnum;
6 import cn.iocoder.yudao.framework.pay.core.enums.order.PayOrderDisplayModeEnum; 6 import cn.iocoder.yudao.framework.pay.core.enums.order.PayOrderDisplayModeEnum;
7 import com.github.binarywang.wxpay.bean.order.WxPayNativeOrderResult; 7 import com.github.binarywang.wxpay.bean.order.WxPayNativeOrderResult;
@@ -32,7 +32,7 @@ public class WxNativePayClient extends AbstractWxPayClient { @@ -32,7 +32,7 @@ public class WxNativePayClient extends AbstractWxPayClient {
32 } 32 }
33 33
34 @Override 34 @Override
35 - protected PayOrderUnifiedRespDTO doUnifiedOrderV2(PayOrderUnifiedReqDTO reqDTO) throws WxPayException { 35 + protected PayOrderRespDTO doUnifiedOrderV2(PayOrderUnifiedReqDTO reqDTO) throws WxPayException {
36 // 构建 WxPayUnifiedOrderRequest 对象 36 // 构建 WxPayUnifiedOrderRequest 对象
37 WxPayUnifiedOrderRequest request = WxPayUnifiedOrderRequest.newBuilder() 37 WxPayUnifiedOrderRequest request = WxPayUnifiedOrderRequest.newBuilder()
38 .outTradeNo(reqDTO.getOutTradeNo()) 38 .outTradeNo(reqDTO.getOutTradeNo())
@@ -48,12 +48,12 @@ public class WxNativePayClient extends AbstractWxPayClient { @@ -48,12 +48,12 @@ public class WxNativePayClient extends AbstractWxPayClient {
48 WxPayNativeOrderResult response = client.createOrder(request); 48 WxPayNativeOrderResult response = client.createOrder(request);
49 49
50 // 转换结果 50 // 转换结果
51 - return new PayOrderUnifiedRespDTO(PayOrderDisplayModeEnum.QR_CODE.getMode(),  
52 - response.getCodeUrl()); 51 + return new PayOrderRespDTO(PayOrderDisplayModeEnum.QR_CODE.getMode(), response.getCodeUrl(),
  52 + reqDTO.getOutTradeNo(), response);
53 } 53 }
54 54
55 @Override 55 @Override
56 - protected PayOrderUnifiedRespDTO doUnifiedOrderV3(PayOrderUnifiedReqDTO reqDTO) throws WxPayException { 56 + protected PayOrderRespDTO doUnifiedOrderV3(PayOrderUnifiedReqDTO reqDTO) throws WxPayException {
57 // 构建 WxPayUnifiedOrderRequest 对象 57 // 构建 WxPayUnifiedOrderRequest 对象
58 WxPayUnifiedOrderV3Request request = new WxPayUnifiedOrderV3Request() 58 WxPayUnifiedOrderV3Request request = new WxPayUnifiedOrderV3Request()
59 .setOutTradeNo(reqDTO.getOutTradeNo()) 59 .setOutTradeNo(reqDTO.getOutTradeNo())
@@ -66,8 +66,8 @@ public class WxNativePayClient extends AbstractWxPayClient { @@ -66,8 +66,8 @@ public class WxNativePayClient extends AbstractWxPayClient {
66 String response = client.createOrderV3(TradeTypeEnum.NATIVE, request); 66 String response = client.createOrderV3(TradeTypeEnum.NATIVE, request);
67 67
68 // 转换结果 68 // 转换结果
69 - return new PayOrderUnifiedRespDTO(PayOrderDisplayModeEnum.QR_CODE.getMode(),  
70 - response); 69 + return new PayOrderRespDTO(PayOrderDisplayModeEnum.QR_CODE.getMode(), response,
  70 + reqDTO.getOutTradeNo(), response);
71 } 71 }
72 72
73 } 73 }
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; @@ -2,9 +2,8 @@ package cn.iocoder.yudao.framework.pay.core.client.impl.weixin;
2 2
3 import cn.hutool.core.map.MapUtil; 3 import cn.hutool.core.map.MapUtil;
4 import cn.hutool.core.util.StrUtil; 4 import cn.hutool.core.util.StrUtil;
5 -import cn.iocoder.yudao.framework.common.util.json.JsonUtils; 5 +import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderRespDTO;
6 import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderUnifiedReqDTO; 6 import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderUnifiedReqDTO;
7 -import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderUnifiedRespDTO;  
8 import cn.iocoder.yudao.framework.pay.core.enums.channel.PayChannelEnum; 7 import cn.iocoder.yudao.framework.pay.core.enums.channel.PayChannelEnum;
9 import cn.iocoder.yudao.framework.pay.core.enums.order.PayOrderDisplayModeEnum; 8 import cn.iocoder.yudao.framework.pay.core.enums.order.PayOrderDisplayModeEnum;
10 import com.github.binarywang.wxpay.bean.order.WxPayMpOrderResult; 9 import com.github.binarywang.wxpay.bean.order.WxPayMpOrderResult;
@@ -17,6 +16,7 @@ import com.github.binarywang.wxpay.exception.WxPayException; @@ -17,6 +16,7 @@ import com.github.binarywang.wxpay.exception.WxPayException;
17 import lombok.extern.slf4j.Slf4j; 16 import lombok.extern.slf4j.Slf4j;
18 17
19 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.invalidParamException; 18 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.invalidParamException;
  19 +import static cn.iocoder.yudao.framework.common.util.json.JsonUtils.toJsonString;
20 20
21 /** 21 /**
22 * 微信支付(公众号)的 PayClient 实现类 22 * 微信支付(公众号)的 PayClient 实现类
@@ -42,7 +42,7 @@ public class WxPubPayClient extends AbstractWxPayClient { @@ -42,7 +42,7 @@ public class WxPubPayClient extends AbstractWxPayClient {
42 } 42 }
43 43
44 @Override 44 @Override
45 - protected PayOrderUnifiedRespDTO doUnifiedOrderV2(PayOrderUnifiedReqDTO reqDTO) throws WxPayException { 45 + protected PayOrderRespDTO doUnifiedOrderV2(PayOrderUnifiedReqDTO reqDTO) throws WxPayException {
46 // 构建 WxPayUnifiedOrderRequest 对象 46 // 构建 WxPayUnifiedOrderRequest 对象
47 WxPayUnifiedOrderRequest request = WxPayUnifiedOrderRequest.newBuilder() 47 WxPayUnifiedOrderRequest request = WxPayUnifiedOrderRequest.newBuilder()
48 .outTradeNo(reqDTO.getOutTradeNo()) 48 .outTradeNo(reqDTO.getOutTradeNo())
@@ -58,12 +58,12 @@ public class WxPubPayClient extends AbstractWxPayClient { @@ -58,12 +58,12 @@ public class WxPubPayClient extends AbstractWxPayClient {
58 WxPayMpOrderResult response = client.createOrder(request); 58 WxPayMpOrderResult response = client.createOrder(request);
59 59
60 // 转换结果 60 // 转换结果
61 - return new PayOrderUnifiedRespDTO(PayOrderDisplayModeEnum.CUSTOM.getMode(),  
62 - JsonUtils.toJsonString(response)); 61 + return new PayOrderRespDTO(PayOrderDisplayModeEnum.APP.getMode(), toJsonString(response),
  62 + reqDTO.getOutTradeNo(), response);
63 } 63 }
64 64
65 @Override 65 @Override
66 - protected PayOrderUnifiedRespDTO doUnifiedOrderV3(PayOrderUnifiedReqDTO reqDTO) throws WxPayException { 66 + protected PayOrderRespDTO doUnifiedOrderV3(PayOrderUnifiedReqDTO reqDTO) throws WxPayException {
67 // 构建 WxPayUnifiedOrderRequest 对象 67 // 构建 WxPayUnifiedOrderRequest 对象
68 WxPayUnifiedOrderV3Request request = new WxPayUnifiedOrderV3Request(); 68 WxPayUnifiedOrderV3Request request = new WxPayUnifiedOrderV3Request();
69 request.setOutTradeNo(reqDTO.getOutTradeNo()); 69 request.setOutTradeNo(reqDTO.getOutTradeNo());
@@ -77,8 +77,8 @@ public class WxPubPayClient extends AbstractWxPayClient { @@ -77,8 +77,8 @@ public class WxPubPayClient extends AbstractWxPayClient {
77 WxPayUnifiedOrderV3Result.JsapiResult response = client.createOrderV3(TradeTypeEnum.JSAPI, request); 77 WxPayUnifiedOrderV3Result.JsapiResult response = client.createOrderV3(TradeTypeEnum.JSAPI, request);
78 78
79 // 转换结果 79 // 转换结果
80 - return new PayOrderUnifiedRespDTO(PayOrderDisplayModeEnum.CUSTOM.getMode(),  
81 - JsonUtils.toJsonString(response)); 80 + return new PayOrderRespDTO(PayOrderDisplayModeEnum.APP.getMode(), toJsonString(response),
  81 + reqDTO.getOutTradeNo(), response);
82 } 82 }
83 83
84 // ========== 各种工具方法 ========== 84 // ========== 各种工具方法 ==========
yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/enums/PayFrameworkErrorCodeConstants.java deleted
1 -package cn.iocoder.yudao.framework.pay.core.enums;  
2 -  
3 -import cn.iocoder.yudao.framework.common.exception.ErrorCode;  
4 -  
5 -/**  
6 - * 支付框架的错误码枚举  
7 - *  
8 - * 支付框架,使用 2-002-000-000 段  
9 - *  
10 - * @author 芋道源码  
11 - */  
12 -public interface PayFrameworkErrorCodeConstants {  
13 -  
14 - ErrorCode ORDER_UNIFIED_ERROR = new ErrorCode(2002000000, "发起支付失败,原因:{}");  
15 -  
16 -}  
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 { @@ -18,8 +18,7 @@ public enum PayOrderDisplayModeEnum {
18 QR_CODE("qr_code"), // 二维码的文字内容 18 QR_CODE("qr_code"), // 二维码的文字内容
19 QR_CODE_URL("qr_code_url"), // 二维码的图片链接 19 QR_CODE_URL("qr_code_url"), // 二维码的图片链接
20 BAR_CODE("bar_code"), // 条形码 20 BAR_CODE("bar_code"), // 条形码
21 - APP("app"), // 应用【目前暂时用不到】  
22 - CUSTOM("custom"), // 自定义:每种支付方式,做个性化处理;例如说,微信公众号支付时,调用 JSAPI 接口 21 + APP("app"), // 应用:Android、iOS、微信小程序、微信公众号等,需要做自定义处理的
23 ; 22 ;
24 23
25 /** 24 /**
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 { @@ -32,4 +32,14 @@ public enum PayOrderStatusRespEnum {
32 return Objects.equals(status, SUCCESS.getStatus()); 32 return Objects.equals(status, SUCCESS.getStatus());
33 } 33 }
34 34
  35 + /**
  36 + * 判断是否支付关闭
  37 + *
  38 + * @param status 状态
  39 + * @return 是否支付关闭
  40 + */
  41 + public static boolean isClosed(Integer status) {
  42 + return Objects.equals(status, CLOSED.getStatus());
  43 + }
  44 +
35 } 45 }
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 @@ -351,7 +351,7 @@ public class TradeAfterSaleServiceImpl implements TradeAfterSaleService, AfterSa
351 public void afterCommit() { 351 public void afterCommit() {
352 // 创建退款单 352 // 创建退款单
353 PayRefundCreateReqDTO createReqDTO = TradeAfterSaleConvert.INSTANCE.convert(userIp, afterSale, tradeOrderProperties); 353 PayRefundCreateReqDTO createReqDTO = TradeAfterSaleConvert.INSTANCE.convert(userIp, afterSale, tradeOrderProperties);
354 - Long payRefundId = payRefundApi.createPayRefund(createReqDTO); 354 + Long payRefundId = payRefundApi.createRefund(createReqDTO);
355 // 更新售后单的退款单号 355 // 更新售后单的退款单号
356 tradeAfterSaleMapper.updateById(new TradeAfterSaleDO().setId(afterSale.getId()).setPayRefundId(payRefundId)); 356 tradeAfterSaleMapper.updateById(new TradeAfterSaleDO().setId(afterSale.getId()).setPayRefundId(payRefundId));
357 } 357 }
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 { @@ -18,7 +18,7 @@ public interface PayRefundApi {
18 * @param reqDTO 创建请求 18 * @param reqDTO 创建请求
19 * @return 退款单编号 19 * @return 退款单编号
20 */ 20 */
21 - Long createPayRefund(@Valid PayRefundCreateReqDTO reqDTO); 21 + Long createRefund(@Valid PayRefundCreateReqDTO reqDTO);
22 22
23 /** 23 /**
24 * 获得退款单 24 * 获得退款单
@@ -26,6 +26,6 @@ public interface PayRefundApi { @@ -26,6 +26,6 @@ public interface PayRefundApi {
26 * @param id 退款单编号 26 * @param id 退款单编号
27 * @return 退款单 27 * @return 退款单
28 */ 28 */
29 - PayRefundRespDTO getPayRefund(Long id); 29 + PayRefundRespDTO getRefund(Long id);
30 30
31 } 31 }
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 { @@ -27,6 +27,7 @@ public interface ErrorCodeConstants {
27 ErrorCode PAY_ORDER_STATUS_IS_NOT_WAITING = new ErrorCode(1007002001, "支付订单不处于待支付"); 27 ErrorCode PAY_ORDER_STATUS_IS_NOT_WAITING = new ErrorCode(1007002001, "支付订单不处于待支付");
28 ErrorCode PAY_ORDER_STATUS_IS_NOT_SUCCESS = new ErrorCode(1007002002, "支付订单不处于已支付"); 28 ErrorCode PAY_ORDER_STATUS_IS_NOT_SUCCESS = new ErrorCode(1007002002, "支付订单不处于已支付");
29 ErrorCode PAY_ORDER_IS_EXPIRED = new ErrorCode(1007002003, "支付订单已经过期"); 29 ErrorCode PAY_ORDER_IS_EXPIRED = new ErrorCode(1007002003, "支付订单已经过期");
  30 + ErrorCode PAY_ORDER_SUBMIT_CHANNEL_ERROR = new ErrorCode(1007002004, "发起支付报错,错误码:{},错误提示:{}");
30 31
31 // ========== ORDER 模块(拓展单) 1007003000 ========== 32 // ========== ORDER 模块(拓展单) 1007003000 ==========
32 ErrorCode PAY_ORDER_EXTENSION_NOT_FOUND = new ErrorCode(1007003000, "支付交易拓展单不存在"); 33 ErrorCode PAY_ORDER_EXTENSION_NOT_FOUND = new ErrorCode(1007003000, "支付交易拓展单不存在");
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 { @@ -22,12 +22,12 @@ public class PayRefundApiImpl implements PayRefundApi {
22 private PayRefundService payRefundService; 22 private PayRefundService payRefundService;
23 23
24 @Override 24 @Override
25 - public Long createPayRefund(PayRefundCreateReqDTO reqDTO) { 25 + public Long createRefund(PayRefundCreateReqDTO reqDTO) {
26 return payRefundService.createPayRefund(reqDTO); 26 return payRefundService.createPayRefund(reqDTO);
27 } 27 }
28 28
29 @Override 29 @Override
30 - public PayRefundRespDTO getPayRefund(Long id) { 30 + public PayRefundRespDTO getRefund(Long id) {
31 return PayRefundConvert.INSTANCE.convert02(payRefundService.getRefund(id)); 31 return PayRefundConvert.INSTANCE.convert02(payRefundService.getRefund(id));
32 } 32 }
33 33
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; @@ -2,7 +2,6 @@ package cn.iocoder.yudao.module.pay.convert.order;
2 2
3 import cn.iocoder.yudao.framework.common.pojo.PageResult; 3 import cn.iocoder.yudao.framework.common.pojo.PageResult;
4 import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderUnifiedReqDTO; 4 import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderUnifiedReqDTO;
5 -import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderUnifiedRespDTO;  
6 import cn.iocoder.yudao.module.pay.api.order.dto.PayOrderCreateReqDTO; 5 import cn.iocoder.yudao.module.pay.api.order.dto.PayOrderCreateReqDTO;
7 import cn.iocoder.yudao.module.pay.api.order.dto.PayOrderRespDTO; 6 import cn.iocoder.yudao.module.pay.api.order.dto.PayOrderRespDTO;
8 import cn.iocoder.yudao.module.pay.controller.admin.order.vo.*; 7 import cn.iocoder.yudao.module.pay.controller.admin.order.vo.*;
@@ -93,7 +92,8 @@ public interface PayOrderConvert { @@ -93,7 +92,8 @@ public interface PayOrderConvert {
93 92
94 PayOrderUnifiedReqDTO convert2(PayOrderSubmitReqVO reqVO, String userIp); 93 PayOrderUnifiedReqDTO convert2(PayOrderSubmitReqVO reqVO, String userIp);
95 94
96 - PayOrderSubmitRespVO convert(PayOrderDO order, PayOrderUnifiedRespDTO unifiedRespDTO); 95 + @Mapping(source = "order.status", target = "status")
  96 + PayOrderSubmitRespVO convert(PayOrderDO order, cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderRespDTO respDTO);
97 97
98 AppPayOrderSubmitRespVO convert3(PayOrderSubmitRespVO bean); 98 AppPayOrderSubmitRespVO convert3(PayOrderSubmitRespVO bean);
99 99
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/dal/dataobject/order/PayOrderExtensionDO.java
1 package cn.iocoder.yudao.module.pay.dal.dataobject.order; 1 package cn.iocoder.yudao.module.pay.dal.dataobject.order;
2 2
  3 +import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderRespDTO;
3 import cn.iocoder.yudao.module.pay.dal.dataobject.channel.PayChannelDO; 4 import cn.iocoder.yudao.module.pay.dal.dataobject.channel.PayChannelDO;
4 import cn.iocoder.yudao.module.pay.enums.order.PayOrderStatusEnum; 5 import cn.iocoder.yudao.module.pay.enums.order.PayOrderStatusEnum;
5 import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; 6 import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
@@ -65,7 +66,6 @@ public class PayOrderExtensionDO extends BaseDO { @@ -65,7 +66,6 @@ public class PayOrderExtensionDO extends BaseDO {
65 * 支付状态 66 * 支付状态
66 * 67 *
67 * 枚举 {@link PayOrderStatusEnum} 68 * 枚举 {@link PayOrderStatusEnum}
68 - * 注意,只包含上述枚举的 WAITING 和 SUCCESS  
69 */ 69 */
70 private Integer status; 70 private Integer status;
71 /** 71 /**
@@ -75,10 +75,20 @@ public class PayOrderExtensionDO extends BaseDO { @@ -75,10 +75,20 @@ public class PayOrderExtensionDO extends BaseDO {
75 */ 75 */
76 @TableField(typeHandler = JacksonTypeHandler.class) 76 @TableField(typeHandler = JacksonTypeHandler.class)
77 private Map<String, String> channelExtras; 77 private Map<String, String> channelExtras;
  78 +
  79 + /**
  80 + * 调用渠道的错误码
  81 + */
  82 + private String channelErrorCode;
  83 + /**
  84 + * 调用渠道报错时,错误信息
  85 + */
  86 + private String channelErrorMsg;
  87 +
78 /** 88 /**
79 - * 支付渠道异步通知的内容 89 + * 支付渠道的同步/异步通知的内容
80 * 90 *
81 - * 在支持成功后,会记录回调的数据 91 + * 对应 {@link PayOrderRespDTO#getRawData()}
82 */ 92 */
83 private String channelNotifyData; 93 private String channelNotifyData;
84 94
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/dal/dataobject/refund/PayRefundDO.java
1 package cn.iocoder.yudao.module.pay.dal.dataobject.refund; 1 package cn.iocoder.yudao.module.pay.dal.dataobject.refund;
2 2
3 import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; 3 import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
  4 +import cn.iocoder.yudao.framework.pay.core.client.dto.refund.PayRefundRespDTO;
4 import cn.iocoder.yudao.framework.pay.core.enums.channel.PayChannelEnum; 5 import cn.iocoder.yudao.framework.pay.core.enums.channel.PayChannelEnum;
5 import cn.iocoder.yudao.module.pay.dal.dataobject.app.PayAppDO; 6 import cn.iocoder.yudao.module.pay.dal.dataobject.app.PayAppDO;
6 import cn.iocoder.yudao.module.pay.dal.dataobject.channel.PayChannelDO; 7 import cn.iocoder.yudao.module.pay.dal.dataobject.channel.PayChannelDO;
@@ -151,9 +152,9 @@ public class PayRefundDO extends BaseDO { @@ -151,9 +152,9 @@ public class PayRefundDO extends BaseDO {
151 private String channelErrorMsg; 152 private String channelErrorMsg;
152 153
153 /** 154 /**
154 - * 支付渠道异步通知的内容 155 + * 支付渠道的同步/异步通知的内容
155 * 156 *
156 - * 在退款成功后,会记录回调的数据 157 + * 对应 {@link PayRefundRespDTO#getRawData()}
157 */ 158 */
158 private String channelNotifyData; 159 private String channelNotifyData;
159 160
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 { @@ -189,7 +189,7 @@ public class PayDemoOrderServiceImpl implements PayDemoOrderService {
189 // 这里我们是个简单的 demo,所以没有售后维权表,直接使用订单 id + "-refund" 来演示 189 // 这里我们是个简单的 demo,所以没有售后维权表,直接使用订单 id + "-refund" 来演示
190 String refundId = order.getId() + "-refund"; 190 String refundId = order.getId() + "-refund";
191 // 2.2 创建退款单 191 // 2.2 创建退款单
192 - Long payRefundId = payRefundApi.createPayRefund(new PayRefundCreateReqDTO() 192 + Long payRefundId = payRefundApi.createRefund(new PayRefundCreateReqDTO()
193 .setAppId(PAY_APP_ID).setUserIp(getClientIP()) // 支付应用 193 .setAppId(PAY_APP_ID).setUserIp(getClientIP()) // 支付应用
194 .setMerchantOrderId(String.valueOf(order.getId())) // 支付单号 194 .setMerchantOrderId(String.valueOf(order.getId())) // 支付单号
195 .setMerchantRefundId(refundId) 195 .setMerchantRefundId(refundId)
@@ -239,7 +239,7 @@ public class PayDemoOrderServiceImpl implements PayDemoOrderService { @@ -239,7 +239,7 @@ public class PayDemoOrderServiceImpl implements PayDemoOrderService {
239 } 239 }
240 240
241 // 2.1 校验退款订单 241 // 2.1 校验退款订单
242 - PayRefundRespDTO payRefund = payRefundApi.getPayRefund(payRefundId); 242 + PayRefundRespDTO payRefund = payRefundApi.getRefund(payRefundId);
243 if (payRefund == null) { 243 if (payRefund == null) {
244 throw exception(PAY_DEMO_ORDER_REFUND_FAIL_REFUND_NOT_FOUND); 244 throw exception(PAY_DEMO_ORDER_REFUND_FAIL_REFUND_NOT_FOUND);
245 } 245 }
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; @@ -4,6 +4,7 @@ import cn.hutool.core.date.DateUtil;
4 import cn.hutool.core.lang.Pair; 4 import cn.hutool.core.lang.Pair;
5 import cn.hutool.core.util.ObjectUtil; 5 import cn.hutool.core.util.ObjectUtil;
6 import cn.hutool.core.util.RandomUtil; 6 import cn.hutool.core.util.RandomUtil;
  7 +import cn.hutool.core.util.StrUtil;
7 import cn.iocoder.yudao.framework.common.pojo.PageResult; 8 import cn.iocoder.yudao.framework.common.pojo.PageResult;
8 import cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils; 9 import cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils;
9 import cn.iocoder.yudao.framework.pay.config.PayProperties; 10 import cn.iocoder.yudao.framework.pay.config.PayProperties;
@@ -11,7 +12,6 @@ import cn.iocoder.yudao.framework.pay.core.client.PayClient; @@ -11,7 +12,6 @@ import cn.iocoder.yudao.framework.pay.core.client.PayClient;
11 import cn.iocoder.yudao.framework.pay.core.client.PayClientFactory; 12 import cn.iocoder.yudao.framework.pay.core.client.PayClientFactory;
12 import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderRespDTO; 13 import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderRespDTO;
13 import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderUnifiedReqDTO; 14 import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderUnifiedReqDTO;
14 -import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderUnifiedRespDTO;  
15 import cn.iocoder.yudao.framework.pay.core.enums.order.PayOrderStatusRespEnum; 15 import cn.iocoder.yudao.framework.pay.core.enums.order.PayOrderStatusRespEnum;
16 import cn.iocoder.yudao.framework.tenant.core.util.TenantUtils; 16 import cn.iocoder.yudao.framework.tenant.core.util.TenantUtils;
17 import cn.iocoder.yudao.module.pay.api.order.dto.PayOrderCreateReqDTO; 17 import cn.iocoder.yudao.module.pay.api.order.dto.PayOrderCreateReqDTO;
@@ -134,8 +134,7 @@ public class PayOrderServiceImpl implements PayOrderService { @@ -134,8 +134,7 @@ public class PayOrderServiceImpl implements PayOrderService {
134 return order.getId(); 134 return order.getId();
135 } 135 }
136 136
137 - @Override  
138 - @Transactional(rollbackFor = Exception.class) 137 + @Override // 注意,这里不能添加事务注解,避免调用支付渠道失败时,将 PayOrderExtensionDO 回滚了
139 public PayOrderSubmitRespVO submitOrder(PayOrderSubmitReqVO reqVO, String userIp) { 138 public PayOrderSubmitRespVO submitOrder(PayOrderSubmitReqVO reqVO, String userIp) {
140 // 1. 获得 PayOrderDO ,并校验其是否存在 139 // 1. 获得 PayOrderDO ,并校验其是否存在
141 PayOrderDO order = validateOrderCanSubmit(reqVO.getId()); 140 PayOrderDO order = validateOrderCanSubmit(reqVO.getId());
@@ -159,17 +158,20 @@ public class PayOrderServiceImpl implements PayOrderService { @@ -159,17 +158,20 @@ public class PayOrderServiceImpl implements PayOrderService {
159 .setReturnUrl(reqVO.getReturnUrl()) 158 .setReturnUrl(reqVO.getReturnUrl())
160 // 订单相关字段 159 // 订单相关字段
161 .setPrice(order.getPrice()).setExpireTime(order.getExpireTime()); 160 .setPrice(order.getPrice()).setExpireTime(order.getExpireTime());
162 - PayOrderUnifiedRespDTO unifiedOrderRespDTO = client.unifiedOrder(unifiedOrderReqDTO); 161 + PayOrderRespDTO unifiedOrderResp = client.unifiedOrder(unifiedOrderReqDTO);
163 162
164 // 4. 如果调用直接支付成功,则直接更新支付单状态为成功。例如说:付款码支付,免密支付时,就直接验证支付成功 163 // 4. 如果调用直接支付成功,则直接更新支付单状态为成功。例如说:付款码支付,免密支付时,就直接验证支付成功
165 - if (unifiedOrderRespDTO.getOrder() != null) {  
166 - notifyPayOrder(channel, unifiedOrderRespDTO.getOrder()); 164 + if (unifiedOrderResp != null) {
  165 + notifyPayOrder(channel, unifiedOrderResp);
  166 + // 如有渠道错误码,则抛出业务异常,提示用户
  167 + if (StrUtil.isNotEmpty(unifiedOrderResp.getChannelErrorCode())) {
  168 + throw exception(PAY_ORDER_SUBMIT_CHANNEL_ERROR, unifiedOrderResp.getChannelErrorCode(),
  169 + unifiedOrderResp.getChannelErrorMsg());
  170 + }
167 // 此处需要读取最新的状态 171 // 此处需要读取最新的状态
168 order = orderMapper.selectById(order.getId()); 172 order = orderMapper.selectById(order.getId());
169 } 173 }
170 -  
171 - // 返回成功  
172 - return PayOrderConvert.INSTANCE.convert(order, unifiedOrderRespDTO); 174 + return PayOrderConvert.INSTANCE.convert(order, unifiedOrderResp);
173 } 175 }
174 176
175 private PayOrderDO validateOrderCanSubmit(Long id) { 177 private PayOrderDO validateOrderCanSubmit(Long id) {
@@ -269,8 +271,10 @@ public class PayOrderServiceImpl implements PayOrderService { @@ -269,8 +271,10 @@ public class PayOrderServiceImpl implements PayOrderService {
269 notifyOrderSuccess(channel, notify); 271 notifyOrderSuccess(channel, notify);
270 return; 272 return;
271 } 273 }
272 - // 情况二:非支付成功的回调,进行忽略  
273 - log.info("[notifyPayOrder][非支付成功的回调({}),直接忽略]", toJsonString(notify)); 274 + // 情况二:支付失败的回调
  275 + if (PayOrderStatusRespEnum.isClosed(notify.getStatus())) {
  276 + notifyOrderClosed(channel, notify);
  277 + }
274 } 278 }
275 279
276 private void notifyOrderSuccess(PayChannelDO channel, PayOrderRespDTO notify) { 280 private void notifyOrderSuccess(PayChannelDO channel, PayOrderRespDTO notify) {
@@ -300,7 +304,7 @@ public class PayOrderServiceImpl implements PayOrderService { @@ -300,7 +304,7 @@ public class PayOrderServiceImpl implements PayOrderService {
300 throw exception(PAY_ORDER_EXTENSION_NOT_FOUND); 304 throw exception(PAY_ORDER_EXTENSION_NOT_FOUND);
301 } 305 }
302 if (PayOrderStatusEnum.isSuccess(orderExtension.getStatus())) { // 如果已经是成功,直接返回,不用重复更新 306 if (PayOrderStatusEnum.isSuccess(orderExtension.getStatus())) { // 如果已经是成功,直接返回,不用重复更新
303 - log.info("[updateOrderExtensionSuccess][支付拓展单({}) 已经是已支付,无需更新为已支付]", orderExtension.getId()); 307 + log.info("[updateOrderExtensionSuccess][支付拓展单({}) 已经是已支付,无需更新]", orderExtension.getId());
304 return orderExtension; 308 return orderExtension;
305 } 309 }
306 if (ObjectUtil.notEqual(orderExtension.getStatus(), PayOrderStatusEnum.WAITING.getStatus())) { // 校验状态,必须是待支付 310 if (ObjectUtil.notEqual(orderExtension.getStatus(), PayOrderStatusEnum.WAITING.getStatus())) { // 校验状态,必须是待支付
@@ -327,7 +331,7 @@ public class PayOrderServiceImpl implements PayOrderService { @@ -327,7 +331,7 @@ public class PayOrderServiceImpl implements PayOrderService {
327 * value:PayOrderDO 对象 331 * value:PayOrderDO 对象
328 */ 332 */
329 private Pair<Boolean, PayOrderDO> updateOrderExtensionSuccess(PayChannelDO channel, PayOrderExtensionDO orderExtension, 333 private Pair<Boolean, PayOrderDO> updateOrderExtensionSuccess(PayChannelDO channel, PayOrderExtensionDO orderExtension,
330 - PayOrderRespDTO notify) { 334 + PayOrderRespDTO notify) {
331 // 1. 判断 PayOrderDO 是否处于待支付 335 // 1. 判断 PayOrderDO 是否处于待支付
332 PayOrderDO order = orderMapper.selectById(orderExtension.getOrderId()); 336 PayOrderDO order = orderMapper.selectById(orderExtension.getOrderId());
333 if (order == null) { 337 if (order == null) {
@@ -335,7 +339,7 @@ public class PayOrderServiceImpl implements PayOrderService { @@ -335,7 +339,7 @@ public class PayOrderServiceImpl implements PayOrderService {
335 } 339 }
336 if (PayOrderStatusEnum.isSuccess(order.getStatus()) // 如果已经是成功,直接返回,不用重复更新 340 if (PayOrderStatusEnum.isSuccess(order.getStatus()) // 如果已经是成功,直接返回,不用重复更新
337 && Objects.equals(order.getSuccessExtensionId(), orderExtension.getId())) { 341 && Objects.equals(order.getSuccessExtensionId(), orderExtension.getId())) {
338 - log.info("[updateOrderExtensionSuccess][支付订单({}) 已经是已支付,无需更新为已支付]", order.getId()); 342 + log.info("[updateOrderExtensionSuccess][支付订单({}) 已经是已支付,无需更新]", order.getId());
339 return Pair.of(true, order); 343 return Pair.of(true, order);
340 } 344 }
341 if (!PayOrderStatusEnum.WAITING.getStatus().equals(order.getStatus())) { // 校验状态,必须是待支付 345 if (!PayOrderStatusEnum.WAITING.getStatus().equals(order.getStatus())) { // 校验状态,必须是待支付
@@ -356,4 +360,37 @@ public class PayOrderServiceImpl implements PayOrderService { @@ -356,4 +360,37 @@ public class PayOrderServiceImpl implements PayOrderService {
356 return Pair.of(false, order); 360 return Pair.of(false, order);
357 } 361 }
358 362
  363 + private void notifyOrderClosed(PayChannelDO channel, PayOrderRespDTO notify) {
  364 + updateOrderExtensionClosed(channel, notify);
  365 + }
  366 +
  367 + private void updateOrderExtensionClosed(PayChannelDO channel, PayOrderRespDTO notify) {
  368 + // 1. 查询 PayOrderExtensionDO
  369 + PayOrderExtensionDO orderExtension = orderExtensionMapper.selectByNo(notify.getOutTradeNo());
  370 + if (orderExtension == null) {
  371 + throw exception(PAY_ORDER_EXTENSION_NOT_FOUND);
  372 + }
  373 + if (PayOrderStatusEnum.isClosed(orderExtension.getStatus())) { // 如果已经是关闭,直接返回,不用重复更新
  374 + log.info("[updateOrderExtensionClosed][支付拓展单({}) 已经是支付关闭,无需更新]", orderExtension.getId());
  375 + return;
  376 + }
  377 + // 一般出现先是支付成功,然后支付关闭,都是全部退款导致关闭的场景。这个情况,我们不更新支付拓展单,只通过退款流程,更新支付单
  378 + if (PayOrderStatusEnum.isSuccess(orderExtension.getStatus())) {
  379 + log.info("[updateOrderExtensionClosed][支付拓展单({}) 是已支付,无需更新为支付关闭]", orderExtension.getId());
  380 + return;
  381 + }
  382 + if (ObjectUtil.notEqual(orderExtension.getStatus(), PayOrderStatusEnum.WAITING.getStatus())) { // 校验状态,必须是待支付
  383 + throw exception(PAY_ORDER_EXTENSION_STATUS_IS_NOT_WAITING);
  384 + }
  385 +
  386 + // 2. 更新 PayOrderExtensionDO
  387 + int updateCounts = orderExtensionMapper.updateByIdAndStatus(orderExtension.getId(), orderExtension.getStatus(),
  388 + PayOrderExtensionDO.builder().status(PayOrderStatusEnum.CLOSED.getStatus()).channelNotifyData(toJsonString(notify))
  389 + .channelErrorCode(notify.getChannelErrorCode()).channelErrorMsg(notify.getChannelErrorMsg()).build());
  390 + if (updateCounts == 0) { // 校验状态,必须是待支付
  391 + throw exception(PAY_ORDER_EXTENSION_STATUS_IS_NOT_WAITING);
  392 + }
  393 + log.info("[updateOrderExtensionClosed][支付拓展单({}) 更新为支付关闭]", orderExtension.getId());
  394 + }
  395 +
359 } 396 }