mall + pay:

1. 增加支付宝 Client 的查询订单接口
This commit is contained in:
YunaiV 2023-07-22 11:19:42 +08:00
parent b426c63eeb
commit f46a037164
6 changed files with 91 additions and 32 deletions

View File

@ -27,7 +27,7 @@ public interface PayClient {
* 调用支付渠道统一下单 * 调用支付渠道统一下单
* *
* @param reqDTO 下单信息 * @param reqDTO 下单信息
* @return 各支付渠道的返回结果 * @return 支付订单信息
*/ */
PayOrderRespDTO unifiedOrder(PayOrderUnifiedReqDTO reqDTO); PayOrderRespDTO unifiedOrder(PayOrderUnifiedReqDTO reqDTO);
@ -40,6 +40,14 @@ public interface PayClient {
*/ */
PayOrderRespDTO parseOrderNotify(Map<String, String> params, String body); PayOrderRespDTO parseOrderNotify(Map<String, String> params, String body);
/**
* 获得支付订单信息
*
* @param outTradeNo 外部订单号
* @return 支付订单信息
*/
PayOrderRespDTO getOrder(String outTradeNo);
// ============ 退款相关 ========== // ============ 退款相关 ==========
/** /**

View File

@ -94,38 +94,40 @@ public class PayOrderRespDTO {
/** /**
* 创建SUCCESS状态的订单返回 * 创建SUCCESS状态的订单返回
*/ */
public PayOrderRespDTO(String channelOrderNo, String channelUserId, LocalDateTime successTime, public static PayOrderRespDTO successOf(String channelOrderNo, String channelUserId, LocalDateTime successTime,
String outTradeNo, Object rawData) { String outTradeNo, Object rawData) {
this.status = PayOrderStatusRespEnum.SUCCESS.getStatus(); PayOrderRespDTO respDTO = new PayOrderRespDTO();
this.channelOrderNo = channelOrderNo; respDTO.status = PayOrderStatusRespEnum.SUCCESS.getStatus();
this.channelUserId = channelUserId; respDTO.channelOrderNo = channelOrderNo;
this.successTime = successTime; respDTO.channelUserId = channelUserId;
respDTO.successTime = successTime;
// 相对通用的字段 // 相对通用的字段
this.outTradeNo = outTradeNo; respDTO.outTradeNo = outTradeNo;
this.rawData = rawData; respDTO.rawData = rawData;
return respDTO;
} }
/** /**
* 创建SUCCESSCLOSED状态的订单返回适合支付渠道回调时 * 创建指定状态的订单返回适合支付渠道回调时
*/ */
public PayOrderRespDTO(Integer status, String channelOrderNo, String channelUserId, LocalDateTime successTime, public static PayOrderRespDTO of(Integer status, String channelOrderNo, String channelUserId, LocalDateTime successTime,
String outTradeNo, Object rawData) { String outTradeNo, Object rawData) {
this.status = status; PayOrderRespDTO respDTO = new PayOrderRespDTO();
this.channelOrderNo = channelOrderNo; respDTO.status = status;
this.channelUserId = channelUserId; respDTO.channelOrderNo = channelOrderNo;
this.successTime = successTime; respDTO.channelUserId = channelUserId;
respDTO.successTime = successTime;
// 相对通用的字段 // 相对通用的字段
this.outTradeNo = outTradeNo; respDTO.outTradeNo = outTradeNo;
this.rawData = rawData; respDTO.rawData = rawData;
return respDTO;
} }
/** /**
* 创建CLOSED状态的订单返回适合调用支付渠道失败时 * 创建CLOSED状态的订单返回适合调用支付渠道失败时
*
* 参数和 {@link #PayOrderRespDTO(String, String, String, Object)} 冲突所以独立个方法出来
*/ */
public static PayOrderRespDTO build(String channelErrorCode, String channelErrorMsg, public static PayOrderRespDTO closedOf(String channelErrorCode, String channelErrorMsg,
String outTradeNo, Object rawData) { String outTradeNo, Object rawData) {
PayOrderRespDTO respDTO = new PayOrderRespDTO(); PayOrderRespDTO respDTO = new PayOrderRespDTO();
respDTO.status = PayOrderStatusRespEnum.CLOSED.getStatus(); respDTO.status = PayOrderStatusRespEnum.CLOSED.getStatus();
respDTO.channelErrorCode = channelErrorCode; respDTO.channelErrorCode = channelErrorCode;

View File

@ -107,6 +107,20 @@ public abstract class AbstractPayClient<Config extends PayClientConfig> implemen
protected abstract PayOrderRespDTO doParseOrderNotify(Map<String, String> params, String body) protected abstract PayOrderRespDTO doParseOrderNotify(Map<String, String> params, String body)
throws Throwable; throws Throwable;
@Override
public PayOrderRespDTO getOrder(String outTradeNo) {
try {
return doGetOrder(outTradeNo);
} catch (Throwable ex) {
log.error("[getOrder][客户端({}) outTradeNo({}) 查询支付单异常]",
getId(), outTradeNo, ex);
throw buildPayException(ex);
}
}
protected abstract PayOrderRespDTO doGetOrder(String outTradeNo)
throws Throwable;
// ============ 退款相关 ========== // ============ 退款相关 ==========
@Override @Override

View File

@ -17,9 +17,12 @@ import com.alipay.api.AlipayApiException;
import com.alipay.api.AlipayConfig; import com.alipay.api.AlipayConfig;
import com.alipay.api.AlipayResponse; import com.alipay.api.AlipayResponse;
import com.alipay.api.DefaultAlipayClient; import com.alipay.api.DefaultAlipayClient;
import com.alipay.api.domain.AlipayTradeQueryModel;
import com.alipay.api.domain.AlipayTradeRefundModel; import com.alipay.api.domain.AlipayTradeRefundModel;
import com.alipay.api.internal.util.AlipaySignature; import com.alipay.api.internal.util.AlipaySignature;
import com.alipay.api.request.AlipayTradeQueryRequest;
import com.alipay.api.request.AlipayTradeRefundRequest; import com.alipay.api.request.AlipayTradeRefundRequest;
import com.alipay.api.response.AlipayTradeQueryResponse;
import com.alipay.api.response.AlipayTradeRefundResponse; import com.alipay.api.response.AlipayTradeRefundResponse;
import lombok.SneakyThrows; import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
@ -63,7 +66,7 @@ public abstract class AbstractAlipayPayClient extends AbstractPayClient<AlipayPa
*/ */
protected PayOrderRespDTO buildClosedPayOrderRespDTO(PayOrderUnifiedReqDTO reqDTO, AlipayResponse response) { protected PayOrderRespDTO buildClosedPayOrderRespDTO(PayOrderUnifiedReqDTO reqDTO, AlipayResponse response) {
Assert.isFalse(response.isSuccess()); Assert.isFalse(response.isSuccess());
return PayOrderRespDTO.build(response.getSubCode(), response.getSubMsg(), return PayOrderRespDTO.closedOf(response.getSubCode(), response.getSubMsg(),
reqDTO.getOutTradeNo(), response); reqDTO.getOutTradeNo(), response);
} }
@ -76,10 +79,7 @@ public abstract class AbstractAlipayPayClient extends AbstractPayClient<AlipayPa
// 2. 解析订单的状态 // 2. 解析订单的状态
// 额外说明支付宝不仅仅支付成功会回调再各种触发支付单数据变化时都会进行回调所以这里 status 的解析会写的比较复杂 // 额外说明支付宝不仅仅支付成功会回调再各种触发支付单数据变化时都会进行回调所以这里 status 的解析会写的比较复杂
String tradeStatus = bodyObj.get("trade_status"); Integer status = parseStatus(bodyObj.get("trade_status"));
Integer status = Objects.equals("WAIT_BUYER_PAY", tradeStatus) ? PayOrderStatusRespEnum.WAITING.getStatus()
: ObjectUtils.equalsAny(tradeStatus, "TRADE_FINISHED", "TRADE_SUCCESS") ? PayOrderStatusRespEnum.SUCCESS.getStatus()
: Objects.equals("TRADE_CLOSED", tradeStatus) ? PayOrderStatusRespEnum.CLOSED.getStatus() : null;
// 特殊逻辑: 支付宝没有退款成功的状态所以如果有退款金额我们认为是退款成功 // 特殊逻辑: 支付宝没有退款成功的状态所以如果有退款金额我们认为是退款成功
if (MapUtil.getDouble(bodyObj, "refund_fee", 0D) > 0) { if (MapUtil.getDouble(bodyObj, "refund_fee", 0D) > 0) {
status = PayOrderStatusRespEnum.REFUND.getStatus(); status = PayOrderStatusRespEnum.REFUND.getStatus();
@ -87,10 +87,40 @@ public abstract class AbstractAlipayPayClient extends AbstractPayClient<AlipayPa
Assert.notNull(status, (Supplier<Throwable>) () -> { Assert.notNull(status, (Supplier<Throwable>) () -> {
throw new IllegalArgumentException(StrUtil.format("body({}) 的 trade_status 不正确", body)); throw new IllegalArgumentException(StrUtil.format("body({}) 的 trade_status 不正确", body));
}); });
return new PayOrderRespDTO(status, bodyObj.get("trade_no"), bodyObj.get("seller_id"), parseTime(params.get("gmt_payment")), return PayOrderRespDTO.of(status, bodyObj.get("trade_no"), bodyObj.get("seller_id"), parseTime(params.get("gmt_payment")),
bodyObj.get("out_trade_no"), body); bodyObj.get("out_trade_no"), body);
} }
@Override
protected PayOrderRespDTO doGetOrder(String outTradeNo) throws Throwable {
// 1.1 构建 AlipayTradeRefundModel 请求
AlipayTradeQueryModel model = new AlipayTradeQueryModel();
model.setOutTradeNo(outTradeNo);
// 1.2 构建 AlipayTradeQueryRequest 请求
AlipayTradeQueryRequest request = new AlipayTradeQueryRequest();
request.setBizModel(model);
// 2.1 执行请求
AlipayTradeQueryResponse response = client.execute(request);
if (!response.isSuccess()) {
return PayOrderRespDTO.closedOf(response.getSubCode(), response.getSubMsg(),
outTradeNo, response);
}
// 2.2 解析订单的状态
Integer status = parseStatus(response.getTradeStatus());
Assert.notNull(status, (Supplier<Throwable>) () -> {
throw new IllegalArgumentException(StrUtil.format("body({}) 的 trade_status 不正确", response.getBody()));
});
return PayOrderRespDTO.of(status, response.getTradeNo(), response.getBuyerUserId(), LocalDateTimeUtil.of(response.getSendPayDate()),
outTradeNo, response);
}
private Integer parseStatus(String tradeStatus) {
return Objects.equals("WAIT_BUYER_PAY", tradeStatus) ? PayOrderStatusRespEnum.WAITING.getStatus()
: ObjectUtils.equalsAny(tradeStatus, "TRADE_FINISHED", "TRADE_SUCCESS") ? PayOrderStatusRespEnum.SUCCESS.getStatus()
: Objects.equals("TRADE_CLOSED", tradeStatus) ? PayOrderStatusRespEnum.CLOSED.getStatus() : null;
}
// ============ 退款相关 ========== // ============ 退款相关 ==========
/** /**

View File

@ -90,7 +90,7 @@ public abstract class AbstractWxPayClient extends AbstractPayClient<WxPayClientC
} catch (WxPayException e) { } catch (WxPayException e) {
String errorCode = getErrorCode(e); String errorCode = getErrorCode(e);
String errorMessage = getErrorMessage(e); String errorMessage = getErrorMessage(e);
return PayOrderRespDTO.build(errorCode, errorMessage, return PayOrderRespDTO.closedOf(errorCode, errorMessage,
reqDTO.getOutTradeNo(), e.getXmlString()); reqDTO.getOutTradeNo(), e.getXmlString());
} }
} }
@ -133,7 +133,7 @@ public abstract class AbstractWxPayClient extends AbstractPayClient<WxPayClientC
// 微信支付的回调只有 SUCCESS 支付成功CLOSED 支付失败两种情况无需像支付宝一样解析的比较复杂 // 微信支付的回调只有 SUCCESS 支付成功CLOSED 支付失败两种情况无需像支付宝一样解析的比较复杂
Integer status = Objects.equals(response.getResultCode(), "SUCCESS") ? Integer status = Objects.equals(response.getResultCode(), "SUCCESS") ?
PayOrderStatusRespEnum.SUCCESS.getStatus() : PayOrderStatusRespEnum.CLOSED.getStatus(); PayOrderStatusRespEnum.SUCCESS.getStatus() : PayOrderStatusRespEnum.CLOSED.getStatus();
return new PayOrderRespDTO(status, response.getTransactionId(), response.getOpenid(), parseDateV2(response.getTimeEnd()), return PayOrderRespDTO.of(status, response.getTransactionId(), response.getOpenid(), parseDateV2(response.getTimeEnd()),
response.getOutTradeNo(), body); response.getOutTradeNo(), body);
} }
@ -146,10 +146,15 @@ public abstract class AbstractWxPayClient extends AbstractPayClient<WxPayClientC
Integer status = Objects.equals(result.getTradeState(), "SUCCESS") ? Integer status = Objects.equals(result.getTradeState(), "SUCCESS") ?
PayOrderStatusRespEnum.SUCCESS.getStatus() : PayOrderStatusRespEnum.CLOSED.getStatus(); PayOrderStatusRespEnum.SUCCESS.getStatus() : PayOrderStatusRespEnum.CLOSED.getStatus();
String openid = result.getPayer() != null ? result.getPayer().getOpenid() : null; String openid = result.getPayer() != null ? result.getPayer().getOpenid() : null;
return new PayOrderRespDTO(status, result.getTransactionId(), openid, parseDateV3(result.getSuccessTime()), return PayOrderRespDTO.of(status, result.getTransactionId(), openid, parseDateV3(result.getSuccessTime()),
result.getOutTradeNo(), body); result.getOutTradeNo(), body);
} }
@Override
protected PayOrderRespDTO doGetOrder(String outTradeNo) throws Throwable {
return null;
}
// ============ 退款相关 ========== // ============ 退款相关 ==========
@Override @Override

View File

@ -68,7 +68,7 @@ public class WxBarPayClient extends AbstractWxPayClient {
try { try {
WxPayMicropayResult response = client.micropay(request); WxPayMicropayResult response = client.micropay(request);
// 支付成功例如说1用户输入了密码2用户免密支付 // 支付成功例如说1用户输入了密码2用户免密支付
return new PayOrderRespDTO(response.getTransactionId(), response.getOpenid(), parseDateV2(response.getTimeEnd()), return PayOrderRespDTO.successOf(response.getTransactionId(), response.getOpenid(), parseDateV2(response.getTimeEnd()),
response.getOutTradeNo(), response) response.getOutTradeNo(), response)
.setDisplayMode(PayOrderDisplayModeEnum.BAR_CODE.getMode()); .setDisplayMode(PayOrderDisplayModeEnum.BAR_CODE.getMode());
} catch (WxPayException ex) { } catch (WxPayException ex) {