Browse Source

转账 - 支付宝转账修改, 支付宝 Client 支持公钥证书模式

jason 1 year ago
parent
commit
183206e2d3
12 changed files with 118 additions and 163 deletions
  1. 0 2
      yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/PayClientFactoryImpl.java
  2. 94 9
      yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/alipay/AbstractAlipayPayClient.java
  3. 0 7
      yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/alipay/AlipayAppPayClient.java
  4. 9 8
      yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/alipay/AlipayBarPayClient.java
  5. 0 7
      yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/alipay/AlipayPcPayClient.java
  6. 11 8
      yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/alipay/AlipayQrPayClient.java
  7. 0 107
      yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/alipay/AlipayTransferClient.java
  8. 0 7
      yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/alipay/AlipayWapPayClient.java
  9. 0 3
      yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/enums/channel/PayChannelEnum.java
  10. 1 2
      yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/enums/transfer/PayTransferTypeEnum.java
  11. 1 1
      yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/service/demo/PayDemoTransferServiceImpl.java
  12. 2 2
      yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/service/transfer/PayTransferServiceImpl.java

+ 0 - 2
yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/PayClientFactoryImpl.java

@@ -50,8 +50,6 @@ public class PayClientFactoryImpl implements PayClientFactory {
         clientClass.put(ALIPAY_APP, AlipayAppPayClient.class);
         clientClass.put(ALIPAY_APP, AlipayAppPayClient.class);
         clientClass.put(ALIPAY_PC, AlipayPcPayClient.class);
         clientClass.put(ALIPAY_PC, AlipayPcPayClient.class);
         clientClass.put(ALIPAY_BAR, AlipayBarPayClient.class);
         clientClass.put(ALIPAY_BAR, AlipayBarPayClient.class);
-        // 支付包转账客户端
-        clientClass.put(ALIPAY_TRANSFER, AlipayTransferClient.class);
         // Mock 支付客户端
         // Mock 支付客户端
         clientClass.put(MOCK, MockPayClient.class);
         clientClass.put(MOCK, MockPayClient.class);
     }
     }

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

@@ -6,24 +6,28 @@ import cn.hutool.core.lang.Assert;
 import cn.hutool.core.map.MapUtil;
 import cn.hutool.core.map.MapUtil;
 import cn.hutool.core.util.StrUtil;
 import cn.hutool.core.util.StrUtil;
 import cn.hutool.http.HttpUtil;
 import cn.hutool.http.HttpUtil;
+import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
 import cn.iocoder.yudao.framework.common.util.object.ObjectUtils;
 import cn.iocoder.yudao.framework.common.util.object.ObjectUtils;
 import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderRespDTO;
 import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderRespDTO;
 import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderUnifiedReqDTO;
 import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderUnifiedReqDTO;
 import cn.iocoder.yudao.framework.pay.core.client.dto.refund.PayRefundRespDTO;
 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.dto.transfer.PayTransferRespDTO;
+import cn.iocoder.yudao.framework.pay.core.client.dto.transfer.PayTransferUnifiedReqDTO;
 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.transfer.PayTransferTypeEnum;
 import com.alipay.api.AlipayApiException;
 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.AlipayTradeFastpayRefundQueryModel;
-import com.alipay.api.domain.AlipayTradeQueryModel;
-import com.alipay.api.domain.AlipayTradeRefundModel;
+import com.alipay.api.domain.*;
 import com.alipay.api.internal.util.AlipaySignature;
 import com.alipay.api.internal.util.AlipaySignature;
+import com.alipay.api.request.AlipayFundTransUniTransferRequest;
 import com.alipay.api.request.AlipayTradeFastpayRefundQueryRequest;
 import com.alipay.api.request.AlipayTradeFastpayRefundQueryRequest;
 import com.alipay.api.request.AlipayTradeQueryRequest;
 import com.alipay.api.request.AlipayTradeQueryRequest;
 import com.alipay.api.request.AlipayTradeRefundRequest;
 import com.alipay.api.request.AlipayTradeRefundRequest;
+import com.alipay.api.response.AlipayFundTransUniTransferResponse;
 import com.alipay.api.response.AlipayTradeFastpayRefundQueryResponse;
 import com.alipay.api.response.AlipayTradeFastpayRefundQueryResponse;
 import com.alipay.api.response.AlipayTradeQueryResponse;
 import com.alipay.api.response.AlipayTradeQueryResponse;
 import com.alipay.api.response.AlipayTradeRefundResponse;
 import com.alipay.api.response.AlipayTradeRefundResponse;
