Commit 518e89dc4b192cbfd965ec1b4fb204b3a8f3a796

Authored by YunaiV
1 parent fbb63ee2

mall + pay:

1. 重构支付回调的逻辑,将回调解析改成 PayOrderRespDTO,为后续轮询做铺垫
2. 调整退款单的表结构
3. 调整退款调用的实现
Showing 44 changed files with 388 additions and 451 deletions
yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/validation/ValidationUtils.java
1 package cn.iocoder.yudao.framework.common.util.validation; 1 package cn.iocoder.yudao.framework.common.util.validation;
2 2
3 import cn.hutool.core.collection.CollUtil; 3 import cn.hutool.core.collection.CollUtil;
  4 +import cn.hutool.core.lang.Assert;
4 import org.springframework.util.StringUtils; 5 import org.springframework.util.StringUtils;
5 6
6 import javax.validation.ConstraintViolation; 7 import javax.validation.ConstraintViolation;
7 import javax.validation.ConstraintViolationException; 8 import javax.validation.ConstraintViolationException;
  9 +import javax.validation.Validation;
8 import javax.validation.Validator; 10 import javax.validation.Validator;
9 import java.util.Set; 11 import java.util.Set;
10 import java.util.regex.Pattern; 12 import java.util.regex.Pattern;
@@ -37,6 +39,12 @@ public class ValidationUtils { @@ -37,6 +39,12 @@ public class ValidationUtils {
37 && PATTERN_XML_NCNAME.matcher(str).matches(); 39 && PATTERN_XML_NCNAME.matcher(str).matches();
38 } 40 }
39 41
  42 + public static void validate(Object object, Class<?>... groups) {
  43 + Validator validator = Validation.buildDefaultValidatorFactory().getValidator();
  44 + Assert.notNull(validator);
  45 + validate(validator, object, groups);
  46 + }
  47 +
