mirror of
https://gitee.com/huangge1199_admin/vue-pro.git
synced 2024-11-22 23:31:52 +08:00
mall + pay:
1. 优化 PayClient 退款逻辑,返回业务失败 errorCode + errorMsg 错误码
This commit is contained in:
parent
6f475f8c85
commit
1c282bd3cb
@ -1,5 +1,6 @@
|
|||||||
package cn.iocoder.yudao.framework.pay.core.client.dto.refund;
|
package cn.iocoder.yudao.framework.pay.core.client.dto.refund;
|
||||||
|
|
||||||
|
import cn.iocoder.yudao.framework.pay.core.client.exception.PayException;
|
||||||
import cn.iocoder.yudao.framework.pay.core.enums.refund.PayRefundStatusRespEnum;
|
import cn.iocoder.yudao.framework.pay.core.enums.refund.PayRefundStatusRespEnum;
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
|
|
||||||
@ -44,4 +45,71 @@ public class PayRefundRespDTO {
|
|||||||
*/
|
*/
|
||||||
private Object rawData;
|
private Object rawData;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 调用渠道的错误码
|
||||||
|
*
|
||||||
|
* 注意:这里返回的是业务异常,而是不系统异常。
|
||||||
|
* 如果是系统异常,则会抛出 {@link PayException}
|
||||||
|
*/
|
||||||
|
private String channelErrorCode;
|
||||||
|
/**
|
||||||
|
* 调用渠道报错时,错误信息
|
||||||
|
*/
|
||||||
|
private String channelErrorMsg;
|
||||||
|
|
||||||
|
private PayRefundRespDTO() {
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建【WAITING】状态的退款返回
|
||||||
|
*/
|
||||||
|
public static PayRefundRespDTO waitingOf(String channelRefundNo,
|
||||||
|
String outRefundNo, Object rawData) {
|
||||||
|
PayRefundRespDTO respDTO = new PayRefundRespDTO();
|
||||||
|
respDTO.status = PayRefundStatusRespEnum.WAITING.getStatus();
|
||||||
|
respDTO.channelRefundNo = channelRefundNo;
|
||||||
|
// 相对通用的字段
|
||||||
|
respDTO.outRefundNo = outRefundNo;
|
||||||
|
respDTO.rawData = rawData;
|
||||||
|
return respDTO;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建【SUCCESS】状态的退款返回
|
||||||
|
*/
|
||||||
|
public static PayRefundRespDTO successOf(String channelRefundNo, LocalDateTime successTime,
|
||||||
|
String outRefundNo, Object rawData) {
|
||||||
|
PayRefundRespDTO respDTO = new PayRefundRespDTO();
|
||||||
|
respDTO.status = PayRefundStatusRespEnum.SUCCESS.getStatus();
|
||||||
|
respDTO.channelRefundNo = channelRefundNo;
|
||||||
|
respDTO.successTime = successTime;
|
||||||
|
// 相对通用的字段
|
||||||
|
respDTO.outRefundNo = outRefundNo;
|
||||||
|
respDTO.rawData = rawData;
|
||||||
|
return respDTO;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建【FAILURE】状态的退款返回
|
||||||
|
*/
|
||||||
|
public static PayRefundRespDTO failureOf(String outRefundNo, Object rawData) {
|
||||||
|
return failureOf(null, null,
|
||||||
|
outRefundNo, rawData);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建【FAILURE】状态的退款返回
|
||||||
|
*/
|
||||||
|
public static PayRefundRespDTO failureOf(String channelErrorCode, String channelErrorMsg,
|
||||||
|
String outRefundNo, Object rawData) {
|
||||||
|
PayRefundRespDTO respDTO = new PayRefundRespDTO();
|
||||||
|
respDTO.status = PayRefundStatusRespEnum.FAILURE.getStatus();
|
||||||
|
respDTO.channelErrorCode = channelErrorCode;
|
||||||
|
respDTO.channelErrorMsg = channelErrorMsg;
|
||||||
|
// 相对通用的字段
|
||||||
|
respDTO.outRefundNo = outRefundNo;
|
||||||
|
respDTO.rawData = rawData;
|
||||||
|
return respDTO;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -98,7 +98,8 @@ public abstract class AbstractPayClient<Config extends PayClientConfig> implemen
|
|||||||
try {
|
try {
|
||||||
return doParseOrderNotify(params, body);
|
return doParseOrderNotify(params, body);
|
||||||
} catch (Throwable ex) {
|
} catch (Throwable ex) {
|
||||||
log.error("[parseOrderNotify][params({}) body({}) 解析失败]", params, body, ex);
|
log.error("[parseOrderNotify][客户端({}) params({}) body({}) 解析失败]",
|
||||||
|
getId(), params, body, ex);
|
||||||
throw buildPayException(ex);
|
throw buildPayException(ex);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -129,6 +130,20 @@ public abstract class AbstractPayClient<Config extends PayClientConfig> implemen
|
|||||||
|
|
||||||
protected abstract PayRefundRespDTO doUnifiedRefund(PayRefundUnifiedReqDTO reqDTO) throws Throwable;
|
protected abstract PayRefundRespDTO doUnifiedRefund(PayRefundUnifiedReqDTO reqDTO) throws Throwable;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public PayRefundRespDTO parseRefundNotify(Map<String, String> params, String body) {
|
||||||
|
try {
|
||||||
|
return doParseRefundNotify(params, body);
|
||||||
|
} catch (Throwable ex) {
|
||||||
|
log.error("[parseRefundNotify][客户端({}) params({}) body({}) 解析失败]",
|
||||||
|
getId(), params, body, ex);
|
||||||
|
throw buildPayException(ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected abstract PayRefundRespDTO doParseRefundNotify(Map<String, String> params, String body)
|
||||||
|
throws Throwable;
|
||||||
|
|
||||||
// ========== 各种工具方法 ==========
|
// ========== 各种工具方法 ==========
|
||||||
|
|
||||||
private PayException buildPayException(Throwable ex) {
|
private PayException buildPayException(Throwable ex) {
|
||||||
|
@ -29,7 +29,6 @@ import java.util.Objects;
|
|||||||
import java.util.function.Supplier;
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
import static cn.hutool.core.date.DatePattern.NORM_DATETIME_FORMATTER;
|
import static cn.hutool.core.date.DatePattern.NORM_DATETIME_FORMATTER;
|
||||||
import static cn.iocoder.yudao.framework.common.util.json.JsonUtils.toJsonString;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 支付宝抽象类,实现支付宝统一的接口、以及部分实现(退款)
|
* 支付宝抽象类,实现支付宝统一的接口、以及部分实现(退款)
|
||||||
@ -94,7 +93,7 @@ public abstract class AbstractAlipayPayClient extends AbstractPayClient<AlipayPa
|
|||||||
* @return 退款请求 Response
|
* @return 退款请求 Response
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
protected PayRefundRespDTO doUnifiedRefund(PayRefundUnifiedReqDTO reqDTO) {
|
protected PayRefundRespDTO doUnifiedRefund(PayRefundUnifiedReqDTO reqDTO) throws AlipayApiException {
|
||||||
// 1.1 构建 AlipayTradeRefundModel 请求
|
// 1.1 构建 AlipayTradeRefundModel 请求
|
||||||
AlipayTradeRefundModel model = new AlipayTradeRefundModel();
|
AlipayTradeRefundModel model = new AlipayTradeRefundModel();
|
||||||
model.setOutTradeNo(reqDTO.getOutTradeNo());
|
model.setOutTradeNo(reqDTO.getOutTradeNo());
|
||||||
@ -104,31 +103,22 @@ public abstract class AbstractAlipayPayClient extends AbstractPayClient<AlipayPa
|
|||||||
// 1.2 构建 AlipayTradePayRequest 请求
|
// 1.2 构建 AlipayTradePayRequest 请求
|
||||||
AlipayTradeRefundRequest request = new AlipayTradeRefundRequest();
|
AlipayTradeRefundRequest request = new AlipayTradeRefundRequest();
|
||||||
request.setBizModel(model);
|
request.setBizModel(model);
|
||||||
try {
|
|
||||||
// 2.1 执行请求
|
// 2.1 执行请求
|
||||||
AlipayTradeRefundResponse response = client.execute(request);
|
AlipayTradeRefundResponse response = client.execute(request);
|
||||||
// 2.2 创建返回结果
|
// 2.2 创建返回结果
|
||||||
PayRefundRespDTO refund = new PayRefundRespDTO()
|
// 支付宝只要退款调用返回 success,就认为退款成功,不需要回调。具体可见 parseNotify 方法的说明。
|
||||||
.setOutRefundNo(reqDTO.getOutRefundNo())
|
// 另外,支付宝没有退款单号,所以不用设置
|
||||||
.setRawData(response);
|
if (response.isSuccess()) {
|
||||||
// 支付宝只要退款调用返回 success,就认为退款成功,不需要回调。具体可见 parseNotify 方法的说明。
|
return PayRefundRespDTO.successOf(null, LocalDateTimeUtil.of(response.getGmtRefundPay()),
|
||||||
// 另外,支付宝没有退款单号,所以不用设置
|
reqDTO.getOutRefundNo(), response);
|
||||||
if (response.isSuccess()) {
|
} else {
|
||||||
refund.setStatus(PayOrderStatusRespEnum.SUCCESS.getStatus())
|
return PayRefundRespDTO.failureOf(reqDTO.getOutRefundNo(), response);
|
||||||
.setSuccessTime(LocalDateTimeUtil.of(response.getGmtRefundPay()));
|
|
||||||
Assert.notNull(refund.getSuccessTime(), "退款成功时间不能为空");
|
|
||||||
} else {
|
|
||||||
refund.setStatus(PayOrderStatusRespEnum.CLOSED.getStatus());
|
|
||||||
}
|
|
||||||
return refund;
|
|
||||||
} catch (AlipayApiException e) {
|
|
||||||
log.error("[doUnifiedRefund][request({}) 发起退款异常]", toJsonString(reqDTO), e);
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public PayRefundRespDTO parseRefundNotify(Map<String, String> params, String body) {
|
public PayRefundRespDTO doParseRefundNotify(Map<String, String> params, String body) {
|
||||||
// 补充说明:支付宝退款时,没有回调,这点和微信支付是不同的。并且,退款分成部分退款、和全部退款。
|
// 补充说明:支付宝退款时,没有回调,这点和微信支付是不同的。并且,退款分成部分退款、和全部退款。
|
||||||
// ① 部分退款:是会有回调,但是它回调的是订单状态的同步回调,不是退款订单的回调
|
// ① 部分退款:是会有回调,但是它回调的是订单状态的同步回调,不是退款订单的回调
|
||||||
// ② 全部退款:Wap 支付有订单状态的同步回调,但是 PC/扫码又没有
|
// ② 全部退款:Wap 支付有订单状态的同步回调,但是 PC/扫码又没有
|
||||||
|
@ -12,7 +12,6 @@ import cn.iocoder.yudao.framework.pay.core.client.dto.refund.PayRefundRespDTO;
|
|||||||
import cn.iocoder.yudao.framework.pay.core.client.dto.refund.PayRefundUnifiedReqDTO;
|
import cn.iocoder.yudao.framework.pay.core.client.dto.refund.PayRefundUnifiedReqDTO;
|
||||||
import cn.iocoder.yudao.framework.pay.core.client.impl.AbstractPayClient;
|
import cn.iocoder.yudao.framework.pay.core.client.impl.AbstractPayClient;
|
||||||
import cn.iocoder.yudao.framework.pay.core.enums.order.PayOrderStatusRespEnum;
|
import cn.iocoder.yudao.framework.pay.core.enums.order.PayOrderStatusRespEnum;
|
||||||
import cn.iocoder.yudao.framework.pay.core.enums.refund.PayRefundStatusRespEnum;
|
|
||||||
import com.github.binarywang.wxpay.bean.notify.WxPayOrderNotifyResult;
|
import com.github.binarywang.wxpay.bean.notify.WxPayOrderNotifyResult;
|
||||||
import com.github.binarywang.wxpay.bean.notify.WxPayOrderNotifyV3Result;
|
import com.github.binarywang.wxpay.bean.notify.WxPayOrderNotifyV3Result;
|
||||||
import com.github.binarywang.wxpay.bean.notify.WxPayRefundNotifyResult;
|
import com.github.binarywang.wxpay.bean.notify.WxPayRefundNotifyResult;
|
||||||
@ -163,9 +162,10 @@ public abstract class AbstractWxPayClient extends AbstractPayClient<WxPayClientC
|
|||||||
throw new IllegalArgumentException(String.format("未知的 API 版本(%s)", config.getApiVersion()));
|
throw new IllegalArgumentException(String.format("未知的 API 版本(%s)", config.getApiVersion()));
|
||||||
}
|
}
|
||||||
} catch (WxPayException e) {
|
} catch (WxPayException e) {
|
||||||
// todo 芋艿:异常的处理;
|
String errorCode = getErrorCode(e);
|
||||||
// throw buildUnifiedOrderException(null, e);
|
String errorMessage = getErrorMessage(e);
|
||||||
return null;
|
return PayRefundRespDTO.failureOf(errorCode, errorMessage,
|
||||||
|
reqDTO.getOutTradeNo(), e.getXmlString());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -181,17 +181,11 @@ public abstract class AbstractWxPayClient extends AbstractPayClient<WxPayClientC
|
|||||||
// 2.1 执行请求
|
// 2.1 执行请求
|
||||||
WxPayRefundResult response = client.refundV2(request);
|
WxPayRefundResult response = client.refundV2(request);
|
||||||
// 2.2 创建返回结果
|
// 2.2 创建返回结果
|
||||||
PayRefundRespDTO refund = new PayRefundRespDTO()
|
|
||||||
.setOutRefundNo(reqDTO.getOutRefundNo())
|
|
||||||
.setRawData(response);
|
|
||||||
if (Objects.equals("SUCCESS", response.getResultCode())) {
|
if (Objects.equals("SUCCESS", response.getResultCode())) {
|
||||||
refund.setStatus(PayRefundStatusRespEnum.WAITING.getStatus())
|
return PayRefundRespDTO.waitingOf(response.getRefundId(),
|
||||||
.setChannelRefundNo(response.getRefundId());
|
reqDTO.getOutRefundNo(), response);
|
||||||
} else {
|
|
||||||
refund.setStatus(PayRefundStatusRespEnum.FAILURE.getStatus());
|
|
||||||
}
|
}
|
||||||
// TODO 芋艿;异常的处理;
|
return PayRefundRespDTO.failureOf(reqDTO.getOutRefundNo(), response);
|
||||||
return refund;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private PayRefundRespDTO doUnifiedRefundV3(PayRefundUnifiedReqDTO reqDTO) throws Throwable {
|
private PayRefundRespDTO doUnifiedRefundV3(PayRefundUnifiedReqDTO reqDTO) throws Throwable {
|
||||||
@ -206,78 +200,51 @@ public abstract class AbstractWxPayClient extends AbstractPayClient<WxPayClientC
|
|||||||
// 2.1 执行请求
|
// 2.1 执行请求
|
||||||
WxPayRefundV3Result response = client.refundV3(request);
|
WxPayRefundV3Result response = client.refundV3(request);
|
||||||
// 2.2 创建返回结果
|
// 2.2 创建返回结果
|
||||||
PayRefundRespDTO refund = new PayRefundRespDTO()
|
|
||||||
.setOutRefundNo(reqDTO.getOutRefundNo())
|
|
||||||
.setRawData(response);
|
|
||||||
if (Objects.equals("SUCCESS", response.getStatus())) {
|
if (Objects.equals("SUCCESS", response.getStatus())) {
|
||||||
refund.setStatus(PayRefundStatusRespEnum.SUCCESS.getStatus())
|
return PayRefundRespDTO.successOf(response.getRefundId(), parseDateV3(response.getSuccessTime()),
|
||||||
.setChannelRefundNo(response.getRefundId())
|
reqDTO.getOutRefundNo(), response);
|
||||||
.setSuccessTime(parseDateV3(response.getSuccessTime()));
|
|
||||||
} else if (Objects.equals("PROCESSING", response.getStatus())) {
|
|
||||||
refund.setStatus(PayRefundStatusRespEnum.WAITING.getStatus())
|
|
||||||
.setChannelRefundNo(response.getRefundId());
|
|
||||||
} else {
|
|
||||||
refund.setStatus(PayRefundStatusRespEnum.FAILURE.getStatus());
|
|
||||||
}
|
}
|
||||||
// TODO 芋艿;异常的处理;
|
if (Objects.equals("PROCESSING", response.getStatus())) {
|
||||||
return refund;
|
return PayRefundRespDTO.waitingOf(response.getRefundId(),
|
||||||
|
reqDTO.getOutRefundNo(), response);
|
||||||
|
}
|
||||||
|
return PayRefundRespDTO.failureOf(reqDTO.getOutRefundNo(), response);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public PayRefundRespDTO parseRefundNotify(Map<String, String> params, String body) {
|
public PayRefundRespDTO doParseRefundNotify(Map<String, String> params, String body) throws WxPayException {
|
||||||
try {
|
switch (config.getApiVersion()) {
|
||||||
// 微信支付 v2 回调结果处理
|
case API_VERSION_V2:
|
||||||
switch (config.getApiVersion()) {
|
return doParseRefundNotifyV2(body);
|
||||||
case API_VERSION_V2:
|
case WxPayClientConfig.API_VERSION_V3:
|
||||||
return parseRefundNotifyV2(body);
|
return parseRefundNotifyV3(body);
|
||||||
case WxPayClientConfig.API_VERSION_V3:
|
default:
|
||||||
return parseRefundNotifyV3(body);
|
throw new IllegalArgumentException(String.format("未知的 API 版本(%s)", config.getApiVersion()));
|
||||||
default:
|
|
||||||
throw new IllegalArgumentException(String.format("未知的 API 版本(%s)", config.getApiVersion()));
|
|
||||||
}
|
|
||||||
} catch (WxPayException e) {
|
|
||||||
log.error("[parseNotify][params({}) body({}) 解析失败]", params, body, e);
|
|
||||||
throw new RuntimeException(e);
|
|
||||||
// TODO 芋艿:缺一个异常翻译
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("DuplicatedCode")
|
private PayRefundRespDTO doParseRefundNotifyV2(String body) throws WxPayException {
|
||||||
private PayRefundRespDTO parseRefundNotifyV2(String body) throws WxPayException {
|
|
||||||
// 1. 解析回调
|
// 1. 解析回调
|
||||||
WxPayRefundNotifyResult response = client.parseRefundNotifyResult(body);
|
WxPayRefundNotifyResult response = client.parseRefundNotifyResult(body);
|
||||||
WxPayRefundNotifyResult.ReqInfo responseResult = response.getReqInfo();
|
WxPayRefundNotifyResult.ReqInfo result = response.getReqInfo();
|
||||||
// 2. 构建结果
|
// 2. 构建结果
|
||||||
PayRefundRespDTO notify = new PayRefundRespDTO()
|
if (Objects.equals("SUCCESS", result.getRefundStatus())) {
|
||||||
.setChannelRefundNo(responseResult.getRefundId())
|
return PayRefundRespDTO.successOf(result.getRefundId(), parseDateV2B(result.getSuccessTime()),
|
||||||
.setOutRefundNo(responseResult.getOutRefundNo())
|
result.getOutRefundNo(), response);
|
||||||
.setRawData(response);
|
|
||||||
if (Objects.equals("SUCCESS", responseResult.getRefundStatus())) {
|
|
||||||
notify.setStatus(PayRefundStatusRespEnum.SUCCESS.getStatus())
|
|
||||||
.setSuccessTime(parseDateV2B(responseResult.getSuccessTime()));
|
|
||||||
} else {
|
|
||||||
notify.setStatus(PayRefundStatusRespEnum.FAILURE.getStatus());
|
|
||||||
}
|
}
|
||||||
return notify;
|
return PayRefundRespDTO.failureOf(result.getOutRefundNo(), response);
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("DuplicatedCode")
|
|
||||||
private PayRefundRespDTO parseRefundNotifyV3(String body) throws WxPayException {
|
private PayRefundRespDTO parseRefundNotifyV3(String body) throws WxPayException {
|
||||||
// 1. 解析回调
|
// 1. 解析回调
|
||||||
WxPayRefundNotifyV3Result response = client.parseRefundNotifyV3Result(body, null);
|
WxPayRefundNotifyV3Result response = client.parseRefundNotifyV3Result(body, null);
|
||||||
WxPayRefundNotifyV3Result.DecryptNotifyResult responseResult = response.getResult();
|
WxPayRefundNotifyV3Result.DecryptNotifyResult result = response.getResult();
|
||||||
// 2. 构建结果
|
// 2. 构建结果
|
||||||
PayRefundRespDTO notify = new PayRefundRespDTO()
|
if (Objects.equals("SUCCESS", result.getRefundStatus())) {
|
||||||
.setChannelRefundNo(responseResult.getRefundId())
|
return PayRefundRespDTO.successOf(result.getRefundId(), parseDateV3(result.getSuccessTime()),
|
||||||
.setOutRefundNo(responseResult.getOutRefundNo())
|
result.getOutRefundNo(), response);
|
||||||
.setRawData(response);
|
|
||||||
if (Objects.equals("SUCCESS", responseResult.getRefundStatus())) {
|
|
||||||
notify.setStatus(PayRefundStatusRespEnum.SUCCESS.getStatus())
|
|
||||||
.setSuccessTime(parseDateV3(responseResult.getSuccessTime()));
|
|
||||||
} else {
|
|
||||||
notify.setStatus(PayRefundStatusRespEnum.FAILURE.getStatus());
|
|
||||||
}
|
}
|
||||||
return notify;
|
return PayRefundRespDTO.failureOf(result.getOutRefundNo(), response);
|
||||||
}
|
}
|
||||||
|
|
||||||
// ========== 各种工具方法 ==========
|
// ========== 各种工具方法 ==========
|
||||||
|
Loading…
Reference in New Issue
Block a user