@@ -39,6 +43,9 @@ 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.exception.enums.GlobalErrorCodeConstants.BAD_REQUEST;
+import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception0;
+import static cn.iocoder.yudao.framework.pay.core.client.impl.alipay.AlipayPayClientConfig.MODE_CERTIFICATE;
 
 
 /**
 /**
  * 支付宝抽象类,实现支付宝统一的接口、以及部分实现(退款)
  * 支付宝抽象类,实现支付宝统一的接口、以及部分实现(退款)
@@ -105,16 +112,20 @@ public abstract class AbstractAlipayPayClient extends AbstractPayClient<AlipayPa
         // 1.2 构建 AlipayTradeQueryRequest 请求
         // 1.2 构建 AlipayTradeQueryRequest 请求
         AlipayTradeQueryRequest request = new AlipayTradeQueryRequest();
         AlipayTradeQueryRequest request = new AlipayTradeQueryRequest();
         request.setBizModel(model);
         request.setBizModel(model);
-
-        // 2.1 执行请求
-        AlipayTradeQueryResponse response =  client.execute(request);
+        AlipayTradeQueryResponse response;
+        if (Objects.equals(config.getMode(), MODE_CERTIFICATE)) {
+            // 证书模式
+            response = client.certificateExecute(request);
+        } else {
+            response = client.execute(request);
+        }
         if (!response.isSuccess()) { // 不成功,例如说订单不存在
         if (!response.isSuccess()) { // 不成功,例如说订单不存在
             return PayOrderRespDTO.closedOf(response.getSubCode(), response.getSubMsg(),
             return PayOrderRespDTO.closedOf(response.getSubCode(), response.getSubMsg(),
                     outTradeNo, response);
                     outTradeNo, response);
         }
         }
         // 2.2 解析订单的状态
         // 2.2 解析订单的状态
         Integer status = parseStatus(response.getTradeStatus());
         Integer status = parseStatus(response.getTradeStatus());
-        Assert.notNull(status, (Supplier<Throwable>) () -> {
+        Assert.notNull(status,  () -> {
             throw new IllegalArgumentException(StrUtil.format("body({}) 的 trade_status 不正确", response.getBody()));
             throw new IllegalArgumentException(StrUtil.format("body({}) 的 trade_status 不正确", response.getBody()));
         });
         });
         return PayOrderRespDTO.of(status, response.getTradeNo(), response.getBuyerUserId(), LocalDateTimeUtil.of(response.getSendPayDate()),
         return PayOrderRespDTO.of(status, response.getTradeNo(), response.getBuyerUserId(), LocalDateTimeUtil.of(response.getSendPayDate()),
@@ -148,7 +159,13 @@ public abstract class AbstractAlipayPayClient extends AbstractPayClient<AlipayPa
         request.setBizModel(model);
         request.setBizModel(model);
 
 
         // 2.1 执行请求
         // 2.1 执行请求
-        AlipayTradeRefundResponse response = client.execute(request);
+        AlipayTradeRefundResponse response;
+        if (Objects.equals(config.getMode(), MODE_CERTIFICATE)) {
+            // 证书模式
+            response = client.certificateExecute(request);
+        } else {
+            response = client.execute(request);
+        }
         if (!response.isSuccess()) {
         if (!response.isSuccess()) {
             // 当出现 ACQ.SYSTEM_ERROR, 退款可能成功也可能失败。 返回 WAIT 状态. 后续 job 会轮询
             // 当出现 ACQ.SYSTEM_ERROR, 退款可能成功也可能失败。 返回 WAIT 状态. 后续 job 会轮询
             if (ObjectUtils.equalsAny(response.getSubCode(), "ACQ.SYSTEM_ERROR", "SYSTEM_ERROR")) {
             if (ObjectUtils.equalsAny(response.getSubCode(), "ACQ.SYSTEM_ERROR", "SYSTEM_ERROR")) {
@@ -185,7 +202,13 @@ public abstract class AbstractAlipayPayClient extends AbstractPayClient<AlipayPa
         request.setBizModel(model);
         request.setBizModel(model);
 
 
         // 2.1 执行请求
         // 2.1 执行请求
-        AlipayTradeFastpayRefundQueryResponse response = client.execute(request);
+        AlipayTradeFastpayRefundQueryResponse response;
+        if (Objects.equals(config.getMode(), MODE_CERTIFICATE)) {
+            // 证书模式
+            response = client.certificateExecute(request);
+        } else {
+            response = client.execute(request);
+        }
         if (!response.isSuccess()) {
         if (!response.isSuccess()) {
             // 明确不存在的情况,应该就是失败,可进行关闭
             // 明确不存在的情况,应该就是失败,可进行关闭
             if (ObjectUtils.equalsAny(response.getSubCode(), "TRADE_NOT_EXIST", "ACQ.TRADE_NOT_EXIST")) {
             if (ObjectUtils.equalsAny(response.getSubCode(), "TRADE_NOT_EXIST", "ACQ.TRADE_NOT_EXIST")) {
@@ -202,7 +225,69 @@ public abstract class AbstractAlipayPayClient extends AbstractPayClient<AlipayPa
         return PayRefundRespDTO.waitingOf(null, outRefundNo, response);
         return PayRefundRespDTO.waitingOf(null, outRefundNo, response);
     }
     }
 
 
+    @Override
+    protected PayTransferRespDTO doUnifiedTransfer(PayTransferUnifiedReqDTO reqDTO) throws AlipayApiException {
+        // 1.1 校验公钥类型 必须使用公钥证书模式
+        if (!Objects.equals(config.getMode(), MODE_CERTIFICATE)) {
+            throw new IllegalStateException("支付宝单笔转账必须使用公钥证书模式");
+        }
 
 
+        // 1.2 构建 AlipayFundTransUniTransferModel
+        AlipayFundTransUniTransferModel model = new AlipayFundTransUniTransferModel();
+        // ① 通用的参数
+        model.setTransAmount(formatAmount(reqDTO.getPrice())); // 转账金额
+        model.setOrderTitle(reqDTO.getTitle());               // 转账业务的标题,用于在支付宝用户的账单里显示。
+        model.setOutBizNo(reqDTO.getOutTransferNo());
+        model.setProductCode("TRANS_ACCOUNT_NO_PWD");    // 销售产品码。单笔无密转账固定为 TRANS_ACCOUNT_NO_PWD
+        model.setBizScene("DIRECT_TRANSFER");           // 业务场景 单笔无密转账固定为 DIRECT_TRANSFER
+        model.setBusinessParams(JsonUtils.toJsonString(reqDTO.getChannelExtras()));
+        PayTransferTypeEnum transferType = PayTransferTypeEnum.typeOf(reqDTO.getType());
+        switch (transferType) {
+            // TODO @jason:是不是不用传递 transferType 参数哈?因为应该已经明确是支付宝啦?
+            // @芋艿。 是不是还要考虑转账到银行卡。所以传 transferType 但是转账到银行卡不知道要如何测试??
+            case ALIPAY_BALANCE: {
+                // ② 个性化的参数
+                Participant payeeInfo = new Participant();
+                payeeInfo.setIdentityType("ALIPAY_LOGON_ID");
+                String logonId = MapUtil.getStr(reqDTO.getPayeeInfo(), "ALIPAY_LOGON_ID");
+                if (StrUtil.isEmpty(logonId)) {
+                    throw exception0(BAD_REQUEST.getCode(), "支付包登录 ID 不能为空");
+                }
+                String accountName = MapUtil.getStr(reqDTO.getPayeeInfo(), "ALIPAY_ACCOUNT_NAME");
+                if (StrUtil.isEmpty(accountName)) {
+                    throw exception0(BAD_REQUEST.getCode(), "支付包账户名称不能为空");
+                }
+                payeeInfo.setIdentity(logonId); // 支付宝登录号
+                payeeInfo.setName(accountName); // 支付宝账号姓名
+                model.setPayeeInfo(payeeInfo);
+                // 1.3 构建 AlipayFundTransUniTransferRequest
+                AlipayFundTransUniTransferRequest request = new AlipayFundTransUniTransferRequest();
+                request.setBizModel(model);
+                // 执行请求
+                AlipayFundTransUniTransferResponse response = client.certificateExecute(request);
+                // 处理结果
+                if (!response.isSuccess()) {
+                    // 当出现 SYSTEM_ERROR, 转账可能成功也可能失败。 返回 WAIT 状态. 后续 job 会轮询
+                    if (ObjectUtils.equalsAny(response.getSubCode(), "SYSTEM_ERROR", "ACQ.SYSTEM_ERROR")) {
+                        return PayTransferRespDTO.waitingOf(null, reqDTO.getOutTransferNo(), response);
+                    }
+                    return PayTransferRespDTO.closedOf(response.getSubCode(), response.getSubMsg(),
+                            reqDTO.getOutTransferNo(), response);
+                }
+                return PayTransferRespDTO.successOf(response.getOrderId(), parseTime(response.getTransDate()),
+                        response.getOutBizNo(), response);
+            }
+            case BANK_CARD: {
+                Participant payeeInfo = new Participant();
+                payeeInfo.setIdentityType("BANKCARD_ACCOUNT");
+                // TODO 待实现
+                throw new UnsupportedOperationException("待实现");
+            }
+            default: {
+                throw new IllegalStateException("不正确的转账类型: " + transferType);
+            }
+        }
+    }
 
 
     // ========== 各种工具方法 ==========
     // ========== 各种工具方法 ==========
 
 

+ 0 - 7
yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/alipay/AlipayAppPayClient.java

@@ -2,8 +2,6 @@ package cn.iocoder.yudao.framework.pay.core.client.impl.alipay;
 
 
 import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderRespDTO;
 import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderRespDTO;
 import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderUnifiedReqDTO;
 import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderUnifiedReqDTO;
-import cn.iocoder.yudao.framework.pay.core.client.dto.transfer.PayTransferRespDTO;
-import cn.iocoder.yudao.framework.pay.core.client.dto.transfer.PayTransferUnifiedReqDTO;
 import cn.iocoder.yudao.framework.pay.core.enums.channel.PayChannelEnum;
 import cn.iocoder.yudao.framework.pay.core.enums.channel.PayChannelEnum;
 import cn.iocoder.yudao.framework.pay.core.enums.order.PayOrderDisplayModeEnum;
 import cn.iocoder.yudao.framework.pay.core.enums.order.PayOrderDisplayModeEnum;
 import com.alipay.api.AlipayApiException;
 import com.alipay.api.AlipayApiException;
@@ -58,9 +56,4 @@ public class AlipayAppPayClient extends AbstractAlipayPayClient {
         return PayOrderRespDTO.waitingOf(displayMode, response.getBody(),
         return PayOrderRespDTO.waitingOf(displayMode, response.getBody(),
                 reqDTO.getOutTradeNo(), response);
                 reqDTO.getOutTradeNo(), response);
     }
     }
-
-    @Override
-    protected PayTransferRespDTO doUnifiedTransfer(PayTransferUnifiedReqDTO reqDTO) {
-        throw new UnsupportedOperationException("支付宝【App 支付】不支持转账操作");
-    }
 }
 }

+ 9 - 8
yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/alipay/AlipayBarPayClient.java

@@ -5,8 +5,6 @@ import cn.hutool.core.map.MapUtil;
 import cn.hutool.core.util.StrUtil;
 import cn.hutool.core.util.StrUtil;
 import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderRespDTO;
 import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderRespDTO;
 import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderUnifiedReqDTO;
 import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderUnifiedReqDTO;
-import cn.iocoder.yudao.framework.pay.core.client.dto.transfer.PayTransferRespDTO;
-import cn.iocoder.yudao.framework.pay.core.client.dto.transfer.PayTransferUnifiedReqDTO;
 import cn.iocoder.yudao.framework.pay.core.enums.channel.PayChannelEnum;
 import cn.iocoder.yudao.framework.pay.core.enums.channel.PayChannelEnum;
 import cn.iocoder.yudao.framework.pay.core.enums.order.PayOrderDisplayModeEnum;
 import cn.iocoder.yudao.framework.pay.core.enums.order.PayOrderDisplayModeEnum;
 import com.alipay.api.AlipayApiException;
 import com.alipay.api.AlipayApiException;
@@ -16,9 +14,11 @@ import com.alipay.api.response.AlipayTradePayResponse;
 import lombok.extern.slf4j.Slf4j;
 import lombok.extern.slf4j.Slf4j;
 
 
 import java.time.LocalDateTime;
 import java.time.LocalDateTime;
+import java.util.Objects;
 
 
 import static cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeConstants.BAD_REQUEST;
 import static cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeConstants.BAD_REQUEST;
 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception0;
 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception0;
+import static cn.iocoder.yudao.framework.pay.core.client.impl.alipay.AlipayPayClientConfig.MODE_CERTIFICATE;
 
 
 /**
 /**
  * 支付宝【条码支付】的 PayClient 实现类
  * 支付宝【条码支付】的 PayClient 实现类
@@ -61,7 +61,13 @@ public class AlipayBarPayClient extends AbstractAlipayPayClient {
         request.setReturnUrl(reqDTO.getReturnUrl());
         request.setReturnUrl(reqDTO.getReturnUrl());
 
 
         // 2.1 执行请求
         // 2.1 执行请求
-        AlipayTradePayResponse response = client.execute(request);
+        AlipayTradePayResponse response;
+        if (Objects.equals(config.getMode(), MODE_CERTIFICATE)) {
+            // 证书模式
+            response = client.certificateExecute(request);
+        } else {
+            response = client.execute(request);
+        }
         // 2.2 处理结果
         // 2.2 处理结果
         if (!response.isSuccess()) {
         if (!response.isSuccess()) {
             return buildClosedPayOrderRespDTO(reqDTO, response);
             return buildClosedPayOrderRespDTO(reqDTO, response);
@@ -76,9 +82,4 @@ public class AlipayBarPayClient extends AbstractAlipayPayClient {
         return PayOrderRespDTO.waitingOf(displayMode, "",
         return PayOrderRespDTO.waitingOf(displayMode, "",
                 reqDTO.getOutTradeNo(), response);
                 reqDTO.getOutTradeNo(), response);
     }
     }
-
-    @Override
-    protected PayTransferRespDTO doUnifiedTransfer(PayTransferUnifiedReqDTO reqDTO) {
-        throw new UnsupportedOperationException("支付宝【条码支付】不支持转账操作");
-    }
 }
 }

+ 0 - 7
yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/alipay/AlipayPcPayClient.java

@@ -4,8 +4,6 @@ import cn.hutool.core.util.ObjectUtil;
 import cn.hutool.http.Method;
 import cn.hutool.http.Method;
 import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderRespDTO;
 import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderRespDTO;
 import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderUnifiedReqDTO;
 import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderUnifiedReqDTO;
-import cn.iocoder.yudao.framework.pay.core.client.dto.transfer.PayTransferRespDTO;
-import cn.iocoder.yudao.framework.pay.core.client.dto.transfer.PayTransferUnifiedReqDTO;
 import cn.iocoder.yudao.framework.pay.core.enums.channel.PayChannelEnum;
 import cn.iocoder.yudao.framework.pay.core.enums.channel.PayChannelEnum;
 import cn.iocoder.yudao.framework.pay.core.enums.order.PayOrderDisplayModeEnum;
 import cn.iocoder.yudao.framework.pay.core.enums.order.PayOrderDisplayModeEnum;
 import com.alipay.api.AlipayApiException;
 import com.alipay.api.AlipayApiException;
@@ -68,9 +66,4 @@ public class AlipayPcPayClient extends AbstractAlipayPayClient {
         return PayOrderRespDTO.waitingOf(displayMode, response.getBody(),
         return PayOrderRespDTO.waitingOf(displayMode, response.getBody(),
                 reqDTO.getOutTradeNo(), response);
                 reqDTO.getOutTradeNo(), response);
     }
     }
-
-    @Override
-    protected PayTransferRespDTO doUnifiedTransfer(PayTransferUnifiedReqDTO reqDTO) {
-        throw new UnsupportedOperationException("支付宝【PC 网站】不支持转账操作");
-    }
 }
 }

+ 11 - 8
yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/alipay/AlipayQrPayClient.java

@@ -2,8 +2,6 @@ package cn.iocoder.yudao.framework.pay.core.client.impl.alipay;
 
 
 import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderRespDTO;
 import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderRespDTO;
 import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderUnifiedReqDTO;
 import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderUnifiedReqDTO;
-import cn.iocoder.yudao.framework.pay.core.client.dto.transfer.PayTransferRespDTO;
-import cn.iocoder.yudao.framework.pay.core.client.dto.transfer.PayTransferUnifiedReqDTO;
 import cn.iocoder.yudao.framework.pay.core.enums.channel.PayChannelEnum;
 import cn.iocoder.yudao.framework.pay.core.enums.channel.PayChannelEnum;
 import cn.iocoder.yudao.framework.pay.core.enums.order.PayOrderDisplayModeEnum;
 import cn.iocoder.yudao.framework.pay.core.enums.order.PayOrderDisplayModeEnum;
 import com.alipay.api.AlipayApiException;
 import com.alipay.api.AlipayApiException;
@@ -12,6 +10,10 @@ import com.alipay.api.request.AlipayTradePrecreateRequest;
 import com.alipay.api.response.AlipayTradePrecreateResponse;
 import com.alipay.api.response.AlipayTradePrecreateResponse;
 import lombok.extern.slf4j.Slf4j;
 import lombok.extern.slf4j.Slf4j;
 
 
+import java.util.Objects;
+
+import static cn.iocoder.yudao.framework.pay.core.client.impl.alipay.AlipayPayClientConfig.MODE_CERTIFICATE;
+
 /**
 /**
  * 支付宝【扫码支付】的 PayClient 实现类
  * 支付宝【扫码支付】的 PayClient 实现类
  *
  *
@@ -47,7 +49,13 @@ public class AlipayQrPayClient extends AbstractAlipayPayClient {
         request.setReturnUrl(reqDTO.getReturnUrl());
         request.setReturnUrl(reqDTO.getReturnUrl());
 
 
         // 2.1 执行请求
         // 2.1 执行请求
-        AlipayTradePrecreateResponse response = client.execute(request);
+        AlipayTradePrecreateResponse response;
+        if (Objects.equals(config.getMode(), MODE_CERTIFICATE)) {
+            // 证书模式
+            response = client.certificateExecute(request);
+        } else {
+            response = client.execute(request);
+        }
         // 2.2 处理结果
         // 2.2 处理结果
         if (!response.isSuccess()) {
         if (!response.isSuccess()) {
             return buildClosedPayOrderRespDTO(reqDTO, response);
             return buildClosedPayOrderRespDTO(reqDTO, response);
@@ -55,9 +63,4 @@ public class AlipayQrPayClient extends AbstractAlipayPayClient {
         return PayOrderRespDTO.waitingOf(displayMode, response.getQrCode(),
         return PayOrderRespDTO.waitingOf(displayMode, response.getQrCode(),
                 reqDTO.getOutTradeNo(), response);
                 reqDTO.getOutTradeNo(), response);
     }
     }
-
-    @Override
-    protected PayTransferRespDTO doUnifiedTransfer(PayTransferUnifiedReqDTO reqDTO)  {
-        throw new UnsupportedOperationException("支付宝【扫码支付】不支持转账操作");
-    }
 }
 }

+ 0 - 107
yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/alipay/AlipayTransferClient.java

@@ -1,107 +0,0 @@
-package cn.iocoder.yudao.framework.pay.core.client.impl.alipay;
-
-import cn.hutool.core.map.MapUtil;
-import cn.hutool.core.util.StrUtil;
-import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
-import cn.iocoder.yudao.framework.common.util.object.ObjectUtils;
-import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderRespDTO;
-import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderUnifiedReqDTO;
-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.transfer.PayTransferRespDTO;
-import cn.iocoder.yudao.framework.pay.core.client.dto.transfer.PayTransferUnifiedReqDTO;
-import cn.iocoder.yudao.framework.pay.core.enums.channel.PayChannelEnum;
-import cn.iocoder.yudao.framework.pay.core.enums.transfer.PayTransferTypeEnum;
-import com.alipay.api.AlipayApiException;
-import com.alipay.api.domain.AlipayFundTransUniTransferModel;
-import com.alipay.api.domain.Participant;
-import com.alipay.api.request.AlipayFundTransUniTransferRequest;
-import com.alipay.api.response.AlipayFundTransUniTransferResponse;
-import lombok.extern.slf4j.Slf4j;
-
-import static cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeConstants.BAD_REQUEST;
-import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception0;
-
-// TODO @jason:看看能不能融合到 AbstractAlipayPayClient 中。
-/**
- * 支付宝转账的 PayClient 实现类
- *
- * @author jason
- */
-@Slf4j
-public class AlipayTransferClient extends AbstractAlipayPayClient {
-    // TODO @jason:方法之间,要有空格噢
-    public AlipayTransferClient(Long channelId, AlipayPayClientConfig config) {
-        super(channelId, PayChannelEnum.ALIPAY_TRANSFER.getCode(), config);
-    }
-    @Override
-    protected PayOrderRespDTO doUnifiedOrder(PayOrderUnifiedReqDTO reqDTO) {
-        throw new UnsupportedOperationException("支付宝转账不支持统一下单请求");
-    }
-    @Override
-    protected PayRefundRespDTO doUnifiedRefund(PayRefundUnifiedReqDTO reqDTO) {
-        throw new UnsupportedOperationException("支付宝转账不支持统一退款请求");
-    }
-    @Override
-    protected PayTransferRespDTO doUnifiedTransfer(PayTransferUnifiedReqDTO reqDTO) throws AlipayApiException {
-        // 1.1 构建 AlipayFundTransUniTransferModel
-        AlipayFundTransUniTransferModel model = new AlipayFundTransUniTransferModel();
-        // ① 通用的参数
-        model.setTransAmount(formatAmount(reqDTO.getPrice())); // 转账金额
-        model.setOrderTitle(reqDTO.getTitle());               // 转账业务的标题,用于在支付宝用户的账单里显示。
-        model.setOutBizNo(reqDTO.getOutTransferNo());
-        model.setProductCode("TRANS_ACCOUNT_NO_PWD");    // 销售产品码。单笔无密转账固定为 TRANS_ACCOUNT_NO_PWD
-        model.setBizScene("DIRECT_TRANSFER");           // 业务场景 单笔无密转账固定为 DIRECT_TRANSFER
-        model.setBusinessParams(JsonUtils.toJsonString(reqDTO.getChannelExtras()));
-        PayTransferTypeEnum transferType = PayTransferTypeEnum.ofType(reqDTO.getType());
-        switch(transferType) {
-            case WX_BALANCE:
-            case WALLET_BALANCE: {
-                log.error("[doUnifiedTransfer],支付宝转账不支持的转账类型{}", transferType);
-                throw new UnsupportedOperationException(String.format("支付宝转账不支持转账类型: %s",transferType.getName()));
-            }
-            // TODO @jason:是不是不用传递 transferType 参数哈?因为应该已经明确是支付宝啦?
-            case ALIPAY_BALANCE: {
-                // ② 个性化的参数
-                Participant payeeInfo = new Participant();
-                payeeInfo.setIdentityType("ALIPAY_LOGON_ID");
-                String logonId = MapUtil.getStr(reqDTO.getPayeeInfo(), "ALIPAY_LOGON_ID");
-                if (StrUtil.isEmpty(logonId)) {
-                    throw exception0(BAD_REQUEST.getCode(), "支付包登录 ID 不能为空");
-                }
-                String accountName = MapUtil.getStr(reqDTO.getPayeeInfo(), "ALIPAY_ACCOUNT_NAME");
-                if (StrUtil.isEmpty(accountName)) {
-                    throw exception0(BAD_REQUEST.getCode(), "支付包账户名称不能为空");
-                }
-                payeeInfo.setIdentity(logonId); // 支付宝登录号
-                payeeInfo.setName(accountName); // 支付宝账号姓名
-                model.setPayeeInfo(payeeInfo);
-                // 1.2 构建 AlipayFundTransUniTransferRequest
-                AlipayFundTransUniTransferRequest request = new AlipayFundTransUniTransferRequest();
-                request.setBizModel(model);
-                // 执行请求
-                AlipayFundTransUniTransferResponse response = client.certificateExecute(request);
-                // 处理结果
-                if (!response.isSuccess()) {
-                    // 当出现 SYSTEM_ERROR, 转账可能成功也可能失败。 返回 WAIT 状态. 后续 job 会轮询
-                    if (ObjectUtils.equalsAny(response.getSubCode(), "SYSTEM_ERROR", "ACQ.SYSTEM_ERROR")) {
-                        return PayTransferRespDTO.waitingOf(null, reqDTO.getOutTransferNo(), response);
-                    }
-                    return PayTransferRespDTO.closedOf(response.getSubCode(), response.getSubMsg(),
-                            reqDTO.getOutTransferNo(), response);
-                }
-                return  PayTransferRespDTO.successOf(response.getOrderId(), parseTime(response.getTransDate()),
-                        response.getOutBizNo(), response);
-            }
-            case BANK_CARD: {
-                Participant payeeInfo = new Participant();
-                payeeInfo.setIdentityType("BANKCARD_ACCOUNT");
-                throw new UnsupportedOperationException("待实现");
-            }
-            default: {
-                throw new IllegalStateException("不正确的转账类型: " + transferType);
-            }
-        }
-    }
-
-}

