Browse Source

feat:【PAY 支付】AbstractAlipayPayClient,支付宝增加回调解析

YunaiV 6 months ago
parent
commit
f81dc105a2

+ 50 - 18
yudao-module-pay/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/alipay/AbstractAlipayPayClient.java

@@ -81,24 +81,11 @@ public abstract class AbstractAlipayPayClient extends AbstractPayClient<AlipayPa
     @Override
     @Override
     public PayOrderRespDTO doParseOrderNotify(Map<String, String> params, String body, Map<String, String> headers) throws Throwable {
     public PayOrderRespDTO doParseOrderNotify(Map<String, String> params, String body, Map<String, String> headers) throws Throwable {
         // 1. 校验回调数据
         // 1. 校验回调数据
-        Map<String, String> bodyObj = HttpUtil.decodeParamMap(body, StandardCharsets.UTF_8);
-        boolean verify;
-        if (Objects.equals(config.getMode(), MODE_PUBLIC_KEY)) {
-            verify = AlipaySignature.rsaCheckV1(params, config.getAlipayPublicKey(),
-                    StandardCharsets.UTF_8.name(), config.getSignType());
-        } else if (Objects.equals(config.getMode(), MODE_CERTIFICATE)) {
-            // 由于 rsaCertCheckV1 的第二个参数是 path,所以不能这么调用!!!通过阅读源码,发现可以采用如下方式!
-            X509Certificate cert = AntCertificationUtil.getCertFromContent(config.getAlipayPublicCertContent());
-            String publicKey = Base64.encodeBase64String(cert.getEncoded());
-            verify = AlipaySignature.rsaCheckV1(bodyObj, publicKey,
-                    StandardCharsets.UTF_8.name(), config.getSignType());
-        } else {
-            throw new IllegalArgumentException("未知的公钥类型:" + config.getMode());
-        }
-        Assert.isTrue(verify, "验签结果不通过");
+        verifyNotifyData(params);
 
 
         // 2. 解析订单的状态
         // 2. 解析订单的状态
         // 额外说明:支付宝不仅仅支付成功会回调,再各种触发支付单数据变化时,都会进行回调,所以这里 status 的解析会写的比较复杂
         // 额外说明:支付宝不仅仅支付成功会回调,再各种触发支付单数据变化时,都会进行回调,所以这里 status 的解析会写的比较复杂
+        Map<String, String> bodyObj = HttpUtil.decodeParamMap(body, StandardCharsets.UTF_8);
         Integer status = parseStatus(bodyObj.get("trade_status"));
         Integer status = parseStatus(bodyObj.get("trade_status"));
         // 特殊逻辑: 支付宝没有退款成功的状态,所以,如果有退款金额,我们认为是退款成功
         // 特殊逻辑: 支付宝没有退款成功的状态,所以,如果有退款金额,我们认为是退款成功
         if (MapUtil.getDouble(bodyObj, "refund_fee", 0D) > 0) {
         if (MapUtil.getDouble(bodyObj, "refund_fee", 0D) > 0) {
@@ -324,10 +311,55 @@ public abstract class AbstractAlipayPayClient extends AbstractPayClient<AlipayPa
                 response.getOutBizNo(), response);
                 response.getOutBizNo(), response);
     }
     }
 
 
-    // TODO @chihuo:这里是不是也要实现,支付宝的。
+    // TODO @芋艿:由于支付宝一直没触发回调,这个方法暂时没办法测试
     @Override
     @Override
-    protected PayTransferRespDTO doParseTransferNotify(Map<String, String> params, String body, Map<String, String> headers) {
-        throw new UnsupportedOperationException("未实现");
+    protected PayTransferRespDTO doParseTransferNotify(Map<String, String> params, String body, Map<String, String> headers)
+            throws Throwable {
+        // 1. 校验回调数据
+        verifyNotifyData(params);
+
+        // 2. 解析转账状态
+        Map<String, String> bodyObj = HttpUtil.decodeParamMap(body, StandardCharsets.UTF_8);
+        String status = bodyObj.get("status");
+        String outBizNo = bodyObj.get("out_biz_no");
+        String orderId = bodyObj.get("order_id");
+        String payDate = bodyObj.get("pay_date");
+
+        // 3. 根据状态返回对应的结果
+        if (Objects.equals(status, "SUCCESS")) {
+            return PayTransferRespDTO.successOf(orderId, parseTime(payDate), outBizNo, bodyObj);
+        }
+        if (Objects.equals(status, "DEALING")) {
+            return PayTransferRespDTO.processingOf(orderId, outBizNo, bodyObj);
+        }
+        if (ObjectUtils.equalsAny(status, "REFUND", "FAIL")) {
+            return PayTransferRespDTO.closedOf(bodyObj.get("sub_code"), bodyObj.get("sub_msg"),
+                    outBizNo, bodyObj);
+        }
+        return PayTransferRespDTO.waitingOf(orderId, outBizNo, bodyObj);
+    }
+
+    /**
+     * 校验回调数据
+     *
+     * @param params 回调参数
+     * @throws Throwable 验签失败时抛出异常
+     */
+    protected void verifyNotifyData(Map<String, String> params) throws Throwable {
+        boolean verify;
+        if (Objects.equals(config.getMode(), MODE_PUBLIC_KEY)) {
+            verify = AlipaySignature.rsaCheckV1(params, config.getAlipayPublicKey(),
+                    StandardCharsets.UTF_8.name(), config.getSignType());
+        } else if (Objects.equals(config.getMode(), MODE_CERTIFICATE)) {
+            // 由于 rsaCertCheckV1 的第二个参数是 path,所以不能这么调用!!!通过阅读源码,发现可以采用如下方式!
+            X509Certificate cert = AntCertificationUtil.getCertFromContent(config.getAlipayPublicCertContent());
+            String publicKey = Base64.encodeBase64String(cert.getEncoded());
+            verify = AlipaySignature.rsaCheckV1(params, publicKey,
+                    StandardCharsets.UTF_8.name(), config.getSignType());
+        } else {
+            throw new IllegalArgumentException("未知的公钥类型:" + config.getMode());
+        }
+        Assert.isTrue(verify, "验签结果不通过");
     }
     }
 
 
     // ========== 各种工具方法 ==========
     // ========== 各种工具方法 ==========