40 public static void validate(Validator validator, Object object, Class<?>... groups) { 48 public static void validate(Validator validator, Object object, Class<?>... groups) {
41 Set<ConstraintViolation<Object>> constraintViolations = validator.validate(object, groups); 49 Set<ConstraintViolation<Object>> constraintViolations = validator.validate(object, groups);
42 if (CollUtil.isNotEmpty(constraintViolations)) { 50 if (CollUtil.isNotEmpty(constraintViolations)) {
yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/PayClient.java
1 package cn.iocoder.yudao.framework.pay.core.client; 1 package cn.iocoder.yudao.framework.pay.core.client;
2 2
3 -import cn.iocoder.yudao.framework.pay.core.client.dto.notify.PayNotifyReqDTO;  
4 -import cn.iocoder.yudao.framework.pay.core.client.dto.notify.PayOrderNotifyRespDTO;  
5 -import cn.iocoder.yudao.framework.pay.core.client.dto.notify.PayRefundNotifyRespDTO; 3 +import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderRespDTO;
6 import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderUnifiedReqDTO; 4 import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderUnifiedReqDTO;
7 import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderUnifiedRespDTO; 5 import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderUnifiedRespDTO;
  6 +import cn.iocoder.yudao.framework.pay.core.client.dto.refund.PayRefundRespDTO;
8 import cn.iocoder.yudao.framework.pay.core.client.dto.refund.PayRefundUnifiedReqDTO; 7 import cn.iocoder.yudao.framework.pay.core.client.dto.refund.PayRefundUnifiedReqDTO;
9 -import cn.iocoder.yudao.framework.pay.core.client.dto.refund.PayRefundUnifiedRespDTO; 8 +
  9 +import java.util.Map;
10 10
11 /** 11 /**
12 * 支付客户端,用于对接各支付渠道的 SDK,实现发起支付、退款等功能 12 * 支付客户端,用于对接各支付渠道的 SDK,实现发起支付、退款等功能
@@ -32,20 +32,22 @@ public interface PayClient { @@ -32,20 +32,22 @@ public interface PayClient {
32 32
33 /** 33 /**
34 * 调用支付渠道,进行退款 34 * 调用支付渠道,进行退款
  35 + *
35 * @param reqDTO 统一退款请求信息 36 * @param reqDTO 统一退款请求信息
36 - * @return 各支付渠道的统一返回结果 37 + * @return 退款信息
37 */ 38 */
38 - PayRefundUnifiedRespDTO unifiedRefund(PayRefundUnifiedReqDTO reqDTO); 39 + PayRefundRespDTO unifiedRefund(PayRefundUnifiedReqDTO reqDTO);
39 40
40 /** 41 /**
41 * 解析回调数据 42 * 解析回调数据
42 * 43 *
43 - * @param rawNotify 通知内容 44 + * @param params HTTP 回调接口 content type 为 application/x-www-form-urlencoded 的所有参数
  45 + * @param body HTTP 回调接口的 request body
44 * @return 回调对象 46 * @return 回调对象
45 - * 1. {@link PayRefundNotifyRespDTO} 退款通知  
46 - * 2. {@link PayOrderNotifyRespDTO} 支付通知 47 + * 1. {@link PayRefundRespDTO} 退款通知
  48 + * 2. {@link PayOrderRespDTO} 支付通知
47 */ 49 */
48 - default Object parseNotify(PayNotifyReqDTO rawNotify) { 50 + default Object parseNotify(Map<String, String> params, String body) {
49 throw new UnsupportedOperationException("未实现 parseNotify 方法!"); 51 throw new UnsupportedOperationException("未实现 parseNotify 方法!");
50 } 52 }
51 53
yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/dto/notify/PayNotifyReqDTO.java deleted
1 -package cn.iocoder.yudao.framework.pay.core.client.dto.notify;  
2 -  
3 -import lombok.Builder;  
4 -import lombok.Data;  
5 -import lombok.ToString;  
6 -  
7 -import java.util.Map;  
8 -  
9 -  
10 -/**  
11 - * 支付订单,退款订单回调,渠道的统一通知请求数据  
12 - */  
13 -@Data  
14 -@ToString  
15 -@Builder  
16 -public class PayNotifyReqDTO {  
17 -  
18 -  
19 - /**  
20 - * HTTP 回调接口的 request body  
21 - */  
22 - private String body;  
23 -  
24 - /**  
25 - * HTTP 回调接口 content type 为 application/x-www-form-urlencoded 的所有参数  
26 - */  
27 - private Map<String,String> params;  
28 -  
29 -}  
yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/dto/notify/PayRefundNotifyRespDTO.java deleted
1 -package cn.iocoder.yudao.framework.pay.core.client.dto.notify;  
2 -  
3 -import cn.iocoder.yudao.framework.pay.core.enums.refund.PayNotifyRefundStatusEnum;  
4 -import lombok.Builder;  
5 -import lombok.Data;  
6 -import lombok.ToString;  
7 -  
8 -import java.time.LocalDateTime;  
9 -  
10 -/**  
11 - * 从渠道返回数据中解析得到的支付退款通知的Notify DTO  
12 - *  
13 - * @author jason  
14 - */  
15 -@Data  
16 -@ToString  
17 -@Builder  
18 -public class PayRefundNotifyRespDTO {  
19 -  
20 - /**  
21 - * 支付渠道编号  
22 - */  
23 - private String channelOrderNo;  
24 -  
25 - /**  
26 - * 交易订单号,根据规则生成  
27 - * 调用支付渠道时,使用该字段作为对接的订单号。  
28 - * 1. 调用微信支付 https://api.mch.weixin.qq.com/pay/unifiedorder 时,使用该字段作为 out_trade_no  
29 - * 2. 调用支付宝 https://opendocs.alipay.com/apis 时,使用该字段作为 out_trade_no  
30 - * 这里对应 pay_extension 里面的 no  
31 - * 例如说,P202110132239124200055  
32 - */  
33 - private String tradeNo;  
34 -  
35 - /**  
36 - * https://api.mch.weixin.qq.com/v3/refund/domestic/refunds 中的 out_refund_no  
37 - * https://opendocs.alipay.com/apis alipay.trade.refund 中的 out_request_no  
38 - * 退款请求号。  
39 - * 标识一次退款请求,需要保证在交易号下唯一,如需部分退款,则此参数必传。  
40 - * 注:针对同一次退款请求,如果调用接口失败或异常了,重试时需要保证退款请求号不能变更,  
41 - * 防止该笔交易重复退款。支付宝会保证同样的退款请求号多次请求只会退一次。  
42 - * 退款单请求号,根据规则生成  
43 - *  
44 - * 例如说,RR202109181134287570000  
45 - */  
46 - private String reqNo;  
47 -  
48 - /**  
49 - * 退款是否成功  
50 - */  
51 - private PayNotifyRefundStatusEnum status;  
52 -  
53 - /**  
54 - * 退款成功时间  
55 - */  
56 - private LocalDateTime refundSuccessTime;  
57 -  
58 -}  
yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/dto/notify/PayOrderNotifyRespDTO.java renamed to 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.notify; 1 +package cn.iocoder.yudao.framework.pay.core.client.dto.order;
2 2
  3 +import cn.iocoder.yudao.framework.pay.core.enums.order.PayOrderStatusRespEnum;
3 import lombok.AllArgsConstructor; 4 import lombok.AllArgsConstructor;
4 import lombok.Builder; 5 import lombok.Builder;
5 import lombok.Data; 6 import lombok.Data;
@@ -8,7 +9,7 @@ import lombok.NoArgsConstructor; @@ -8,7 +9,7 @@ import lombok.NoArgsConstructor;
8 import java.time.LocalDateTime; 9 import java.time.LocalDateTime;
9 10
10 /** 11 /**
11 - * 支付通知 Response DTO 12 + * 渠道支付订单 Response DTO
12 * 13 *
13 * @author 芋道源码 14 * @author 芋道源码
14 */ 15 */
@@ -16,12 +17,22 @@ import java.time.LocalDateTime; @@ -16,12 +17,22 @@ import java.time.LocalDateTime;
16 @Builder 17 @Builder
17 @NoArgsConstructor 18 @NoArgsConstructor
18 @AllArgsConstructor 19 @AllArgsConstructor
19 -public class PayOrderNotifyRespDTO { 20 +public class PayOrderRespDTO {
20 21
21 /** 22 /**
22 - * 支付订单号(支付模块的) 23 + * 支付状态
  24 + *
  25 + * 枚举:{@link PayOrderStatusRespEnum}
23 */ 26 */
24 - private String orderExtensionNo; 27 + private Integer status;
  28 +
  29 + /**
  30 + * 外部订单号
  31 + *
  32 + * 对应 PayOrderExtensionDO 的 no 字段
  33 + */
  34 + private String outTradeNo;
  35 +
25 /** 36 /**
26 * 支付渠道编号 37 * 支付渠道编号
27 */ 38 */
@@ -30,9 +41,15 @@ public class PayOrderNotifyRespDTO { @@ -30,9 +41,15 @@ public class PayOrderNotifyRespDTO {
30 * 支付渠道用户编号 41 * 支付渠道用户编号
31 */ 42 */
32 private String channelUserId; 43 private String channelUserId;
  44 +
33 /** 45 /**
34 * 支付成功时间 46 * 支付成功时间
35 */ 47 */
36 private LocalDateTime successTime; 48 private LocalDateTime successTime;
37 49
  50 + /**
  51 + * 原始的异步通知结果
  52 + */
  53 + private Object rawData;
  54 +
38 } 55 }
yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/dto/order/PayOrderUnifiedReqDTO.java
@@ -28,10 +28,12 @@ public class PayOrderUnifiedReqDTO { @@ -28,10 +28,12 @@ public class PayOrderUnifiedReqDTO {
28 // ========== 商户相关字段 ========== 28 // ========== 商户相关字段 ==========
29 29
30 /** 30 /**
31 - * 商户订单编号 31 + * 外部订单号
  32 + *
  33 + * 对应 PayOrderExtensionDO 的 no 字段
32 */ 34 */
33 - @NotEmpty(message = "商户订单编号不能为空")  
34 - private String merchantOrderId; 35 + @NotEmpty(message = "外部订单编号不能为空")
  36 + private String outTradeNo;
35 /** 37 /**
36 * 商品标题 38 * 商品标题
37 */ 39 */
@@ -63,7 +65,7 @@ public class PayOrderUnifiedReqDTO { @@ -63,7 +65,7 @@ public class PayOrderUnifiedReqDTO {
63 */ 65 */
64 @NotNull(message = "支付金额不能为空") 66 @NotNull(message = "支付金额不能为空")
65 @DecimalMin(value = "0", inclusive = false, message = "支付金额必须大于零") 67 @DecimalMin(value = "0", inclusive = false, message = "支付金额必须大于零")
66 - private Integer amount; 68 + private Integer price;
67 69
68 /** 70 /**
69 * 支付过期时间 71 * 支付过期时间
yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/dto/order/PayOrderUnifiedRespDTO.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.dto.notify.PayOrderNotifyRespDTO;  
4 import cn.iocoder.yudao.framework.pay.core.enums.order.PayOrderDisplayModeEnum; 3 import cn.iocoder.yudao.framework.pay.core.enums.order.PayOrderDisplayModeEnum;
5 import lombok.Data; 4 import lombok.Data;
6 5
@@ -24,11 +23,12 @@ public class PayOrderUnifiedRespDTO { @@ -24,11 +23,12 @@ public class PayOrderUnifiedRespDTO {
24 private String displayContent; 23 private String displayContent;
25 24
26 /** 25 /**
27 - * 同步的通知信息 26 + * 渠道支付订单
28 * 27 *
  28 + * 只有在订单直接支付成功时,才会进行返回。
29 * 目前只有 bar 条码支付才会出现,它是支付发起时,直接返回是否支付成功的,而其它支付还是异步通知 29 * 目前只有 bar 条码支付才会出现,它是支付发起时,直接返回是否支付成功的,而其它支付还是异步通知
30 */ 30 */
31 - private PayOrderNotifyRespDTO notify; 31 + private PayOrderRespDTO order;
32 32
33 public PayOrderUnifiedRespDTO(String displayMode, String displayContent) { 33 public PayOrderUnifiedRespDTO(String displayMode, String displayContent) {
34 this.displayMode = displayMode; 34 this.displayMode = displayMode;
yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/dto/refund/PayRefundRespDTO.java 0 → 100644
  1 +package cn.iocoder.yudao.framework.pay.core.client.dto.refund;
  2 +
  3 +import cn.iocoder.yudao.framework.pay.core.enums.refund.PayRefundStatusRespEnum;
  4 +import lombok.Data;
  5 +
  6 +import java.time.LocalDateTime;
  7 +
  8 +/**
  9 + * 渠道退款订单 Response DTO
  10 + *
  11 + * @author jason
  12 + */
  13 +@Data
  14 +public class PayRefundRespDTO {
  15 +
  16 + /**
  17 + * 退款状态
  18 + *
  19 + * 枚举 {@link PayRefundStatusRespEnum}
  20 + */
  21 + private Integer status;
  22 +
  23 + /**
  24 + * 渠道退款单号
  25 + *
  26 + * 对应 PayRefundDO.channelRefundNo 字段
  27 + */
  28 + private String channelRefundNo;
  29 +
  30 + /**
  31 + * 退款成功时间
  32 + */
  33 + private LocalDateTime successTime;
  34 +
  35 + /**
  36 + * 原始的异步通知结果
  37 + */
  38 + private Object rawData;
  39 +
  40 +}
yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/dto/refund/PayRefundUnifiedReqDTO.java
@@ -24,33 +24,20 @@ import javax.validation.constraints.NotNull; @@ -24,33 +24,20 @@ import javax.validation.constraints.NotNull;
24 public class PayRefundUnifiedReqDTO { 24 public class PayRefundUnifiedReqDTO {
25 25
26 /** 26 /**
27 - * 用户 IP 27 + * 外部订单号
  28 + *
  29 + * 对应 PayOrderExtensionDO 的 no 字段
28 */ 30 */
29 - private String userIp; 31 + @NotEmpty(message = "外部订单编号不能为空")
  32 + private String outTradeNo;
30 33
31 - // TODO @jason:这个是否为非必传字段呀,只需要传递 payTradeNo 字段即可。尽可能精简  
32 /** 34 /**
33 - * https://api.mch.weixin.qq.com/v3/refund/domestic/refunds 中的 transaction_id  
34 - * https://opendocs.alipay.com/apis alipay.trade.refund 中的 trade_no  
35 - * 渠道订单号 35 + * 外部退款号
  36 + *
  37 + * 对应 PayRefundDO 的 no 字段
36 */ 38 */
37 - private String channelOrderNo;  
38 -  
39 - /**  
40 - * https://api.mch.weixin.qq.com/v3/refund/domestic/refunds 中的 out_trade_no  
41 - * https://opendocs.alipay.com/apis alipay.trade.refund 中的 out_trade_no  
42 - * 支付交易号 {PayOrderExtensionDO no字段} 和 渠道订单号 不能同时为空  
43 - */  
44 - private String payTradeNo;  
45 -  
46 - /**  
47 - * https://api.mch.weixin.qq.com/v3/refund/domestic/refunds 中的 out_refund_no  
48 - * https://opendocs.alipay.com/apis alipay.trade.refund 中的 out_trade_no  
49 - * 退款请求单号 同一退款请求单号多次请求只退一笔。  
50 - * 使用 商户的退款单号。{PayRefundDO 字段 merchantRefundNo}  
51 - */  
52 - @NotEmpty(message = "退款请求单号")  
53 - private String merchantRefundId; 39 + @NotEmpty(message = "退款请求单号不能为空")
  40 + private String outRefundNo;
54 41
55 /** 42 /**
56 * 退款原因 43 * 退款原因
@@ -63,11 +50,12 @@ public class PayRefundUnifiedReqDTO { @@ -63,11 +50,12 @@ public class PayRefundUnifiedReqDTO {
63 */ 50 */
64 @NotNull(message = "退款金额不能为空") 51 @NotNull(message = "退款金额不能为空")
65 @DecimalMin(value = "0", inclusive = false, message = "支付金额必须大于零") 52 @DecimalMin(value = "0", inclusive = false, message = "支付金额必须大于零")
66 - private Integer amount; 53 + private Integer price;
67 54
68 /** 55 /**
69 - * 退款结果 notify 回调地址, 支付宝退款不需要回调地址, 微信需要 56 + * 退款结果的 notify 回调地址
70 */ 57 */
  58 + @NotEmpty(message = "支付结果的回调地址不能为空")
71 @URL(message = "支付结果的 notify 回调地址必须是 URL 格式") 59 @URL(message = "支付结果的 notify 回调地址必须是 URL 格式")
72 private String notifyUrl; 60 private String notifyUrl;
73 61
yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/dto/refund/PayRefundUnifiedRespDTO.java deleted
1 -package cn.iocoder.yudao.framework.pay.core.client.dto.refund;  
2 -  
3 -import lombok.AllArgsConstructor;  
4 -import lombok.Builder;  
5 -import lombok.Data;  
6 -import lombok.NoArgsConstructor;  
7 -import lombok.experimental.Accessors;  
8 -/**  
9 - * 统一退款 Response DTO  
10 - *  
11 - * @author jason  
12 - */  
13 -@Accessors(chain = true)  
14 -@Builder  
15 -@NoArgsConstructor  
16 -@AllArgsConstructor  
17 -@Data  
18 -public class PayRefundUnifiedRespDTO {  
19 -  
20 - /**  
21 - * 渠道退款单编号  
22 - */  
23 - private String channelRefundId;  
24 -}  
yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/AbstractPayClient.java
@@ -5,8 +5,8 @@ import cn.iocoder.yudao.framework.pay.core.client.PayClient; @@ -5,8 +5,8 @@ import cn.iocoder.yudao.framework.pay.core.client.PayClient;
5 import cn.iocoder.yudao.framework.pay.core.client.PayClientConfig; 5 import cn.iocoder.yudao.framework.pay.core.client.PayClientConfig;
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; 7 import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderUnifiedRespDTO;
  8 +import cn.iocoder.yudao.framework.pay.core.client.dto.refund.PayRefundRespDTO;
8 import cn.iocoder.yudao.framework.pay.core.client.dto.refund.PayRefundUnifiedReqDTO; 9 import cn.iocoder.yudao.framework.pay.core.client.dto.refund.PayRefundUnifiedReqDTO;
9 -import cn.iocoder.yudao.framework.pay.core.client.dto.refund.PayRefundUnifiedRespDTO;  
10 import cn.iocoder.yudao.framework.pay.core.client.exception.PayException; 10 import cn.iocoder.yudao.framework.pay.core.client.exception.PayException;
11 import lombok.extern.slf4j.Slf4j; 11 import lombok.extern.slf4j.Slf4j;
12 12
@@ -92,10 +92,10 @@ public abstract class AbstractPayClient&lt;Config extends PayClientConfig&gt; implemen @@ -92,10 +92,10 @@ public abstract class AbstractPayClient&lt;Config extends PayClientConfig&gt; implemen
92 throws Throwable; 92 throws Throwable;
93 93
94 @Override 94 @Override
95 - public PayRefundUnifiedRespDTO unifiedRefund(PayRefundUnifiedReqDTO reqDTO) { 95 + public PayRefundRespDTO unifiedRefund(PayRefundUnifiedReqDTO reqDTO) {
96 Validation.buildDefaultValidatorFactory().getValidator().validate(reqDTO); 96 Validation.buildDefaultValidatorFactory().getValidator().validate(reqDTO);
97 // 执行统一退款 97 // 执行统一退款
98 - PayRefundUnifiedRespDTO resp; 98 + PayRefundRespDTO resp;
99 try { 99 try {
100 resp = doUnifiedRefund(reqDTO); 100 resp = doUnifiedRefund(reqDTO);
101 } catch (ServiceException ex) { 101 } catch (ServiceException ex) {
@@ -109,7 +109,7 @@ public abstract class AbstractPayClient&lt;Config extends PayClientConfig&gt; implemen @@ -109,7 +109,7 @@ public abstract class AbstractPayClient&lt;Config extends PayClientConfig&gt; implemen
109 return resp; 109 return resp;
110 } 110 }
111 111
112 - protected abstract PayRefundUnifiedRespDTO doUnifiedRefund(PayRefundUnifiedReqDTO reqDTO) throws Throwable; 112 + protected abstract PayRefundRespDTO doUnifiedRefund(PayRefundUnifiedReqDTO reqDTO) throws Throwable;
113 113
114 // ========== 各种工具方法 ========== 114 // ========== 各种工具方法 ==========
115 115
yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/alipay/AbstractAlipayPayClient.java
@@ -2,16 +2,19 @@ package cn.iocoder.yudao.framework.pay.core.client.impl.alipay; @@ -2,16 +2,19 @@ package cn.iocoder.yudao.framework.pay.core.client.impl.alipay;
2 2
3 import cn.hutool.core.bean.BeanUtil; 3 import cn.hutool.core.bean.BeanUtil;
4 import cn.hutool.core.date.LocalDateTimeUtil; 4 import cn.hutool.core.date.LocalDateTimeUtil;
  5 +import cn.hutool.core.lang.Assert;
  6 +import cn.hutool.core.util.StrUtil;
5 import cn.hutool.http.HttpUtil; 7 import cn.hutool.http.HttpUtil;
6 import cn.iocoder.yudao.framework.common.util.json.JsonUtils; 8 import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
7 -import cn.iocoder.yudao.framework.pay.core.client.dto.notify.PayNotifyReqDTO;  
8 -import cn.iocoder.yudao.framework.pay.core.client.dto.notify.PayOrderNotifyRespDTO;  
9 -import cn.iocoder.yudao.framework.pay.core.client.dto.notify.PayRefundNotifyRespDTO; 9 +import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderRespDTO;
  10 +import cn.iocoder.yudao.framework.pay.core.client.dto.refund.PayRefundRespDTO;
10 import cn.iocoder.yudao.framework.pay.core.client.dto.refund.PayRefundUnifiedReqDTO; 11 import cn.iocoder.yudao.framework.pay.core.client.dto.refund.PayRefundUnifiedReqDTO;
11 -import cn.iocoder.yudao.framework.pay.core.client.dto.refund.PayRefundUnifiedRespDTO;  
12 import cn.iocoder.yudao.framework.pay.core.client.impl.AbstractPayClient; 12 import cn.iocoder.yudao.framework.pay.core.client.impl.AbstractPayClient;
13 -import cn.iocoder.yudao.framework.pay.core.enums.refund.PayNotifyRefundStatusEnum;  
14 -import com.alipay.api.*; 13 +import cn.iocoder.yudao.framework.pay.core.enums.order.PayOrderStatusRespEnum;
  14 +import com.alipay.api.AlipayApiException;
  15 +import com.alipay.api.AlipayConfig;
  16 +import com.alipay.api.AlipayResponse;
  17 +import com.alipay.api.DefaultAlipayClient;
15 import com.alipay.api.domain.AlipayTradeRefundModel; 18 import com.alipay.api.domain.AlipayTradeRefundModel;
16 import com.alipay.api.internal.util.AlipaySignature; 19 import com.alipay.api.internal.util.AlipaySignature;
17 import com.alipay.api.request.AlipayTradeRefundRequest; 20 import com.alipay.api.request.AlipayTradeRefundRequest;
@@ -22,6 +25,8 @@ import lombok.extern.slf4j.Slf4j; @@ -22,6 +25,8 @@ import lombok.extern.slf4j.Slf4j;
22 import java.nio.charset.StandardCharsets; 25 import java.nio.charset.StandardCharsets;
23 import java.time.LocalDateTime; 26 import java.time.LocalDateTime;
24 import java.util.Map; 27 import java.util.Map;
  28 +import java.util.Objects;
  29 +import java.util.function.Supplier;
25 30
26 import static cn.hutool.core.date.DatePattern.NORM_DATETIME_FORMATTER; 31 import static cn.hutool.core.date.DatePattern.NORM_DATETIME_FORMATTER;
27 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception0; 32 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception0;
@@ -52,69 +57,72 @@ public abstract class AbstractAlipayPayClient extends AbstractPayClient&lt;AlipayPa @@ -52,69 +57,72 @@ public abstract class AbstractAlipayPayClient extends AbstractPayClient&lt;AlipayPa
52 57
53 /** 58 /**
54 * 支付宝统一的退款接口 alipay.trade.refund 59 * 支付宝统一的退款接口 alipay.trade.refund
  60 + *
55 * @param reqDTO 退款请求 request DTO 61 * @param reqDTO 退款请求 request DTO
56 * @return 退款请求 Response 62 * @return 退款请求 Response
57 */ 63 */
58 @Override 64 @Override
59 - protected PayRefundUnifiedRespDTO doUnifiedRefund(PayRefundUnifiedReqDTO reqDTO) {  
60 - AlipayTradeRefundModel model=new AlipayTradeRefundModel();  
61 - model.setTradeNo(reqDTO.getChannelOrderNo());  
62 - model.setOutTradeNo(reqDTO.getPayTradeNo());  
63 -  
64 - model.setOutRequestNo(reqDTO.getMerchantRefundId());  
65 - model.setRefundAmount(formatAmount(reqDTO.getAmount())); 65 + protected PayRefundRespDTO doUnifiedRefund(PayRefundUnifiedReqDTO reqDTO) {
  66 + // 1.1 构建 AlipayTradeRefundModel 请求
  67 + AlipayTradeRefundModel model = new AlipayTradeRefundModel();
  68 + model.setOutTradeNo(reqDTO.getOutTradeNo());
  69 + model.setOutRequestNo(reqDTO.getOutRefundNo());
  70 + model.setRefundAmount(formatAmount(reqDTO.getPrice()));
  71 +// model.setRefundAmount(formatAmount(reqDTO.getPrice() / 2));
66 model.setRefundReason(reqDTO.getReason()); 72 model.setRefundReason(reqDTO.getReason());
67 -  
68 - AlipayTradeRefundRequest refundRequest = new AlipayTradeRefundRequest();  
69 - refundRequest.setBizModel(model);  
70 - refundRequest.setNotifyUrl(reqDTO.getNotifyUrl());  
71 - refundRequest.setReturnUrl(reqDTO.getNotifyUrl()); 73 + // 1.2 构建 AlipayTradePayRequest 请求
  74 + AlipayTradeRefundRequest request = new AlipayTradeRefundRequest();
  75 + request.setBizModel(model);
72 try { 76 try {
73 - AlipayTradeRefundResponse response = client.execute(refundRequest);  
74 - log.info("[doUnifiedRefund][response({}) 发起退款 渠道返回", toJsonString(response)); 77 + // 2.1 执行请求
  78 + AlipayTradeRefundResponse response = client.execute(request);
  79 + PayRefundRespDTO refund = new PayRefundRespDTO()
  80 + .setRawData(response);
  81 + // 支付宝只要退款调用返回 success,就认为退款成功,不需要回调。具体可见 parseNotify 方法的说明。
  82 + // 另外,支付宝没有退款单号,所以不用设置
75 if (response.isSuccess()) { 83 if (response.isSuccess()) {
76 - //退款导致触发的异步通知是发送到支付接口中设置的notify_url  
77 - //支付宝不返回退款单号,设置为空  
78 - PayRefundUnifiedRespDTO respDTO = new PayRefundUnifiedRespDTO();  
79 - respDTO.setChannelRefundId("");  
80 -// return PayCommonResult.build(response.getCode(), response.getMsg(), respDTO, codeMapping); TODO  
81 - return null; 84 + refund.setStatus(PayOrderStatusRespEnum.SUCCESS.getStatus())
  85 + .setSuccessTime(LocalDateTimeUtil.of(response.getGmtRefundPay()));
  86 + Assert.notNull(refund.getSuccessTime(), "退款成功时间不能为空");
  87 + } else {
  88 + refund.setStatus(PayOrderStatusRespEnum.CLOSED.getStatus());
82 } 89 }
83 - // 失败。需要抛出异常  
84 -// return PayCommonResult.build(response.getCode(), response.getMsg(), null, codeMapping); TODO  
85 - return null; 90 + return refund;
86 } catch (AlipayApiException e) { 91 } catch (AlipayApiException e) {
87 - // TODO 记录异常日志  
88 - log.error("[doUnifiedRefund][request({}) 发起退款失败,网络读超时,退款状态未知]", toJsonString(reqDTO), e);  
89 -// return PayCommonResult.build(e.getErrCode(), e.getErrMsg(), null, codeMapping); TODO 92 + log.error("[doUnifiedRefund][request({}) 发起退款异常]", toJsonString(reqDTO), e);
90 return null; 93 return null;
91 } 94 }
92 } 95 }
93 96
94 @Override 97 @Override
95 @SneakyThrows 98 @SneakyThrows
96 - public Object parseNotify(PayNotifyReqDTO rawNotify) { 99 + public Object parseNotify(Map<String, String> params, String body) {
  100 + // 补充说明:支付宝退款时,没有回调,这点和微信支付是不同的。并且,退款分成部分退款、和全部退款。
  101 + // ① 部分退款:是会有回调,但是它回调的是订单状态的同步回调,不是退款订单的回调
  102 + // ② 全部退款:Wap 支付有订单状态的同步回调,但是 PC/扫码又没有
  103 + // 所以,这里在解析时,即使是退款导致的订单状态同步,我们也忽略不做为“退款同步”,而是订单的回调。
  104 + // 实际上,支付宝退款只要发起成功,就可以认为退款成功,不需要等待回调。
  105 +
97 // 1. 校验回调数据 106 // 1. 校验回调数据
98 - String body = rawNotify.getBody();  
99 - Map<String, String> params = rawNotify.getParams();  
100 Map<String, String> bodyObj = HttpUtil.decodeParamMap(body, StandardCharsets.UTF_8); 107 Map<String, String> bodyObj = HttpUtil.decodeParamMap(body, StandardCharsets.UTF_8);
101 AlipaySignature.rsaCheckV1(bodyObj, config.getAlipayPublicKey(), 108 AlipaySignature.rsaCheckV1(bodyObj, config.getAlipayPublicKey(),
102 - StandardCharsets.UTF_8.name(), "RSA2");  
103 -  
104 - // 2.1 退款的情况  
105 - if (bodyObj.containsKey("refund_fee")) {  
106 - return PayRefundNotifyRespDTO.builder().channelOrderNo(bodyObj.get("trade_no"))  
107 - .tradeNo(bodyObj.get("out_trade_no")).reqNo(bodyObj.get("out_biz_no"))  
108 - .status(PayNotifyRefundStatusEnum.SUCCESS)  
109 - .refundSuccessTime(parseTime(params.get("gmt_refund")))  
110 - .build();  
111 - }  
112 - // 2.2 支付的情况  
113 - return PayOrderNotifyRespDTO.builder()  
114 - .orderExtensionNo(bodyObj.get("out_trade_no")) 109 + StandardCharsets.UTF_8.name(), config.getSignType());
  110 +
  111 + // 2. 解析订单的状态
  112 + String tradeStatus = bodyObj.get("trade_status");
  113 + PayOrderStatusRespEnum status = Objects.equals("WAIT_BUYER_PAY", tradeStatus) ? PayOrderStatusRespEnum.WAITING
  114 + : Objects.equals("TRADE_SUCCESS", tradeStatus) ? PayOrderStatusRespEnum.SUCCESS
  115 + : Objects.equals("TRADE_CLOSED", tradeStatus) ? PayOrderStatusRespEnum.CLOSED : null;
  116 + Assert.notNull(status, (Supplier<Throwable>) () -> {
  117 + throw new IllegalArgumentException(StrUtil.format("body({}) 的 trade_status 不正确", body));
  118 + });
  119 + return PayOrderRespDTO.builder()
  120 + .status(Objects.requireNonNull(status).getStatus())
  121 + .outTradeNo(bodyObj.get("out_trade_no"))
115 .channelOrderNo(bodyObj.get("trade_no")) 122 .channelOrderNo(bodyObj.get("trade_no"))
116 .channelUserId(bodyObj.get("seller_id")) 123 .channelUserId(bodyObj.get("seller_id"))
117 - .successTime(parseTime(params.get("notify_time"))) 124 + .successTime(parseTime(params.get("gmt_payment")))
  125 + .rawData(body)
118 .build(); 126 .build();
119 } 127 }
120 128
yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/alipay/AlipayAppPayClient.java
@@ -31,10 +31,10 @@ public class AlipayAppPayClient extends AbstractAlipayPayClient { @@ -31,10 +31,10 @@ public class AlipayAppPayClient extends AbstractAlipayPayClient {
31 // 1.1 构建 AlipayTradeAppPayModel 请求 31 // 1.1 构建 AlipayTradeAppPayModel 请求
32 AlipayTradeAppPayModel model = new AlipayTradeAppPayModel(); 32 AlipayTradeAppPayModel model = new AlipayTradeAppPayModel();
33 // ① 通用的参数 33 // ① 通用的参数
34 - model.setOutTradeNo(reqDTO.getMerchantOrderId()); 34 + model.setOutTradeNo(reqDTO.getOutTradeNo());
35 model.setSubject(reqDTO.getSubject()); 35 model.setSubject(reqDTO.getSubject());
36 model.setBody(reqDTO.getBody()); 36 model.setBody(reqDTO.getBody());
37 - model.setTotalAmount(formatAmount(reqDTO.getAmount())); 37 + model.setTotalAmount(formatAmount(reqDTO.getPrice()));
38 model.setProductCode(" QUICK_MSECURITY_PAY"); // 销售产品码:无线快捷支付产品 38 model.setProductCode(" QUICK_MSECURITY_PAY"); // 销售产品码:无线快捷支付产品
39 // ② 个性化的参数【无】 39 // ② 个性化的参数【无】
40 // ③ 支付宝扫码支付只有一种展示 40 // ③ 支付宝扫码支付只有一种展示
yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/alipay/AlipayBarPayClient.java
@@ -39,10 +39,10 @@ public class AlipayBarPayClient extends AbstractAlipayPayClient { @@ -39,10 +39,10 @@ public class AlipayBarPayClient extends AbstractAlipayPayClient {
39 // 1.1 构建 AlipayTradePayModel 请求 39 // 1.1 构建 AlipayTradePayModel 请求
40 AlipayTradePayModel model = new AlipayTradePayModel(); 40 AlipayTradePayModel model = new AlipayTradePayModel();
41 // ① 通用的参数 41 // ① 通用的参数
42 - model.setOutTradeNo(reqDTO.getMerchantOrderId()); 42 + model.setOutTradeNo(reqDTO.getOutTradeNo());
43 model.setSubject(reqDTO.getSubject()); 43 model.setSubject(reqDTO.getSubject());
44 model.setBody(reqDTO.getBody()); 44 model.setBody(reqDTO.getBody());
45 - model.setTotalAmount(formatAmount(reqDTO.getAmount())); 45 + model.setTotalAmount(formatAmount(reqDTO.getPrice()));
46 model.setScene("bar_code"); // 当面付条码支付场景 46 model.setScene("bar_code"); // 当面付条码支付场景
47 // ② 个性化的参数 47 // ② 个性化的参数
48 model.setAuthCode(authCode); 48 model.setAuthCode(authCode);
yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/alipay/AlipayPcPayClient.java
@@ -33,10 +33,10 @@ public class AlipayPcPayClient extends AbstractAlipayPayClient { @@ -33,10 +33,10 @@ public class AlipayPcPayClient extends AbstractAlipayPayClient {
33 // 1.1 构建 AlipayTradePagePayModel 请求 33 // 1.1 构建 AlipayTradePagePayModel 请求
34 AlipayTradePagePayModel model = new AlipayTradePagePayModel(); 34 AlipayTradePagePayModel model = new AlipayTradePagePayModel();
35 // ① 通用的参数 35 // ① 通用的参数
36 - model.setOutTradeNo(reqDTO.getMerchantOrderId()); 36 + model.setOutTradeNo(reqDTO.getOutTradeNo());
37 model.setSubject(reqDTO.getSubject()); 37 model.setSubject(reqDTO.getSubject());
38 model.setBody(reqDTO.getBody()); 38 model.setBody(reqDTO.getBody());
39 - model.setTotalAmount(formatAmount(reqDTO.getAmount())); 39 + model.setTotalAmount(formatAmount(reqDTO.getPrice()));
40 model.setTimeExpire(formatTime(reqDTO.getExpireTime())); 40 model.setTimeExpire(formatTime(reqDTO.getExpireTime()));
41 model.setProductCode("FAST_INSTANT_TRADE_PAY"); // 销售产品码. 目前 PC 支付场景下仅支持 FAST_INSTANT_TRADE_PAY 41 model.setProductCode("FAST_INSTANT_TRADE_PAY"); // 销售产品码. 目前 PC 支付场景下仅支持 FAST_INSTANT_TRADE_PAY
42 // ② 个性化的参数 42 // ② 个性化的参数
yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/alipay/AlipayQrPayClient.java
@@ -29,10 +29,10 @@ public class AlipayQrPayClient extends AbstractAlipayPayClient { @@ -29,10 +29,10 @@ public class AlipayQrPayClient extends AbstractAlipayPayClient {
29 // 1.1 构建 AlipayTradePrecreateModel 请求 29 // 1.1 构建 AlipayTradePrecreateModel 请求
30 AlipayTradePrecreateModel model = new AlipayTradePrecreateModel(); 30 AlipayTradePrecreateModel model = new AlipayTradePrecreateModel();
31 // ① 通用的参数 31 // ① 通用的参数
32 - model.setOutTradeNo(reqDTO.getMerchantOrderId()); 32 + model.setOutTradeNo(reqDTO.getOutTradeNo());
33 model.setSubject(reqDTO.getSubject()); 33 model.setSubject(reqDTO.getSubject());
34 model.setBody(reqDTO.getBody()); 34 model.setBody(reqDTO.getBody());
35 - model.setTotalAmount(formatAmount(reqDTO.getAmount())); 35 + model.setTotalAmount(formatAmount(reqDTO.getPrice()));
36 model.setProductCode("FACE_TO_FACE_PAYMENT"); // 销售产品码. 目前扫码支付场景下仅支持 FACE_TO_FACE_PAYMENT 36 model.setProductCode("FACE_TO_FACE_PAYMENT"); // 销售产品码. 目前扫码支付场景下仅支持 FACE_TO_FACE_PAYMENT
37 // ② 个性化的参数【无】 37 // ② 个性化的参数【无】
38 // ③ 支付宝扫码支付只有一种展示,考虑到前端可能希望二维码扫描后,手机打开 38 // ③ 支付宝扫码支付只有一种展示,考虑到前端可能希望二维码扫描后,手机打开
yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/alipay/AlipayWapPayClient.java
@@ -30,10 +30,10 @@ public class AlipayWapPayClient extends AbstractAlipayPayClient { @@ -30,10 +30,10 @@ public class AlipayWapPayClient extends AbstractAlipayPayClient {
30 // 1.1 构建 AlipayTradeWapPayModel 请求 30 // 1.1 构建 AlipayTradeWapPayModel 请求
31 AlipayTradeWapPayModel model = new AlipayTradeWapPayModel(); 31 AlipayTradeWapPayModel model = new AlipayTradeWapPayModel();
32 // ① 通用的参数 32 // ① 通用的参数
33 - model.setOutTradeNo(reqDTO.getMerchantOrderId()); 33 + model.setOutTradeNo(reqDTO.getOutTradeNo());
34 model.setSubject(reqDTO.getSubject()); 34 model.setSubject(reqDTO.getSubject());
35 model.setBody(reqDTO.getBody()); 35 model.setBody(reqDTO.getBody());
36 - model.setTotalAmount(formatAmount(reqDTO.getAmount())); 36 + model.setTotalAmount(formatAmount(reqDTO.getPrice()));
37 model.setProductCode("QUICK_WAP_PAY"); // 销售产品码. 目前 Wap 支付场景下仅支持 QUICK_WAP_PAY 37 model.setProductCode("QUICK_WAP_PAY"); // 销售产品码. 目前 Wap 支付场景下仅支持 QUICK_WAP_PAY
38 // ② 个性化的参数【无】 38 // ② 个性化的参数【无】
39 // ③ 支付宝 Wap 支付只有一种展示:URL 39 // ③ 支付宝 Wap 支付只有一种展示:URL
yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/weixin/AbstractWxPayClient.java
@@ -6,8 +6,7 @@ import cn.hutool.core.date.TemporalAccessorUtil; @@ -6,8 +6,7 @@ import cn.hutool.core.date.TemporalAccessorUtil;
6 import cn.hutool.core.lang.Assert; 6 import cn.hutool.core.lang.Assert;
7 import cn.hutool.core.util.StrUtil; 7 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.notify.PayNotifyReqDTO;  
10 -import cn.iocoder.yudao.framework.pay.core.client.dto.notify.PayOrderNotifyRespDTO; 9 +import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderRespDTO;
11 import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderUnifiedReqDTO; 10 import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderUnifiedReqDTO;
12 import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderUnifiedRespDTO; 11 import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderUnifiedRespDTO;
13 import cn.iocoder.yudao.framework.pay.core.client.impl.AbstractPayClient; 12 import cn.iocoder.yudao.framework.pay.core.client.impl.AbstractPayClient;
@@ -22,11 +21,13 @@ import lombok.extern.slf4j.Slf4j; @@ -22,11 +21,13 @@ import lombok.extern.slf4j.Slf4j;
22 21
23 import java.time.LocalDateTime; 22 import java.time.LocalDateTime;
24 import java.time.ZoneId; 23 import java.time.ZoneId;
  24 +import java.util.Map;
25 import java.util.Objects; 25 import java.util.Objects;
26 26
27 import static cn.hutool.core.date.DatePattern.PURE_DATETIME_PATTERN; 27 import static cn.hutool.core.date.DatePattern.PURE_DATETIME_PATTERN;
28 import static cn.hutool.core.date.DatePattern.UTC_WITH_XXX_OFFSET_PATTERN; 28 import static cn.hutool.core.date.DatePattern.UTC_WITH_XXX_OFFSET_PATTERN;
29 -import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.*; 29 +import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
  30 +import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.invalidParamException;
30 import static cn.iocoder.yudao.framework.common.util.json.JsonUtils.toJsonString; 31 import static cn.iocoder.yudao.framework.common.util.json.JsonUtils.toJsonString;
31 32
32 /** 33 /**
@@ -103,47 +104,47 @@ public abstract class AbstractWxPayClient extends AbstractPayClient&lt;WxPayClientC @@ -103,47 +104,47 @@ public abstract class AbstractWxPayClient extends AbstractPayClient&lt;WxPayClientC
103 104
104 105
105 @Override 106 @Override
106 - public Object parseNotify(PayNotifyReqDTO rawNotify) {  
107 - log.info("[parseNotify][微信支付回调 data 数据: {}]", rawNotify.getBody()); 107 + public Object parseNotify(Map<String, String> params, String body) {
  108 + log.info("[parseNotify][微信支付回调 data 数据: {}]", body);
108 try { 109 try {
109 // 微信支付 v2 回调结果处理 110 // 微信支付 v2 回调结果处理
110 switch (config.getApiVersion()) { 111 switch (config.getApiVersion()) {
111 case WxPayClientConfig.API_VERSION_V2: 112 case WxPayClientConfig.API_VERSION_V2:
112 - return parseOrderNotifyV2(rawNotify); 113 + return parseOrderNotifyV2(body);
113 case WxPayClientConfig.API_VERSION_V3: 114 case WxPayClientConfig.API_VERSION_V3:
114 - return parseOrderNotifyV3(rawNotify); 115 + return parseOrderNotifyV3(body);
115 default: 116 default:
116 throw new IllegalArgumentException(String.format("未知的 API 版本(%s)", config.getApiVersion())); 117 throw new IllegalArgumentException(String.format("未知的 API 版本(%s)", config.getApiVersion()));
117 } 118 }
118 } catch (WxPayException e) { 119 } catch (WxPayException e) {
119 - log.error("[parseNotify][rawNotify({}) 解析失败]", toJsonString(rawNotify), e); 120 + log.error("[parseNotify][params({}) body({}) 解析失败]", params, body, e);
120 // throw buildPayException(e); 121 // throw buildPayException(e);
121 throw new RuntimeException(e); 122 throw new RuntimeException(e);
122 // TODO 芋艿:缺一个异常翻译 123 // TODO 芋艿:缺一个异常翻译
123 } 124 }
124 } 125 }
125 126
126 - private PayOrderNotifyRespDTO parseOrderNotifyV2(PayNotifyReqDTO data) throws WxPayException {  
127 - WxPayOrderNotifyResult notifyResult = client.parseOrderNotifyResult(data.getBody()); 127 + private PayOrderRespDTO parseOrderNotifyV2(String body) throws WxPayException {
  128 + WxPayOrderNotifyResult notifyResult = client.parseOrderNotifyResult(body);
128 Assert.isTrue(Objects.equals(notifyResult.getResultCode(), "SUCCESS"), "支付结果非 SUCCESS"); 129 Assert.isTrue(Objects.equals(notifyResult.getResultCode(), "SUCCESS"), "支付结果非 SUCCESS");
129 // 转换结果 130 // 转换结果
130 - return PayOrderNotifyRespDTO 131 + return PayOrderRespDTO
131 .builder() 132 .builder()
132 - .orderExtensionNo(notifyResult.getOutTradeNo()) 133 + .outTradeNo(notifyResult.getOutTradeNo())
133 .channelOrderNo(notifyResult.getTransactionId()) 134 .channelOrderNo(notifyResult.getTransactionId())
134 .channelUserId(notifyResult.getOpenid()) 135 .channelUserId(notifyResult.getOpenid())
135 .successTime(parseDateV2(notifyResult.getTimeEnd())) 136 .successTime(parseDateV2(notifyResult.getTimeEnd()))
136 .build(); 137 .build();
137 } 138 }
138 139
139 - private PayOrderNotifyRespDTO parseOrderNotifyV3(PayNotifyReqDTO data) throws WxPayException {  
140 - WxPayOrderNotifyV3Result notifyResult = client.parseOrderNotifyV3Result(data.getBody(), null); 140 + private PayOrderRespDTO parseOrderNotifyV3(String body) throws WxPayException {
  141 + WxPayOrderNotifyV3Result notifyResult = client.parseOrderNotifyV3Result(body, null);
141 WxPayOrderNotifyV3Result.DecryptNotifyResult result = notifyResult.getResult(); 142 WxPayOrderNotifyV3Result.DecryptNotifyResult result = notifyResult.getResult();
142 // 转换结果 143 // 转换结果
143 Assert.isTrue(Objects.equals(notifyResult.getResult().getTradeState(), "SUCCESS"), 144 Assert.isTrue(Objects.equals(notifyResult.getResult().getTradeState(), "SUCCESS"),
144 "支付结果非 SUCCESS"); 145 "支付结果非 SUCCESS");
145 - return PayOrderNotifyRespDTO.builder()  
146 - .orderExtensionNo(result.getOutTradeNo()) 146 + return PayOrderRespDTO.builder()
  147 + .outTradeNo(result.getOutTradeNo())
147 .channelOrderNo(result.getTradeState()) 148 .channelOrderNo(result.getTradeState())
148 .channelUserId(result.getPayer() != null ? result.getPayer().getOpenid() : null) 149 .channelUserId(result.getPayer() != null ? result.getPayer().getOpenid() : null)
149 .successTime(parseDateV3(result.getSuccessTime())) 150 .successTime(parseDateV3(result.getSuccessTime()))
@@ -175,7 +176,7 @@ public abstract class AbstractWxPayClient extends AbstractPayClient&lt;WxPayClientC @@ -175,7 +176,7 @@ public abstract class AbstractWxPayClient extends AbstractPayClient&lt;WxPayClientC
175 if (Objects.equals(e.getReturnCode(), "FAIL")) { 176 if (Objects.equals(e.getReturnCode(), "FAIL")) {
176 throw exception(PayFrameworkErrorCodeConstants.ORDER_UNIFIED_ERROR, e.getReturnMsg()); 177 throw exception(PayFrameworkErrorCodeConstants.ORDER_UNIFIED_ERROR, e.getReturnMsg());
177 } 178 }
178 - // 情况三:系统异常,这里暂时不打,交给上层的 AbstractPayClient 统一打 179 + // 情况三:系统异常,这里暂时不打,交给上层的 AbstractPayClient 统一打日志
179 return e; 180 return e;
180 } 181 }
181 182
yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/weixin/WxAppPayClient.java
@@ -2,8 +2,8 @@ package cn.iocoder.yudao.framework.pay.core.client.impl.weixin; @@ -2,8 +2,8 @@ package cn.iocoder.yudao.framework.pay.core.client.impl.weixin;
2 2
3 import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderUnifiedReqDTO; 3 import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderUnifiedReqDTO;
4 import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderUnifiedRespDTO; 4 import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderUnifiedRespDTO;
  5 +import cn.iocoder.yudao.framework.pay.core.client.dto.refund.PayRefundRespDTO;
5 import cn.iocoder.yudao.framework.pay.core.client.dto.refund.PayRefundUnifiedReqDTO; 6 import cn.iocoder.yudao.framework.pay.core.client.dto.refund.PayRefundUnifiedReqDTO;
6 -import cn.iocoder.yudao.framework.pay.core.client.dto.refund.PayRefundUnifiedRespDTO;  
7 import cn.iocoder.yudao.framework.pay.core.enums.channel.PayChannelEnum; 7 import cn.iocoder.yudao.framework.pay.core.enums.channel.PayChannelEnum;
8 import com.github.binarywang.wxpay.constant.WxPayConstants; 8 import com.github.binarywang.wxpay.constant.WxPayConstants;
9 import com.github.binarywang.wxpay.exception.WxPayException; 9 import com.github.binarywang.wxpay.exception.WxPayException;
@@ -21,7 +21,7 @@ public class WxAppPayClient extends AbstractWxPayClient { @@ -21,7 +21,7 @@ public class WxAppPayClient extends AbstractWxPayClient {
21 } 21 }
22 22
23 @Override 23 @Override
24 - protected PayRefundUnifiedRespDTO doUnifiedRefund(PayRefundUnifiedReqDTO reqDTO) throws Throwable { 24 + protected PayRefundRespDTO doUnifiedRefund(PayRefundUnifiedReqDTO reqDTO) throws Throwable {
25 return null; 25 return null;
26 } 26 }
27 27
yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/weixin/WxBarPayClient.java
@@ -5,13 +5,14 @@ import cn.hutool.core.thread.ThreadUtil; @@ -5,13 +5,14 @@ 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; 7 import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
8 -import cn.iocoder.yudao.framework.pay.core.client.dto.notify.PayOrderNotifyRespDTO; 8 +import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderRespDTO;
9 import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderUnifiedReqDTO; 9 import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderUnifiedReqDTO;
10 import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderUnifiedRespDTO; 10 import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderUnifiedRespDTO;
  11 +import cn.iocoder.yudao.framework.pay.core.client.dto.refund.PayRefundRespDTO;
11 import cn.iocoder.yudao.framework.pay.core.client.dto.refund.PayRefundUnifiedReqDTO; 12 import cn.iocoder.yudao.framework.pay.core.client.dto.refund.PayRefundUnifiedReqDTO;
12 -import cn.iocoder.yudao.framework.pay.core.client.dto.refund.PayRefundUnifiedRespDTO;  
13 import cn.iocoder.yudao.framework.pay.core.enums.channel.PayChannelEnum; 13 import cn.iocoder.yudao.framework.pay.core.enums.channel.PayChannelEnum;
14 import cn.iocoder.yudao.framework.pay.core.enums.order.PayOrderDisplayModeEnum; 14 import cn.iocoder.yudao.framework.pay.core.enums.order.PayOrderDisplayModeEnum;
  15 +import cn.iocoder.yudao.framework.pay.core.enums.order.PayOrderStatusRespEnum;
15 import com.github.binarywang.wxpay.bean.request.WxPayMicropayRequest; 16 import com.github.binarywang.wxpay.bean.request.WxPayMicropayRequest;
16 import com.github.binarywang.wxpay.bean.result.WxPayMicropayResult; 17 import com.github.binarywang.wxpay.bean.result.WxPayMicropayResult;
17 import com.github.binarywang.wxpay.constant.WxPayConstants; 18 import com.github.binarywang.wxpay.constant.WxPayConstants;
@@ -57,10 +58,10 @@ public class WxBarPayClient extends AbstractWxPayClient { @@ -57,10 +58,10 @@ public class WxBarPayClient extends AbstractWxPayClient {
57 } 58 }
58 // 构建 WxPayMicropayRequest 对象 59 // 构建 WxPayMicropayRequest 对象
59 WxPayMicropayRequest request = WxPayMicropayRequest.newBuilder() 60 WxPayMicropayRequest request = WxPayMicropayRequest.newBuilder()
60 - .outTradeNo(reqDTO.getMerchantOrderId()) 61 + .outTradeNo(reqDTO.getOutTradeNo())
61 .body(reqDTO.getSubject()) 62 .body(reqDTO.getSubject())
62 .detail(reqDTO.getBody()) 63 .detail(reqDTO.getBody())
63 - .totalFee(reqDTO.getAmount()) // 单位分 64 + .totalFee(reqDTO.getPrice()) // 单位分
64 .timeExpire(formatDateV2(expireTime)) 65 .timeExpire(formatDateV2(expireTime))
65 .spbillCreateIp(reqDTO.getUserIp()) 66 .spbillCreateIp(reqDTO.getUserIp())
66 .authCode(getAuthCode(reqDTO)) 67 .authCode(getAuthCode(reqDTO))
@@ -70,15 +71,17 @@ public class WxBarPayClient extends AbstractWxPayClient { @@ -70,15 +71,17 @@ public class WxBarPayClient extends AbstractWxPayClient {
70 try { 71 try {
71 WxPayMicropayResult response = client.micropay(request); 72 WxPayMicropayResult response = client.micropay(request);
72 // 支付成功(例如说,用户输入了密码) 73 // 支付成功(例如说,用户输入了密码)
73 - PayOrderNotifyRespDTO notify = PayOrderNotifyRespDTO.builder()  
74 - .orderExtensionNo(response.getOutTradeNo()) 74 + PayOrderRespDTO order = PayOrderRespDTO.builder()
  75 + .status(PayOrderStatusRespEnum.SUCCESS.getStatus())
  76 + .outTradeNo(response.getOutTradeNo())
75 .channelOrderNo(response.getTransactionId()) 77 .channelOrderNo(response.getTransactionId())
76 .channelUserId(response.getOpenid()) 78 .channelUserId(response.getOpenid())
77 .successTime(parseDateV2(response.getTimeEnd())) 79 .successTime(parseDateV2(response.getTimeEnd()))
  80 + .rawData(response)
78 .build(); 81 .build();
79 return new PayOrderUnifiedRespDTO(PayOrderDisplayModeEnum.BAR_CODE.getMode(), 82 return new PayOrderUnifiedRespDTO(PayOrderDisplayModeEnum.BAR_CODE.getMode(),
80 JsonUtils.toJsonString(response)) 83 JsonUtils.toJsonString(response))
81 - .setNotify(notify); 84 + .setOrder(order);
82 } catch (WxPayException ex) { 85 } catch (WxPayException ex) {
83 // 如果不满足这 3 种任一的,则直接抛出 WxPayException 异常,不仅需处理 86 // 如果不满足这 3 种任一的,则直接抛出 WxPayException 异常,不仅需处理
84 // 1. SYSTEMERROR:接口返回错误:请立即调用被扫订单结果查询API,查询当前订单状态,并根据订单的状态决定下一步的操作。 87 // 1. SYSTEMERROR:接口返回错误:请立即调用被扫订单结果查询API,查询当前订单状态,并根据订单的状态决定下一步的操作。
@@ -102,7 +105,7 @@ public class WxBarPayClient extends AbstractWxPayClient { @@ -102,7 +105,7 @@ public class WxBarPayClient extends AbstractWxPayClient {
102 } 105 }
103 106
104 @Override 107 @Override
105 - protected PayRefundUnifiedRespDTO doUnifiedRefund(PayRefundUnifiedReqDTO reqDTO) { 108 + protected PayRefundRespDTO doUnifiedRefund(PayRefundUnifiedReqDTO reqDTO) {
106 return null; 109 return null;
107 } 110 }
108 111
yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/weixin/WxH5PayClient.java
@@ -2,8 +2,8 @@ package cn.iocoder.yudao.framework.pay.core.client.impl.weixin; @@ -2,8 +2,8 @@ package cn.iocoder.yudao.framework.pay.core.client.impl.weixin;
2 2
3 import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderUnifiedReqDTO; 3 import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderUnifiedReqDTO;
4 import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderUnifiedRespDTO; 4 import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderUnifiedRespDTO;
  5 +import cn.iocoder.yudao.framework.pay.core.client.dto.refund.PayRefundRespDTO;
5 import cn.iocoder.yudao.framework.pay.core.client.dto.refund.PayRefundUnifiedReqDTO; 6 import cn.iocoder.yudao.framework.pay.core.client.dto.refund.PayRefundUnifiedReqDTO;
6 -import cn.iocoder.yudao.framework.pay.core.client.dto.refund.PayRefundUnifiedRespDTO;  
7 import cn.iocoder.yudao.framework.pay.core.enums.channel.PayChannelEnum; 7 import cn.iocoder.yudao.framework.pay.core.enums.channel.PayChannelEnum;
8 import com.github.binarywang.wxpay.constant.WxPayConstants; 8 import com.github.binarywang.wxpay.constant.WxPayConstants;
9 import com.github.binarywang.wxpay.exception.WxPayException; 9 import com.github.binarywang.wxpay.exception.WxPayException;
@@ -21,7 +21,7 @@ public class WxH5PayClient extends AbstractWxPayClient { @@ -21,7 +21,7 @@ public class WxH5PayClient extends AbstractWxPayClient {
21 } 21 }
22 22
23 @Override 23 @Override
24 - protected PayRefundUnifiedRespDTO doUnifiedRefund(PayRefundUnifiedReqDTO reqDTO) throws Throwable { 24 + protected PayRefundRespDTO doUnifiedRefund(PayRefundUnifiedReqDTO reqDTO) throws Throwable {
25 return null; 25 return null;
26 } 26 }
27 27
yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/weixin/WxNativePayClient.java
@@ -2,8 +2,8 @@ package cn.iocoder.yudao.framework.pay.core.client.impl.weixin; @@ -2,8 +2,8 @@ package cn.iocoder.yudao.framework.pay.core.client.impl.weixin;
2 2
3 import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderUnifiedReqDTO; 3 import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderUnifiedReqDTO;
4 import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderUnifiedRespDTO; 4 import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderUnifiedRespDTO;
  5 +import cn.iocoder.yudao.framework.pay.core.client.dto.refund.PayRefundRespDTO;
5 import cn.iocoder.yudao.framework.pay.core.client.dto.refund.PayRefundUnifiedReqDTO; 6 import cn.iocoder.yudao.framework.pay.core.client.dto.refund.PayRefundUnifiedReqDTO;
6 -import cn.iocoder.yudao.framework.pay.core.client.dto.refund.PayRefundUnifiedRespDTO;  
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.github.binarywang.wxpay.bean.order.WxPayNativeOrderResult; 9 import com.github.binarywang.wxpay.bean.order.WxPayNativeOrderResult;
@@ -37,11 +37,11 @@ public class WxNativePayClient extends AbstractWxPayClient { @@ -37,11 +37,11 @@ public class WxNativePayClient extends AbstractWxPayClient {
37 protected PayOrderUnifiedRespDTO doUnifiedOrderV2(PayOrderUnifiedReqDTO reqDTO) throws WxPayException { 37 protected PayOrderUnifiedRespDTO doUnifiedOrderV2(PayOrderUnifiedReqDTO reqDTO) throws WxPayException {
38 // 构建 WxPayUnifiedOrderRequest 对象 38 // 构建 WxPayUnifiedOrderRequest 对象
39 WxPayUnifiedOrderRequest request = WxPayUnifiedOrderRequest.newBuilder() 39 WxPayUnifiedOrderRequest request = WxPayUnifiedOrderRequest.newBuilder()
40 - .outTradeNo(reqDTO.getMerchantOrderId()) 40 + .outTradeNo(reqDTO.getOutTradeNo())
41 .body(reqDTO.getSubject()) 41 .body(reqDTO.getSubject())
42 .detail(reqDTO.getBody()) 42 .detail(reqDTO.getBody())
43 - .totalFee(reqDTO.getAmount()) // 单位分  
44 - .productId(reqDTO.getMerchantOrderId()) 43 + .totalFee(reqDTO.getPrice()) // 单位分
  44 + .productId(reqDTO.getOutTradeNo())
45 .timeExpire(formatDateV2(reqDTO.getExpireTime())) 45 .timeExpire(formatDateV2(reqDTO.getExpireTime()))
46 .spbillCreateIp(reqDTO.getUserIp()) 46 .spbillCreateIp(reqDTO.getUserIp())
47 .notifyUrl(reqDTO.getNotifyUrl()) 47 .notifyUrl(reqDTO.getNotifyUrl())
@@ -58,9 +58,9 @@ public class WxNativePayClient extends AbstractWxPayClient { @@ -58,9 +58,9 @@ public class WxNativePayClient extends AbstractWxPayClient {
58 protected PayOrderUnifiedRespDTO doUnifiedOrderV3(PayOrderUnifiedReqDTO reqDTO) throws WxPayException { 58 protected PayOrderUnifiedRespDTO doUnifiedOrderV3(PayOrderUnifiedReqDTO reqDTO) throws WxPayException {
59 // 构建 WxPayUnifiedOrderRequest 对象 59 // 构建 WxPayUnifiedOrderRequest 对象
60 WxPayUnifiedOrderV3Request request = new WxPayUnifiedOrderV3Request(); 60 WxPayUnifiedOrderV3Request request = new WxPayUnifiedOrderV3Request();
61 - request.setOutTradeNo(reqDTO.getMerchantOrderId()); 61 + request.setOutTradeNo(reqDTO.getOutTradeNo());
62 request.setDescription(reqDTO.getBody()); 62 request.setDescription(reqDTO.getBody());
63 - request.setAmount(new WxPayUnifiedOrderV3Request.Amount().setTotal(reqDTO.getAmount())); // 单位分 63 + request.setAmount(new WxPayUnifiedOrderV3Request.Amount().setTotal(reqDTO.getPrice())); // 单位分
64 request.setTimeExpire(formatDateV3(reqDTO.getExpireTime())); 64 request.setTimeExpire(formatDateV3(reqDTO.getExpireTime()));
65 request.setSceneInfo(new WxPayUnifiedOrderV3Request.SceneInfo().setPayerClientIp(reqDTO.getUserIp())); 65 request.setSceneInfo(new WxPayUnifiedOrderV3Request.SceneInfo().setPayerClientIp(reqDTO.getUserIp()));
66 request.setNotifyUrl(reqDTO.getNotifyUrl()); 66 request.setNotifyUrl(reqDTO.getNotifyUrl());
@@ -73,7 +73,7 @@ public class WxNativePayClient extends AbstractWxPayClient { @@ -73,7 +73,7 @@ public class WxNativePayClient extends AbstractWxPayClient {
73 } 73 }
74 74
75 @Override 75 @Override
76 - protected PayRefundUnifiedRespDTO doUnifiedRefund(PayRefundUnifiedReqDTO reqDTO) throws Throwable { 76 + protected PayRefundRespDTO doUnifiedRefund(PayRefundUnifiedReqDTO reqDTO) throws Throwable {
77 return null; 77 return null;
78 } 78 }
79 79
yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/weixin/WxPubPayClient.java
@@ -5,8 +5,8 @@ import cn.hutool.core.util.StrUtil; @@ -5,8 +5,8 @@ import cn.hutool.core.util.StrUtil;
5 import cn.iocoder.yudao.framework.common.util.json.JsonUtils; 5 import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
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; 7 import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderUnifiedRespDTO;
  8 +import cn.iocoder.yudao.framework.pay.core.client.dto.refund.PayRefundRespDTO;
8 import cn.iocoder.yudao.framework.pay.core.client.dto.refund.PayRefundUnifiedReqDTO; 9 import cn.iocoder.yudao.framework.pay.core.client.dto.refund.PayRefundUnifiedReqDTO;
9 -import cn.iocoder.yudao.framework.pay.core.client.dto.refund.PayRefundUnifiedRespDTO;  
10 import cn.iocoder.yudao.framework.pay.core.enums.channel.PayChannelEnum; 10 import cn.iocoder.yudao.framework.pay.core.enums.channel.PayChannelEnum;
11 import cn.iocoder.yudao.framework.pay.core.enums.order.PayOrderDisplayModeEnum; 11 import cn.iocoder.yudao.framework.pay.core.enums.order.PayOrderDisplayModeEnum;
12 import com.github.binarywang.wxpay.bean.order.WxPayMpOrderResult; 12 import com.github.binarywang.wxpay.bean.order.WxPayMpOrderResult;
@@ -47,10 +47,10 @@ public class WxPubPayClient extends AbstractWxPayClient { @@ -47,10 +47,10 @@ public class WxPubPayClient extends AbstractWxPayClient {
47 protected PayOrderUnifiedRespDTO doUnifiedOrderV2(PayOrderUnifiedReqDTO reqDTO) throws WxPayException { 47 protected PayOrderUnifiedRespDTO doUnifiedOrderV2(PayOrderUnifiedReqDTO reqDTO) throws WxPayException {
48 // 构建 WxPayUnifiedOrderRequest 对象 48 // 构建 WxPayUnifiedOrderRequest 对象
49 WxPayUnifiedOrderRequest request = WxPayUnifiedOrderRequest.newBuilder() 49 WxPayUnifiedOrderRequest request = WxPayUnifiedOrderRequest.newBuilder()
50 - .outTradeNo(reqDTO.getMerchantOrderId()) 50 + .outTradeNo(reqDTO.getOutTradeNo())
51 .body(reqDTO.getSubject()) 51 .body(reqDTO.getSubject())
52 .detail(reqDTO.getBody()) 52 .detail(reqDTO.getBody())
53 - .totalFee(reqDTO.getAmount()) // 单位分 53 + .totalFee(reqDTO.getPrice()) // 单位分
54 .timeExpire(formatDateV2(reqDTO.getExpireTime())) 54 .timeExpire(formatDateV2(reqDTO.getExpireTime()))
55 .spbillCreateIp(reqDTO.getUserIp()) 55 .spbillCreateIp(reqDTO.getUserIp())
56 .openid(getOpenid(reqDTO)) 56 .openid(getOpenid(reqDTO))
@@ -68,9 +68,9 @@ public class WxPubPayClient extends AbstractWxPayClient { @@ -68,9 +68,9 @@ public class WxPubPayClient extends AbstractWxPayClient {
68 protected PayOrderUnifiedRespDTO doUnifiedOrderV3(PayOrderUnifiedReqDTO reqDTO) throws WxPayException { 68 protected PayOrderUnifiedRespDTO doUnifiedOrderV3(PayOrderUnifiedReqDTO reqDTO) throws WxPayException {
69 // 构建 WxPayUnifiedOrderRequest 对象 69 // 构建 WxPayUnifiedOrderRequest 对象
70 WxPayUnifiedOrderV3Request request = new WxPayUnifiedOrderV3Request(); 70 WxPayUnifiedOrderV3Request request = new WxPayUnifiedOrderV3Request();
71 - request.setOutTradeNo(reqDTO.getMerchantOrderId()); 71 + request.setOutTradeNo(reqDTO.getOutTradeNo());
72 request.setDescription(reqDTO.getSubject()); 72 request.setDescription(reqDTO.getSubject());
73 - request.setAmount(new WxPayUnifiedOrderV3Request.Amount().setTotal(reqDTO.getAmount())); // 单位分 73 + request.setAmount(new WxPayUnifiedOrderV3Request.Amount().setTotal(reqDTO.getPrice())); // 单位分
74 request.setTimeExpire(formatDateV3(reqDTO.getExpireTime())); 74 request.setTimeExpire(formatDateV3(reqDTO.getExpireTime()));
75 request.setPayer(new WxPayUnifiedOrderV3Request.Payer().setOpenid(getOpenid(reqDTO))); 75 request.setPayer(new WxPayUnifiedOrderV3Request.Payer().setOpenid(getOpenid(reqDTO)));
76 request.setSceneInfo(new WxPayUnifiedOrderV3Request.SceneInfo().setPayerClientIp(reqDTO.getUserIp())); 76 request.setSceneInfo(new WxPayUnifiedOrderV3Request.SceneInfo().setPayerClientIp(reqDTO.getUserIp()));
@@ -84,7 +84,7 @@ public class WxPubPayClient extends AbstractWxPayClient { @@ -84,7 +84,7 @@ public class WxPubPayClient extends AbstractWxPayClient {
84 } 84 }
85 85
86 @Override 86 @Override
87 - protected PayRefundUnifiedRespDTO doUnifiedRefund(PayRefundUnifiedReqDTO reqDTO) { 87 + protected PayRefundRespDTO doUnifiedRefund(PayRefundUnifiedReqDTO reqDTO) {
88 // TODO 需要实现 88 // TODO 需要实现
89 throw new UnsupportedOperationException(); 89 throw new UnsupportedOperationException();
90 } 90 }
yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/enums/order/PayOrderStatusRespEnum.java
@@ -3,10 +3,12 @@ package cn.iocoder.yudao.framework.pay.core.enums.order; @@ -3,10 +3,12 @@ package cn.iocoder.yudao.framework.pay.core.enums.order;
3 import lombok.AllArgsConstructor; 3 import lombok.AllArgsConstructor;
4 import lombok.Getter; 4 import lombok.Getter;
5 5
  6 +import java.util.Objects;
  7 +
6 /** 8 /**
7 * 渠道的支付状态枚举 9 * 渠道的支付状态枚举
8 * 10 *
9 - * @author 遇到源码 11 + * @author 芋道源码
10 */ 12 */
11 @Getter 13 @Getter
12 @AllArgsConstructor 14 @AllArgsConstructor
@@ -20,4 +22,14 @@ public enum PayOrderStatusRespEnum { @@ -20,4 +22,14 @@ public enum PayOrderStatusRespEnum {
20 private final Integer status; 22 private final Integer status;
21 private final String name; 23 private final String name;
22 24
  25 + /**
  26 + * 判断是否支付成功
  27 + *
  28 + * @param status 状态
  29 + * @return 是否支付成功
  30 + */
  31 + public static boolean isSuccess(Integer status) {
  32 + return Objects.equals(status, SUCCESS.getStatus());
  33 + }
  34 +
23 } 35 }
yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/enums/refund/PayNotifyRefundStatusEnum.java deleted
1 -package cn.iocoder.yudao.framework.pay.core.enums.refund;  
2 -  
3 -// TODO 芋艿:看看能不能去掉  
4 -/**  
5 - * 退款通知, 统一的渠道退款状态  
6 - *  
7 - * @author jason  
8 - */  
9 -public enum PayNotifyRefundStatusEnum {  
10 -  
11 - /**  
12 - * 支付宝 中 全额退款 trade_status=TRADE_CLOSED, 部分退款 trade_status=TRADE_SUCCESS  
13 - * 退款成功  
14 - */  
15 - SUCCESS,  
16 -  
17 - /**  
18 - * 支付宝退款通知没有这个状态  
19 - * 退款异常  
20 - */  
21 - ABNORMAL;  
22 -  
23 -}  
yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/enums/refund/PayRefundRespEnum.java renamed to yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/enums/refund/PayRefundStatusRespEnum.java
@@ -3,6 +3,8 @@ package cn.iocoder.yudao.framework.pay.core.enums.refund; @@ -3,6 +3,8 @@ package cn.iocoder.yudao.framework.pay.core.enums.refund;
3 import lombok.AllArgsConstructor; 3 import lombok.AllArgsConstructor;
4 import lombok.Getter; 4 import lombok.Getter;
5 5
  6 +import java.util.Objects;
  7 +
6 /** 8 /**
7 * 渠道的退款状态枚举 9 * 渠道的退款状态枚举
8 * 10 *
@@ -10,14 +12,17 @@ import lombok.Getter; @@ -10,14 +12,17 @@ import lombok.Getter;
10 */ 12 */
11 @Getter 13 @Getter
12 @AllArgsConstructor 14 @AllArgsConstructor
13 -public enum PayRefundRespEnum { 15 +public enum PayRefundStatusRespEnum {
14 16
15 - SUCCESS(1, "退款成功"),  
16 - FAILURE(2, "退款失败"),  
17 - PROCESSING(3,"退款处理中"),  
18 - CLOSED(4, "退款关闭"); 17 + WAITING(0, "未退款"),
  18 + SUCCESS(10, "退款成功"),
  19 + FAILURE(20, "退款失败");
19 20
20 private final Integer status; 21 private final Integer status;
21 private final String name; 22 private final String name;
22 23
  24 + public static boolean isSuccess(Integer status) {
  25 + return Objects.equals(status, SUCCESS.getStatus());
  26 + }
  27 +
23 } 28 }
yudao-framework/yudao-spring-boot-starter-biz-pay/src/test/java/cn/iocoder/yudao/framework/pay/core/client/impl/PayClientFactoryImplIntegrationTest.java
@@ -121,10 +121,10 @@ public class PayClientFactoryImplIntegrationTest { @@ -121,10 +121,10 @@ public class PayClientFactoryImplIntegrationTest {
121 121
122 private static PayOrderUnifiedReqDTO buildPayOrderUnifiedReqDTO() { 122 private static PayOrderUnifiedReqDTO buildPayOrderUnifiedReqDTO() {
123 PayOrderUnifiedReqDTO reqDTO = new PayOrderUnifiedReqDTO(); 123 PayOrderUnifiedReqDTO reqDTO = new PayOrderUnifiedReqDTO();
124 - reqDTO.setAmount(123); 124 + reqDTO.setPrice(123);
125 reqDTO.setSubject("IPhone 13"); 125 reqDTO.setSubject("IPhone 13");
126 reqDTO.setBody("biubiubiu"); 126 reqDTO.setBody("biubiubiu");
127 - reqDTO.setMerchantOrderId(String.valueOf(System.currentTimeMillis())); 127 + reqDTO.setOutTradeNo(String.valueOf(System.currentTimeMillis()));
128 reqDTO.setUserIp("127.0.0.1"); 128 reqDTO.setUserIp("127.0.0.1");
129 reqDTO.setNotifyUrl("http://127.0.0.1:8080"); 129 reqDTO.setNotifyUrl("http://127.0.0.1:8080");
130 return reqDTO; 130 return reqDTO;
yudao-framework/yudao-spring-boot-starter-biz-pay/src/test/java/cn/iocoder/yudao/framework/pay/core/client/impl/alipay/AlipayQrPayClientTest.java
1 package cn.iocoder.yudao.framework.pay.core.client.impl.alipay; 1 package cn.iocoder.yudao.framework.pay.core.client.impl.alipay;
2 import cn.hutool.core.util.ReflectUtil; 2 import cn.hutool.core.util.ReflectUtil;
3 -import cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeConstants;  
4 import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderUnifiedReqDTO; 3 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.test.core.ut.BaseMockitoUnitTest; 4 import cn.iocoder.yudao.framework.test.core.ut.BaseMockitoUnitTest;
7 import com.alipay.api.AlipayApiException; 5 import com.alipay.api.AlipayApiException;
8 import com.alipay.api.DefaultAlipayClient; 6 import com.alipay.api.DefaultAlipayClient;
@@ -74,8 +72,8 @@ public class AlipayQrPayClientTest extends BaseMockitoUnitTest { @@ -74,8 +72,8 @@ public class AlipayQrPayClientTest extends BaseMockitoUnitTest {
74 // 这里,设置可以直接随机整个对象。 72 // 这里,设置可以直接随机整个对象。
75 Long shopOrderId = System.currentTimeMillis(); 73 Long shopOrderId = System.currentTimeMillis();
76 PayOrderUnifiedReqDTO reqDTO=new PayOrderUnifiedReqDTO(); 74 PayOrderUnifiedReqDTO reqDTO=new PayOrderUnifiedReqDTO();
77 - reqDTO.setMerchantOrderId(String.valueOf(System.currentTimeMillis()));  
78 - reqDTO.setAmount(1); 75 + reqDTO.setOutTradeNo(String.valueOf(System.currentTimeMillis()));
  76 + reqDTO.setPrice(1);
79 reqDTO.setBody("内容:" + shopOrderId); 77 reqDTO.setBody("内容:" + shopOrderId);
80 reqDTO.setSubject("标题:"+shopOrderId); 78 reqDTO.setSubject("标题:"+shopOrderId);
81 String notify="http://niubi.natapp1.cc/api/pay/order/notify"; 79 String notify="http://niubi.natapp1.cc/api/pay/order/notify";
yudao-module-pay/yudao-module-pay-api/src/main/java/cn/iocoder/yudao/module/pay/enums/refund/PayRefundStatusEnum.java
@@ -5,14 +5,18 @@ import lombok.Getter; @@ -5,14 +5,18 @@ import lombok.Getter;
5 5
6 import java.util.Objects; 6 import java.util.Objects;
7 7
  8 +/**
  9 + * 渠道的退款状态枚举
  10 + *
  11 + * @author 芋道源码
  12 + */
8 @Getter 13 @Getter
9 @AllArgsConstructor 14 @AllArgsConstructor
10 public enum PayRefundStatusEnum { 15 public enum PayRefundStatusEnum {
11 16
12 - CREATE(0, "退款订单生成"),  
13 - SUCCESS(1, "退款成功"),  
14 - FAILURE(2, "退款失败"),  
15 - CLOSE(99, "退款关闭"); 17 + WAITING(0, "未退款"),
  18 + SUCCESS(10, "退款成功"),
  19 + FAILURE(20, "退款失败");
16 20
17 private final Integer status; 21 private final Integer status;
18 private final String name; 22 private final String name;
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/controller/admin/notify/PayNotifyController.java
@@ -3,9 +3,8 @@ package cn.iocoder.yudao.module.pay.controller.admin.notify; @@ -3,9 +3,8 @@ package cn.iocoder.yudao.module.pay.controller.admin.notify;
3 import cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog; 3 import cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog;
4 import cn.iocoder.yudao.framework.pay.core.client.PayClient; 4 import cn.iocoder.yudao.framework.pay.core.client.PayClient;
5 import cn.iocoder.yudao.framework.pay.core.client.PayClientFactory; 5 import cn.iocoder.yudao.framework.pay.core.client.PayClientFactory;
6 -import cn.iocoder.yudao.framework.pay.core.client.dto.notify.PayNotifyReqDTO;  
7 -import cn.iocoder.yudao.framework.pay.core.client.dto.notify.PayOrderNotifyRespDTO;  
8 -import cn.iocoder.yudao.framework.pay.core.client.dto.notify.PayRefundNotifyRespDTO; 6 +import cn.iocoder.yudao.framework.pay.core.client.dto.refund.PayRefundRespDTO;
  7 +import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderRespDTO;
9 import cn.iocoder.yudao.module.pay.service.order.PayOrderService; 8 import cn.iocoder.yudao.module.pay.service.order.PayOrderService;
10 import cn.iocoder.yudao.module.pay.service.refund.PayRefundService; 9 import cn.iocoder.yudao.module.pay.service.refund.PayRefundService;
11 import io.swagger.v3.oas.annotations.Operation; 10 import io.swagger.v3.oas.annotations.Operation;
@@ -61,18 +60,17 @@ public class PayNotifyController { @@ -61,18 +60,17 @@ public class PayNotifyController {
61 } 60 }
62 61
63 // 2. 解析通知数据 62 // 2. 解析通知数据
64 - PayNotifyReqDTO rawNotify = PayNotifyReqDTO.builder().params(params).body(body).build();  
65 - Object notify = payClient.parseNotify(rawNotify); 63 + Object notify = payClient.parseNotify(params, body);
66 64
67 // 3. 处理通知 65 // 3. 处理通知
68 // 3.1:退款通知 66 // 3.1:退款通知
69 - if (notify instanceof PayRefundNotifyRespDTO) {  
70 - refundService.notifyPayRefund(channelId, (PayRefundNotifyRespDTO) notify, rawNotify); 67 + if (notify instanceof PayRefundRespDTO) {
  68 + refundService.notifyPayRefund(channelId, (PayRefundRespDTO) notify);
71 return "success"; 69 return "success";
72 } 70 }
73 // 3.2:支付通知 71 // 3.2:支付通知
74 - if (notify instanceof PayOrderNotifyRespDTO) {  
75 - orderService.notifyPayOrder(channelId, (PayOrderNotifyRespDTO) notify, rawNotify); 72 + if (notify instanceof PayOrderRespDTO) {
  73 + orderService.notifyPayOrder(channelId, (PayOrderRespDTO) notify);
76 return "success"; 74 return "success";
77 } 75 }
78 throw new UnsupportedOperationException("未知通知:" + toJsonString(notify)); 76 throw new UnsupportedOperationException("未知通知:" + toJsonString(notify));
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/controller/admin/refund/vo/PayRefundExcelVO.java
@@ -76,9 +76,6 @@ public class PayRefundExcelVO { @@ -76,9 +76,6 @@ public class PayRefundExcelVO {
76 @ExcelProperty("退款成功时间") 76 @ExcelProperty("退款成功时间")
77 private LocalDateTime successTime; 77 private LocalDateTime successTime;
78 78
79 - @ExcelProperty("退款通知时间")  
80 - private LocalDateTime notifyTime;  
81 -  
82 @ExcelProperty("退款失效时间") 79 @ExcelProperty("退款失效时间")
83 private LocalDateTime expireTime; 80 private LocalDateTime expireTime;
84 81
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/convert/refund/PayRefundConvert.java
@@ -60,9 +60,10 @@ public interface PayRefundConvert { @@ -60,9 +60,10 @@ public interface PayRefundConvert {
60 PayRefundExcelVO payRefundExcelVO = new PayRefundExcelVO(); 60 PayRefundExcelVO payRefundExcelVO = new PayRefundExcelVO();
61 61
62 payRefundExcelVO.setId(bean.getId()); 62 payRefundExcelVO.setId(bean.getId());
63 - payRefundExcelVO.setTradeNo(bean.getTradeNo()); 63 + payRefundExcelVO.setTradeNo(bean.getNo());
64 payRefundExcelVO.setMerchantOrderId(bean.getMerchantOrderId()); 64 payRefundExcelVO.setMerchantOrderId(bean.getMerchantOrderId());
65 - payRefundExcelVO.setMerchantRefundNo(bean.getMerchantRefundNo()); 65 + // TODO 芋艿:晚点在改
  66 +// payRefundExcelVO.setMerchantRefundNo(bean.getMerchantRefundNo());
66 payRefundExcelVO.setNotifyUrl(bean.getNotifyUrl()); 67 payRefundExcelVO.setNotifyUrl(bean.getNotifyUrl());
67 payRefundExcelVO.setNotifyStatus(bean.getNotifyStatus()); 68 payRefundExcelVO.setNotifyStatus(bean.getNotifyStatus());
68 payRefundExcelVO.setStatus(bean.getStatus()); 69 payRefundExcelVO.setStatus(bean.getStatus());
@@ -71,9 +72,7 @@ public interface PayRefundConvert { @@ -71,9 +72,7 @@ public interface PayRefundConvert {
71 payRefundExcelVO.setUserIp(bean.getUserIp()); 72 payRefundExcelVO.setUserIp(bean.getUserIp());
72 payRefundExcelVO.setChannelOrderNo(bean.getChannelOrderNo()); 73 payRefundExcelVO.setChannelOrderNo(bean.getChannelOrderNo());
73 payRefundExcelVO.setChannelRefundNo(bean.getChannelRefundNo()); 74 payRefundExcelVO.setChannelRefundNo(bean.getChannelRefundNo());
74 - payRefundExcelVO.setExpireTime(bean.getExpireTime());  
75 payRefundExcelVO.setSuccessTime(bean.getSuccessTime()); 75 payRefundExcelVO.setSuccessTime(bean.getSuccessTime());
76 - payRefundExcelVO.setNotifyTime(bean.getNotifyTime());  
77 payRefundExcelVO.setCreateTime(bean.getCreateTime()); 76 payRefundExcelVO.setCreateTime(bean.getCreateTime());
78 77
79 BigDecimal multiple = new BigDecimal(100); 78 BigDecimal multiple = new BigDecimal(100);
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/dal/dataobject/order/PayOrderDO.java
@@ -55,7 +55,8 @@ public class PayOrderDO extends BaseDO { @@ -55,7 +55,8 @@ public class PayOrderDO extends BaseDO {
55 55
56 /** 56 /**
57 * 商户订单编号 57 * 商户订单编号
58 - * 例如说,内部系统 A 的订单号。需要保证每个 PayMerchantDO 唯一 58 + *
  59 + * 例如说,内部系统 A 的订单号,需要保证每个 PayAppDO 唯一
59 */ 60 */
60 private String merchantOrderId; 61 private String merchantOrderId;
61 /** 62 /**
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/dal/dataobject/order/PayOrderExtensionDO.java
@@ -32,10 +32,11 @@ public class PayOrderExtensionDO extends BaseDO { @@ -32,10 +32,11 @@ public class PayOrderExtensionDO extends BaseDO {
32 */ 32 */
33 private Long id; 33 private Long id;
34 /** 34 /**
35 - * 支付订单号,根据规则生成  
36 - * 调用支付渠道时,使用该字段作为对接的订单号。  
37 - * 1. 调用微信支付 https://api.mch.weixin.qq.com/pay/unifiedorder 时,使用该字段作为 out_trade_no  
38 - * 2. 调用支付宝 https://opendocs.alipay.com/apis 时,使用该字段作为 out_trade_no 35 + * 外部订单号,根据规则生成
  36 + *
  37 + * 调用支付渠道时,使用该字段作为对接的订单号:
  38 + * 1. 微信支付:对应 <a href="https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter3_1_1.shtml">JSAPI 支付</a> 的 out_trade_no 字段
  39 + * 2. 支付宝支付:对应 <a href="https://opendocs.alipay.com/open/270/105898">电脑网站支付</a> 的 out_trade_no 字段
39 * 40 *
40 * 例如说,P202110132239124200055 41 * 例如说,P202110132239124200055
41 */ 42 */
@@ -70,7 +71,7 @@ public class PayOrderExtensionDO extends BaseDO { @@ -70,7 +71,7 @@ public class PayOrderExtensionDO extends BaseDO {
70 /** 71 /**
71 * 支付渠道的额外参数 72 * 支付渠道的额外参数
72 * 73 *
73 - * 参见 https://www.pingxx.com/api/支付渠道%20extra%20参数说明.html 74 + * 参见 <a href="https://www.pingxx.com/api/支付渠道%20extra%20参数说明.html">参数说明</>
74 */ 75 */
75 @TableField(typeHandler = JacksonTypeHandler.class) 76 @TableField(typeHandler = JacksonTypeHandler.class)
76 private Map<String, String> channelExtras; 77 private Map<String, String> channelExtras;
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/dal/dataobject/refund/PayRefundDO.java
@@ -5,6 +5,7 @@ import cn.iocoder.yudao.framework.pay.core.enums.channel.PayChannelEnum; @@ -5,6 +5,7 @@ import cn.iocoder.yudao.framework.pay.core.enums.channel.PayChannelEnum;
5 import cn.iocoder.yudao.module.pay.dal.dataobject.app.PayAppDO; 5 import cn.iocoder.yudao.module.pay.dal.dataobject.app.PayAppDO;
6 import cn.iocoder.yudao.module.pay.dal.dataobject.channel.PayChannelDO; 6 import cn.iocoder.yudao.module.pay.dal.dataobject.channel.PayChannelDO;
7 import cn.iocoder.yudao.module.pay.dal.dataobject.order.PayOrderDO; 7 import cn.iocoder.yudao.module.pay.dal.dataobject.order.PayOrderDO;
  8 +import cn.iocoder.yudao.module.pay.enums.notify.PayNotifyStatusEnum;
8 import cn.iocoder.yudao.module.pay.enums.refund.PayRefundStatusEnum; 9 import cn.iocoder.yudao.module.pay.enums.refund.PayRefundStatusEnum;
9 import cn.iocoder.yudao.module.pay.enums.refund.PayRefundTypeEnum; 10 import cn.iocoder.yudao.module.pay.enums.refund.PayRefundTypeEnum;
10 import com.baomidou.mybatisplus.annotation.KeySequence; 11 import com.baomidou.mybatisplus.annotation.KeySequence;
@@ -37,6 +38,14 @@ public class PayRefundDO extends BaseDO { @@ -37,6 +38,14 @@ public class PayRefundDO extends BaseDO {
37 */ 38 */
38 @TableId 39 @TableId
39 private Long id; 40 private Long id;
  41 + /**
  42 + * 外部退款号,根据规则生成
  43 + *
  44 + * 调用支付渠道时,使用该字段作为对接的退款号:
  45 + * 1. 微信退款:对应 <a href="https://pay.weixin.qq.com/wiki/doc/api/micropay.php?chapter=9_4">申请退款</a> 的 out_refund_no 字段
  46 + * 2. 支付宝退款:对应 <a href="https://opendocs.alipay.com/open/02e7go"统一收单交易退款接口></a> 的 out_request_no 字段
  47 + */
  48 + private String no;
40 49
41 /** 50 /**
42 * 应用编号 51 * 应用编号
@@ -63,47 +72,27 @@ public class PayRefundDO extends BaseDO { @@ -63,47 +72,27 @@ public class PayRefundDO extends BaseDO {
63 */ 72 */
64 private Long orderId; 73 private Long orderId;
65 74
66 - /**  
67 - * 交易订单号,根据规则生成  
68 - * 调用支付渠道时,使用该字段作为对接的订单号。  
69 - * 1. 调用微信支付 https://api.mch.weixin.qq.com/v3/refund/domestic/refunds 时,使用该字段作为 out_trade_no  
70 - * 2. 调用支付宝 https://opendocs.alipay.com/apis 时,使用该字段作为 out_trade_no  
71 - * 这里对应 pay_extension 里面的 no  
72 - * 例如说,P202110132239124200055  
73 - */  
74 - private String tradeNo;  
75 -  
76 // ========== 商户相关字段 ========== 75 // ========== 商户相关字段 ==========
77 /** 76 /**
78 * 商户订单编号 77 * 商户订单编号
  78 + *
  79 + * 例如说,内部系统 A 的订单号,需要保证每个 PayAppDO 唯一
79 */ 80 */
80 private String merchantOrderId; 81 private String merchantOrderId;
81 -  
82 /** 82 /**
83 - * 商户退款订单号, 由商户系统产生, 由他们保证唯一,不能为空,通知商户时会传该字段。  
84 - * 例如说,内部系统 A 的退款订单号。需要保证每个 PayMerchantDO 唯一  
85 - * 个商户退款订单,对应一条退款请求记录。可多次提交。 渠道保持幂等  
86 - * 使用商户退款单,作为退款请求号  
87 - * https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter3_1_9.shtml 中的 out_refund_no  
88 - * https://opendocs.alipay.com/apis alipay.trade.refund 中的 out_request_no  
89 - * 退款请求号。  
90 - * 标识一次退款请求,需要保证在交易号下唯一,如需部分退款,则此参数必传。  
91 - * 注:针对同一次退款请求,如果调用接口失败或异常了,重试时需要保证退款请求号不能变更,  
92 - * 防止该笔交易重复退款。支付宝会保证同样的退款请求号多次请求只会退一次。  
93 - * 退款单请求号,根据规则生成  
94 - * 例如说,R202109181134287570000  
95 - */  
96 - // TODO @jason:merchantRefundNo =》merchantRefundOId  
97 - private String merchantRefundNo;  
98 - 83 + * 商户退款订单号
  84 + *
  85 + * 例如说,内部系统 A 的订单号,需要保证每个 PayAppDO 唯一
  86 + */
  87 + private String merchantRefundId;
99 /** 88 /**
100 * 异步通知地址 89 * 异步通知地址
101 */ 90 */
102 private String notifyUrl; 91 private String notifyUrl;
103 -  
104 /** 92 /**
105 * 通知商户退款结果的回调状态 93 * 通知商户退款结果的回调状态
106 - * TODO 0 未发送 1 已发送 94 + *
  95 + * 枚举 {@link PayNotifyStatusEnum}
107 */ 96 */
108 private Integer notifyStatus; 97 private Integer notifyStatus;
109 98
@@ -142,22 +131,27 @@ public class PayRefundDO extends BaseDO { @@ -142,22 +131,27 @@ public class PayRefundDO extends BaseDO {
142 131
143 // ========== 渠道相关字段 ========== 132 // ========== 渠道相关字段 ==========
144 /** 133 /**
145 - * 渠道订单号,pay_order 中的channel_order_no 对应 134 + * 渠道订单号
  135 + *
  136 + * 冗余 {@link PayOrderDO#getChannelOrderNo()}
146 */ 137 */
147 private String channelOrderNo; 138 private String channelOrderNo;
148 /** 139 /**
149 - * 微信中的 refund_id  
150 - * https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter3_1_9.shtml  
151 - * 支付宝没有  
152 - * 渠道退款单号,渠道返回 140 + * 渠道退款单号
  141 + *
  142 + * 1. 微信退款:对应 <a href="https://pay.weixin.qq.com/wiki/doc/api/micropay.php?chapter=9_4">申请退款</a> 的 refund_id 字段
  143 + * 2. 支付宝退款:没有字段
153 */ 144 */
154 private String channelRefundNo; 145 private String channelRefundNo;
  146 + /**
  147 + * 退款成功时间
  148 + */
  149 + private LocalDateTime successTime;
155 150
156 /** 151 /**
157 * 调用渠道的错误码 152 * 调用渠道的错误码
158 */ 153 */
159 private String channelErrorCode; 154 private String channelErrorCode;
160 -  
161 /** 155 /**
162 * 调用渠道报错时,错误信息 156 * 调用渠道报错时,错误信息
163 */ 157 */
@@ -165,22 +159,15 @@ public class PayRefundDO extends BaseDO { @@ -165,22 +159,15 @@ public class PayRefundDO extends BaseDO {
165 159
166 /** 160 /**
167 * 支付渠道的额外参数 161 * 支付渠道的额外参数
168 - * 参见 https://www.pingxx.com/api/Refunds%20退款概述.html 162 + *
  163 + * 参见 <a href="https://www.pingxx.com/api/支付渠道%20extra%20参数说明.html">参数说明</>
169 */ 164 */
170 private String channelExtras; 165 private String channelExtras;
171 -  
172 /** 166 /**
173 - * TODO  
174 - * 退款失效时间  
175 - */  
176 - private LocalDateTime expireTime;  
177 - /**  
178 - * 退款成功时间  
179 - */  
180 - private LocalDateTime successTime;  
181 - /**  
182 - * 退款通知时间 167 + * 支付渠道异步通知的内容
  168 + *
  169 + * 在退款成功后,会记录回调的数据
183 */ 170 */
184 - private LocalDateTime notifyTime; 171 + private String channelNotifyData;
185 172
186 } 173 }
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/dal/mysql/refund/PayRefundMapper.java
@@ -45,8 +45,10 @@ public interface PayRefundMapper extends BaseMapperX&lt;PayRefundDO&gt; { @@ -45,8 +45,10 @@ public interface PayRefundMapper extends BaseMapperX&lt;PayRefundDO&gt; {
45 return selectOne("req_no", reqNo); 45 return selectOne("req_no", reqNo);
46 } 46 }
47 47
  48 + // TODO 芋艿:要重构
48 default PayRefundDO selectByTradeNoAndMerchantRefundNo(String tradeNo, String merchantRefundNo){ 49 default PayRefundDO selectByTradeNoAndMerchantRefundNo(String tradeNo, String merchantRefundNo){
49 - return selectOne("trade_no", tradeNo, "merchant_refund_no", merchantRefundNo); 50 +// return selectOne("trade_no", tradeNo, "merchant_refund_no", merchantRefundNo);
  51 + return null;
50 } 52 }
51 53
52 } 54 }
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/service/channel/PayChannelServiceImpl.java
@@ -83,17 +83,20 @@ public class PayChannelServiceImpl implements PayChannelService { @@ -83,17 +83,20 @@ public class PayChannelServiceImpl implements PayChannelService {
83 */ 83 */
84 @Scheduled(initialDelay = 60, fixedRate = 60, timeUnit = TimeUnit.SECONDS) 84 @Scheduled(initialDelay = 60, fixedRate = 60, timeUnit = TimeUnit.SECONDS)
85 public void refreshLocalCache() { 85 public void refreshLocalCache() {
86 - // 情况一:如果缓存里没有数据,则直接刷新缓存  
87 - if (CollUtil.isEmpty(channelCache)) {  
88 - initLocalCache();  
89 - return;  
90 - }  
91 -  
92 - // 情况二,如果缓存里数据,则通过 updateTime 判断是否有数据变更,有变更则刷新缓存  
93 - LocalDateTime maxTime = CollectionUtils.getMaxValue(channelCache, PayChannelDO::getUpdateTime);  
94 - if (channelMapper.selectCountByUpdateTimeGt(maxTime) > 0) {  
95 - initLocalCache();  
96 - } 86 + // 注意:忽略自动多租户,因为要全局初始化缓存
  87 + TenantUtils.executeIgnore(() -> {
  88 + // 情况一:如果缓存里没有数据,则直接刷新缓存
  89 + if (CollUtil.isEmpty(channelCache)) {
  90 + initLocalCache();
  91 + return;
  92 + }
  93 +
  94 + // 情况二,如果缓存里数据,则通过 updateTime 判断是否有数据变更,有变更则刷新缓存
  95 + LocalDateTime maxTime = CollectionUtils.getMaxValue(channelCache, PayChannelDO::getUpdateTime);
  96 + if (channelMapper.selectCountByUpdateTimeGt(maxTime) > 0) {
  97 + initLocalCache();
  98 + }
  99 + });
97 } 100 }
98 101
99 @Override 102 @Override
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/service/order/PayOrderService.java
@@ -2,8 +2,7 @@ package cn.iocoder.yudao.module.pay.service.order; @@ -2,8 +2,7 @@ package cn.iocoder.yudao.module.pay.service.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.common.util.collection.CollectionUtils; 4 import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
5 -import cn.iocoder.yudao.framework.pay.core.client.dto.notify.PayNotifyReqDTO;  
6 -import cn.iocoder.yudao.framework.pay.core.client.dto.notify.PayOrderNotifyRespDTO; 5 +import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderRespDTO;
7 import cn.iocoder.yudao.module.pay.api.order.dto.PayOrderCreateReqDTO; 6 import cn.iocoder.yudao.module.pay.api.order.dto.PayOrderCreateReqDTO;
8 import cn.iocoder.yudao.module.pay.controller.admin.order.vo.PayOrderExportReqVO; 7 import cn.iocoder.yudao.module.pay.controller.admin.order.vo.PayOrderExportReqVO;
9 import cn.iocoder.yudao.module.pay.controller.admin.order.vo.PayOrderPageReqVO; 8 import cn.iocoder.yudao.module.pay.controller.admin.order.vo.PayOrderPageReqVO;
@@ -103,8 +102,7 @@ public interface PayOrderService { @@ -103,8 +102,7 @@ public interface PayOrderService {
103 * 102 *
104 * @param channelId 渠道编号 103 * @param channelId 渠道编号
105 * @param notify 通知 104 * @param notify 通知
106 - * @param rawNotify 通知数据  
107 */ 105 */
108 - void notifyPayOrder(Long channelId, PayOrderNotifyRespDTO notify, PayNotifyReqDTO rawNotify); 106 + void notifyPayOrder(Long channelId, PayOrderRespDTO notify);
109 107
110 } 108 }
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/service/order/PayOrderServiceImpl.java
@@ -9,10 +9,10 @@ import cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils; @@ -9,10 +9,10 @@ import cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils;
9 import cn.iocoder.yudao.framework.pay.config.PayProperties; 9 import cn.iocoder.yudao.framework.pay.config.PayProperties;
10 import cn.iocoder.yudao.framework.pay.core.client.PayClient; 10 import cn.iocoder.yudao.framework.pay.core.client.PayClient;
11 import cn.iocoder.yudao.framework.pay.core.client.PayClientFactory; 11 import cn.iocoder.yudao.framework.pay.core.client.PayClientFactory;
12 -import cn.iocoder.yudao.framework.pay.core.client.dto.notify.PayNotifyReqDTO;  
13 -import cn.iocoder.yudao.framework.pay.core.client.dto.notify.PayOrderNotifyRespDTO; 12 +import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderRespDTO;
14 import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderUnifiedReqDTO; 13 import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderUnifiedReqDTO;
15 import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderUnifiedRespDTO; 14 import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderUnifiedRespDTO;
  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;
18 import cn.iocoder.yudao.module.pay.controller.admin.order.vo.PayOrderExportReqVO; 18 import cn.iocoder.yudao.module.pay.controller.admin.order.vo.PayOrderExportReqVO;
@@ -148,17 +148,17 @@ public class PayOrderServiceImpl implements PayOrderService { @@ -148,17 +148,17 @@ public class PayOrderServiceImpl implements PayOrderService {
148 // 3. 调用三方接口 148 // 3. 调用三方接口
149 PayOrderUnifiedReqDTO unifiedOrderReqDTO = PayOrderConvert.INSTANCE.convert2(reqVO, userIp) 149 PayOrderUnifiedReqDTO unifiedOrderReqDTO = PayOrderConvert.INSTANCE.convert2(reqVO, userIp)
150 // 商户相关的字段 150 // 商户相关的字段
151 - .setMerchantOrderId(orderExtension.getNo()) // 注意,此处使用的是 PayOrderExtensionDO.no 属性! 151 + .setOutTradeNo(orderExtension.getNo()) // 注意,此处使用的是 PayOrderExtensionDO.no 属性!
152 .setSubject(order.getSubject()).setBody(order.getBody()) 152 .setSubject(order.getSubject()).setBody(order.getBody())
153 .setNotifyUrl(genChannelPayNotifyUrl(channel)) 153 .setNotifyUrl(genChannelPayNotifyUrl(channel))
154 .setReturnUrl(reqVO.getReturnUrl()) 154 .setReturnUrl(reqVO.getReturnUrl())
155 // 订单相关字段 155 // 订单相关字段
156 - .setAmount(order.getPrice()).setExpireTime(order.getExpireTime()); 156 + .setPrice(order.getPrice()).setExpireTime(order.getExpireTime());
157 PayOrderUnifiedRespDTO unifiedOrderRespDTO = client.unifiedOrder(unifiedOrderReqDTO); 157 PayOrderUnifiedRespDTO unifiedOrderRespDTO = client.unifiedOrder(unifiedOrderReqDTO);
158 158
159 // 4. 如果调用直接支付成功,则直接更新支付单状态为成功。例如说:付款码支付,免密支付时,就直接验证支付成功 159 // 4. 如果调用直接支付成功,则直接更新支付单状态为成功。例如说:付款码支付,免密支付时,就直接验证支付成功
160 - if (unifiedOrderRespDTO.getNotify() != null) {  
161 - notifyPayOrderSuccess(channel, unifiedOrderRespDTO.getNotify(), null); 160 + if (unifiedOrderRespDTO.getOrder() != null) {
  161 + notifyPayOrder(channel, unifiedOrderRespDTO.getOrder());
162 // 此处需要读取最新的状态 162 // 此处需要读取最新的状态
163 order = orderMapper.selectById(order.getId()); 163 order = orderMapper.selectById(order.getId());
164 } 164 }
@@ -226,16 +226,26 @@ public class PayOrderServiceImpl implements PayOrderService { @@ -226,16 +226,26 @@ public class PayOrderServiceImpl implements PayOrderService {
226 226
227 @Override 227 @Override
228 @Transactional(rollbackFor = Exception.class) 228 @Transactional(rollbackFor = Exception.class)
229 - public void notifyPayOrder(Long channelId, PayOrderNotifyRespDTO notify, PayNotifyReqDTO rawNotify) { 229 + public void notifyPayOrder(Long channelId, PayOrderRespDTO notify) {
230 // 校验支付渠道是否有效 230 // 校验支付渠道是否有效
231 PayChannelDO channel = channelService.validPayChannel(channelId); 231 PayChannelDO channel = channelService.validPayChannel(channelId);
232 // 更新支付订单为已支付 232 // 更新支付订单为已支付
233 - TenantUtils.execute(channel.getTenantId(), () -> notifyPayOrderSuccess(channel, notify, rawNotify)); 233 + TenantUtils.execute(channel.getTenantId(), () -> notifyPayOrder(channel, notify));
234 } 234 }
235 235
236 - private void notifyPayOrderSuccess(PayChannelDO channel, PayOrderNotifyRespDTO notify, PayNotifyReqDTO rawNotify) { 236 + private void notifyPayOrder(PayChannelDO channel, PayOrderRespDTO notify) {
  237 + // 情况一:支付成功的回调
  238 + if (PayOrderStatusRespEnum.isSuccess(notify.getStatus())) {
  239 + notifyPayOrderSuccess(channel, notify);
  240 + return;
  241 + }
  242 + // 情况二:非支付成功的回调,进行忽略
  243 + log.info("[notifyPayOrder][非支付成功的回调({}),直接忽略]", toJsonString(notify));
  244 + }
  245 +
  246 + private void notifyPayOrderSuccess(PayChannelDO channel, PayOrderRespDTO notify) {
237 // 1. 更新 PayOrderExtensionDO 支付成功 247 // 1. 更新 PayOrderExtensionDO 支付成功
238 - PayOrderExtensionDO orderExtension = updatePayOrderExtensionSuccess(notify.getOrderExtensionNo(), rawNotify); 248 + PayOrderExtensionDO orderExtension = updatePayOrderExtensionSuccess(notify);
239 // 2. 更新 PayOrderDO 支付成功 249 // 2. 更新 PayOrderDO 支付成功
240 Pair<Boolean, PayOrderDO> order = updatePayOrderSuccess(channel, orderExtension, notify); 250 Pair<Boolean, PayOrderDO> order = updatePayOrderSuccess(channel, orderExtension, notify);
241 if (order.getKey()) { // 如果之前已经成功回调,则直接返回,不用重复记录支付通知记录;例如说:支付平台重复回调 251 if (order.getKey()) { // 如果之前已经成功回调,则直接返回,不用重复记录支付通知记录;例如说:支付平台重复回调
@@ -250,13 +260,12 @@ public class PayOrderServiceImpl implements PayOrderService { @@ -250,13 +260,12 @@ public class PayOrderServiceImpl implements PayOrderService {
250 /** 260 /**
251 * 更新 PayOrderExtensionDO 支付成功 261 * 更新 PayOrderExtensionDO 支付成功
252 * 262 *
253 - * @param no 支付订单号(支付模块)  
254 - * @param rawNotify 通知数据 263 + * @param notify 通知
255 * @return PayOrderExtensionDO 对象 264 * @return PayOrderExtensionDO 对象
256 */ 265 */
257 - private PayOrderExtensionDO updatePayOrderExtensionSuccess(String no, PayNotifyReqDTO rawNotify) { 266 + private PayOrderExtensionDO updatePayOrderExtensionSuccess(PayOrderRespDTO notify) {
258 // 1. 查询 PayOrderExtensionDO 267 // 1. 查询 PayOrderExtensionDO
259 - PayOrderExtensionDO orderExtension = orderExtensionMapper.selectByNo(no); 268 + PayOrderExtensionDO orderExtension = orderExtensionMapper.selectByNo(notify.getOutTradeNo());
260 if (orderExtension == null) { 269 if (orderExtension == null) {
261 throw exception(ErrorCodeConstants.PAY_ORDER_EXTENSION_NOT_FOUND); 270 throw exception(ErrorCodeConstants.PAY_ORDER_EXTENSION_NOT_FOUND);
262 } 271 }
@@ -269,10 +278,8 @@ public class PayOrderServiceImpl implements PayOrderService { @@ -269,10 +278,8 @@ public class PayOrderServiceImpl implements PayOrderService {
269 } 278 }
270 279
271 // 2. 更新 PayOrderExtensionDO 280 // 2. 更新 PayOrderExtensionDO
272 - int updateCounts = orderExtensionMapper.updateByIdAndStatus(orderExtension.getId(),  
273 - PayOrderStatusEnum.WAITING.getStatus(), PayOrderExtensionDO.builder().id(orderExtension.getId())  
274 - .status(PayOrderStatusEnum.SUCCESS.getStatus())  
275 - .channelNotifyData(toJsonString(rawNotify)).build()); 281 + int updateCounts = orderExtensionMapper.updateByIdAndStatus(orderExtension.getId(), PayOrderStatusEnum.WAITING.getStatus(),
  282 + PayOrderExtensionDO.builder().status(PayOrderStatusEnum.SUCCESS.getStatus()).channelNotifyData(toJsonString(notify)).build());
276 if (updateCounts == 0) { // 校验状态,必须是待支付 283 if (updateCounts == 0) { // 校验状态,必须是待支付
277 throw exception(ErrorCodeConstants.PAY_ORDER_EXTENSION_STATUS_IS_NOT_WAITING); 284 throw exception(ErrorCodeConstants.PAY_ORDER_EXTENSION_STATUS_IS_NOT_WAITING);
278 } 285 }
@@ -290,7 +297,7 @@ public class PayOrderServiceImpl implements PayOrderService { @@ -290,7 +297,7 @@ public class PayOrderServiceImpl implements PayOrderService {
290 * value:PayOrderDO 对象 297 * value:PayOrderDO 对象
291 */ 298 */
292 private Pair<Boolean, PayOrderDO> updatePayOrderSuccess(PayChannelDO channel, PayOrderExtensionDO orderExtension, 299 private Pair<Boolean, PayOrderDO> updatePayOrderSuccess(PayChannelDO channel, PayOrderExtensionDO orderExtension,
293 - PayOrderNotifyRespDTO notify) { 300 + PayOrderRespDTO notify) {
294 // 1. 判断 PayOrderDO 是否处于待支付 301 // 1. 判断 PayOrderDO 是否处于待支付
295 PayOrderDO order = orderMapper.selectById(orderExtension.getOrderId()); 302 PayOrderDO order = orderMapper.selectById(orderExtension.getOrderId());
296 if (order == null) { 303 if (order == null) {
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/service/refund/PayRefundService.java
1 package cn.iocoder.yudao.module.pay.service.refund; 1 package cn.iocoder.yudao.module.pay.service.refund;
2 2
3 -import cn.iocoder.yudao.framework.pay.core.client.dto.notify.PayNotifyReqDTO;  
4 -import cn.iocoder.yudao.framework.pay.core.client.dto.notify.PayRefundNotifyRespDTO; 3 +import cn.iocoder.yudao.framework.common.pojo.PageResult;
  4 +import cn.iocoder.yudao.framework.pay.core.client.dto.refund.PayRefundRespDTO;
5 import cn.iocoder.yudao.module.pay.api.refund.dto.PayRefundCreateReqDTO; 5 import cn.iocoder.yudao.module.pay.api.refund.dto.PayRefundCreateReqDTO;
6 import cn.iocoder.yudao.module.pay.controller.admin.refund.vo.PayRefundExportReqVO; 6 import cn.iocoder.yudao.module.pay.controller.admin.refund.vo.PayRefundExportReqVO;
7 import cn.iocoder.yudao.module.pay.controller.admin.refund.vo.PayRefundPageReqVO; 7 import cn.iocoder.yudao.module.pay.controller.admin.refund.vo.PayRefundPageReqVO;
8 -import cn.iocoder.yudao.framework.common.pojo.PageResult;  
9 import cn.iocoder.yudao.module.pay.dal.dataobject.refund.PayRefundDO; 8 import cn.iocoder.yudao.module.pay.dal.dataobject.refund.PayRefundDO;
10 9
11 import java.util.List; 10 import java.util.List;
@@ -62,8 +61,7 @@ public interface PayRefundService { @@ -62,8 +61,7 @@ public interface PayRefundService {
62 * 61 *
63 * @param channelId 渠道编号 62 * @param channelId 渠道编号
64 * @param notify 通知 63 * @param notify 通知
65 - * @param rawNotify 通知数据  
66 */ 64 */
67 - void notifyPayRefund(Long channelId, PayRefundNotifyRespDTO notify, PayNotifyReqDTO rawNotify); 65 + void notifyPayRefund(Long channelId, PayRefundRespDTO notify);
68 66
69 } 67 }
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/service/refund/PayRefundServiceImpl.java
@@ -7,10 +7,9 @@ import cn.iocoder.yudao.framework.common.pojo.PageResult; @@ -7,10 +7,9 @@ import cn.iocoder.yudao.framework.common.pojo.PageResult;
7 import cn.iocoder.yudao.framework.pay.config.PayProperties; 7 import cn.iocoder.yudao.framework.pay.config.PayProperties;
8 import cn.iocoder.yudao.framework.pay.core.client.PayClient; 8 import cn.iocoder.yudao.framework.pay.core.client.PayClient;
9 import cn.iocoder.yudao.framework.pay.core.client.PayClientFactory; 9 import cn.iocoder.yudao.framework.pay.core.client.PayClientFactory;
10 -import cn.iocoder.yudao.framework.pay.core.client.dto.notify.PayNotifyReqDTO;  
11 -import cn.iocoder.yudao.framework.pay.core.client.dto.notify.PayRefundNotifyRespDTO; 10 +import cn.iocoder.yudao.framework.pay.core.client.dto.refund.PayRefundRespDTO;
12 import cn.iocoder.yudao.framework.pay.core.client.dto.refund.PayRefundUnifiedReqDTO; 11 import cn.iocoder.yudao.framework.pay.core.client.dto.refund.PayRefundUnifiedReqDTO;
13 -import cn.iocoder.yudao.framework.pay.core.enums.refund.PayNotifyRefundStatusEnum; 12 +import cn.iocoder.yudao.framework.pay.core.enums.refund.PayRefundStatusRespEnum;
14 import cn.iocoder.yudao.module.pay.api.refund.dto.PayRefundCreateReqDTO; 13 import cn.iocoder.yudao.module.pay.api.refund.dto.PayRefundCreateReqDTO;
15 import cn.iocoder.yudao.module.pay.controller.admin.refund.vo.PayRefundExportReqVO; 14 import cn.iocoder.yudao.module.pay.controller.admin.refund.vo.PayRefundExportReqVO;
16 import cn.iocoder.yudao.module.pay.controller.admin.refund.vo.PayRefundPageReqVO; 15 import cn.iocoder.yudao.module.pay.controller.admin.refund.vo.PayRefundPageReqVO;
@@ -39,14 +38,13 @@ import org.springframework.transaction.annotation.Transactional; @@ -39,14 +38,13 @@ import org.springframework.transaction.annotation.Transactional;
39 import org.springframework.validation.annotation.Validated; 38 import org.springframework.validation.annotation.Validated;
40 39
41 import javax.annotation.Resource; 40 import javax.annotation.Resource;
42 -import java.time.LocalDateTime;  
43 import java.util.List; 41 import java.util.List;
44 import java.util.Objects; 42 import java.util.Objects;
45 43
46 /** 44 /**
47 * 退款订单 Service 实现类 45 * 退款订单 Service 实现类
48 * 46 *
49 - * @author aquan 47 + * @author jason
50 */ 48 */
51 @Service 49 @Service
52 @Slf4j 50 @Slf4j
@@ -116,7 +114,7 @@ public class PayRefundServiceImpl implements PayRefundService { @@ -116,7 +114,7 @@ public class PayRefundServiceImpl implements PayRefundService {
116 } 114 }
117 115
118 // TODO 芋艿:待实现 116 // TODO 芋艿:待实现
119 - String merchantRefundId = RandomUtil.randomNumbers(16); 117 + String merchantRefundId = "rrr" + RandomUtil.randomNumbers(16);
120 118
121 // 校验退款的条件 119 // 校验退款的条件
122 validatePayRefund(reqDTO, order); 120 validatePayRefund(reqDTO, order);
@@ -128,12 +126,11 @@ public class PayRefundServiceImpl implements PayRefundService { @@ -128,12 +126,11 @@ public class PayRefundServiceImpl implements PayRefundService {
128 PayOrderExtensionDO orderExtensionDO = orderExtensionService.getOrderExtension(order.getSuccessExtensionId()); 126 PayOrderExtensionDO orderExtensionDO = orderExtensionService.getOrderExtension(order.getSuccessExtensionId());
129 PayRefundDO payRefundDO = refundMapper.selectByTradeNoAndMerchantRefundNo(orderExtensionDO.getNo(), 127 PayRefundDO payRefundDO = refundMapper.selectByTradeNoAndMerchantRefundNo(orderExtensionDO.getNo(),
130 merchantRefundId); // TODO 芋艿:需要优化 128 merchantRefundId); // TODO 芋艿:需要优化
131 - if(Objects.nonNull(payRefundDO)){ 129 + if (Objects.nonNull(payRefundDO)) {
132 // 退款订单已经提交过。 130 // 退款订单已经提交过。
133 //TODO 校验相同退款单的金额 131 //TODO 校验相同退款单的金额
134 // TODO @jason:咱要不封装一个 ObjectUtils.equalsAny 132 // TODO @jason:咱要不封装一个 ObjectUtils.equalsAny
135 - if (Objects.equals(PayRefundStatusEnum.SUCCESS.getStatus(), payRefundDO.getStatus())  
136 - || Objects.equals(PayRefundStatusEnum.CLOSE.getStatus(), payRefundDO.getStatus())) { 133 + if (Objects.equals(PayRefundStatusEnum.SUCCESS.getStatus(), payRefundDO.getStatus())) {
137 //已成功退款 134 //已成功退款
138 throw ServiceExceptionUtil.exception(ErrorCodeConstants.PAY_REFUND_SUCCEED); 135 throw ServiceExceptionUtil.exception(ErrorCodeConstants.PAY_REFUND_SUCCEED);
139 } 136 }
@@ -147,14 +144,14 @@ public class PayRefundServiceImpl implements PayRefundService { @@ -147,14 +144,14 @@ public class PayRefundServiceImpl implements PayRefundService {
147 .channelCode(order.getChannelCode()) 144 .channelCode(order.getChannelCode())
148 .channelId(order.getChannelId()) 145 .channelId(order.getChannelId())
149 .orderId(order.getId()) 146 .orderId(order.getId())
150 - .merchantRefundNo(merchantRefundId) // TODO 芋艿:需要优化 147 + .merchantRefundId(merchantRefundId)
151 .notifyUrl(app.getRefundNotifyUrl()) 148 .notifyUrl(app.getRefundNotifyUrl())
152 .payPrice(order.getPrice()) 149 .payPrice(order.getPrice())
153 .refundPrice(reqDTO.getPrice()) 150 .refundPrice(reqDTO.getPrice())
154 .userIp(reqDTO.getUserIp()) 151 .userIp(reqDTO.getUserIp())
155 .merchantOrderId(order.getMerchantOrderId()) 152 .merchantOrderId(order.getMerchantOrderId())
156 - .tradeNo(orderExtensionDO.getNo())  
157 - .status(PayRefundStatusEnum.CREATE.getStatus()) 153 + .no(orderExtensionDO.getNo())
  154 + .status(PayRefundStatusEnum.WAITING.getStatus())
158 .reason(reqDTO.getReason()) 155 .reason(reqDTO.getReason())
159 .notifyStatus(PayOrderNotifyStatusEnum.NO.getStatus()) 156 .notifyStatus(PayOrderNotifyStatusEnum.NO.getStatus())
160 .type(refundType.getStatus()) 157 .type(refundType.getStatus())
@@ -163,11 +160,9 @@ public class PayRefundServiceImpl implements PayRefundService { @@ -163,11 +160,9 @@ public class PayRefundServiceImpl implements PayRefundService {
163 } 160 }
164 // TODO @jason:搞到 convert 里。一些额外的自动,手动 set 下; 161 // TODO @jason:搞到 convert 里。一些额外的自动,手动 set 下;
165 PayRefundUnifiedReqDTO unifiedReqDTO = new PayRefundUnifiedReqDTO(); 162 PayRefundUnifiedReqDTO unifiedReqDTO = new PayRefundUnifiedReqDTO();
166 - unifiedReqDTO.setUserIp(reqDTO.getUserIp())  
167 - .setAmount(reqDTO.getPrice())  
168 - .setChannelOrderNo(order.getChannelOrderNo())  
169 - .setPayTradeNo(orderExtensionDO.getNo())  
170 - .setMerchantRefundId(merchantRefundId) // TODO 芋艿:需要优化 163 + unifiedReqDTO.setPrice(reqDTO.getPrice())
  164 + .setOutTradeNo(orderExtensionDO.getNo())
  165 + .setOutRefundNo(merchantRefundId) // TODO 芋艿:需要优化
171 .setNotifyUrl(genChannelPayNotifyUrl(channel)) // TODO 芋艿:优化下 notifyUrl 166 .setNotifyUrl(genChannelPayNotifyUrl(channel)) // TODO 芋艿:优化下 notifyUrl
172 .setReason(reqDTO.getReason()); 167 .setReason(reqDTO.getReason());
173 // 向渠道发起退款申请 168 // 向渠道发起退款申请
@@ -191,24 +186,25 @@ public class PayRefundServiceImpl implements PayRefundService { @@ -191,24 +186,25 @@ public class PayRefundServiceImpl implements PayRefundService {
191 186
192 @Override 187 @Override
193 @Transactional(rollbackFor = Exception.class) 188 @Transactional(rollbackFor = Exception.class)
194 - public void notifyPayRefund(Long channelId, PayRefundNotifyRespDTO notify, PayNotifyReqDTO rawNotify) { 189 + public void notifyPayRefund(Long channelId, PayRefundRespDTO notify) {
195 // 校验支付渠道是否有效 190 // 校验支付渠道是否有效
196 // TODO 芋艿:需要重构下这块的逻辑 191 // TODO 芋艿:需要重构下这块的逻辑
197 PayChannelDO channel = channelService.validPayChannel(channelId); 192 PayChannelDO channel = channelService.validPayChannel(channelId);
198 - if (Objects.equals(PayNotifyRefundStatusEnum.SUCCESS, notify.getStatus())){ 193 + if (PayRefundStatusRespEnum.isSuccess(notify.getStatus())) {
199 payRefundSuccess(notify); 194 payRefundSuccess(notify);
200 } else { 195 } else {
201 - //TODO 支付异常, 支付宝似乎没有支付异常的通知。  
202 // TODO @jason:那这里可以考虑打个 error logger @芋艿 微信是否存在支付异常通知 196 // TODO @jason:那这里可以考虑打个 error logger @芋艿 微信是否存在支付异常通知
203 } 197 }
204 } 198 }
205 199
206 - private void payRefundSuccess(PayRefundNotifyRespDTO refundNotify) { 200 + private void payRefundSuccess(PayRefundRespDTO refundNotify) {
207 // 校验退款单存在 201 // 校验退款单存在
208 - PayRefundDO refundDO = refundMapper.selectByTradeNoAndMerchantRefundNo(refundNotify.getTradeNo(),  
209 - refundNotify.getReqNo()); 202 + PayRefundDO refundDO = null; // TODO 芋艿:临时注释
  203 +// PayRefundDO refundDO = refundMapper.selectByTradeNoAndMerchantRefundNo(refundNotify.getTradeNo(),
  204 +// refundNotify.getReqNo());
210 if (refundDO == null) { 205 if (refundDO == null) {
211 - log.error("[payRefundSuccess][不存在 seqNo 为{} 的支付退款单]", refundNotify.getReqNo()); 206 + // TODO 芋艿:临时注释
  207 +// log.error("[payRefundSuccess][不存在 seqNo 为{} 的支付退款单]", refundNotify.getReqNo());
212 throw ServiceExceptionUtil.exception(ErrorCodeConstants.PAY_REFUND_NOT_FOUND); 208 throw ServiceExceptionUtil.exception(ErrorCodeConstants.PAY_REFUND_NOT_FOUND);
213 } 209 }
214 210
@@ -233,10 +229,10 @@ public class PayRefundServiceImpl implements PayRefundService { @@ -233,10 +229,10 @@ public class PayRefundServiceImpl implements PayRefundService {
233 // 更新退款订单 229 // 更新退款订单
234 PayRefundDO updateRefundDO = new PayRefundDO(); 230 PayRefundDO updateRefundDO = new PayRefundDO();
235 updateRefundDO.setId(refundDO.getId()) 231 updateRefundDO.setId(refundDO.getId())
236 - .setSuccessTime(refundNotify.getRefundSuccessTime())  
237 - .setChannelRefundNo(refundNotify.getChannelOrderNo())  
238 - .setTradeNo(refundNotify.getTradeNo())  
239 - .setNotifyTime(LocalDateTime.now()) 232 + .setSuccessTime(refundNotify.getSuccessTime())
  233 + // TODO 芋艿:如下两行,临时注释
  234 +// .setChannelRefundNo(refundNotify.getChannelOrderNo())
  235 +// .setNo(refundNotify.getTradeNo())
240 .setStatus(PayRefundStatusEnum.SUCCESS.getStatus()); 236 .setStatus(PayRefundStatusEnum.SUCCESS.getStatus());
241 refundMapper.updateById(updateRefundDO); 237 refundMapper.updateById(updateRefundDO);
242 238
yudao-module-pay/yudao-module-pay-biz/src/test/java/cn/iocoder/yudao/module/pay/service/app/PayAppServiceTest.java
@@ -3,9 +3,7 @@ package cn.iocoder.yudao.module.pay.service.app; @@ -3,9 +3,7 @@ package cn.iocoder.yudao.module.pay.service.app;
3 import cn.hutool.core.util.RandomUtil; 3 import cn.hutool.core.util.RandomUtil;
4 import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; 4 import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
5 import cn.iocoder.yudao.framework.common.pojo.PageResult; 5 import cn.iocoder.yudao.framework.common.pojo.PageResult;
6 -import cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils;  
7 import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest; 6 import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest;
8 -import cn.iocoder.yudao.framework.test.core.util.RandomUtils;  
9 import cn.iocoder.yudao.module.pay.controller.admin.app.vo.PayAppCreateReqVO; 7 import cn.iocoder.yudao.module.pay.controller.admin.app.vo.PayAppCreateReqVO;
10 import cn.iocoder.yudao.module.pay.controller.admin.app.vo.PayAppPageReqVO; 8 import cn.iocoder.yudao.module.pay.controller.admin.app.vo.PayAppPageReqVO;
11 import cn.iocoder.yudao.module.pay.controller.admin.app.vo.PayAppUpdateReqVO; 9 import cn.iocoder.yudao.module.pay.controller.admin.app.vo.PayAppUpdateReqVO;
@@ -18,8 +16,6 @@ import org.springframework.boot.test.mock.mockito.MockBean; @@ -18,8 +16,6 @@ import org.springframework.boot.test.mock.mockito.MockBean;
18 import org.springframework.context.annotation.Import; 16 import org.springframework.context.annotation.Import;
19 17
20 import javax.annotation.Resource; 18 import javax.annotation.Resource;
21 -import java.time.LocalDateTime;  
22 -import java.util.Collections;  
23 import java.util.Map; 19 import java.util.Map;
24 20
25 import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.buildBetweenTime; 21 import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.buildBetweenTime;
yudao-module-pay/yudao-module-pay-biz/src/test/java/cn/iocoder/yudao/module/pay/service/refund/PayRefundServiceTest.java
@@ -62,7 +62,7 @@ public class PayRefundServiceTest extends BaseDbUnitTest { @@ -62,7 +62,7 @@ public class PayRefundServiceTest extends BaseDbUnitTest {
62 o.setChannelId(1L); 62 o.setChannelId(1L);
63 o.setChannelCode(PayChannelEnum.WX_PUB.getCode()); 63 o.setChannelCode(PayChannelEnum.WX_PUB.getCode());
64 o.setOrderId(1L); 64 o.setOrderId(1L);
65 - o.setTradeNo("OT0000001"); 65 + o.setNo("OT0000001");
66 o.setMerchantOrderId("MOT0000001"); 66 o.setMerchantOrderId("MOT0000001");
67 o.setMerchantRefundNo("MRF0000001"); 67 o.setMerchantRefundNo("MRF0000001");
68 o.setNotifyUrl("https://www.cancanzi.com"); 68 o.setNotifyUrl("https://www.cancanzi.com");
@@ -127,7 +127,7 @@ public class PayRefundServiceTest extends BaseDbUnitTest { @@ -127,7 +127,7 @@ public class PayRefundServiceTest extends BaseDbUnitTest {
127 o.setChannelId(1L); 127 o.setChannelId(1L);
128 o.setChannelCode(PayChannelEnum.WX_PUB.getCode()); 128 o.setChannelCode(PayChannelEnum.WX_PUB.getCode());
129 o.setOrderId(1L); 129 o.setOrderId(1L);
130 - o.setTradeNo("OT0000001"); 130 + o.setNo("OT0000001");
131 o.setMerchantOrderId("MOT0000001"); 131 o.setMerchantOrderId("MOT0000001");
132 o.setMerchantRefundNo("MRF0000001"); 132 o.setMerchantRefundNo("MRF0000001");
133 o.setNotifyUrl("https://www.cancanzi.com"); 133 o.setNotifyUrl("https://www.cancanzi.com");
yudao-ui-admin/src/api/pay/channel.js
@@ -28,14 +28,14 @@ export function deleteChannel(id) { @@ -28,14 +28,14 @@ export function deleteChannel(id) {
28 } 28 }
29 29
30 // 获得支付渠道 30 // 获得支付渠道
31 -export function getChannel(appId,code) { 31 +export function getChannel(appId, code) {
32 return request({ 32 return request({
33 - url: '/pay/channel/get-channel', 33 + url: '/pay/channel/get',
  34 + method: 'get',
34 params:{ 35 params:{
35 - appId:appId,  
36 - code:code 36 + appId,
  37 + code
37 }, 38 },
38 - method: 'get'  
39 }) 39 })
40 } 40 }
41 41