+ 0 - 7
yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/alipay/AlipayWapPayClient.java

@@ -3,8 +3,6 @@ package cn.iocoder.yudao.framework.pay.core.client.impl.alipay;
 import cn.hutool.http.Method;
 import cn.hutool.http.Method;
 import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderRespDTO;
 import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderRespDTO;
 import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderUnifiedReqDTO;
 import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderUnifiedReqDTO;
-import cn.iocoder.yudao.framework.pay.core.client.dto.transfer.PayTransferRespDTO;
-import cn.iocoder.yudao.framework.pay.core.client.dto.transfer.PayTransferUnifiedReqDTO;
 import cn.iocoder.yudao.framework.pay.core.enums.channel.PayChannelEnum;
 import cn.iocoder.yudao.framework.pay.core.enums.channel.PayChannelEnum;
 import cn.iocoder.yudao.framework.pay.core.enums.order.PayOrderDisplayModeEnum;
 import cn.iocoder.yudao.framework.pay.core.enums.order.PayOrderDisplayModeEnum;
 import com.alipay.api.AlipayApiException;
 import com.alipay.api.AlipayApiException;
@@ -57,9 +55,4 @@ public class AlipayWapPayClient extends AbstractAlipayPayClient {
         return PayOrderRespDTO.waitingOf(displayMode, response.getBody(),
         return PayOrderRespDTO.waitingOf(displayMode, response.getBody(),
                 reqDTO.getOutTradeNo(), response);
                 reqDTO.getOutTradeNo(), response);
     }
     }
