Commit 5b066ea69303e1045d9d598196d07f1e7c06f818

Authored by YunaiV
1 parent ac5646b8

mall + pay:

1. 增加支付宝退款查询接口
yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/PayClient.java
@@ -67,4 +67,13 @@ public interface PayClient { @@ -67,4 +67,13 @@ public interface PayClient {
67 */ 67 */
68 PayRefundRespDTO parseRefundNotify(Map<String, String> params, String body); 68 PayRefundRespDTO parseRefundNotify(Map<String, String> params, String body);
69 69
  70 + /**
  71 + * 获得退款订单信息
  72 + *
  73 + * @param outTradeNo 外部订单号
  74 + * @param outRefundNo 外部退款号
  75 + * @return 退款订单信息
  76 + */
  77 + PayRefundRespDTO getRefund(String outTradeNo, String outRefundNo);
  78 +
70 } 79 }
yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/AbstractPayClient.java
@@ -94,7 +94,7 @@ public abstract class AbstractPayClient&lt;Config extends PayClientConfig&gt; implemen @@ -94,7 +94,7 @@ public abstract class AbstractPayClient&lt;Config extends PayClientConfig&gt; implemen
94 throws Throwable; 94 throws Throwable;
95 95
96 @Override 96 @Override
97 - public PayOrderRespDTO parseOrderNotify(Map<String, String> params, String body) { 97 + public final PayOrderRespDTO parseOrderNotify(Map<String, String> params, String body) {
98 try { 98 try {
99 return doParseOrderNotify(params, body); 99 return doParseOrderNotify(params, body);
100 } catch (Throwable ex) { 100 } catch (Throwable ex) {
@@ -108,7 +108,7 @@ public abstract class AbstractPayClient&lt;Config extends PayClientConfig&gt; implemen @@ -108,7 +108,7 @@ public abstract class AbstractPayClient&lt;Config extends PayClientConfig&gt; implemen
108 throws Throwable; 108 throws Throwable;
109 109
110 @Override 110 @Override
111 - public PayOrderRespDTO getOrder(String outTradeNo) { 111 + public final PayOrderRespDTO getOrder(String outTradeNo) {
112 try { 112 try {
113 return doGetOrder(outTradeNo); 113 return doGetOrder(outTradeNo);
114 } catch (Throwable ex) { 114 } catch (Throwable ex) {
@@ -124,7 +124,7 @@ public abstract class AbstractPayClient&lt;Config extends PayClientConfig&gt; implemen @@ -124,7 +124,7 @@ public abstract class AbstractPayClient&lt;Config extends PayClientConfig&gt; implemen
124 // ============ 退款相关 ========== 124 // ============ 退款相关 ==========
125 125
126 @Override 126 @Override
127 - public PayRefundRespDTO unifiedRefund(PayRefundUnifiedReqDTO reqDTO) { 127 + public final PayRefundRespDTO unifiedRefund(PayRefundUnifiedReqDTO reqDTO) {
128 ValidationUtils.validate(reqDTO); 128 ValidationUtils.validate(reqDTO);
129 // 执行统一退款 129 // 执行统一退款
130 PayRefundRespDTO resp; 130 PayRefundRespDTO resp;
@@ -145,7 +145,7 @@ public abstract class AbstractPayClient&lt;Config extends PayClientConfig&gt; implemen @@ -145,7 +145,7 @@ public abstract class AbstractPayClient&lt;Config extends PayClientConfig&gt; implemen
145 protected abstract PayRefundRespDTO doUnifiedRefund(PayRefundUnifiedReqDTO reqDTO) throws Throwable; 145 protected abstract PayRefundRespDTO doUnifiedRefund(PayRefundUnifiedReqDTO reqDTO) throws Throwable;
146 146
147 @Override 147 @Override
148 - public PayRefundRespDTO parseRefundNotify(Map<String, String> params, String body) { 148 + public final PayRefundRespDTO parseRefundNotify(Map<String, String> params, String body) {
149 try { 149 try {
150 return doParseRefundNotify(params, body); 150 return doParseRefundNotify(params, body);
151 } catch (Throwable ex) { 151 } catch (Throwable ex) {
@@ -158,6 +158,20 @@ public abstract class AbstractPayClient&lt;Config extends PayClientConfig&gt; implemen @@ -158,6 +158,20 @@ public abstract class AbstractPayClient&lt;Config extends PayClientConfig&gt; implemen
158 protected abstract PayRefundRespDTO doParseRefundNotify(Map<String, String> params, String body) 158 protected abstract PayRefundRespDTO doParseRefundNotify(Map<String, String> params, String body)
159 throws Throwable; 159 throws Throwable;
160 160
  161 + @Override
  162 + public final PayRefundRespDTO getRefund(String outTradeNo, String outRefundNo) {
  163 + try {
  164 + return doGetRefund(outTradeNo, outRefundNo);
  165 + } catch (Throwable ex) {
  166 + log.error("[getRefund][客户端({}) outTradeNo({}) outRefundNo({}) 查询退款单异常]",
  167 + getId(), outTradeNo, outRefundNo, ex);
  168 + throw buildPayException(ex);
  169 + }
  170 + }
  171 +
  172 + protected abstract PayRefundRespDTO doGetRefund(String outTradeNo, String outRefundNo)
  173 + throws Throwable;
  174 +
161 // ========== 各种工具方法 ========== 175 // ========== 各种工具方法 ==========
162 176
163 private PayException buildPayException(Throwable ex) { 177 private PayException buildPayException(Throwable ex) {
yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/alipay/AbstractAlipayPayClient.java
@@ -17,11 +17,14 @@ import com.alipay.api.AlipayApiException; @@ -17,11 +17,14 @@ import com.alipay.api.AlipayApiException;
17 import com.alipay.api.AlipayConfig; 17 import com.alipay.api.AlipayConfig;
18 import com.alipay.api.AlipayResponse; 18 import com.alipay.api.AlipayResponse;
19 import com.alipay.api.DefaultAlipayClient; 19 import com.alipay.api.DefaultAlipayClient;
  20 +import com.alipay.api.domain.AlipayTradeFastpayRefundQueryModel;
20 import com.alipay.api.domain.AlipayTradeQueryModel; 21 import com.alipay.api.domain.AlipayTradeQueryModel;
21 import com.alipay.api.domain.AlipayTradeRefundModel; 22 import com.alipay.api.domain.AlipayTradeRefundModel;
22 import com.alipay.api.internal.util.AlipaySignature; 23 import com.alipay.api.internal.util.AlipaySignature;
  24 +import com.alipay.api.request.AlipayTradeFastpayRefundQueryRequest;
23 import com.alipay.api.request.AlipayTradeQueryRequest; 25 import com.alipay.api.request.AlipayTradeQueryRequest;
24 import com.alipay.api.request.AlipayTradeRefundRequest; 26 import com.alipay.api.request.AlipayTradeRefundRequest;
  27 +import com.alipay.api.response.AlipayTradeFastpayRefundQueryResponse;
25 import com.alipay.api.response.AlipayTradeQueryResponse; 28 import com.alipay.api.response.AlipayTradeQueryResponse;
26 import com.alipay.api.response.AlipayTradeRefundResponse; 29 import com.alipay.api.response.AlipayTradeRefundResponse;
27 import lombok.SneakyThrows; 30 import lombok.SneakyThrows;
@@ -29,6 +32,7 @@ import lombok.extern.slf4j.Slf4j; @@ -29,6 +32,7 @@ import lombok.extern.slf4j.Slf4j;
29 32
30 import java.nio.charset.StandardCharsets; 33 import java.nio.charset.StandardCharsets;
31 import java.time.LocalDateTime; 34 import java.time.LocalDateTime;
  35 +import java.util.Collections;
32 import java.util.Map; 36 import java.util.Map;
33 import java.util.Objects; 37 import java.util.Objects;
34 import java.util.function.Supplier; 38 import java.util.function.Supplier;
@@ -102,7 +106,7 @@ public abstract class AbstractAlipayPayClient extends AbstractPayClient&lt;AlipayPa @@ -102,7 +106,7 @@ public abstract class AbstractAlipayPayClient extends AbstractPayClient&lt;AlipayPa
102 106
103 // 2.1 执行请求 107 // 2.1 执行请求
104 AlipayTradeQueryResponse response = client.execute(request); 108 AlipayTradeQueryResponse response = client.execute(request);
105 - if (!response.isSuccess()) { 109 + if (!response.isSuccess()) { // 不成功,例如说订单不存在
106 return PayOrderRespDTO.closedOf(response.getSubCode(), response.getSubMsg(), 110 return PayOrderRespDTO.closedOf(response.getSubCode(), response.getSubMsg(),
107 outTradeNo, response); 111 outTradeNo, response);
108 } 112 }
@@ -142,16 +146,15 @@ public abstract class AbstractAlipayPayClient extends AbstractPayClient&lt;AlipayPa @@ -142,16 +146,15 @@ public abstract class AbstractAlipayPayClient extends AbstractPayClient&lt;AlipayPa
142 request.setBizModel(model); 146 request.setBizModel(model);
143 147
144 // 2.1 执行请求 148 // 2.1 执行请求
145 - AlipayTradeRefundResponse response = client.execute(request); 149 + AlipayTradeRefundResponse response = client.execute(request);
  150 + if (!response.isSuccess()) {
  151 + return PayRefundRespDTO.failureOf(reqDTO.getOutRefundNo(), response);
  152 + }
146 // 2.2 创建返回结果 153 // 2.2 创建返回结果
147 // 支付宝只要退款调用返回 success,就认为退款成功,不需要回调。具体可见 parseNotify 方法的说明。 154 // 支付宝只要退款调用返回 success,就认为退款成功,不需要回调。具体可见 parseNotify 方法的说明。
148 // 另外,支付宝没有退款单号,所以不用设置 155 // 另外,支付宝没有退款单号,所以不用设置
149 - if (response.isSuccess()) {  
150 - return PayRefundRespDTO.successOf(null, LocalDateTimeUtil.of(response.getGmtRefundPay()),  
151 - reqDTO.getOutRefundNo(), response);  
152 - } else {  
153 - return PayRefundRespDTO.failureOf(reqDTO.getOutRefundNo(), response);  
154 - } 156 + return PayRefundRespDTO.successOf(null, LocalDateTimeUtil.of(response.getGmtRefundPay()),
  157 + reqDTO.getOutRefundNo(), response);
155 } 158 }
156 159
157 @Override 160 @Override
@@ -164,6 +167,35 @@ public abstract class AbstractAlipayPayClient extends AbstractPayClient&lt;AlipayPa @@ -164,6 +167,35 @@ public abstract class AbstractAlipayPayClient extends AbstractPayClient&lt;AlipayPa
164 throw new UnsupportedOperationException("支付宝无退款回调"); 167 throw new UnsupportedOperationException("支付宝无退款回调");
165 } 168 }
166 169
  170 + @Override
  171 + protected PayRefundRespDTO doGetRefund(String outTradeNo, String outRefundNo) throws AlipayApiException {
  172 + // 1.1 构建 AlipayTradeFastpayRefundQueryModel 请求
  173 + AlipayTradeFastpayRefundQueryModel model = new AlipayTradeFastpayRefundQueryModel();
  174 + model.setOutTradeNo(outTradeNo);
  175 + model.setOutRequestNo(outRefundNo);
  176 + model.setQueryOptions(Collections.singletonList("gmt_refund_pay"));
  177 + // 1.2 构建 AlipayTradeFastpayRefundQueryRequest 请求
  178 + AlipayTradeFastpayRefundQueryRequest request = new AlipayTradeFastpayRefundQueryRequest();
  179 + request.setBizModel(model);
  180 +
  181 + // 2.1 执行请求
  182 + AlipayTradeFastpayRefundQueryResponse response = client.execute(request);
  183 + if (!response.isSuccess()) {
  184 + // 明确不存在的情况,应该就是失败,可进行关闭
  185 + if (ObjectUtils.equalsAny(response.getSubCode(), "TRADE_NOT_EXIST", "ACQ.TRADE_NOT_EXIST")) {
  186 + return PayRefundRespDTO.failureOf(outRefundNo, response);
  187 + }
  188 + // 可能存在“ACQ.SYSTEM_ERROR”系统错误等情况,所以返回 WAIT 继续等待
  189 + return PayRefundRespDTO.waitingOf(null, outRefundNo, response);
  190 + }
  191 + // 2.2 创建返回结果
  192 + if (Objects.equals(response.getRefundStatus(), "REFUND_SUCCESS")) {
  193 + return PayRefundRespDTO.successOf(null, LocalDateTimeUtil.of(response.getGmtRefundPay()),
  194 + outRefundNo, response);
  195 + }
  196 + return PayRefundRespDTO.waitingOf(null, outRefundNo, response);
  197 + }
  198 +
167 // ========== 各种工具方法 ========== 199 // ========== 各种工具方法 ==========
168 200
169 protected String formatAmount(Integer amount) { 201 protected String formatAmount(Integer amount) {
yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/weixin/AbstractWxPayClient.java
@@ -307,6 +307,11 @@ public abstract class AbstractWxPayClient extends AbstractPayClient&lt;WxPayClientC @@ -307,6 +307,11 @@ public abstract class AbstractWxPayClient extends AbstractPayClient&lt;WxPayClientC
307 return PayRefundRespDTO.failureOf(result.getOutRefundNo(), response); 307 return PayRefundRespDTO.failureOf(result.getOutRefundNo(), response);
308 } 308 }
309 309
  310 + @Override
  311 + protected PayRefundRespDTO doGetRefund(String outTradeNo, String outRefundNo) {
  312 + return null;
  313 + }
  314 +
310 // ========== 各种工具方法 ========== 315 // ========== 各种工具方法 ==========
311 316
312 static String formatDateV2(LocalDateTime time) { 317 static String formatDateV2(LocalDateTime time) {
yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/weixin/WxPubPayClient.java
@@ -4,6 +4,7 @@ import cn.hutool.core.map.MapUtil; @@ -4,6 +4,7 @@ import cn.hutool.core.map.MapUtil;
4 import cn.hutool.core.util.StrUtil; 4 import cn.hutool.core.util.StrUtil;
5 import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderRespDTO; 5 import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderRespDTO;
6 import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderUnifiedReqDTO; 6 import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderUnifiedReqDTO;
  7 +import cn.iocoder.yudao.framework.pay.core.client.dto.refund.PayRefundRespDTO;
7 import cn.iocoder.yudao.framework.pay.core.enums.channel.PayChannelEnum; 8 import cn.iocoder.yudao.framework.pay.core.enums.channel.PayChannelEnum;
8 import cn.iocoder.yudao.framework.pay.core.enums.order.PayOrderDisplayModeEnum; 9 import cn.iocoder.yudao.framework.pay.core.enums.order.PayOrderDisplayModeEnum;
9 import com.github.binarywang.wxpay.bean.order.WxPayMpOrderResult; 10 import com.github.binarywang.wxpay.bean.order.WxPayMpOrderResult;
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/dal/dataobject/refund/PayRefundDO.java
@@ -70,6 +70,12 @@ public class PayRefundDO extends BaseDO { @@ -70,6 +70,12 @@ public class PayRefundDO extends BaseDO {
70 * 关联 {@link PayOrderDO#getId()} 70 * 关联 {@link PayOrderDO#getId()}
71 */ 71 */
72 private Long orderId; 72 private Long orderId;
  73 + /**
  74 + * 支付订单编号
  75 + *
  76 + * 冗余 {@link PayOrderDO#getNo()}
  77 + */
  78 + private String orderNo;
73 79
74 // ========== 商户相关字段 ========== 80 // ========== 商户相关字段 ==========
75 /** 81 /**