Commit 0e5e6c7ce0ea5c5f7c7e14370db0807a9a355003

Authored by YunaiV
1 parent 5b066ea6

mall + pay:

1. 增加微信退款的查询接口
yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/weixin/AbstractWxPayClient.java
@@ -2,10 +2,12 @@ package cn.iocoder.yudao.framework.pay.core.client.impl.weixin; @@ -2,10 +2,12 @@ package cn.iocoder.yudao.framework.pay.core.client.impl.weixin;
2 2
3 import cn.hutool.core.bean.BeanUtil; 3 import cn.hutool.core.bean.BeanUtil;
4 import cn.hutool.core.codec.Base64; 4 import cn.hutool.core.codec.Base64;
  5 +import cn.hutool.core.collection.CollUtil;
5 import cn.hutool.core.date.LocalDateTimeUtil; 6 import cn.hutool.core.date.LocalDateTimeUtil;
6 import cn.hutool.core.date.TemporalAccessorUtil; 7 import cn.hutool.core.date.TemporalAccessorUtil;
7 import cn.hutool.core.util.StrUtil; 8 import cn.hutool.core.util.StrUtil;
8 import cn.iocoder.yudao.framework.common.util.io.FileUtils; 9 import cn.iocoder.yudao.framework.common.util.io.FileUtils;
  10 +import cn.iocoder.yudao.framework.common.util.object.ObjectUtils;
9 import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderRespDTO; 11 import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderRespDTO;
10 import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderUnifiedReqDTO; 12 import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderUnifiedReqDTO;
11 import cn.iocoder.yudao.framework.pay.core.client.dto.refund.PayRefundRespDTO; 13 import cn.iocoder.yudao.framework.pay.core.client.dto.refund.PayRefundRespDTO;
@@ -16,14 +18,8 @@ import com.github.binarywang.wxpay.bean.notify.WxPayOrderNotifyResult; @@ -16,14 +18,8 @@ import com.github.binarywang.wxpay.bean.notify.WxPayOrderNotifyResult;
16 import com.github.binarywang.wxpay.bean.notify.WxPayOrderNotifyV3Result; 18 import com.github.binarywang.wxpay.bean.notify.WxPayOrderNotifyV3Result;
17 import com.github.binarywang.wxpay.bean.notify.WxPayRefundNotifyResult; 19 import com.github.binarywang.wxpay.bean.notify.WxPayRefundNotifyResult;
18 import com.github.binarywang.wxpay.bean.notify.WxPayRefundNotifyV3Result; 20 import com.github.binarywang.wxpay.bean.notify.WxPayRefundNotifyV3Result;
19 -import com.github.binarywang.wxpay.bean.request.WxPayOrderQueryRequest;  
20 -import com.github.binarywang.wxpay.bean.request.WxPayOrderQueryV3Request;  
21 -import com.github.binarywang.wxpay.bean.request.WxPayRefundRequest;  
22 -import com.github.binarywang.wxpay.bean.request.WxPayRefundV3Request;  
23 -import com.github.binarywang.wxpay.bean.result.WxPayOrderQueryResult;  
24 -import com.github.binarywang.wxpay.bean.result.WxPayOrderQueryV3Result;  
25 -import com.github.binarywang.wxpay.bean.result.WxPayRefundResult;  
26 -import com.github.binarywang.wxpay.bean.result.WxPayRefundV3Result; 21 +import com.github.binarywang.wxpay.bean.request.*;
  22 +import com.github.binarywang.wxpay.bean.result.*;