-
-    @Override
-    public PayTransferRespDTO doUnifiedTransfer(PayTransferUnifiedReqDTO reqDTO) {
-        throw new UnsupportedOperationException("支付宝【Wap 网站】不支持转账操作");
-    }
 }
 }

+ 0 - 3
yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/enums/channel/PayChannelEnum.java

@@ -28,9 +28,6 @@ public enum PayChannelEnum {
     ALIPAY_APP("alipay_app", "支付宝App 支付", AlipayPayClientConfig.class),
     ALIPAY_APP("alipay_app", "支付宝App 支付", AlipayPayClientConfig.class),
     ALIPAY_QR("alipay_qr", "支付宝扫码支付", AlipayPayClientConfig.class),
     ALIPAY_QR("alipay_qr", "支付宝扫码支付", AlipayPayClientConfig.class),
     ALIPAY_BAR("alipay_bar", "支付宝条码支付", AlipayPayClientConfig.class),
     ALIPAY_BAR("alipay_bar", "支付宝条码支付", AlipayPayClientConfig.class),
-    // TODO @jason:是不是按照微信聊的,合并回 ALIPAY_PC;因为支付宝、微信都是多种支付方式,选择其中的一种即可;
-    ALIPAY_TRANSFER("alipay_transfer", "支付宝转账", AlipayPayClientConfig.class),
-
     MOCK("mock", "模拟支付", NonePayClientConfig.class),
     MOCK("mock", "模拟支付", NonePayClientConfig.class),
 
 
     WALLET("wallet", "钱包支付", NonePayClientConfig.class);
     WALLET("wallet", "钱包支付", NonePayClientConfig.class);

+ 1 - 2
yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/enums/transfer/PayTransferTypeEnum.java

@@ -34,8 +34,7 @@ public enum PayTransferTypeEnum implements IntArrayValuable {
         return ARRAYS;
         return ARRAYS;
     }
     }
 
 
