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,7 +27,7 @@ public interface PayClient {
27 * 调用支付渠道,统一下单 27 * 调用支付渠道,统一下单
28 * 28 *
29 * @param reqDTO 下单信息 29 * @param reqDTO 下单信息
30 - * @return 各支付渠道的返回结果 30 + * @return 支付订单信息
31 */ 31 */
32 PayOrderRespDTO unifiedOrder(PayOrderUnifiedReqDTO reqDTO); 32 PayOrderRespDTO unifiedOrder(PayOrderUnifiedReqDTO reqDTO);
33 33
@@ -40,6 +40,14 @@ public interface PayClient { @@ -40,6 +40,14 @@ public interface PayClient {
40 */ 40 */
41 PayOrderRespDTO parseOrderNotify(Map<String, String> params, String body); 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,38 +94,40 @@ public class PayOrderRespDTO {
94 /** 94 /**
95 * 创建【SUCCESS】状态的订单返回 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 * 创建【CLOSED】状态的订单返回,适合调用支付渠道失败时 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 PayOrderRespDTO respDTO = new PayOrderRespDTO(); 131 PayOrderRespDTO respDTO = new PayOrderRespDTO();
130 respDTO.status = PayOrderStatusRespEnum.CLOSED.getStatus(); 132 respDTO.status = PayOrderStatusRespEnum.CLOSED.getStatus();
131 respDTO.channelErrorCode = channelErrorCode; 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,6 +107,20 @@ public abstract class AbstractPayClient&lt;Config extends PayClientConfig&gt; implemen
107 protected abstract PayOrderRespDTO doParseOrderNotify(Map<String, String> params, String body) 107 protected abstract PayOrderRespDTO doParseOrderNotify(Map<String, String> params, String body)
108 throws Throwable; 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 @Override 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,9 +17,12 @@ 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.AlipayTradeQueryModel;
20 import com.alipay.api.domain.AlipayTradeRefundModel; 21 import com.alipay.api.domain.AlipayTradeRefundModel;
21 import com.alipay.api.internal.util.AlipaySignature; 22 import com.alipay.api.internal.util.AlipaySignature;
  23 +import com.alipay.api.request.AlipayTradeQueryRequest;
22 import com.alipay.api.request.AlipayTradeRefundRequest; 24 import com.alipay.api.request.AlipayTradeRefundRequest;
  25 +import com.alipay.api.response.AlipayTradeQueryResponse;
23 import com.alipay.api.response.AlipayTradeRefundResponse; 26 import com.alipay.api.response.AlipayTradeRefundResponse;
24 import lombok.SneakyThrows; 27 import lombok.SneakyThrows;
25 import lombok.extern.slf4j.Slf4j; 28 import lombok.extern.slf4j.Slf4j;
@@ -63,7 +66,7 @@ public abstract class AbstractAlipayPayClient extends AbstractPayClient&lt;AlipayPa @@ -63,7 +66,7 @@ public abstract class AbstractAlipayPayClient extends AbstractPayClient&lt;AlipayPa
63 */ 66 */
64 protected PayOrderRespDTO buildClosedPayOrderRespDTO(PayOrderUnifiedReqDTO reqDTO, AlipayResponse response) { 67 protected PayOrderRespDTO buildClosedPayOrderRespDTO(PayOrderUnifiedReqDTO reqDTO, AlipayResponse response) {
65 Assert.isFalse(response.isSuccess()); 68 Assert.isFalse(response.isSuccess());
66 - return PayOrderRespDTO.build(response.getSubCode(), response.getSubMsg(), 69 + return PayOrderRespDTO.closedOf(response.getSubCode(), response.getSubMsg(),
67 reqDTO.getOutTradeNo(), response); 70 reqDTO.getOutTradeNo(), response);
68 } 71 }
69 72
@@ -76,10 +79,7 @@ public abstract class AbstractAlipayPayClient extends AbstractPayClient&lt;AlipayPa @@ -76,10 +79,7 @@ public abstract class AbstractAlipayPayClient extends AbstractPayClient&lt;AlipayPa
76 79
77 // 2. 解析订单的状态 80 // 2. 解析订单的状态
78 // 额外说明:支付宝不仅仅支付成功会回调,再各种触发支付单数据变化时,都会进行回调,所以这里 status 的解析会写的比较复杂 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 if (MapUtil.getDouble(bodyObj, "refund_fee", 0D) > 0) { 84 if (MapUtil.getDouble(bodyObj, "refund_fee", 0D) > 0) {
85 status = PayOrderStatusRespEnum.REFUND.getStatus(); 85 status = PayOrderStatusRespEnum.REFUND.getStatus();
@@ -87,10 +87,40 @@ public abstract class AbstractAlipayPayClient extends AbstractPayClient&lt;AlipayPa @@ -87,10 +87,40 @@ public abstract class AbstractAlipayPayClient extends AbstractPayClient&lt;AlipayPa
87 Assert.notNull(status, (Supplier<Throwable>) () -> { 87 Assert.notNull(status, (Supplier<Throwable>) () -> {
88 throw new IllegalArgumentException(StrUtil.format("body({}) 的 trade_status 不正确", body)); 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 bodyObj.get("out_trade_no"), body); 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,7 +90,7 @@ public abstract class AbstractWxPayClient extends AbstractPayClient&lt;WxPayClientC
90 } catch (WxPayException e) { 90 } catch (WxPayException e) {
91 String errorCode = getErrorCode(e); 91 String errorCode = getErrorCode(e);
92 String errorMessage = getErrorMessage(e); 92 String errorMessage = getErrorMessage(e);
93 - return PayOrderRespDTO.build(errorCode, errorMessage, 93 + return PayOrderRespDTO.closedOf(errorCode, errorMessage,
94 reqDTO.getOutTradeNo(), e.getXmlString()); 94 reqDTO.getOutTradeNo(), e.getXmlString());
95 } 95 }
96 } 96 }
@@ -133,7 +133,7 @@ public abstract class AbstractWxPayClient extends AbstractPayClient&lt;WxPayClientC @@ -133,7 +133,7 @@ public abstract class AbstractWxPayClient extends AbstractPayClient&lt;WxPayClientC
133 // 微信支付的回调,只有 SUCCESS 支付成功、CLOSED 支付失败两种情况,无需像支付宝一样解析的比较复杂 133 // 微信支付的回调,只有 SUCCESS 支付成功、CLOSED 支付失败两种情况,无需像支付宝一样解析的比较复杂
134 Integer status = Objects.equals(response.getResultCode(), "SUCCESS") ? 134 Integer status = Objects.equals(response.getResultCode(), "SUCCESS") ?
135 PayOrderStatusRespEnum.SUCCESS.getStatus() : PayOrderStatusRespEnum.CLOSED.getStatus(); 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 response.getOutTradeNo(), body); 137 response.getOutTradeNo(), body);
138 } 138 }
139 139
@@ -146,10 +146,15 @@ public abstract class AbstractWxPayClient extends AbstractPayClient&lt;WxPayClientC @@ -146,10 +146,15 @@ public abstract class AbstractWxPayClient extends AbstractPayClient&lt;WxPayClientC
146 Integer status = Objects.equals(result.getTradeState(), "SUCCESS") ? 146 Integer status = Objects.equals(result.getTradeState(), "SUCCESS") ?
147 PayOrderStatusRespEnum.SUCCESS.getStatus() : PayOrderStatusRespEnum.CLOSED.getStatus(); 147 PayOrderStatusRespEnum.SUCCESS.getStatus() : PayOrderStatusRespEnum.CLOSED.getStatus();
148 String openid = result.getPayer() != null ? result.getPayer().getOpenid() : null; 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 result.getOutTradeNo(), body); 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 @Override 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,7 +68,7 @@ public class WxBarPayClient extends AbstractWxPayClient {
68 try { 68 try {
69 WxPayMicropayResult response = client.micropay(request); 69 WxPayMicropayResult response = client.micropay(request);
70 // 支付成功,例如说:1)用户输入了密码;2)用户免密支付 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 response.getOutTradeNo(), response) 72 response.getOutTradeNo(), response)
73 .setDisplayMode(PayOrderDisplayModeEnum.BAR_CODE.getMode()); 73 .setDisplayMode(PayOrderDisplayModeEnum.BAR_CODE.getMode());
74 } catch (WxPayException ex) { 74 } catch (WxPayException ex) {