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 1 package cn.iocoder.yudao.framework.common.util.validation;
2 2  
3 3 import cn.hutool.core.collection.CollUtil;
  4 +import cn.hutool.core.lang.Assert;
4 5 import org.springframework.util.StringUtils;
5 6  
6 7 import javax.validation.ConstraintViolation;
7 8 import javax.validation.ConstraintViolationException;
  9 +import javax.validation.Validation;
8 10 import javax.validation.Validator;
9 11 import java.util.Set;
10 12 import java.util.regex.Pattern;
... ... @@ -37,6 +39,12 @@ public class ValidationUtils {
37 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 48 public static void validate(Validator validator, Object object, Class<?>... groups) {
41 49 Set<ConstraintViolation<Object>> constraintViolations = validator.validate(object, groups);
42 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 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 4 import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderUnifiedReqDTO;
7 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 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 12 * 支付客户端,用于对接各支付渠道的 SDK,实现发起支付、退款等功能
... ... @@ -32,20 +32,22 @@ public interface PayClient {
32 32  
33 33 /**
34 34 * 调用支付渠道,进行退款
  35 + *
35 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 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 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 4 import lombok.AllArgsConstructor;
4 5 import lombok.Builder;
5 6 import lombok.Data;
... ... @@ -8,7 +9,7 @@ import lombok.NoArgsConstructor;
8 9 import java.time.LocalDateTime;
9 10  
10 11 /**
11   - * 支付通知 Response DTO
  12 + * 渠道支付订单 Response DTO
12 13 *
13 14 * @author 芋道源码
14 15 */
... ... @@ -16,12 +17,22 @@ import java.time.LocalDateTime;
16 17 @Builder
17 18 @NoArgsConstructor
18 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 41 * 支付渠道用户编号
31 42 */
32 43 private String channelUserId;
  44 +
33 45 /**
34 46 * 支付成功时间
35 47 */
36 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 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 65 */
64 66 @NotNull(message = "支付金额不能为空")
65 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 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 3 import cn.iocoder.yudao.framework.pay.core.enums.order.PayOrderDisplayModeEnum;
5 4 import lombok.Data;
6 5  
... ... @@ -24,11 +23,12 @@ public class PayOrderUnifiedRespDTO {
24 23 private String displayContent;
25 24  
26 25 /**
27   - * 同步的通知信息
  26 + * 渠道支付订单
28 27 *
  28 + * 只有在订单直接支付成功时,才会进行返回。
29 29 * 目前只有 bar 条码支付才会出现,它是支付发起时,直接返回是否支付成功的,而其它支付还是异步通知
30 30 */
31   - private PayOrderNotifyRespDTO notify;
  31 + private PayOrderRespDTO order;
32 32  
33 33 public PayOrderUnifiedRespDTO(String displayMode, String displayContent) {
34 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 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 50 */
64 51 @NotNull(message = "退款金额不能为空")
65 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 59 @URL(message = "支付结果的 notify 回调地址必须是 URL 格式")
72 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 5 import cn.iocoder.yudao.framework.pay.core.client.PayClientConfig;
6 6 import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderUnifiedReqDTO;
7 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 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 10 import cn.iocoder.yudao.framework.pay.core.client.exception.PayException;
11 11 import lombok.extern.slf4j.Slf4j;
12 12  
... ... @@ -92,10 +92,10 @@ public abstract class AbstractPayClient&lt;Config extends PayClientConfig&gt; implemen
92 92 throws Throwable;
93 93  
94 94 @Override
95   - public PayRefundUnifiedRespDTO unifiedRefund(PayRefundUnifiedReqDTO reqDTO) {
  95 + public PayRefundRespDTO unifiedRefund(PayRefundUnifiedReqDTO reqDTO) {
96 96 Validation.buildDefaultValidatorFactory().getValidator().validate(reqDTO);
97 97 // 执行统一退款
98   - PayRefundUnifiedRespDTO resp;
  98 + PayRefundRespDTO resp;
99 99 try {
100 100 resp = doUnifiedRefund(reqDTO);
101 101 } catch (ServiceException ex) {
... ... @@ -109,7 +109,7 @@ public abstract class AbstractPayClient&lt;Config extends PayClientConfig&gt; implemen
109 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 2  
3 3 import cn.hutool.core.bean.BeanUtil;
4 4 import cn.hutool.core.date.LocalDateTimeUtil;
  5 +import cn.hutool.core.lang.Assert;
  6 +import cn.hutool.core.util.StrUtil;
5 7 import cn.hutool.http.HttpUtil;
6 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 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 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 18 import com.alipay.api.domain.AlipayTradeRefundModel;
16 19 import com.alipay.api.internal.util.AlipaySignature;
17 20 import com.alipay.api.request.AlipayTradeRefundRequest;
... ... @@ -22,6 +25,8 @@ import lombok.extern.slf4j.Slf4j;
22 25 import java.nio.charset.StandardCharsets;
23 26 import java.time.LocalDateTime;
24 27 import java.util.Map;
  28 +import java.util.Objects;
  29 +import java.util.function.Supplier;
25 30  
26 31 import static cn.hutool.core.date.DatePattern.NORM_DATETIME_FORMATTER;
27 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 57  
53 58 /**
54 59 * 支付宝统一的退款接口 alipay.trade.refund
  60 + *
55 61 * @param reqDTO 退款请求 request DTO
56 62 * @return 退款请求 Response
57 63 */
58 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 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 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 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 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 93 return null;
91 94 }
92 95 }
93 96  
94 97 @Override
95 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 106 // 1. 校验回调数据
98   - String body = rawNotify.getBody();
99   - Map<String, String> params = rawNotify.getParams();
100 107 Map<String, String> bodyObj = HttpUtil.decodeParamMap(body, StandardCharsets.UTF_8);
101 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 122 .channelOrderNo(bodyObj.get("trade_no"))
116 123 .channelUserId(bodyObj.get("seller_id"))
117   - .successTime(parseTime(params.get("notify_time")))
  124 + .successTime(parseTime(params.get("gmt_payment")))
  125 + .rawData(body)
118 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 31 // 1.1 构建 AlipayTradeAppPayModel 请求
32 32 AlipayTradeAppPayModel model = new AlipayTradeAppPayModel();
33 33 // ① 通用的参数
34   - model.setOutTradeNo(reqDTO.getMerchantOrderId());
  34 + model.setOutTradeNo(reqDTO.getOutTradeNo());
35 35 model.setSubject(reqDTO.getSubject());
36 36 model.setBody(reqDTO.getBody());
37   - model.setTotalAmount(formatAmount(reqDTO.getAmount()));
  37 + model.setTotalAmount(formatAmount(reqDTO.getPrice()));
38 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 39 // 1.1 构建 AlipayTradePayModel 请求
40 40 AlipayTradePayModel model = new AlipayTradePayModel();
41 41 // ① 通用的参数
42   - model.setOutTradeNo(reqDTO.getMerchantOrderId());
  42 + model.setOutTradeNo(reqDTO.getOutTradeNo());
43 43 model.setSubject(reqDTO.getSubject());
44 44 model.setBody(reqDTO.getBody());
45   - model.setTotalAmount(formatAmount(reqDTO.getAmount()));
  45 + model.setTotalAmount(formatAmount(reqDTO.getPrice()));
46 46 model.setScene("bar_code"); // 当面付条码支付场景
47 47 // ② 个性化的参数
48 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 33 // 1.1 构建 AlipayTradePagePayModel 请求
34 34 AlipayTradePagePayModel model = new AlipayTradePagePayModel();
35 35 // ① 通用的参数
36   - model.setOutTradeNo(reqDTO.getMerchantOrderId());
  36 + model.setOutTradeNo(reqDTO.getOutTradeNo());
37 37 model.setSubject(reqDTO.getSubject());
38 38 model.setBody(reqDTO.getBody());
39   - model.setTotalAmount(formatAmount(reqDTO.getAmount()));
  39 + model.setTotalAmount(formatAmount(reqDTO.getPrice()));
40 40 model.setTimeExpire(formatTime(reqDTO.getExpireTime()));
41 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 29 // 1.1 构建 AlipayTradePrecreateModel 请求
30 30 AlipayTradePrecreateModel model = new AlipayTradePrecreateModel();
31 31 // ① 通用的参数
32   - model.setOutTradeNo(reqDTO.getMerchantOrderId());
  32 + model.setOutTradeNo(reqDTO.getOutTradeNo());
33 33 model.setSubject(reqDTO.getSubject());
34 34 model.setBody(reqDTO.getBody());
35   - model.setTotalAmount(formatAmount(reqDTO.getAmount()));
  35 + model.setTotalAmount(formatAmount(reqDTO.getPrice()));
36 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 30 // 1.1 构建 AlipayTradeWapPayModel 请求
31 31 AlipayTradeWapPayModel model = new AlipayTradeWapPayModel();
32 32 // ① 通用的参数
33   - model.setOutTradeNo(reqDTO.getMerchantOrderId());
  33 + model.setOutTradeNo(reqDTO.getOutTradeNo());
34 34 model.setSubject(reqDTO.getSubject());
35 35 model.setBody(reqDTO.getBody());
36   - model.setTotalAmount(formatAmount(reqDTO.getAmount()));
  36 + model.setTotalAmount(formatAmount(reqDTO.getPrice()));
37 37 model.setProductCode("QUICK_WAP_PAY"); // 销售产品码. 目前 Wap 支付场景下仅支持 QUICK_WAP_PAY
38 38 // ② 个性化的参数【无】
39 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 6 import cn.hutool.core.lang.Assert;
7 7 import cn.hutool.core.util.StrUtil;
8 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 10 import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderUnifiedReqDTO;
12 11 import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderUnifiedRespDTO;
13 12 import cn.iocoder.yudao.framework.pay.core.client.impl.AbstractPayClient;
... ... @@ -22,11 +21,13 @@ import lombok.extern.slf4j.Slf4j;
22 21  
23 22 import java.time.LocalDateTime;
24 23 import java.time.ZoneId;
  24 +import java.util.Map;
25 25 import java.util.Objects;
26 26  
27 27 import static cn.hutool.core.date.DatePattern.PURE_DATETIME_PATTERN;
28 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 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 104  
104 105  
105 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 109 try {
109 110 // 微信支付 v2 回调结果处理
110 111 switch (config.getApiVersion()) {
111 112 case WxPayClientConfig.API_VERSION_V2:
112   - return parseOrderNotifyV2(rawNotify);
  113 + return parseOrderNotifyV2(body);
113 114 case WxPayClientConfig.API_VERSION_V3:
114   - return parseOrderNotifyV3(rawNotify);
  115 + return parseOrderNotifyV3(body);
115 116 default:
116 117 throw new IllegalArgumentException(String.format("未知的 API 版本(%s)", config.getApiVersion()));
117 118 }
118 119 } catch (WxPayException e) {
119   - log.error("[parseNotify][rawNotify({}) 解析失败]", toJsonString(rawNotify), e);
  120 + log.error("[parseNotify][params({}) body({}) 解析失败]", params, body, e);
120 121 // throw buildPayException(e);
121 122 throw new RuntimeException(e);
122 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 129 Assert.isTrue(Objects.equals(notifyResult.getResultCode(), "SUCCESS"), "支付结果非 SUCCESS");
129 130 // 转换结果
130   - return PayOrderNotifyRespDTO
  131 + return PayOrderRespDTO
131 132 .builder()
132   - .orderExtensionNo(notifyResult.getOutTradeNo())
  133 + .outTradeNo(notifyResult.getOutTradeNo())
133 134 .channelOrderNo(notifyResult.getTransactionId())
134 135 .channelUserId(notifyResult.getOpenid())
135 136 .successTime(parseDateV2(notifyResult.getTimeEnd()))
136 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 142 WxPayOrderNotifyV3Result.DecryptNotifyResult result = notifyResult.getResult();
142 143 // 转换结果
143 144 Assert.isTrue(Objects.equals(notifyResult.getResult().getTradeState(), "SUCCESS"),
144 145 "支付结果非 SUCCESS");
145   - return PayOrderNotifyRespDTO.builder()
146   - .orderExtensionNo(result.getOutTradeNo())
  146 + return PayOrderRespDTO.builder()
  147 + .outTradeNo(result.getOutTradeNo())
147 148 .channelOrderNo(result.getTradeState())
148 149 .channelUserId(result.getPayer() != null ? result.getPayer().getOpenid() : null)
149 150 .successTime(parseDateV3(result.getSuccessTime()))
... ... @@ -175,7 +176,7 @@ public abstract class AbstractWxPayClient extends AbstractPayClient&lt;WxPayClientC
175 176 if (Objects.equals(e.getReturnCode(), "FAIL")) {
176 177 throw exception(PayFrameworkErrorCodeConstants.ORDER_UNIFIED_ERROR, e.getReturnMsg());
177 178 }
178   - // 情况三:系统异常,这里暂时不打,交给上层的 AbstractPayClient 统一打
  179 + // 情况三:系统异常,这里暂时不打,交给上层的 AbstractPayClient 统一打日志
179 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 2  
3 3 import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderUnifiedReqDTO;
4 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 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 7 import cn.iocoder.yudao.framework.pay.core.enums.channel.PayChannelEnum;
8 8 import com.github.binarywang.wxpay.constant.WxPayConstants;
9 9 import com.github.binarywang.wxpay.exception.WxPayException;
... ... @@ -21,7 +21,7 @@ public class WxAppPayClient extends AbstractWxPayClient {
21 21 }
22 22  
23 23 @Override
24   - protected PayRefundUnifiedRespDTO doUnifiedRefund(PayRefundUnifiedReqDTO reqDTO) throws Throwable {
  24 + protected PayRefundRespDTO doUnifiedRefund(PayRefundUnifiedReqDTO reqDTO) throws Throwable {
25 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 5 import cn.hutool.core.util.StrUtil;
6 6 import cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils;
7 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 9 import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderUnifiedReqDTO;
10 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 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 13 import cn.iocoder.yudao.framework.pay.core.enums.channel.PayChannelEnum;
14 14 import cn.iocoder.yudao.framework.pay.core.enums.order.PayOrderDisplayModeEnum;
  15 +import cn.iocoder.yudao.framework.pay.core.enums.order.PayOrderStatusRespEnum;
15 16 import com.github.binarywang.wxpay.bean.request.WxPayMicropayRequest;
16 17 import com.github.binarywang.wxpay.bean.result.WxPayMicropayResult;
17 18 import com.github.binarywang.wxpay.constant.WxPayConstants;
... ... @@ -57,10 +58,10 @@ public class WxBarPayClient extends AbstractWxPayClient {
57 58 }
58 59 // 构建 WxPayMicropayRequest 对象
59 60 WxPayMicropayRequest request = WxPayMicropayRequest.newBuilder()
60   - .outTradeNo(reqDTO.getMerchantOrderId())
  61 + .outTradeNo(reqDTO.getOutTradeNo())
61 62 .body(reqDTO.getSubject())
62 63 .detail(reqDTO.getBody())
63   - .totalFee(reqDTO.getAmount()) // 单位分
  64 + .totalFee(reqDTO.getPrice()) // 单位分
64 65 .timeExpire(formatDateV2(expireTime))
65 66 .spbillCreateIp(reqDTO.getUserIp())
66 67 .authCode(getAuthCode(reqDTO))
... ... @@ -70,15 +71,17 @@ public class WxBarPayClient extends AbstractWxPayClient {
70 71 try {
71 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 77 .channelOrderNo(response.getTransactionId())
76 78 .channelUserId(response.getOpenid())
77 79 .successTime(parseDateV2(response.getTimeEnd()))
  80 + .rawData(response)
78 81 .build();
79 82 return new PayOrderUnifiedRespDTO(PayOrderDisplayModeEnum.BAR_CODE.getMode(),
80 83 JsonUtils.toJsonString(response))
81   - .setNotify(notify);
  84 + .setOrder(order);
82 85 } catch (WxPayException ex) {
83 86 // 如果不满足这 3 种任一的,则直接抛出 WxPayException 异常,不仅需处理
84 87 // 1. SYSTEMERROR:接口返回错误:请立即调用被扫订单结果查询API,查询当前订单状态,并根据订单的状态决定下一步的操作。
... ... @@ -102,7 +105,7 @@ public class WxBarPayClient extends AbstractWxPayClient {
102 105 }
103 106  
104 107 @Override
105   - protected PayRefundUnifiedRespDTO doUnifiedRefund(PayRefundUnifiedReqDTO reqDTO) {
  108 + protected PayRefundRespDTO doUnifiedRefund(PayRefundUnifiedReqDTO reqDTO) {
106 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 2  
3 3 import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderUnifiedReqDTO;
4 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 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 7 import cn.iocoder.yudao.framework.pay.core.enums.channel.PayChannelEnum;
8 8 import com.github.binarywang.wxpay.constant.WxPayConstants;
9 9 import com.github.binarywang.wxpay.exception.WxPayException;
... ... @@ -21,7 +21,7 @@ public class WxH5PayClient extends AbstractWxPayClient {
21 21 }
22 22  
23 23 @Override
24   - protected PayRefundUnifiedRespDTO doUnifiedRefund(PayRefundUnifiedReqDTO reqDTO) throws Throwable {
  24 + protected PayRefundRespDTO doUnifiedRefund(PayRefundUnifiedReqDTO reqDTO) throws Throwable {
25 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 2  
3 3 import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderUnifiedReqDTO;
4 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 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 7 import cn.iocoder.yudao.framework.pay.core.enums.channel.PayChannelEnum;
8 8 import cn.iocoder.yudao.framework.pay.core.enums.order.PayOrderDisplayModeEnum;
9 9 import com.github.binarywang.wxpay.bean.order.WxPayNativeOrderResult;
... ... @@ -37,11 +37,11 @@ public class WxNativePayClient extends AbstractWxPayClient {
37 37 protected PayOrderUnifiedRespDTO doUnifiedOrderV2(PayOrderUnifiedReqDTO reqDTO) throws WxPayException {
38 38 // 构建 WxPayUnifiedOrderRequest 对象
39 39 WxPayUnifiedOrderRequest request = WxPayUnifiedOrderRequest.newBuilder()
40   - .outTradeNo(reqDTO.getMerchantOrderId())
  40 + .outTradeNo(reqDTO.getOutTradeNo())
41 41 .body(reqDTO.getSubject())
42 42 .detail(reqDTO.getBody())
43   - .totalFee(reqDTO.getAmount()) // 单位分
44   - .productId(reqDTO.getMerchantOrderId())
  43 + .totalFee(reqDTO.getPrice()) // 单位分
  44 + .productId(reqDTO.getOutTradeNo())
45 45 .timeExpire(formatDateV2(reqDTO.getExpireTime()))
46 46 .spbillCreateIp(reqDTO.getUserIp())
47 47 .notifyUrl(reqDTO.getNotifyUrl())
... ... @@ -58,9 +58,9 @@ public class WxNativePayClient extends AbstractWxPayClient {
58 58 protected PayOrderUnifiedRespDTO doUnifiedOrderV3(PayOrderUnifiedReqDTO reqDTO) throws WxPayException {
59 59 // 构建 WxPayUnifiedOrderRequest 对象
60 60 WxPayUnifiedOrderV3Request request = new WxPayUnifiedOrderV3Request();
61   - request.setOutTradeNo(reqDTO.getMerchantOrderId());
  61 + request.setOutTradeNo(reqDTO.getOutTradeNo());
62 62 request.setDescription(reqDTO.getBody());
63   - request.setAmount(new WxPayUnifiedOrderV3Request.Amount().setTotal(reqDTO.getAmount())); // 单位分
  63 + request.setAmount(new WxPayUnifiedOrderV3Request.Amount().setTotal(reqDTO.getPrice())); // 单位分
64 64 request.setTimeExpire(formatDateV3(reqDTO.getExpireTime()));
65 65 request.setSceneInfo(new WxPayUnifiedOrderV3Request.SceneInfo().setPayerClientIp(reqDTO.getUserIp()));
66 66 request.setNotifyUrl(reqDTO.getNotifyUrl());
... ... @@ -73,7 +73,7 @@ public class WxNativePayClient extends AbstractWxPayClient {
73 73 }
74 74  
75 75 @Override
76   - protected PayRefundUnifiedRespDTO doUnifiedRefund(PayRefundUnifiedReqDTO reqDTO) throws Throwable {
  76 + protected PayRefundRespDTO doUnifiedRefund(PayRefundUnifiedReqDTO reqDTO) throws Throwable {
77 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 5 import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
6 6 import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderUnifiedReqDTO;
7 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 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 10 import cn.iocoder.yudao.framework.pay.core.enums.channel.PayChannelEnum;
11 11 import cn.iocoder.yudao.framework.pay.core.enums.order.PayOrderDisplayModeEnum;
12 12 import com.github.binarywang.wxpay.bean.order.WxPayMpOrderResult;
... ... @@ -47,10 +47,10 @@ public class WxPubPayClient extends AbstractWxPayClient {
47 47 protected PayOrderUnifiedRespDTO doUnifiedOrderV2(PayOrderUnifiedReqDTO reqDTO) throws WxPayException {
48 48 // 构建 WxPayUnifiedOrderRequest 对象
49 49 WxPayUnifiedOrderRequest request = WxPayUnifiedOrderRequest.newBuilder()
50   - .outTradeNo(reqDTO.getMerchantOrderId())
  50 + .outTradeNo(reqDTO.getOutTradeNo())
51 51 .body(reqDTO.getSubject())
52 52 .detail(reqDTO.getBody())
53   - .totalFee(reqDTO.getAmount()) // 单位分
  53 + .totalFee(reqDTO.getPrice()) // 单位分
54 54 .timeExpire(formatDateV2(reqDTO.getExpireTime()))
55 55 .spbillCreateIp(reqDTO.getUserIp())
56 56 .openid(getOpenid(reqDTO))
... ... @@ -68,9 +68,9 @@ public class WxPubPayClient extends AbstractWxPayClient {
68 68 protected PayOrderUnifiedRespDTO doUnifiedOrderV3(PayOrderUnifiedReqDTO reqDTO) throws WxPayException {
69 69 // 构建 WxPayUnifiedOrderRequest 对象
70 70 WxPayUnifiedOrderV3Request request = new WxPayUnifiedOrderV3Request();
71   - request.setOutTradeNo(reqDTO.getMerchantOrderId());
  71 + request.setOutTradeNo(reqDTO.getOutTradeNo());
72 72 request.setDescription(reqDTO.getSubject());
73   - request.setAmount(new WxPayUnifiedOrderV3Request.Amount().setTotal(reqDTO.getAmount())); // 单位分
  73 + request.setAmount(new WxPayUnifiedOrderV3Request.Amount().setTotal(reqDTO.getPrice())); // 单位分
74 74 request.setTimeExpire(formatDateV3(reqDTO.getExpireTime()));
75 75 request.setPayer(new WxPayUnifiedOrderV3Request.Payer().setOpenid(getOpenid(reqDTO)));
76 76 request.setSceneInfo(new WxPayUnifiedOrderV3Request.SceneInfo().setPayerClientIp(reqDTO.getUserIp()));
... ... @@ -84,7 +84,7 @@ public class WxPubPayClient extends AbstractWxPayClient {
84 84 }
85 85  
86 86 @Override
87   - protected PayRefundUnifiedRespDTO doUnifiedRefund(PayRefundUnifiedReqDTO reqDTO) {
  87 + protected PayRefundRespDTO doUnifiedRefund(PayRefundUnifiedReqDTO reqDTO) {
88 88 // TODO 需要实现
89 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 3 import lombok.AllArgsConstructor;
4 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 13 @Getter
12 14 @AllArgsConstructor
... ... @@ -20,4 +22,14 @@ public enum PayOrderStatusRespEnum {
20 22 private final Integer status;
21 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 3 import lombok.AllArgsConstructor;
4 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 12 */
11 13 @Getter
12 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 21 private final Integer status;
21 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 121  
122 122 private static PayOrderUnifiedReqDTO buildPayOrderUnifiedReqDTO() {
123 123 PayOrderUnifiedReqDTO reqDTO = new PayOrderUnifiedReqDTO();
124   - reqDTO.setAmount(123);
  124 + reqDTO.setPrice(123);
125 125 reqDTO.setSubject("IPhone 13");
126 126 reqDTO.setBody("biubiubiu");
127   - reqDTO.setMerchantOrderId(String.valueOf(System.currentTimeMillis()));
  127 + reqDTO.setOutTradeNo(String.valueOf(System.currentTimeMillis()));
128 128 reqDTO.setUserIp("127.0.0.1");
129 129 reqDTO.setNotifyUrl("http://127.0.0.1:8080");
130 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 1 package cn.iocoder.yudao.framework.pay.core.client.impl.alipay;
2 2 import cn.hutool.core.util.ReflectUtil;
3   -import cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeConstants;
4 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 4 import cn.iocoder.yudao.framework.test.core.ut.BaseMockitoUnitTest;
7 5 import com.alipay.api.AlipayApiException;
8 6 import com.alipay.api.DefaultAlipayClient;
... ... @@ -74,8 +72,8 @@ public class AlipayQrPayClientTest extends BaseMockitoUnitTest {
74 72 // 这里,设置可以直接随机整个对象。
75 73 Long shopOrderId = System.currentTimeMillis();
76 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 77 reqDTO.setBody("内容:" + shopOrderId);
80 78 reqDTO.setSubject("标题:"+shopOrderId);
81 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 5  
6 6 import java.util.Objects;
7 7  
  8 +/**
  9 + * 渠道的退款状态枚举
  10 + *
  11 + * @author 芋道源码
  12 + */
8 13 @Getter
9 14 @AllArgsConstructor
10 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 21 private final Integer status;
18 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 3 import cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog;
4 4 import cn.iocoder.yudao.framework.pay.core.client.PayClient;
5 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 8 import cn.iocoder.yudao.module.pay.service.order.PayOrderService;
10 9 import cn.iocoder.yudao.module.pay.service.refund.PayRefundService;
11 10 import io.swagger.v3.oas.annotations.Operation;
... ... @@ -61,18 +60,17 @@ public class PayNotifyController {
61 60 }
62 61  
63 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 65 // 3. 处理通知
68 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 69 return "success";
72 70 }
73 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 74 return "success";
77 75 }
78 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 76 @ExcelProperty("退款成功时间")
77 77 private LocalDateTime successTime;
78 78  
79   - @ExcelProperty("退款通知时间")
80   - private LocalDateTime notifyTime;
81   -
82 79 @ExcelProperty("退款失效时间")
83 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 60 PayRefundExcelVO payRefundExcelVO = new PayRefundExcelVO();
61 61  
62 62 payRefundExcelVO.setId(bean.getId());
63   - payRefundExcelVO.setTradeNo(bean.getTradeNo());
  63 + payRefundExcelVO.setTradeNo(bean.getNo());
64 64 payRefundExcelVO.setMerchantOrderId(bean.getMerchantOrderId());
65   - payRefundExcelVO.setMerchantRefundNo(bean.getMerchantRefundNo());
  65 + // TODO 芋艿:晚点在改
  66 +// payRefundExcelVO.setMerchantRefundNo(bean.getMerchantRefundNo());
66 67 payRefundExcelVO.setNotifyUrl(bean.getNotifyUrl());
67 68 payRefundExcelVO.setNotifyStatus(bean.getNotifyStatus());
68 69 payRefundExcelVO.setStatus(bean.getStatus());
... ... @@ -71,9 +72,7 @@ public interface PayRefundConvert {
71 72 payRefundExcelVO.setUserIp(bean.getUserIp());
72 73 payRefundExcelVO.setChannelOrderNo(bean.getChannelOrderNo());
73 74 payRefundExcelVO.setChannelRefundNo(bean.getChannelRefundNo());
74   - payRefundExcelVO.setExpireTime(bean.getExpireTime());
75 75 payRefundExcelVO.setSuccessTime(bean.getSuccessTime());
76   - payRefundExcelVO.setNotifyTime(bean.getNotifyTime());
77 76 payRefundExcelVO.setCreateTime(bean.getCreateTime());
78 77  
79 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 55  
56 56 /**
57 57 * 商户订单编号
58   - * 例如说,内部系统 A 的订单号。需要保证每个 PayMerchantDO 唯一
  58 + *
  59 + * 例如说,内部系统 A 的订单号,需要保证每个 PayAppDO 唯一
59 60 */
60 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 32 */
33 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 41 * 例如说,P202110132239124200055
41 42 */
... ... @@ -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 76 @TableField(typeHandler = JacksonTypeHandler.class)
76 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 5 import cn.iocoder.yudao.module.pay.dal.dataobject.app.PayAppDO;
6 6 import cn.iocoder.yudao.module.pay.dal.dataobject.channel.PayChannelDO;
7 7 import cn.iocoder.yudao.module.pay.dal.dataobject.order.PayOrderDO;
  8 +import cn.iocoder.yudao.module.pay.enums.notify.PayNotifyStatusEnum;
8 9 import cn.iocoder.yudao.module.pay.enums.refund.PayRefundStatusEnum;
9 10 import cn.iocoder.yudao.module.pay.enums.refund.PayRefundTypeEnum;
10 11 import com.baomidou.mybatisplus.annotation.KeySequence;
... ... @@ -37,6 +38,14 @@ public class PayRefundDO extends BaseDO {
37 38 */
38 39 @TableId
39 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 72 */
64 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 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 91 private String notifyUrl;
103   -
104 92 /**
105 93 * 通知商户退款结果的回调状态
106   - * TODO 0 未发送 1 已发送
  94 + *
  95 + * 枚举 {@link PayNotifyStatusEnum}
107 96 */
108 97 private Integer notifyStatus;
109 98  
... ... @@ -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 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 145 private String channelRefundNo;
  146 + /**
  147 + * 退款成功时间
  148 + */
  149 + private LocalDateTime successTime;
155 150  
156 151 /**
157 152 * 调用渠道的错误码
158 153 */
159 154 private String channelErrorCode;
160   -
161 155 /**
162 156 * 调用渠道报错时,错误信息
163 157 */
... ... @@ -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 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 45 return selectOne("req_no", reqNo);
46 46 }
47 47  
  48 + // TODO 芋艿:要重构
48 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 83 */
84 84 @Scheduled(initialDelay = 60, fixedRate = 60, timeUnit = TimeUnit.SECONDS)
85 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 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 2  
3 3 import cn.iocoder.yudao.framework.common.pojo.PageResult;
4 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 6 import cn.iocoder.yudao.module.pay.api.order.dto.PayOrderCreateReqDTO;
8 7 import cn.iocoder.yudao.module.pay.controller.admin.order.vo.PayOrderExportReqVO;
9 8 import cn.iocoder.yudao.module.pay.controller.admin.order.vo.PayOrderPageReqVO;
... ... @@ -103,8 +102,7 @@ public interface PayOrderService {
103 102 *
104 103 * @param channelId 渠道编号
105 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 9 import cn.iocoder.yudao.framework.pay.config.PayProperties;
10 10 import cn.iocoder.yudao.framework.pay.core.client.PayClient;
11 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 13 import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderUnifiedReqDTO;
15 14 import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderUnifiedRespDTO;
  15 +import cn.iocoder.yudao.framework.pay.core.enums.order.PayOrderStatusRespEnum;
16 16 import cn.iocoder.yudao.framework.tenant.core.util.TenantUtils;
17 17 import cn.iocoder.yudao.module.pay.api.order.dto.PayOrderCreateReqDTO;
18 18 import cn.iocoder.yudao.module.pay.controller.admin.order.vo.PayOrderExportReqVO;
... ... @@ -148,17 +148,17 @@ public class PayOrderServiceImpl implements PayOrderService {
148 148 // 3. 调用三方接口
149 149 PayOrderUnifiedReqDTO unifiedOrderReqDTO = PayOrderConvert.INSTANCE.convert2(reqVO, userIp)
150 150 // 商户相关的字段
151   - .setMerchantOrderId(orderExtension.getNo()) // 注意,此处使用的是 PayOrderExtensionDO.no 属性!
  151 + .setOutTradeNo(orderExtension.getNo()) // 注意,此处使用的是 PayOrderExtensionDO.no 属性!
152 152 .setSubject(order.getSubject()).setBody(order.getBody())
153 153 .setNotifyUrl(genChannelPayNotifyUrl(channel))
154 154 .setReturnUrl(reqVO.getReturnUrl())
155 155 // 订单相关字段
156   - .setAmount(order.getPrice()).setExpireTime(order.getExpireTime());
  156 + .setPrice(order.getPrice()).setExpireTime(order.getExpireTime());
157 157 PayOrderUnifiedRespDTO unifiedOrderRespDTO = client.unifiedOrder(unifiedOrderReqDTO);
158 158  
159 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 163 order = orderMapper.selectById(order.getId());
164 164 }
... ... @@ -226,16 +226,26 @@ public class PayOrderServiceImpl implements PayOrderService {
226 226  
227 227 @Override
228 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 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 247 // 1. 更新 PayOrderExtensionDO 支付成功
238   - PayOrderExtensionDO orderExtension = updatePayOrderExtensionSuccess(notify.getOrderExtensionNo(), rawNotify);
  248 + PayOrderExtensionDO orderExtension = updatePayOrderExtensionSuccess(notify);
239 249 // 2. 更新 PayOrderDO 支付成功
240 250 Pair<Boolean, PayOrderDO> order = updatePayOrderSuccess(channel, orderExtension, notify);
241 251 if (order.getKey()) { // 如果之前已经成功回调,则直接返回,不用重复记录支付通知记录;例如说:支付平台重复回调
... ... @@ -250,13 +260,12 @@ public class PayOrderServiceImpl implements PayOrderService {
250 260 /**
251 261 * 更新 PayOrderExtensionDO 支付成功
252 262 *
253   - * @param no 支付订单号(支付模块)
254   - * @param rawNotify 通知数据
  263 + * @param notify 通知
255 264 * @return PayOrderExtensionDO 对象
256 265 */
257   - private PayOrderExtensionDO updatePayOrderExtensionSuccess(String no, PayNotifyReqDTO rawNotify) {
  266 + private PayOrderExtensionDO updatePayOrderExtensionSuccess(PayOrderRespDTO notify) {
258 267 // 1. 查询 PayOrderExtensionDO
259   - PayOrderExtensionDO orderExtension = orderExtensionMapper.selectByNo(no);
  268 + PayOrderExtensionDO orderExtension = orderExtensionMapper.selectByNo(notify.getOutTradeNo());
260 269 if (orderExtension == null) {
261 270 throw exception(ErrorCodeConstants.PAY_ORDER_EXTENSION_NOT_FOUND);
262 271 }
... ... @@ -269,10 +278,8 @@ public class PayOrderServiceImpl implements PayOrderService {
269 278 }
270 279  
271 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 283 if (updateCounts == 0) { // 校验状态,必须是待支付
277 284 throw exception(ErrorCodeConstants.PAY_ORDER_EXTENSION_STATUS_IS_NOT_WAITING);
278 285 }
... ... @@ -290,7 +297,7 @@ public class PayOrderServiceImpl implements PayOrderService {
290 297 * value:PayOrderDO 对象
291 298 */
292 299 private Pair<Boolean, PayOrderDO> updatePayOrderSuccess(PayChannelDO channel, PayOrderExtensionDO orderExtension,
293   - PayOrderNotifyRespDTO notify) {
  300 + PayOrderRespDTO notify) {
294 301 // 1. 判断 PayOrderDO 是否处于待支付
295 302 PayOrderDO order = orderMapper.selectById(orderExtension.getOrderId());
296 303 if (order == null) {
... ...
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/service/refund/PayRefundService.java
1 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 5 import cn.iocoder.yudao.module.pay.api.refund.dto.PayRefundCreateReqDTO;
6 6 import cn.iocoder.yudao.module.pay.controller.admin.refund.vo.PayRefundExportReqVO;
7 7 import cn.iocoder.yudao.module.pay.controller.admin.refund.vo.PayRefundPageReqVO;
8   -import cn.iocoder.yudao.framework.common.pojo.PageResult;
9 8 import cn.iocoder.yudao.module.pay.dal.dataobject.refund.PayRefundDO;
10 9  
11 10 import java.util.List;
... ... @@ -62,8 +61,7 @@ public interface PayRefundService {
62 61 *
63 62 * @param channelId 渠道编号
64 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 7 import cn.iocoder.yudao.framework.pay.config.PayProperties;
8 8 import cn.iocoder.yudao.framework.pay.core.client.PayClient;
9 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 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 13 import cn.iocoder.yudao.module.pay.api.refund.dto.PayRefundCreateReqDTO;
15 14 import cn.iocoder.yudao.module.pay.controller.admin.refund.vo.PayRefundExportReqVO;
16 15 import cn.iocoder.yudao.module.pay.controller.admin.refund.vo.PayRefundPageReqVO;
... ... @@ -39,14 +38,13 @@ import org.springframework.transaction.annotation.Transactional;
39 38 import org.springframework.validation.annotation.Validated;
40 39  
41 40 import javax.annotation.Resource;
42   -import java.time.LocalDateTime;
43 41 import java.util.List;
44 42 import java.util.Objects;
45 43  
46 44 /**
47 45 * 退款订单 Service 实现类
48 46 *
49   - * @author aquan
  47 + * @author jason
50 48 */
51 49 @Service
52 50 @Slf4j
... ... @@ -116,7 +114,7 @@ public class PayRefundServiceImpl implements PayRefundService {
116 114 }
117 115  
118 116 // TODO 芋艿:待实现
119   - String merchantRefundId = RandomUtil.randomNumbers(16);
  117 + String merchantRefundId = "rrr" + RandomUtil.randomNumbers(16);
120 118  
121 119 // 校验退款的条件
122 120 validatePayRefund(reqDTO, order);
... ... @@ -128,12 +126,11 @@ public class PayRefundServiceImpl implements PayRefundService {
128 126 PayOrderExtensionDO orderExtensionDO = orderExtensionService.getOrderExtension(order.getSuccessExtensionId());
129 127 PayRefundDO payRefundDO = refundMapper.selectByTradeNoAndMerchantRefundNo(orderExtensionDO.getNo(),
130 128 merchantRefundId); // TODO 芋艿:需要优化
131   - if(Objects.nonNull(payRefundDO)){
  129 + if (Objects.nonNull(payRefundDO)) {
132 130 // 退款订单已经提交过。
133 131 //TODO 校验相同退款单的金额
134 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 135 throw ServiceExceptionUtil.exception(ErrorCodeConstants.PAY_REFUND_SUCCEED);
139 136 }
... ... @@ -147,14 +144,14 @@ public class PayRefundServiceImpl implements PayRefundService {
147 144 .channelCode(order.getChannelCode())
148 145 .channelId(order.getChannelId())
149 146 .orderId(order.getId())
150   - .merchantRefundNo(merchantRefundId) // TODO 芋艿:需要优化
  147 + .merchantRefundId(merchantRefundId)
151 148 .notifyUrl(app.getRefundNotifyUrl())
152 149 .payPrice(order.getPrice())
153 150 .refundPrice(reqDTO.getPrice())
154 151 .userIp(reqDTO.getUserIp())
155 152 .merchantOrderId(order.getMerchantOrderId())
156   - .tradeNo(orderExtensionDO.getNo())
157   - .status(PayRefundStatusEnum.CREATE.getStatus())
  153 + .no(orderExtensionDO.getNo())
  154 + .status(PayRefundStatusEnum.WAITING.getStatus())
158 155 .reason(reqDTO.getReason())
159 156 .notifyStatus(PayOrderNotifyStatusEnum.NO.getStatus())
160 157 .type(refundType.getStatus())
... ... @@ -163,11 +160,9 @@ public class PayRefundServiceImpl implements PayRefundService {
163 160 }
164 161 // TODO @jason:搞到 convert 里。一些额外的自动,手动 set 下;
165 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 166 .setNotifyUrl(genChannelPayNotifyUrl(channel)) // TODO 芋艿:优化下 notifyUrl
172 167 .setReason(reqDTO.getReason());
173 168 // 向渠道发起退款申请
... ... @@ -191,24 +186,25 @@ public class PayRefundServiceImpl implements PayRefundService {
191 186  
192 187 @Override
193 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 191 // TODO 芋艿:需要重构下这块的逻辑
197 192 PayChannelDO channel = channelService.validPayChannel(channelId);
198   - if (Objects.equals(PayNotifyRefundStatusEnum.SUCCESS, notify.getStatus())){
  193 + if (PayRefundStatusRespEnum.isSuccess(notify.getStatus())) {
199 194 payRefundSuccess(notify);
200 195 } else {
201   - //TODO 支付异常, 支付宝似乎没有支付异常的通知。
202 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 205 if (refundDO == null) {
211   - log.error("[payRefundSuccess][不存在 seqNo 为{} 的支付退款单]", refundNotify.getReqNo());
  206 + // TODO 芋艿:临时注释
  207 +// log.error("[payRefundSuccess][不存在 seqNo 为{} 的支付退款单]", refundNotify.getReqNo());
212 208 throw ServiceExceptionUtil.exception(ErrorCodeConstants.PAY_REFUND_NOT_FOUND);
213 209 }
214 210  
... ... @@ -233,10 +229,10 @@ public class PayRefundServiceImpl implements PayRefundService {
233 229 // 更新退款订单
234 230 PayRefundDO updateRefundDO = new PayRefundDO();
235 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 236 .setStatus(PayRefundStatusEnum.SUCCESS.getStatus());
241 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 3 import cn.hutool.core.util.RandomUtil;
4 4 import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
5 5 import cn.iocoder.yudao.framework.common.pojo.PageResult;
6   -import cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils;
7 6 import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest;
8   -import cn.iocoder.yudao.framework.test.core.util.RandomUtils;
9 7 import cn.iocoder.yudao.module.pay.controller.admin.app.vo.PayAppCreateReqVO;
10 8 import cn.iocoder.yudao.module.pay.controller.admin.app.vo.PayAppPageReqVO;
11 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 16 import org.springframework.context.annotation.Import;
19 17  
20 18 import javax.annotation.Resource;
21   -import java.time.LocalDateTime;
22   -import java.util.Collections;
23 19 import java.util.Map;
24 20  
25 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 62 o.setChannelId(1L);
63 63 o.setChannelCode(PayChannelEnum.WX_PUB.getCode());
64 64 o.setOrderId(1L);
65   - o.setTradeNo("OT0000001");
  65 + o.setNo("OT0000001");
66 66 o.setMerchantOrderId("MOT0000001");
67 67 o.setMerchantRefundNo("MRF0000001");
68 68 o.setNotifyUrl("https://www.cancanzi.com");
... ... @@ -127,7 +127,7 @@ public class PayRefundServiceTest extends BaseDbUnitTest {
127 127 o.setChannelId(1L);
128 128 o.setChannelCode(PayChannelEnum.WX_PUB.getCode());
129 129 o.setOrderId(1L);
130   - o.setTradeNo("OT0000001");
  130 + o.setNo("OT0000001");
131 131 o.setMerchantOrderId("MOT0000001");
132 132 o.setMerchantRefundNo("MRF0000001");
133 133 o.setNotifyUrl("https://www.cancanzi.com");
... ...
yudao-ui-admin/src/api/pay/channel.js
... ... @@ -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 32 return request({
33   - url: '/pay/channel/get-channel',
  33 + url: '/pay/channel/get',
  34 + method: 'get',
34 35 params:{
35   - appId:appId,
36   - code:code
  36 + appId,
  37 + code
37 38 },
38   - method: 'get'
39 39 })
40 40 }
41 41  
... ...