-    // TODO @jason:是不是 typeOf 更符合预期哈?
-    public static PayTransferTypeEnum ofType(Integer type) {
+    public static PayTransferTypeEnum typeOf(Integer type) {
         return ArrayUtil.firstMatch(item -> item.getType().equals(type), values());
         return ArrayUtil.firstMatch(item -> item.getType().equals(type), values());
     }
     }
 
 

+ 1 - 1
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/service/demo/PayDemoTransferServiceImpl.java

@@ -66,7 +66,7 @@ public class PayDemoTransferServiceImpl implements PayDemoTransferService {
 
 
     // TODO @jason:可以参考 AppBrokerageWithdrawCreateReqVO 搞下字段哈,进行校验
     // TODO @jason:可以参考 AppBrokerageWithdrawCreateReqVO 搞下字段哈,进行校验
     private void validatePayeeInfo(Integer transferType, Map<String, String> payeeInfo) {
     private void validatePayeeInfo(Integer transferType, Map<String, String> payeeInfo) {
-        PayTransferTypeEnum transferTypeEnum = ofType(transferType);
+        PayTransferTypeEnum transferTypeEnum = typeOf(transferType);
         switch (transferTypeEnum) {
         switch (transferTypeEnum) {
             case ALIPAY_BALANCE: {
             case ALIPAY_BALANCE: {
                 if (StrUtil.isEmpty(MapUtil.getStr(payeeInfo, ALIPAY_LOGON_ID))) {
                 if (StrUtil.isEmpty(MapUtil.getStr(payeeInfo, ALIPAY_LOGON_ID))) {

+ 2 - 2
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/service/transfer/PayTransferServiceImpl.java

@@ -214,11 +214,11 @@ public class PayTransferServiceImpl implements PayTransferService {
     }
     }
 
 
     private void validateChannelCodeAndTypeMatch(String channelCode, Integer type) {
     private void validateChannelCodeAndTypeMatch(String channelCode, Integer type) {
-        PayTransferTypeEnum transferType = PayTransferTypeEnum.ofType(type);
+        PayTransferTypeEnum transferType = PayTransferTypeEnum.typeOf(type);
         PayChannelEnum payChannel = PayChannelEnum.getByCode(channelCode);
         PayChannelEnum payChannel = PayChannelEnum.getByCode(channelCode);
         switch (transferType) {
         switch (transferType) {
             case ALIPAY_BALANCE: {
             case ALIPAY_BALANCE: {
-                if (payChannel != PayChannelEnum.ALIPAY_TRANSFER) {
+                if (!payChannel.getCode().startsWith("alipay")) {
                     throw exception(PAY_TRANSFER_TYPE_AND_CHANNEL_NOT_MATCH);
                     throw exception(PAY_TRANSFER_TYPE_AND_CHANNEL_NOT_MATCH);
                 }
                 }
                 break;
                 break;