Prechádzať zdrojové kódy

增加支付相关表的 SQL,调整相关的实体

YunaiV 3 rokov pred
rodič
commit
6dc65234ef
29 zmenil súbory, kde vykonal 664 pridanie a 105 odobranie
  1. 172 0
      sql/pay2.sql
  2. 1 1
      yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/system/service/sms/SysSmsChannelService.java
  3. 3 0
      yudao-core-service/src/main/java/cn/iocoder/yudao/coreservice/modules/pay/convert/order/PayOrderCoreConvert.java
  4. 8 6
      yudao-core-service/src/main/java/cn/iocoder/yudao/coreservice/modules/pay/dal/dataobject/merchant/PayAppDO.java
  5. 18 5
      yudao-core-service/src/main/java/cn/iocoder/yudao/coreservice/modules/pay/dal/dataobject/merchant/PayChannelDO.java
  6. 9 2
      yudao-core-service/src/main/java/cn/iocoder/yudao/coreservice/modules/pay/dal/dataobject/merchant/PayMerchantDO.java
  7. 25 34
      yudao-core-service/src/main/java/cn/iocoder/yudao/coreservice/modules/pay/dal/dataobject/order/PayOrderDO.java
  8. 8 7
      yudao-core-service/src/main/java/cn/iocoder/yudao/coreservice/modules/pay/dal/dataobject/order/PayOrderExtensionDO.java
  9. 6 16
      yudao-core-service/src/main/java/cn/iocoder/yudao/coreservice/modules/pay/dal/dataobject/order/PayRefundDO.java
  10. 6 0
      yudao-core-service/src/main/java/cn/iocoder/yudao/coreservice/modules/pay/dal/mysql/merchant/PayChannelCoreMapper.java
  11. 4 3
      yudao-core-service/src/main/java/cn/iocoder/yudao/coreservice/modules/pay/enums/PayErrorCodeConstants.java
  12. 5 0
      yudao-core-service/src/main/java/cn/iocoder/yudao/coreservice/modules/pay/service/merchant/PayChannelCoreService.java
  13. 1 1
      yudao-core-service/src/main/java/cn/iocoder/yudao/coreservice/modules/pay/service/merchant/impl/PayAppCoreServiceImpl.java
  14. 69 3
      yudao-core-service/src/main/java/cn/iocoder/yudao/coreservice/modules/pay/service/merchant/impl/PayChannelCoreServiceImpl.java
  15. 4 4
      yudao-core-service/src/main/java/cn/iocoder/yudao/coreservice/modules/pay/service/order/dto/PayOrderCreateReqDTO.java
  16. 3 3
      yudao-core-service/src/main/java/cn/iocoder/yudao/coreservice/modules/pay/service/order/dto/PayOrderSubmitReqDTO.java
  17. 16 5
      yudao-core-service/src/main/java/cn/iocoder/yudao/coreservice/modules/pay/service/order/impl/PayOrderCoreServiceImpl.java
  18. 38 0
      yudao-core-service/src/test-integration/java/cn/iocoder/yudao/coreservice/BaseDbAndRedisIntegrationTest.java
  19. 30 0
      yudao-core-service/src/test-integration/java/cn/iocoder/yudao/coreservice/BaseDbIntegrationTest.java
  20. 23 0
      yudao-core-service/src/test-integration/java/cn/iocoder/yudao/coreservice/BaseRedisIntegrationTest.java
  21. 29 0
      yudao-core-service/src/test-integration/java/cn/iocoder/yudao/coreservice/modules/pay/dal/dataobject/merchant/PayChannelDOTest.java
  22. 56 0
      yudao-core-service/src/test-integration/java/cn/iocoder/yudao/coreservice/modules/pay/dal/mysql/merchant/PayChannelCoreMapperTest.java
  23. 92 0
      yudao-core-service/src/test-integration/resources/application-integration-test.yaml
  24. 6 10
      yudao-framework/yudao-spring-boot-starter-biz-pay/pom.xml
  25. 21 0
      yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/config/YudaoPayAutoConfiguration.java
  26. 6 0
      yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/PayClientConfig.java
  27. 3 3
      yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/dto/PayOrderUnifiedReqDTO.java
  28. 1 1
      yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/alipay/AlipayQrPayClient.java
  29. 1 1
      yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/alipay/AlipayWapPayClient.java

Rozdielové dáta súboru neboli zobrazené, pretože súbor je príliš veľký
+ 172 - 0
sql/pay2.sql


+ 1 - 1
yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/system/service/sms/SysSmsChannelService.java

@@ -11,7 +11,7 @@ import java.util.Collection;
 import java.util.List;
 
 /**
- * 短信渠道Service接口
+ * 短信渠道 Service 接口
  *
  * @author zzf
  * @date 2021/1/25 9:24

+ 3 - 0
yudao-core-service/src/main/java/cn/iocoder/yudao/coreservice/modules/pay/convert/order/PayOrderCoreConvert.java

@@ -4,6 +4,7 @@ import cn.iocoder.yudao.coreservice.modules.pay.dal.dataobject.order.PayOrderDO;
 import cn.iocoder.yudao.coreservice.modules.pay.dal.dataobject.order.PayOrderExtensionDO;
 import cn.iocoder.yudao.coreservice.modules.pay.service.order.dto.PayOrderCreateReqDTO;
 import cn.iocoder.yudao.coreservice.modules.pay.service.order.dto.PayOrderSubmitReqDTO;
+import cn.iocoder.yudao.framework.pay.core.client.dto.PayOrderUnifiedReqDTO;
 import org.mapstruct.Mapper;
 import org.mapstruct.factory.Mappers;
 
@@ -16,4 +17,6 @@ public interface PayOrderCoreConvert {
 
     PayOrderExtensionDO convert(PayOrderSubmitReqDTO bean);
 
+    PayOrderUnifiedReqDTO convert2(PayOrderSubmitReqDTO bean);
+
 }

+ 8 - 6
yudao-core-service/src/main/java/cn/iocoder/yudao/coreservice/modules/pay/dal/dataobject/merchant/PayAppDO.java

@@ -3,7 +3,8 @@ package cn.iocoder.yudao.coreservice.modules.pay.dal.dataobject.merchant;
 import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
 import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
 import com.baomidou.mybatisplus.annotation.TableId;
-import lombok.Data;
+import com.baomidou.mybatisplus.annotation.TableName;
+import lombok.*;
 
 /**
  * 支付应用 DO
@@ -14,7 +15,13 @@ import lombok.Data;
  *
  * @author 芋道源码
  */