27 import com.github.binarywang.wxpay.config.WxPayConfig; 23 import com.github.binarywang.wxpay.config.WxPayConfig;
28 import com.github.binarywang.wxpay.exception.WxPayException; 24 import com.github.binarywang.wxpay.exception.WxPayException;
29 import com.github.binarywang.wxpay.service.WxPayService; 25 import com.github.binarywang.wxpay.service.WxPayService;
@@ -153,13 +149,23 @@ public abstract class AbstractWxPayClient extends AbstractPayClient<WxPayClientC @@ -153,13 +149,23 @@ public abstract class AbstractWxPayClient extends AbstractPayClient<WxPayClientC
153 149
154 @Override 150 @Override
155 protected PayOrderRespDTO doGetOrder(String outTradeNo) throws Throwable { 151 protected PayOrderRespDTO doGetOrder(String outTradeNo) throws Throwable {
156 - switch (config.getApiVersion()) {  
157 - case API_VERSION_V2:  
158 - return doGetOrderV2(outTradeNo);  
159 - case WxPayClientConfig.API_VERSION_V3:  
160 - return doGetOrderV3(outTradeNo);  
161 - default:  
162 - throw new IllegalArgumentException(String.format("未知的 API 版本(%s)", config.getApiVersion())); 152 + try {
  153 + switch (config.getApiVersion()) {
  154 + case API_VERSION_V2:
  155 + return doGetOrderV2(outTradeNo);
  156 + case WxPayClientConfig.API_VERSION_V3:
  157 + return doGetOrderV3(outTradeNo);
  158 + default:
  159 + throw new IllegalArgumentException(String.format("未知的 API 版本(%s)", config.getApiVersion()));
  160 + }
  161 + } catch (WxPayException e) {
  162 + if (ObjectUtils.equalsAny(e.getErrCode(), "ORDERNOTEXIST", "ORDER_NOT_EXIST")) {
  163 + String errorCode = getErrorCode(e);
  164 + String errorMessage = getErrorMessage(e);
  165 + return PayOrderRespDTO.closedOf(errorCode, errorMessage,
  166 + outTradeNo, e.getXmlString());
  167 + }
  168 + throw e;
163 } 169 }
164 } 170 }
165 171
@@ -241,7 +247,7 @@ public abstract class AbstractWxPayClient extends AbstractPayClient<WxPayClientC @@ -241,7 +247,7 @@ public abstract class AbstractWxPayClient extends AbstractPayClient<WxPayClientC
241 // 2.1 执行请求 247 // 2.1 执行请求
242 WxPayRefundResult response = client.refundV2(request); 248 WxPayRefundResult response = client.refundV2(request);
243 // 2.2 创建返回结果 249 // 2.2 创建返回结果
244 - if (Objects.equals("SUCCESS", response.getResultCode())) { 250 + if (Objects.equals("SUCCESS", response.getResultCode())) { // V2 情况下,不直接返回退款成功,而是等待异步通知
245 return PayRefundRespDTO.waitingOf(response.getRefundId(), 251 return PayRefundRespDTO.waitingOf(response.getRefundId(),
246 reqDTO.getOutRefundNo(), response); 252 reqDTO.getOutRefundNo(), response);
247 } 253 }
@@ -308,8 +314,80 @@ public abstract class AbstractWxPayClient extends AbstractPayClient<WxPayClientC @@ -308,8 +314,80 @@ public abstract class AbstractWxPayClient extends AbstractPayClient<WxPayClientC
308 } 314 }
309 315
310 @Override 316 @Override
311 - protected PayRefundRespDTO doGetRefund(String outTradeNo, String outRefundNo) {  
312 - return null; 317 + protected PayRefundRespDTO doGetRefund(String outTradeNo, String outRefundNo) throws WxPayException {
  318 + try {
  319 + switch (config.getApiVersion()) {
  320 + case API_VERSION_V2:
  321 + return doGetRefundV2(outTradeNo, outRefundNo);
  322 + case WxPayClientConfig.API_VERSION_V3:
  323 + return doGetRefundV3(outTradeNo, outRefundNo);
  324 + default:
  325 + throw new IllegalArgumentException(String.format("未知的 API 版本(%s)", config.getApiVersion()));
  326 + }
  327 + } catch (WxPayException e) {
  328 + if (ObjectUtils.equalsAny(e.getErrCode(), "REFUNDNOTEXIST", "RESOURCE_NOT_EXISTS")) {
  329 + String errorCode = getErrorCode(e);
  330 + String errorMessage = getErrorMessage(e);
  331 + return PayRefundRespDTO.failureOf(errorCode, errorMessage,
  332 + outRefundNo, e.getXmlString());
  333 + }
  334 + throw e;
  335 + }
  336 + }
  337 +
  338 + private PayRefundRespDTO doGetRefundV2(String outTradeNo, String outRefundNo) throws WxPayException {
  339 + // 1. 构建 WxPayRefundRequest 请求
  340 + WxPayRefundQueryRequest request = WxPayRefundQueryRequest.newBuilder()
  341 + .outTradeNo(outTradeNo)
  342 + .outRefundNo(outRefundNo)
  343 + .build();
  344 + // 2.1 执行请求
  345 + WxPayRefundQueryResult response = client.refundQuery(request);
  346 + // 2.2 创建返回结果
  347 + if (!Objects.equals("SUCCESS", response.getResultCode())) {
  348 + return PayRefundRespDTO.waitingOf(null,
  349 + outRefundNo, response);
  350 + }
  351 + WxPayRefundQueryResult.RefundRecord refund = CollUtil.findOne(response.getRefundRecords(),
  352 + record -> record.getOutRefundNo().equals(outRefundNo));
  353 + if (refund == null) {
  354 + return PayRefundRespDTO.failureOf(outRefundNo, response);
  355 + }
  356 + switch (refund.getRefundStatus()) {
  357 + case "SUCCESS":
  358 + return PayRefundRespDTO.successOf(refund.getRefundId(), parseDateV2B(refund.getRefundSuccessTime()),
  359 + outRefundNo, response);
  360 + case "PROCESSING":
  361 + return PayRefundRespDTO.waitingOf(refund.getRefundId(),
  362 + outRefundNo, response);
  363 + case "CHANGE": // 退款到银行发现用户的卡作废或者冻结了,导致原路退款银行卡失败,资金回流到商户的现金帐号,需要商户人工干预,通过线下或者财付通转账的方式进行退款
  364 + case "FAIL":
  365 + return PayRefundRespDTO.failureOf(outRefundNo, response);
  366 + default:
  367 + throw new IllegalArgumentException(String.format("未知的退款状态(%s)", refund.getRefundStatus()));
  368 + }
  369 + }
  370 +
  371 + private PayRefundRespDTO doGetRefundV3(String outTradeNo, String outRefundNo) throws WxPayException {
  372 + // 1. 构建 WxPayRefundRequest 请求
  373 + WxPayRefundQueryV3Request request = new WxPayRefundQueryV3Request();
  374 + request.setOutRefundNo(outRefundNo);
  375 + // 2.1 执行请求
  376 + WxPayRefundQueryV3Result response = client.refundQueryV3(request);
  377 + // 2.2 创建返回结果
  378 + switch (response.getStatus()) {
  379 + case "SUCCESS":
  380 + return PayRefundRespDTO.successOf(response.getRefundId(), parseDateV3(response.getSuccessTime()),
  381 + outRefundNo, response);
  382 + case "PROCESSING":
  383 + return PayRefundRespDTO.waitingOf(response.getRefundId(),
  384 + outRefundNo, response);
  385 + case "ABNORMAL": // 退款异常
  386 + case "CLOSED":
  387 + return PayRefundRespDTO.failureOf(outRefundNo, response);
  388 + default:
  389 + throw new IllegalArgumentException(String.format("未知的退款状态(%s)", response.getStatus()));
  390 + }
313 } 391 }
314 392
315 // ========== 各种工具方法 ========== 393 // ========== 各种工具方法 ==========
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/service/refund/PayRefundServiceImpl.java
@@ -114,7 +114,7 @@ public class PayRefundServiceImpl implements PayRefundService { @@ -114,7 +114,7 @@ public class PayRefundServiceImpl implements PayRefundService {
114 // 2.1 插入退款单 114 // 2.1 插入退款单
115 String no = noRedisDAO.generate(payProperties.getRefundNoPrefix()); 115 String no = noRedisDAO.generate(payProperties.getRefundNoPrefix());
116 refund = PayRefundConvert.INSTANCE.convert(reqDTO) 116 refund = PayRefundConvert.INSTANCE.convert(reqDTO)
117 - .setNo(no).setOrderId(order.getId()) 117 + .setNo(no).setOrderId(order.getId()).setOrderNo(order.getNo())
118 .setChannelId(order.getChannelId()).setChannelCode(order.getChannelCode()) 118 .setChannelId(order.getChannelId()).setChannelCode(order.getChannelCode())
119 // 商户相关的字段 119 // 商户相关的字段
120 .setNotifyUrl(app.getRefundNotifyUrl()) 120 .setNotifyUrl(app.getRefundNotifyUrl())