Commit f46a0371640d4ceb14e361a2284c9b51455c2285

Authored by YunaiV
1 parent b426c63e

mall + pay:

1. 增加支付宝 Client 的查询订单接口
yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/PayClient.java
... ... @@ -27,7 +27,7 @@ public interface PayClient {
27 27 * 调用支付渠道,统一下单
28 28 *
29 29 * @param reqDTO 下单信息
30   - * @return 各支付渠道的返回结果
  30 + * @return 支付订单信息
31 31 */
32 32 PayOrderRespDTO unifiedOrder(PayOrderUnifiedReqDTO reqDTO);
33 33  
... ... @@ -40,6 +40,14 @@ public interface PayClient {
40 40 */
41 41 PayOrderRespDTO parseOrderNotify(Map<String, String> params, String body);
42 42  
  43 + /**
  44 + * 获得支付订单信息
  45 + *
  46 + * @param outTradeNo 外部订单号
  47 + * @return 支付订单信息
  48 + */
  49 + PayOrderRespDTO getOrder(String outTradeNo);
  50 +
43 51 // ============ 退款相关 ==========
44 52  
45 53 /**
... ...
yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/dto/order/PayOrderRespDTO.java
... ... @@ -94,38 +94,40 @@ public class PayOrderRespDTO {
94 94 /**
95 95 * 创建【SUCCESS】状态的订单返回
96 96 */
97   - public PayOrderRespDTO(String channelOrderNo, String channelUserId, LocalDateTime successTime,
98   - String outTradeNo, Object rawData) {
99   - this.status = PayOrderStatusRespEnum.SUCCESS.getStatus();
100   - this.channelOrderNo = channelOrderNo;
101   - this.channelUserId = channelUserId;
102   - this.successTime = successTime;
  97 + public static PayOrderRespDTO successOf(String channelOrderNo, String channelUserId, LocalDateTime successTime,
  98 + String outTradeNo, Object rawData) {
  99 + PayOrderRespDTO respDTO = new PayOrderRespDTO();
  100 + respDTO.status = PayOrderStatusRespEnum.SUCCESS.getStatus();
  101 + respDTO.channelOrderNo = channelOrderNo;
  102 + respDTO.channelUserId = channelUserId;
  103 + respDTO.successTime = successTime;
103 104 // 相对通用的字段
104   - this.outTradeNo = outTradeNo;
105   - this.rawData = rawData;
  105 + respDTO.outTradeNo = outTradeNo;
  106 + respDTO.rawData = rawData;
  107 + return respDTO;
106 108 }
107 109  
108 110 /**
109   - * 创建【SUCCESS】或【CLOSED】状态的订单返回,适合支付渠道回调时
  111 + * 创建指定状态的订单返回,适合支付渠道回调时
110 112 */
111   - public PayOrderRespDTO(Integer status, String channelOrderNo, String channelUserId, LocalDateTime successTime,
112   - String outTradeNo, Object rawData) {
113   - this.status = status;
114   - this.channelOrderNo = channelOrderNo;
115   - this.channelUserId = channelUserId;
116   - this.successTime = successTime;
  113 + public static PayOrderRespDTO of(Integer status, String channelOrderNo, String channelUserId, LocalDateTime successTime,
  114 + String outTradeNo, Object rawData) {
  115 + PayOrderRespDTO respDTO = new PayOrderRespDTO();
  116 + respDTO.status = status;
  117 + respDTO.channelOrderNo = channelOrderNo;
  118 + respDTO.channelUserId = channelUserId;
  119 + respDTO.successTime = successTime;
117 120 // 相对通用的字段
118   - this.outTradeNo = outTradeNo;
119   - this.rawData = rawData;
  121 + respDTO.outTradeNo = outTradeNo;
  122 + respDTO.rawData = rawData;
  123 + return respDTO;
120 124 }
121 125  
122 126 /**
123 127 * 创建【CLOSED】状态的订单返回,适合调用支付渠道失败时
124   - *
125   - * 参数和 {@link #PayOrderRespDTO(String, String, String, Object)} 冲突,所以独立个方法出来
126 128 */
127   - public static PayOrderRespDTO build(String channelErrorCode, String channelErrorMsg,
128   - String outTradeNo, Object rawData) {
  129 + public static PayOrderRespDTO closedOf(String channelErrorCode, String channelErrorMsg,
  130 + String outTradeNo, Object rawData) {
129 131 PayOrderRespDTO respDTO = new PayOrderRespDTO();
130 132 respDTO.status = PayOrderStatusRespEnum.CLOSED.getStatus();
131 133 respDTO.channelErrorCode = channelErrorCode;
... ...
yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/AbstractPayClient.java
... ... @@ -107,6 +107,20 @@ public abstract class AbstractPayClient&lt;Config extends PayClientConfig&gt; implemen
107 107 protected abstract PayOrderRespDTO doParseOrderNotify(Map<String, String> params, String body)
108 108 throws Throwable;
109 109  
  110 + @Override
  111 + public PayOrderRespDTO getOrder(String outTradeNo) {
  112 + try {
  113 + return doGetOrder(outTradeNo);
  114 + } catch (Throwable ex) {
  115 + log.error("[getOrder][客户端({}) outTradeNo({}) 查询支付单异常]",
  116 + getId(), outTradeNo, ex);
  117 + throw buildPayException(ex);
  118 + }
  119 + }
  120 +
  121 + protected abstract PayOrderRespDTO doGetOrder(String outTradeNo)
  122 + throws Throwable;
  123 +
110 124 // ============ 退款相关 ==========
111 125  
112 126 @Override
... ...
yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/alipay/AbstractAlipayPayClient.java
... ... @@ -17,9 +17,12 @@ import com.alipay.api.AlipayApiException;
17 17 import com.alipay.api.AlipayConfig;
18 18 import com.alipay.api.AlipayResponse;
19 19 import com.alipay.api.DefaultAlipayClient;
  20 +import com.alipay.api.domain.AlipayTradeQueryModel;
20 21 import com.alipay.api.domain.AlipayTradeRefundModel;
21 22 import com.alipay.api.internal.util.AlipaySignature;
  23 +import com.alipay.api.request.AlipayTradeQueryRequest;
22 24 import com.alipay.api.request.AlipayTradeRefundRequest;
  25 +import com.alipay.api.response.AlipayTradeQueryResponse;
23 26 import com.alipay.api.response.AlipayTradeRefundResponse;
24 27 import lombok.SneakyThrows;
25 28 import lombok.extern.slf4j.Slf4j;
... ... @@ -63,7 +66,7 @@ public abstract class AbstractAlipayPayClient extends AbstractPayClient&lt;AlipayPa
63 66 */
64 67 protected PayOrderRespDTO buildClosedPayOrderRespDTO(PayOrderUnifiedReqDTO reqDTO, AlipayResponse response) {
65 68 Assert.isFalse(response.isSuccess());
66   - return PayOrderRespDTO.build(response.getSubCode(), response.getSubMsg(),
  69 + return PayOrderRespDTO.closedOf(response.getSubCode(), response.getSubMsg(),
67 70 reqDTO.getOutTradeNo(), response);
68 71 }
69 72  
... ... @@ -76,10 +79,7 @@ public abstract class AbstractAlipayPayClient extends AbstractPayClient&lt;AlipayPa
76 79  
77 80 // 2. 解析订单的状态
78 81 // 额外说明:支付宝不仅仅支付成功会回调,再各种触发支付单数据变化时,都会进行回调,所以这里 status 的解析会写的比较复杂
79   - String tradeStatus = bodyObj.get("trade_status");
80   - Integer status = Objects.equals("WAIT_BUYER_PAY", tradeStatus) ? PayOrderStatusRespEnum.WAITING.getStatus()
81   - : ObjectUtils.equalsAny(tradeStatus, "TRADE_FINISHED", "TRADE_SUCCESS") ? PayOrderStatusRespEnum.SUCCESS.getStatus()
82   - : Objects.equals("TRADE_CLOSED", tradeStatus) ? PayOrderStatusRespEnum.CLOSED.getStatus() : null;
  82 + Integer status = parseStatus(bodyObj.get("trade_status"));
83 83 // 特殊逻辑: 支付宝没有退款成功的状态,所以,如果有退款金额,我们认为是退款成功
84 84 if (MapUtil.getDouble(bodyObj, "refund_fee", 0D) > 0) {
85 85 status = PayOrderStatusRespEnum.REFUND.getStatus();
... ... @@ -87,10 +87,40 @@ public abstract class AbstractAlipayPayClient extends AbstractPayClient&lt;AlipayPa
87 87 Assert.notNull(status, (Supplier<Throwable>) () -> {
88 88 throw new IllegalArgumentException(StrUtil.format("body({}) 的 trade_status 不正确", body));
89 89 });
90   - return new PayOrderRespDTO(status, bodyObj.get("trade_no"), bodyObj.get("seller_id"), parseTime(params.get("gmt_payment")),
  90 + return PayOrderRespDTO.of(status, bodyObj.get("trade_no"), bodyObj.get("seller_id"), parseTime(params.get("gmt_payment")),
91 91 bodyObj.get("out_trade_no"), body);
92 92 }
93 93  
  94 + @Override
  95 + protected PayOrderRespDTO doGetOrder(String outTradeNo) throws Throwable {
  96 + // 1.1 构建 AlipayTradeRefundModel 请求
  97 + AlipayTradeQueryModel model = new AlipayTradeQueryModel();
  98 + model.setOutTradeNo(outTradeNo);
  99 + // 1.2 构建 AlipayTradeQueryRequest 请求
  100 + AlipayTradeQueryRequest request = new AlipayTradeQueryRequest();
  101 + request.setBizModel(model);
  102 +
  103 + // 2.1 执行请求
  104 + AlipayTradeQueryResponse response = client.execute(request);
  105 + if (!response.isSuccess()) {
  106 + return PayOrderRespDTO.closedOf(response.getSubCode(), response.getSubMsg(),
  107 + outTradeNo, response);
  108 + }
  109 + // 2.2 解析订单的状态
  110 + Integer status = parseStatus(response.getTradeStatus());
  111 + Assert.notNull(status, (Supplier<Throwable>) () -> {
  112 + throw new IllegalArgumentException(StrUtil.format("body({}) 的 trade_status 不正确", response.getBody()));
  113 + });
  114 + return PayOrderRespDTO.of(status, response.getTradeNo(), response.getBuyerUserId(), LocalDateTimeUtil.of(response.getSendPayDate()),
  115 + outTradeNo, response);
  116 + }
  117 +
  118 + private Integer parseStatus(String tradeStatus) {
  119 + return Objects.equals("WAIT_BUYER_PAY", tradeStatus) ? PayOrderStatusRespEnum.WAITING.getStatus()
  120 + : ObjectUtils.equalsAny(tradeStatus, "TRADE_FINISHED", "TRADE_SUCCESS") ? PayOrderStatusRespEnum.SUCCESS.getStatus()
  121 + : Objects.equals("TRADE_CLOSED", tradeStatus) ? PayOrderStatusRespEnum.CLOSED.getStatus() : null;
  122 + }
  123 +
94 124 // ============ 退款相关 ==========
95 125  
96 126 /**
... ...
yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/weixin/AbstractWxPayClient.java
... ... @@ -90,7 +90,7 @@ public abstract class AbstractWxPayClient extends AbstractPayClient&lt;WxPayClientC
90 90 } catch (WxPayException e) {
91 91 String errorCode = getErrorCode(e);
92 92 String errorMessage = getErrorMessage(e);
93   - return PayOrderRespDTO.build(errorCode, errorMessage,
  93 + return PayOrderRespDTO.closedOf(errorCode, errorMessage,
94 94 reqDTO.getOutTradeNo(), e.getXmlString());
95 95 }
96 96 }
... ... @@ -133,7 +133,7 @@ public abstract class AbstractWxPayClient extends AbstractPayClient&lt;WxPayClientC
133 133 // 微信支付的回调,只有 SUCCESS 支付成功、CLOSED 支付失败两种情况,无需像支付宝一样解析的比较复杂
134 134 Integer status = Objects.equals(response.getResultCode(), "SUCCESS") ?
135 135 PayOrderStatusRespEnum.SUCCESS.getStatus() : PayOrderStatusRespEnum.CLOSED.getStatus();
136   - return new PayOrderRespDTO(status, response.getTransactionId(), response.getOpenid(), parseDateV2(response.getTimeEnd()),
  136 + return PayOrderRespDTO.of(status, response.getTransactionId(), response.getOpenid(), parseDateV2(response.getTimeEnd()),
137 137 response.getOutTradeNo(), body);
138 138 }
139 139  
... ... @@ -146,10 +146,15 @@ public abstract class AbstractWxPayClient extends AbstractPayClient&lt;WxPayClientC
146 146 Integer status = Objects.equals(result.getTradeState(), "SUCCESS") ?
147 147 PayOrderStatusRespEnum.SUCCESS.getStatus() : PayOrderStatusRespEnum.CLOSED.getStatus();
148 148 String openid = result.getPayer() != null ? result.getPayer().getOpenid() : null;
149   - return new PayOrderRespDTO(status, result.getTransactionId(), openid, parseDateV3(result.getSuccessTime()),
  149 + return PayOrderRespDTO.of(status, result.getTransactionId(), openid, parseDateV3(result.getSuccessTime()),
150 150 result.getOutTradeNo(), body);
151 151 }
152 152  
  153 + @Override
  154 + protected PayOrderRespDTO doGetOrder(String outTradeNo) throws Throwable {
  155 + return null;
  156 + }
  157 +
153 158 // ============ 退款相关 ==========
154 159  
155 160 @Override
... ...
yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/weixin/WxBarPayClient.java
... ... @@ -68,7 +68,7 @@ public class WxBarPayClient extends AbstractWxPayClient {
68 68 try {
69 69 WxPayMicropayResult response = client.micropay(request);
70 70 // 支付成功,例如说:1)用户输入了密码;2)用户免密支付
71   - return new PayOrderRespDTO(response.getTransactionId(), response.getOpenid(), parseDateV2(response.getTimeEnd()),
  71 + return PayOrderRespDTO.successOf(response.getTransactionId(), response.getOpenid(), parseDateV2(response.getTimeEnd()),
72 72 response.getOutTradeNo(), response)
73 73 .setDisplayMode(PayOrderDisplayModeEnum.BAR_CODE.getMode());
74 74 } catch (WxPayException ex) {
... ...