+@TableName("pay_app")
 @Data
+@EqualsAndHashCode(callSuper = true)
+@ToString(callSuper = true)
+@Builder
+@NoArgsConstructor
+@AllArgsConstructor
 public class PayAppDO extends BaseDO {
 
     /**
@@ -37,11 +44,6 @@ public class PayAppDO extends BaseDO {
      */
     private String remark;
     /**
-     * 应用私钥
-     * TODO 芋艿:用途
-     */
-    private String secret;
-    /**
      * 支付结果的回调地址
      */
     private String payNotifyUrl;

+ 18 - 5
yudao-core-service/src/main/java/cn/iocoder/yudao/coreservice/modules/pay/dal/dataobject/merchant/PayChannelDO.java

@@ -1,9 +1,13 @@
 package cn.iocoder.yudao.coreservice.modules.pay.dal.dataobject.merchant;
 
-import cn.iocoder.yudao.framework.pay.core.enums.PayChannelEnum;
 import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
 import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
-import lombok.Data;
+import cn.iocoder.yudao.framework.pay.core.client.PayClientConfig;
+import cn.iocoder.yudao.framework.pay.core.enums.PayChannelEnum;
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableName;
+import com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler;
+import lombok.*;
 
 /**
  * 支付渠道 DO
@@ -14,6 +18,12 @@ import lombok.Data;
  * @author 芋道源码
  */
 @Data
+@TableName(value = "pay_channel", autoResultMap = true)
+@EqualsAndHashCode(callSuper = true)
+@ToString(callSuper = true)
+@Builder
+@NoArgsConstructor
+@AllArgsConstructor
 public class PayChannelDO extends BaseDO {
 
     /**
@@ -48,8 +58,11 @@ public class PayChannelDO extends BaseDO {
      *
      * 关联 {@link PayAppDO#getId()}
      */
-    private String appId;
-
-    // TODO 芋艿:不同渠道的配置。暂时考虑硬编码
+    private Long appId;
+    /**
+     * 支付渠道配置
+     */
+    @TableField(typeHandler = JacksonTypeHandler.class)
+    private PayClientConfig config;
 
 }

+ 9 - 2
yudao-core-service/src/main/java/cn/iocoder/yudao/coreservice/modules/pay/dal/dataobject/merchant/PayMerchantDO.java

@@ -3,15 +3,22 @@ package cn.iocoder.yudao.coreservice.modules.pay.dal.dataobject.merchant;
 import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
 import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
 import com.baomidou.mybatisplus.annotation.TableId;
-import lombok.Data;
+import com.baomidou.mybatisplus.annotation.TableName;
+import lombok.*;
 
 /**
- * 商户信息 DO
+ * 支付商户信息 DO
  * 目前暂时没有特别的用途,主要为未来多商户提供基础。
  *
  * @author 芋道源码
  */
 @Data
+@TableName("pay_merchant")
+@EqualsAndHashCode(callSuper = true)
+@ToString(callSuper = true)
+@Builder
+@NoArgsConstructor
+@AllArgsConstructor
 public class PayMerchantDO extends BaseDO {
 
     /**

+ 25 - 34
yudao-core-service/src/main/java/cn/iocoder/yudao/coreservice/modules/pay/dal/dataobject/order/PayOrderDO.java

@@ -3,15 +3,16 @@ package cn.iocoder.yudao.coreservice.modules.pay.dal.dataobject.order;
 import cn.iocoder.yudao.coreservice.modules.pay.dal.dataobject.merchant.PayAppDO;
 import cn.iocoder.yudao.coreservice.modules.pay.dal.dataobject.merchant.PayChannelDO;
 import cn.iocoder.yudao.coreservice.modules.pay.dal.dataobject.merchant.PayMerchantDO;
-import cn.iocoder.yudao.framework.pay.core.enums.PayChannelEnum;
 import cn.iocoder.yudao.coreservice.modules.pay.enums.order.PayOrderStatusEnum;
 import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
+import cn.iocoder.yudao.framework.pay.core.enums.PayChannelEnum;
+import com.baomidou.mybatisplus.annotation.TableField;
 import com.baomidou.mybatisplus.annotation.TableName;
-import lombok.Data;
-import lombok.EqualsAndHashCode;
-import lombok.ToString;
+import com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler;
+import lombok.*;
 
 import java.util.Date;
+import java.util.Map;
 
 /**
  * 支付订单 DO
@@ -22,6 +23,9 @@ import java.util.Date;
 @Data
 @EqualsAndHashCode(callSuper = true)
 @ToString(callSuper = true)
+@Builder
+@NoArgsConstructor
+@AllArgsConstructor
 public class PayOrderDO extends BaseDO {
 
     /**
@@ -68,10 +72,10 @@ public class PayOrderDO extends BaseDO {
      * 商品描述信息
      */
     private String body;
-    /**
-     * 商户拓展参数
-     */
-    private String merchantExtra;
+//    /**
+//     * 商户拓展参数
+//     */
+//    private Map<String, String> merchantExtras;
 
     // ========== 订单相关字段 ==========
 
@@ -80,13 +84,13 @@ public class PayOrderDO extends BaseDO {
      */
     private Long amount;
     /**
-     * 渠道手续费
+     * 渠道手续费,单位:百分比
      *
      * 冗余 {@link PayChannelDO#getFeeRate()}
      */
     private Double channelFeeRate;
     /**
-     * 渠道手续金额
+     * 渠道手续金额,单位:分
      */
     private Long channelFeeAmount;
     /**
@@ -101,47 +105,34 @@ public class PayOrderDO extends BaseDO {
      */
     private Integer notifyStatus;
     /**
-     * 客户端 IP
+     * 用户 IP
      */
-    private String clientIp;
-    /**
-     * 订单支付成功时间
-     */
-    private Date successTime;
+    private String userIp;
     /**
      * 订单失效时间
      */
     private Date expireTime;
     /**
-     * 支付渠道的额外参数
-     *
-     * 参见 https://www.pingxx.com/api/支付渠道%20extra%20参数说明.html
-     */
-    private String channelExtra;
-    /**
-     * 异步通知地址
-     */
-    private String notifyUrl;
-    /**
-     * 页面跳转地址
+     * 订单支付成功时间
      */
-    private String returnUrl;
+    private Date successTime;
     /**
      * 支付成功的订单拓展单编号
      *
      * 关联 {@link PayOrderDO#getId()}
      */
     private Long successExtensionId;
-
-    // TODO 芋艿:可能要优化
     /**
-     * 渠道支付错误码
+     * 支付渠道的额外参数
+     *
+     * 参见 https://www.pingxx.com/api/支付渠道%20extra%20参数说明.html
      */
-    private String errorCode;
+    @TableField(typeHandler = JacksonTypeHandler.class)
+    private Map<String, String> channelExtras;
     /**
-     * 渠道支付错误消息
+     * 异步通知地址
      */
-    private String errorMessage;
+    private String notifyUrl;
 
     // ========== 退款相关字段 ==========
     /**

+ 8 - 7
yudao-core-service/src/main/java/cn/iocoder/yudao/coreservice/modules/pay/dal/dataobject/order/PayOrderExtensionDO.java

@@ -4,9 +4,7 @@ import cn.iocoder.yudao.coreservice.modules.pay.dal.dataobject.merchant.PayChann
 import cn.iocoder.yudao.coreservice.modules.pay.enums.order.PayOrderStatusEnum;
 import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
 import com.baomidou.mybatisplus.annotation.TableName;
-import lombok.Data;
-import lombok.EqualsAndHashCode;
-import lombok.experimental.Accessors;
+import lombok.*;
 
 /**
  * 支付订单拓展 DO
@@ -17,7 +15,10 @@ import lombok.experimental.Accessors;
 @TableName("pay_order_extension")
 @Data
 @EqualsAndHashCode(callSuper = true)
-@Accessors(chain = true)
+@ToString(callSuper = true)
+@Builder
+@NoArgsConstructor
+@AllArgsConstructor
 public class PayOrderExtensionDO extends BaseDO {
 
     /**
@@ -25,7 +26,7 @@ public class PayOrderExtensionDO extends BaseDO {
      */
     private Long id;
     /**
-     * 订单号,根据规则生成
+     * 支付订单号,根据规则生成
      * 调用支付渠道时,使用该字段作为对接的订单号。
      * 1. 调用微信支付 https://api.mch.weixin.qq.com/pay/unifiedorder 时,使用该字段作为 out_trade_no
      * 2. 调用支付宝 https://opendocs.alipay.com/apis 时,使用该字段作为 out_trade_no
@@ -50,9 +51,9 @@ public class PayOrderExtensionDO extends BaseDO {
      */
     private Integer channelCode;
     /**
-     * 客户端 IP
+     * 用户 IP
      */
-    private String clientIp;
+    private String userIp;
     /**
      * 支付状态
      *

+ 6 - 16
yudao-core-service/src/main/java/cn/iocoder/yudao/coreservice/modules/pay/dal/dataobject/order/PayRefundDO.java

@@ -67,10 +67,10 @@ public class PayRefundDO extends BaseDO {
      * 例如说,内部系统 A 的退款订单号。需要保证每个 PayMerchantDO 唯一 TODO 芋艿:需要在测试下
      */
     private String merchantRefundNo;
-    /**
-     * 商户拓展参数
-     */
-    private String merchantExtra;
+//    /**
+//     * 商户拓展参数
+//     */
+//    private String merchantExtra;
 
     // ========== 退款相关字段 ==========
     /**
@@ -85,9 +85,9 @@ public class PayRefundDO extends BaseDO {
      */
     private Integer notifyStatus;
     /**
-     * 客户端 IP
+     * 用户 IP
      */
-    private String clientIp;
+    private String userIp;
     /**
      * 退款金额,单位:分
      */
@@ -115,16 +115,6 @@ public class PayRefundDO extends BaseDO {
      */
     private String notifyUrl;
 
-    // TODO 芋艿:可能要优化
-    /**
-     * 渠道支付错误码
-     */
-    private String errorCode;
-    /**
-     * 渠道支付错误消息
-     */
-    private String errorMessage;
-
     // ========== 渠道相关字段 ==========
     /**
      * 渠道订单号

+ 6 - 0
yudao-core-service/src/main/java/cn/iocoder/yudao/coreservice/modules/pay/dal/mysql/merchant/PayChannelCoreMapper.java

@@ -3,6 +3,9 @@ package cn.iocoder.yudao.coreservice.modules.pay.dal.mysql.merchant;
 import cn.iocoder.yudao.coreservice.modules.pay.dal.dataobject.merchant.PayChannelDO;
 import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
 import org.apache.ibatis.annotations.Mapper;
+import org.apache.ibatis.annotations.Select;
+
+import java.util.Date;
 
 @Mapper
 public interface PayChannelCoreMapper extends BaseMapperX<PayChannelDO> {
@@ -11,4 +14,7 @@ public interface PayChannelCoreMapper extends BaseMapperX<PayChannelDO> {
         return selectOne("app_id", appId, "code", code);
     }
 
+    @Select("SELECT id FROM pay_channel WHERE update_time > #{maxUpdateTime} LIMIT 1")
+    Long selectExistsByUpdateTimeAfter(Date maxUpdateTime);
+
 }

+ 4 - 3
yudao-core-service/src/main/java/cn/iocoder/yudao/coreservice/modules/pay/enums/PayErrorCodeConstants.java

@@ -3,11 +3,11 @@ package cn.iocoder.yudao.coreservice.modules.pay.enums;
 import cn.iocoder.yudao.framework.common.exception.ErrorCode;
 
 /**
- * Pay 错误码枚举类
+ * Pay 错误码 Core 枚举类
  *
  * pay 系统,使用 1-007-000-000 段
  */
-public interface PayErrorCodeConstants {
+public interface PayErrorCodeCoreConstants {
 
     // ========== APP 模块 1-007-000-000 ==========
     ErrorCode PAY_APP_NOT_FOUND = new ErrorCode(1007000000, "App 不存在");
@@ -15,7 +15,8 @@ public interface PayErrorCodeConstants {
 
     // ========== CHANNEL 模块 1-007-001-000 ==========
     ErrorCode PAY_CHANNEL_NOT_FOUND = new ErrorCode(1007001000, "支付渠道的配置不存在");
-    ErrorCode PAY_CHANNEL_IS_DISABLE = new ErrorCode(1007001000, "支付渠道已经禁用");
+    ErrorCode PAY_CHANNEL_IS_DISABLE = new ErrorCode(1007001001, "支付渠道已经禁用");
+    ErrorCode PAY_CHANNEL_CLIENT_NOT_FOUND = new ErrorCode(1007001002, "支付渠道的客户端不存在");
 
     // ========== ORDER 模块 1-007-002-000 ==========
     ErrorCode PAY_ORDER_NOT_FOUND = new ErrorCode(100401000, "支付订单不存在");

+ 5 - 0
yudao-core-service/src/main/java/cn/iocoder/yudao/coreservice/modules/pay/service/merchant/PayChannelCoreService.java

@@ -11,6 +11,11 @@ import cn.iocoder.yudao.framework.common.exception.ServiceException;
 public interface PayChannelCoreService {
 
     /**
+     * 初始化支付客户端
+     */
+    void initPayClients();
+
+    /**
      * 支付渠道的合法性
      *
      * 如果不合法,抛出 {@link ServiceException} 业务异常

+ 1 - 1
yudao-core-service/src/main/java/cn/iocoder/yudao/coreservice/modules/pay/service/merchant/impl/PayAppCoreServiceImpl.java

@@ -10,7 +10,7 @@ import org.springframework.stereotype.Service;
 import javax.annotation.Resource;
 import javax.validation.Valid;
 
-import static cn.iocoder.yudao.coreservice.modules.pay.enums.PayErrorCodeConstants.*;
+import static cn.iocoder.yudao.coreservice.modules.pay.enums.PayErrorCodeCoreConstants.*;
 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
 
 /**

+ 69 - 3
yudao-core-service/src/main/java/cn/iocoder/yudao/coreservice/modules/pay/service/merchant/impl/PayChannelCoreServiceImpl.java

@@ -1,17 +1,25 @@
 package cn.iocoder.yudao.coreservice.modules.pay.service.merchant.impl;
 
+import cn.hutool.core.collection.CollUtil;
 import cn.iocoder.yudao.coreservice.modules.pay.dal.dataobject.merchant.PayChannelDO;
 import cn.iocoder.yudao.coreservice.modules.pay.dal.mysql.merchant.PayChannelCoreMapper;
-import cn.iocoder.yudao.coreservice.modules.pay.enums.PayErrorCodeConstants;
+import cn.iocoder.yudao.coreservice.modules.pay.enums.PayErrorCodeCoreConstants;
 import cn.iocoder.yudao.coreservice.modules.pay.service.merchant.PayChannelCoreService;
 import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
+import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
+import cn.iocoder.yudao.framework.pay.core.client.PayClientFactory;
 import lombok.extern.slf4j.Slf4j;
+import org.springframework.scheduling.annotation.Scheduled;
 import org.springframework.stereotype.Service;
 
 import javax.annotation.Resource;
 import javax.validation.Valid;
 
-import static cn.iocoder.yudao.coreservice.modules.pay.enums.PayErrorCodeConstants.*;
+import java.util.Comparator;
+import java.util.Date;
+import java.util.List;
+
+import static cn.iocoder.yudao.coreservice.modules.pay.enums.PayErrorCodeCoreConstants.*;
 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
 
 /**
@@ -24,9 +32,67 @@ import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionU
 @Slf4j
 public class PayChannelCoreServiceImpl implements PayChannelCoreService {
 
+    /**
+     * 定时执行 {@link #schedulePeriodicRefresh()} 的周期
+     * 因为已经通过 Redis Pub/Sub 机制,所以频率不需要高
+     */
+    private static final long SCHEDULER_PERIOD = 5 * 60 * 1000L;
+
+    /**
+     * 缓存菜单的最大更新时间,用于后续的增量轮询,判断是否有更新
+     */
+    private volatile Date maxUpdateTime;
+
     @Resource
     private PayChannelCoreMapper payChannelCoreMapper;
 
+    @Resource
+    private PayClientFactory payClientFactory;
+
+    @Override
+    public void initPayClients() {
+        // 获取支付渠道,如果有更新
+        List<PayChannelDO> payChannels = this.loadPayChannelIfUpdate(maxUpdateTime);
+        if (CollUtil.isEmpty(payChannels)) {
+            return;
+        }
+
+        // 创建或更新支付 Client
+        payChannels.forEach(payChannel -> payClientFactory.createOrUpdatePayClient(payChannel.getId(),
+                payChannel.getCode(), payChannel.getConfig()));
+
+        // 写入缓存
+        assert payChannels.size() > 0; // 断言,避免告警
+        maxUpdateTime = payChannels.stream().max(Comparator.comparing(BaseDO::getUpdateTime)).get().getUpdateTime();
+        log.info("[initPayClients][初始化 PayChannel 数量为 {}]", payChannels.size());
+    }
+
+    @Scheduled(fixedDelay = SCHEDULER_PERIOD, initialDelay = SCHEDULER_PERIOD)
+    public void schedulePeriodicRefresh() {
+        initPayClients();
+    }
+
+    /**
+     * 如果支付渠道发生变化,从数据库中获取最新的全量支付渠道。
+     * 如果未发生变化,则返回空
+     *
+     * @param maxUpdateTime 当前支付渠道的最大更新时间
+     * @return 支付渠道列表
+     */
+    private List<PayChannelDO> loadPayChannelIfUpdate(Date maxUpdateTime) {
+        // 第一步,判断是否要更新。
+        if (maxUpdateTime == null) { // 如果更新时间为空,说明 DB 一定有新数据
+            log.info("[loadPayChannelIfUpdate][首次加载全量支付渠道]");
+        } else { // 判断数据库中是否有更新的支付渠道
+            if (payChannelCoreMapper.selectExistsByUpdateTimeAfter(maxUpdateTime) == null) {
+                return null;
+            }
+            log.info("[loadPayChannelIfUpdate][增量加载全量支付渠道]");
+        }
+        // 第二步,如果有更新,则从数据库加载所有支付渠道
+        return payChannelCoreMapper.selectList();
+    }
+
     @Override
     public PayChannelDO validPayChannel(Long appId, String code) {
         PayChannelDO channel = payChannelCoreMapper.selectByAppIdAndCode(appId, code);
@@ -34,7 +100,7 @@ public class PayChannelCoreServiceImpl implements PayChannelCoreService {
             throw exception(PAY_CHANNEL_NOT_FOUND);
         }
         if (CommonStatusEnum.DISABLE.getStatus().equals(channel.getStatus())) {
-            throw exception(PayErrorCodeConstants.PAY_CHANNEL_IS_DISABLE);
+            throw exception(PayErrorCodeCoreConstants.PAY_CHANNEL_IS_DISABLE);
         }
         return channel;
     }

+ 4 - 4
yudao-core-service/src/main/java/cn/iocoder/yudao/coreservice/modules/pay/service/order/dto/PayOrderCreateReqDTO.java

@@ -21,10 +21,10 @@ public class PayOrderCreateReqDTO implements Serializable {
     @NotEmpty(message = "应用编号不能为空")
     private Long appId;
     /**
-     * 客户端 IP
+     * 用户 IP
      */
-    @NotEmpty(message = "客户端 IP 不能为空")
-    private String clientIp;
+    @NotEmpty(message = "用户 IP 不能为空")
+    private String userIp;
 
     // ========== 商户相关字段 ==========
 
@@ -40,7 +40,7 @@ public class PayOrderCreateReqDTO implements Serializable {
     @Length(max = 32, message = "商品标题不能超过 32")
     private String subject;
     /**
-     * 商品描述信息
+     * 商品描述
      */
     @NotEmpty(message = "商品描述信息不能为空")
     @Length(max = 128, message = "商品描述信息长度不能超过128")

+ 3 - 3
yudao-core-service/src/main/java/cn/iocoder/yudao/coreservice/modules/pay/service/order/dto/PayOrderSubmitReqDTO.java

@@ -33,9 +33,9 @@ public class PayOrderSubmitReqDTO implements Serializable {
     private String channelCode;
 
     /**
-     * 客户端 IP
+     * 用户 IP
      */
-    @NotEmpty(message = "客户端 IP 不能为空")
-    private String clientIp;
+    @NotEmpty(message = "用户 IP 不能为空")
+    private String userIp;
 
 }

+ 16 - 5
yudao-core-service/src/main/java/cn/iocoder/yudao/coreservice/modules/pay/service/order/impl/PayOrderCoreServiceImpl.java

@@ -18,6 +18,8 @@ import cn.iocoder.yudao.coreservice.modules.pay.service.order.dto.PayOrderSubmit
 import cn.iocoder.yudao.coreservice.modules.pay.service.order.dto.PayOrderSubmitRespDTO;
 import cn.iocoder.yudao.framework.common.pojo.CommonResult;
 import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
+import cn.iocoder.yudao.framework.pay.core.client.PayClient;
+import cn.iocoder.yudao.framework.pay.core.client.PayClientFactory;
 import lombok.extern.slf4j.Slf4j;
 import org.springframework.stereotype.Service;
 
@@ -25,8 +27,7 @@ import javax.annotation.Resource;
 import javax.validation.Valid;
 import java.util.Date;
 
-import static cn.iocoder.yudao.coreservice.modules.pay.enums.PayErrorCodeConstants.PAY_ORDER_NOT_FOUND;
-import static cn.iocoder.yudao.coreservice.modules.pay.enums.PayErrorCodeConstants.PAY_ORDER_STATUS_IS_NOT_WAITING;
+import static cn.iocoder.yudao.coreservice.modules.pay.enums.PayErrorCodeCoreConstants.*;
 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
 
 /**
@@ -45,6 +46,9 @@ public class PayOrderCoreServiceImpl implements PayOrderCoreService {
     private PayChannelCoreService payChannelCoreService;
 
     @Resource
+    private PayClientFactory payClientFactory;
+
+    @Resource
     private PayOrderCoreMapper payOrderCoreMapper;
     @Resource
     private PayOrderExtensionCoreMapper payOrderExtensionCoreMapper;
@@ -79,6 +83,12 @@ public class PayOrderCoreServiceImpl implements PayOrderCoreService {
         PayAppDO app = payAppCoreService.validPayApp(reqDTO.getId());
         // 校验支付渠道是否有效
         PayChannelDO channel = payChannelCoreService.validPayChannel(reqDTO.getId(), reqDTO.getChannelCode());
+        // 校验支付客户端是否正确初始化
+        PayClient client = payClientFactory.getPayClient(channel.getId());
+        if (client == null) {
+            log.error("[submitPayOrder][渠道编号({}) 找不到对应的支付客户端]", channel.getId());
+            throw exception(PAY_CHANNEL_CLIENT_NOT_FOUND);
+        }
 
         // 获得 PayOrderDO ,并校验其是否存在
         PayOrderDO order = payOrderCoreMapper.selectById(reqDTO.getId());
@@ -96,13 +106,14 @@ public class PayOrderCoreServiceImpl implements PayOrderCoreService {
         payOrderExtensionCoreMapper.insert(orderExtension);
 
         // 调用三方接口
-        AbstractThirdPayClient thirdPayClient = ThirdPayClientFactory.getThirdPayClient(submitReqDTO.getPayChannel());
-        CommonResult<String> invokeResult = thirdPayClient.submitTransaction(payTransaction, orderExtension, null); // TODO 暂时传入 extra = null
+        // TODO 暂时传入 extra = null
+        CommonResult<?> invokeResult = client.unifiedOrder(PayOrderCoreConvert.INSTANCE.convert2(reqDTO));
         invokeResult.checkError();
 
         // TODO 轮询三方接口,是否已经支付的任务
         // 返回成功
-        return new PayOrderSubmitRespDTO().setExtensionId(orderExtension.getId()).setInvokeResponse(invokeResult.getData());
+        return new PayOrderSubmitRespDTO().setExtensionId(orderExtension.getId())
+                .setInvokeResponse(JsonUtils.toJsonString(invokeResult));
     }
 
     private String generateOrderExtensionNo() {

+ 38 - 0
yudao-core-service/src/test-integration/java/cn/iocoder/yudao/coreservice/BaseDbAndRedisIntegrationTest.java

@@ -0,0 +1,38 @@
+package cn.iocoder.yudao.coreservice;
+
+import cn.iocoder.yudao.framework.datasource.config.YudaoDataSourceAutoConfiguration;
+import cn.iocoder.yudao.framework.mybatis.config.YudaoMybatisAutoConfiguration;
+import cn.iocoder.yudao.framework.redis.config.YudaoRedisAutoConfiguration;
+import com.baomidou.dynamic.datasource.spring.boot.autoconfigure.DynamicDataSourceAutoConfiguration;
+import com.baomidou.mybatisplus.autoconfigure.MybatisPlusAutoConfiguration;
+import org.redisson.spring.starter.RedissonAutoConfiguration;
+import org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration;
+import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
+import org.springframework.boot.autoconfigure.jdbc.DataSourceTransactionManagerAutoConfiguration;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.context.annotation.Import;
+import org.springframework.test.context.ActiveProfiles;
+
+@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.NONE, classes = BaseDbAndRedisIntegrationTest.Application.class)
+@ActiveProfiles("integration-test") // 设置使用 application-integration-test 配置文件
+public class BaseDbAndRedisIntegrationTest {
+
+    @Import({
+            // DB 配置类
+            DynamicDataSourceAutoConfiguration.class, // Dynamic Datasource 配置类
+            YudaoDataSourceAutoConfiguration.class, // 自己的 DB 配置类
+            DataSourceAutoConfiguration.class, // Spring DB 自动配置类
+            DataSourceTransactionManagerAutoConfiguration.class, // Spring 事务自动配置类
+            // MyBatis 配置类
+            YudaoMybatisAutoConfiguration.class, // 自己的 MyBatis 配置类
+            MybatisPlusAutoConfiguration.class, // MyBatis 的自动配置类
+
+            // Redis 配置类
+            RedisAutoConfiguration.class, // Spring Redis 自动配置类
+            YudaoRedisAutoConfiguration.class, // 自己的 Redis 配置类
+            RedissonAutoConfiguration.class, // Redisson 自动高配置类
+    })
+    public static class Application {
+    }
+
+}

+ 30 - 0
yudao-core-service/src/test-integration/java/cn/iocoder/yudao/coreservice/BaseDbIntegrationTest.java

@@ -0,0 +1,30 @@
+package cn.iocoder.yudao.coreservice;
+
+import cn.iocoder.yudao.framework.datasource.config.YudaoDataSourceAutoConfiguration;
+import cn.iocoder.yudao.framework.mybatis.config.YudaoMybatisAutoConfiguration;
+import com.baomidou.dynamic.datasource.spring.boot.autoconfigure.DynamicDataSourceAutoConfiguration;
+import com.baomidou.mybatisplus.autoconfigure.MybatisPlusAutoConfiguration;
+import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
+import org.springframework.boot.autoconfigure.jdbc.DataSourceTransactionManagerAutoConfiguration;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.context.annotation.Import;
+import org.springframework.test.context.ActiveProfiles;
+
+@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.NONE, classes = BaseDbIntegrationTest.Application.class)
+@ActiveProfiles("integration-test") // 设置使用 application-integration-test 配置文件
+public class BaseDbIntegrationTest {
+
+    @Import({
+            // DB 配置类
+            DynamicDataSourceAutoConfiguration.class, // Dynamic Datasource 配置类
+            YudaoDataSourceAutoConfiguration.class, // 自己的 DB 配置类
+            DataSourceAutoConfiguration.class, // Spring DB 自动配置类
+            DataSourceTransactionManagerAutoConfiguration.class, // Spring 事务自动配置类
+            // MyBatis 配置类
+            YudaoMybatisAutoConfiguration.class, // 自己的 MyBatis 配置类
+            MybatisPlusAutoConfiguration.class, // MyBatis 的自动配置类
+    })
+    public static class Application {
+    }
+
+}

+ 23 - 0
yudao-core-service/src/test-integration/java/cn/iocoder/yudao/coreservice/BaseRedisIntegrationTest.java

@@ -0,0 +1,23 @@
+package cn.iocoder.yudao.coreservice;
+
+import cn.iocoder.yudao.framework.redis.config.YudaoRedisAutoConfiguration;
+import org.redisson.spring.starter.RedissonAutoConfiguration;
+import org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.context.annotation.Import;
+import org.springframework.test.context.ActiveProfiles;
+
+@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.NONE, classes = BaseRedisIntegrationTest.Application.class)
+@ActiveProfiles("integration-test") // 设置使用 application-integration-test 配置文件
+public class BaseRedisIntegrationTest {
+
+    @Import({
+            // Redis 配置类
+            RedisAutoConfiguration.class, // Spring Redis 自动配置类
+            YudaoRedisAutoConfiguration.class, // 自己的 Redis 配置类
+            RedissonAutoConfiguration.class, // Redisson 自动高配置类
+    })
+    public static class Application {
+    }
+
+}

+ 29 - 0
yudao-core-service/src/test-integration/java/cn/iocoder/yudao/coreservice/modules/pay/dal/dataobject/merchant/PayChannelDOTest.java

@@ -0,0 +1,29 @@
+package cn.iocoder.yudao.coreservice.modules.pay.dal.dataobject.merchant;
+
+import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
+import cn.iocoder.yudao.framework.pay.core.client.impl.wx.WXPayClientConfig;
+import org.junit.jupiter.api.Test;
+
+public class PayChannelDOTest {
+
+    @Test
+    public void testSerialization() {
+        PayChannelDO payChannelDO = new PayChannelDO();
+        // 创建配置
+        WXPayClientConfig config = new WXPayClientConfig();
+        config.setAppId("wx041349c6f39b268b");
+        config.setMchId("1545083881");
+        config.setApiVersion(WXPayClientConfig.API_VERSION_V2);
+        config.setMchKey("0alL64UDQdlCwiKZ73ib7ypaIjMns06p");
+        payChannelDO.setConfig(config);
+
+        // 序列化
+        String text = JsonUtils.toJsonString(payChannelDO);
+        System.out.println(text);
+
+        // 反序列化
+        payChannelDO = JsonUtils.parseObject(text, PayChannelDO.class);
+        System.out.println(payChannelDO.getConfig().getClass());
+    }
+
+}

+ 56 - 0
yudao-core-service/src/test-integration/java/cn/iocoder/yudao/coreservice/modules/pay/dal/mysql/merchant/PayChannelCoreMapperTest.java

@@ -0,0 +1,56 @@
+package cn.iocoder.yudao.coreservice.modules.pay.dal.mysql.merchant;
+
+import cn.hutool.core.io.IoUtil;
+import cn.iocoder.yudao.coreservice.BaseDbAndRedisIntegrationTest;
+import cn.iocoder.yudao.coreservice.modules.pay.dal.dataobject.merchant.PayChannelDO;
+import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
+import cn.iocoder.yudao.framework.pay.core.client.impl.wx.WXPayClientConfig;
+import cn.iocoder.yudao.framework.pay.core.enums.PayChannelEnum;
+import org.junit.jupiter.api.Test;
+
+import javax.annotation.Resource;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.util.List;
+
+@Resource
+public class PayChannelCoreMapperTest extends BaseDbAndRedisIntegrationTest {
+
+    @Resource
+    private PayChannelCoreMapper payChannelCoreMapper;
+
+    /**
+     * 插入初始配置
+     */
+    @Test
+    public void testInsert() throws FileNotFoundException {
+        PayChannelDO payChannelDO = new PayChannelDO();
+        payChannelDO.setCode(PayChannelEnum.WX_PUB.getCode());
+        payChannelDO.setStatus(CommonStatusEnum.ENABLE.getStatus());
+        payChannelDO.setFeeRate(1D);
+        payChannelDO.setMerchantId(1L);
+        payChannelDO.setAppId(6L);
+        // 配置
+        WXPayClientConfig config = new WXPayClientConfig();
+        config.setAppId("wx041349c6f39b268b");
+        config.setMchId("1545083881");
+        config.setApiVersion(WXPayClientConfig.API_VERSION_V2);
+        config.setMchKey("0alL64UDQdlCwiKZ73ib7ypaIjMns06p");
+        config.setPrivateKeyContent(IoUtil.readUtf8(new FileInputStream("/Users/yunai/Downloads/wx_pay/apiclient_key.pem")));
+        config.setPrivateCertContent(IoUtil.readUtf8(new FileInputStream("/Users/yunai/Downloads/wx_pay/apiclient_cert.pem")));
+        config.setApiV3Key("joerVi8y5DJ3o4ttA0o1uH47Xz1u2Ase");
+        payChannelDO.setConfig(config);
+        // 执行插入
+        payChannelCoreMapper.insert(payChannelDO);
+    }
+
+    /**
+     * 查询所有支付配置,看看是否都是 ok 的
+     */
+    @Test
+    public void testSelectList() {
+        List<PayChannelDO> payChannels = payChannelCoreMapper.selectList();
+        System.out.println(payChannels.size());
+    }
+
+}

+ 92 - 0
yudao-core-service/src/test-integration/resources/application-integration-test.yaml

@@ -0,0 +1,92 @@
+spring:
+  main:
+    lazy-initialization: true # 开启懒加载,加快速度
+    banner-mode: off # 单元测试,禁用 Banner
+
+--- #################### 数据库相关配置 ####################
+
+spring:
+  # 数据源配置项
+  autoconfigure:
+    exclude:
+      - com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceAutoConfigure # 排除 Druid 的自动配置,使用 dynamic-datasource-spring-boot-starter 配置多数据源
+  datasource:
+    druid: # Druid 【监控】相关的全局配置
+      web-stat-filter:
+        enabled: true
+    dynamic: # 多数据源配置
+      druid: # Druid 【连接池】相关的全局配置
+        initial-size: 5 # 初始连接数
+        min-idle: 10 # 最小连接池数量
+        max-active: 20 # 最大连接池数量
+        max-wait: 600000 # 配置获取连接等待超时的时间,单位:毫秒
+        time-between-eviction-runs-millis: 60000 # 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位:毫秒
+        min-evictable-idle-time-millis: 300000 # 配置一个连接在池中最小生存的时间,单位:毫秒
+        max-evictable-idle-time-millis: 900000 # 配置一个连接在池中最大生存的时间,单位:毫秒
+        validation-query: SELECT 1 FROM DUAL # 配置检测连接是否有效
+        test-while-idle: true
+        test-on-borrow: false
+        test-on-return: false
+      primary: master
+      datasource:
+        master:
+          name: ruoyi-vue-pro
+          url: jdbc:mysql://127.0.0.1:3306/${spring.datasource.dynamic.datasource.master.name}?useSSL=false&useUnicode=true&characterEncoding=UTF-8&serverTimezone=CTT
+          driver-class-name: com.mysql.jdbc.Driver
+          username: root
+          password: 123456
+        slave: # 模拟从库,可根据自己需要修改
+          name: ruoyi-vue-pro
+          url: jdbc:mysql://127.0.0.1:3306/${spring.datasource.dynamic.datasource.slave.name}?useSSL=false&useUnicode=true&characterEncoding=UTF-8&serverTimezone=CTT
+          driver-class-name: com.mysql.jdbc.Driver
+          username: root
+          password: 123456
+
+  # Redis 配置。Redisson 默认的配置足够使用,一般不需要进行调优
+  redis:
+    host: 127.0.0.1 # 地址
+    port: 6379 # 端口
+    database: 0 # 数据库索引
+
+mybatis:
+  lazy-initialization: true # 单元测试,设置 MyBatis Mapper 延迟加载,加速每个单元测试
+mybatis-plus:
+  configuration:
+    map-underscore-to-camel-case: true # 虽然默认为 true ,但是还是显示去指定下。
+    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl # 打印日志
+  global-config:
+    db-config:
+      id-type: AUTO # 自增 ID
+      logic-delete-value: 1 # 逻辑已删除值(默认为 1)
+      logic-not-delete-value: 0 # 逻辑未删除值(默认为 0)
+  mapper-locations: classpath*:mapper/*.xml
+  type-aliases-package: ${yudao.info.base-package}.modules.*.dal.dataobject, ${yudao.core-service.base-package}.modules.*.dal.dataobject
+
+--- #################### 定时任务相关配置 ####################
+
+--- #################### 配置中心相关配置 ####################
+
+--- #################### 服务保障相关配置 ####################
+
+# Lock4j 配置项(单元测试,禁用 Lock4j)
+
+# Resilience4j 配置项
+resilience4j:
+  ratelimiter:
+    instances:
+      backendA:
+        limit-for-period: 1 # 每个周期内,允许的请求数。默认为 50
+        limit-refresh-period: 60s # 每个周期的时长,单位:微秒。默认为 500
+        timeout-duration: 1s # 被限流时,阻塞等待的时长,单位:微秒。默认为 5s
+        register-health-indicator: true # 是否注册到健康监测
+
+--- #################### 监控相关配置 ####################
+
+--- #################### 芋道相关配置 ####################
+
+yudao:
+  info:
+    version: 1.0.0
+    base-package: cn.iocoder.yudao.adminserver
+  core-service:
+    base-package: cn.iocoder.yudao.coreservice

+ 6 - 10
yudao-framework/yudao-spring-boot-starter-biz-pay/pom.xml

@@ -22,6 +22,12 @@
             <artifactId>yudao-common</artifactId>
         </dependency>
 
+        <!-- Spring 核心 -->
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter</artifactId>
+        </dependency>
+
         <!-- 工具类相关 -->
         <dependency>
             <groupId>jakarta.validation</groupId>
@@ -47,16 +53,6 @@
         </dependency>
 
         <!-- 三方云服务相关 -->
-<!--        <dependency>-->
-<!--            <groupId>com.github.javen205</groupId>-->
-<!--            <artifactId>IJPay-AliPay</artifactId>-->
-<!--            <version>2.7.8</version>-->
-<!--        </dependency>-->
-<!--        <dependency>-->
-<!--            <groupId>com.github.javen205</groupId>-->
-<!--            <artifactId>IJPay-WxPay</artifactId>-->
-<!--            <version>2.7.8</version>-->
-<!--        </dependency>-->
         <dependency>
             <groupId>com.alipay.sdk</groupId>
             <artifactId>alipay-sdk-java</artifactId>

+ 21 - 0
yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/config/YudaoPayAutoConfiguration.java

@@ -0,0 +1,21 @@
+package cn.iocoder.yudao.framework.pay.config;
+
+import cn.iocoder.yudao.framework.pay.core.client.PayClientFactory;
+import cn.iocoder.yudao.framework.pay.core.client.impl.PayClientFactoryImpl;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+/**
+ * 支付配置类
+ *
+ * @author 芋道源码
+ */
+@Configuration
+public class YudaoPayAutoConfiguration {
+
+    @Bean
+    public PayClientFactory payClientFactory() {
+        return new PayClientFactoryImpl();
+    }
+
+}

+ 6 - 0
yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/PayClientConfig.java

@@ -1,10 +1,16 @@
 package cn.iocoder.yudao.framework.pay.core.client;
 
+import com.fasterxml.jackson.annotation.JsonTypeInfo;
+
 /**
  * 支付客户端的配置,本质是支付渠道的配置
  * 每个不同的渠道,需要不同的配置,通过子类来定义
  *
  * @author 芋道源码
  */
+@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS)
+// @JsonTypeInfo 注解的作用,Jackson 多态
+// 1. 序列化到时数据库时,增加 @class 属性。
+// 2. 反序列化到内存对象时,通过 @class 属性,可以创建出正确的类型
 public interface PayClientConfig {
 }

+ 3 - 3
yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/dto/PayOrderUnifiedReqDTO.java

@@ -16,10 +16,10 @@ import java.util.Date;
 public class PayOrderUnifiedReqDTO {
 
     /**
-     * 客户端 IP
+     * 用户 IP
      */
-    @NotEmpty(message = "客户端 IP 不能为空")
-    private String clientIp;
+    @NotEmpty(message = "用户 IP 不能为空")
+    private String userIp;
 
     // ========== 商户相关字段 ==========
 

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

@@ -49,7 +49,7 @@ public class AlipayQrPayClient extends AbstractPayClient<AlipayPayClientConfig>
         model.setSubject(reqDTO.getSubject());
         model.setBody(reqDTO.getBody());
         model.setTotalAmount(calculateAmount(reqDTO.getAmount()).toString()); // 单位:元
-        // TODO 芋艿:clientIp + expireTime
+        // TODO 芋艿:userIp + expireTime
         // 构建 AlipayTradePrecreateRequest
         AlipayTradePrecreateRequest request = new AlipayTradePrecreateRequest();
         request.setBizModel(model);

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

@@ -46,7 +46,7 @@ public class AlipayWapPayClient extends AbstractPayClient<AlipayPayClientConfig>
         model.setTotalAmount(calculateAmount(reqDTO.getAmount()).toString());
         model.setProductCode("QUICK_WAP_PAY"); // TODO 芋艿:这里咋整
         model.setSellerId("2088102147948060"); // TODO 芋艿:这里咋整
-        // TODO 芋艿:clientIp + expireTime
+        // TODO 芋艿:userIp + expireTime
         // 构建 AlipayTradeWapPayRequest
         AlipayTradeWapPayRequest request = new AlipayTradeWapPayRequest();
         request.setBizModel(model);