Browse Source

完成支付的下单和提交订单的逻辑

YunaiV 3 years ago
parent
commit
ab19228ca6
24 changed files with 251 additions and 94 deletions
  1. 0 28
      yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/pay/enums/PayChannelCodeEnum.java
  2. 14 21
      yudao-core-service/src/main/java/cn/iocoder/yudao/coreservice/modules/pay/dal/dataobject/order/PayOrderDO.java
  3. 12 1
      yudao-core-service/src/main/java/cn/iocoder/yudao/coreservice/modules/pay/dal/dataobject/order/PayOrderExtensionDO.java
  4. 9 9
      yudao-core-service/src/main/java/cn/iocoder/yudao/coreservice/modules/pay/dal/dataobject/order/PayRefundDO.java
  5. 29 0
      yudao-core-service/src/main/java/cn/iocoder/yudao/coreservice/modules/pay/enums/order/PayOrderNotifyStatusEnum.java
  6. 29 0
      yudao-core-service/src/main/java/cn/iocoder/yudao/coreservice/modules/pay/enums/order/PayOrderRefundStatusEnum.java
  7. 2 0
      yudao-core-service/src/main/java/cn/iocoder/yudao/coreservice/modules/pay/service/merchant/impl/PayChannelCoreServiceImpl.java
  8. 1 1
      yudao-core-service/src/main/java/cn/iocoder/yudao/coreservice/modules/pay/service/order/PayOrderCoreService.java
  9. 9 0
      yudao-core-service/src/main/java/cn/iocoder/yudao/coreservice/modules/pay/service/order/dto/PayOrderSubmitReqDTO.java
  10. 28 12
      yudao-core-service/src/main/java/cn/iocoder/yudao/coreservice/modules/pay/service/order/impl/PayOrderCoreServiceImpl.java
  11. 52 0
      yudao-core-service/src/test-integration/java/cn/iocoder/yudao/coreservice/modules/pay/service/order/PayOrderCoreServiceTest.java
  12. 1 0
      yudao-core-service/src/test-integration/java/cn/iocoder/yudao/coreservice/modules/pay/service/package-info.java
  13. 1 1
      yudao-core-service/src/test-integration/resources/application-integration-test.yaml
  14. 9 0
      yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/object/ObjectUtils.java
  15. 7 3
      yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/PayClient.java
  16. 1 1
      yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/dto/PayOrderUnifiedReqDTO.java
  17. 26 2
      yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/AbstractPayClient.java
  18. 1 2
      yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/alipay/AlipayQrPayClient.java
  19. 1 2
      yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/alipay/AlipayWapPayClient.java
  20. 6 0
      yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/wx/WXCodeMapping.java
  21. 8 9
      yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/wx/WXPubPayClient.java
  22. 1 0
      yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/enums/PayFrameworkErrorCodeConstants.java
  23. 2 0
      yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/resources/META-INF/spring.factories
  24. 2 2
      yudao-framework/yudao-spring-boot-starter-biz-pay/src/test-integration/java/cn/iocoder/yudao/framework/core/client/impl/PayClientFactoryImplTest.java

+ 0 - 28
yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/pay/enums/PayChannelCodeEnum.java

@@ -1,28 +0,0 @@
-package cn.iocoder.yudao.adminserver.modules.pay.enums;
-
-import lombok.AllArgsConstructor;
-import lombok.Getter;
-
-/**
- * 支付渠道的编码的枚举
- *
- * @author 芋道源码
- */
-@Getter
-@AllArgsConstructor
-public enum PayChannelCodeEnum {
-
-    wx_pub("wx_pub", "微信 JSAPI 支付");
-
-    /**
-     * 编码
-     *
-     * 参考 https://www.pingxx.com/api/支付渠道属性值.html
-     */
-    private String code;
-    /**
-     * 名字
-     */
-    private String name;
-
-}

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

@@ -3,16 +3,15 @@ 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.coreservice.modules.pay.enums.order.PayOrderNotifyStatusEnum;
+import cn.iocoder.yudao.coreservice.modules.pay.enums.order.PayOrderRefundStatusEnum;
 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 com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler;
 import lombok.*;
 
 import java.util.Date;
-import java.util.Map;
 
 /**
  * 支付订单 DO
@@ -61,7 +60,7 @@ public class PayOrderDO extends BaseDO {
 
     /**
      * 商户订单编号
-     * 例如说,内部系统 A 的订单号。需要保证每个 PayMerchantDO 唯一 TODO 芋艿:需要在测试下
+     * 例如说,内部系统 A 的订单号。需要保证每个 PayMerchantDO 唯一
      */
     private String merchantOrderId;
     /**
@@ -72,6 +71,16 @@ public class PayOrderDO extends BaseDO {
      * 商品描述信息
      */
     private String body;
+    /**
+     * 异步通知地址
+     */
+    private String notifyUrl;
+    /**
+     * 通知商户支付结果的回调状态
+     *
+     * 枚举 {@link PayOrderNotifyStatusEnum}
+     */
+    private Integer notifyStatus;
 //    /**
 //     * 商户拓展参数
 //     */
@@ -99,11 +108,6 @@ public class PayOrderDO extends BaseDO {
      * 枚举 {@link PayOrderStatusEnum}
      */
     private Integer status;
-    /**
-     * 通知商户支付结果的回调状态
-     * TODO 芋艿:0 未发送 1 已发送
-     */
-    private Integer notifyStatus;
     /**
      * 用户 IP
      */
@@ -122,23 +126,12 @@ public class PayOrderDO extends BaseDO {
      * 关联 {@link PayOrderDO#getId()}
      */
     private Long successExtensionId;
-    /**
-     * 支付渠道的额外参数
-     *
-     * 参见 https://www.pingxx.com/api/支付渠道%20extra%20参数说明.html
-     */
-    @TableField(typeHandler = JacksonTypeHandler.class)
-    private Map<String, String> channelExtras;
-    /**
-     * 异步通知地址
-     */
-    private String notifyUrl;
 
     // ========== 退款相关字段 ==========
     /**
      * 退款状态
      *
-     * TODO 芋艿:0 - 未退款;1 - 部分退款; 2 - 全额退款
+     * 枚举 {@link PayOrderRefundStatusEnum}
      */
     private Integer refundStatus;
     /**

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

@@ -3,9 +3,13 @@ package cn.iocoder.yudao.coreservice.modules.pay.dal.dataobject.order;
 import cn.iocoder.yudao.coreservice.modules.pay.dal.dataobject.merchant.PayChannelDO;
 import cn.iocoder.yudao.coreservice.modules.pay.enums.order.PayOrderStatusEnum;
 import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
+import com.baomidou.mybatisplus.annotation.TableField;
 import com.baomidou.mybatisplus.annotation.TableName;
+import com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler;
 import lombok.*;
 
+import java.util.Map;
+
 /**
  * 支付订单拓展 DO
  *
@@ -49,7 +53,7 @@ public class PayOrderExtensionDO extends BaseDO {
     /**
      * 渠道编码
      */
-    private Integer channelCode;
+    private String channelCode;
     /**
      * 用户 IP
      */
@@ -61,6 +65,13 @@ public class PayOrderExtensionDO extends BaseDO {
      * 注意,只包含上述枚举的 WAITING 和 SUCCESS
      */
     private Integer status;
+    /**
+     * 支付渠道的额外参数
+     *
+     * 参见 https://www.pingxx.com/api/支付渠道%20extra%20参数说明.html
+     */
+    @TableField(typeHandler = JacksonTypeHandler.class)
+    private Map<String, String> channelExtras;
     /**
      * 支付渠道异步通知的内容
      *

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

@@ -71,6 +71,15 @@ public class PayRefundDO extends BaseDO {
 //     * 商户拓展参数
 //     */
 //    private String merchantExtra;
+    /**
+     * 异步通知地址
+     */
+    private String notifyUrl;
+    /**
+     * 通知商户退款结果的回调状态
+     * TODO 芋艿:0 未发送 1 已发送
+     */
+    private Integer notifyStatus;
 
     // ========== 退款相关字段 ==========
     /**
@@ -79,11 +88,6 @@ public class PayRefundDO extends BaseDO {
      * TODO 芋艿:状态枚举
      */
     private Integer status;
-    /**
-     * 通知商户退款结果的回调状态
-     * TODO 芋艿:0 未发送 1 已发送
-     */
-    private Integer notifyStatus;
     /**
      * 用户 IP
      */
@@ -110,10 +114,6 @@ public class PayRefundDO extends BaseDO {
      * 参见 https://www.pingxx.com/api/Refunds%20退款概述.html
      */
     private String channelExtra;
-    /**
-     * 异步通知地址
-     */
-    private String notifyUrl;
 
     // ========== 渠道相关字段 ==========
     /**

+ 29 - 0
yudao-core-service/src/main/java/cn/iocoder/yudao/coreservice/modules/pay/enums/order/PayOrderNotifyStatusEnum.java

@@ -0,0 +1,29 @@
+package cn.iocoder.yudao.coreservice.modules.pay.enums.order;
+
+import cn.iocoder.yudao.framework.common.core.IntArrayValuable;
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+
+/**
+ * 支付订单的通知状态枚举
+ *
+ * @author 芋道源码
+ */
+@Getter
+@AllArgsConstructor
+public enum PayOrderNotifyStatusEnum implements IntArrayValuable {
+
+    NO(0, "未通知"),
+    SUCCESS(10, "通知成功"),
+    FAILURE(20, "通知失败")
+    ;
+
+    private final Integer status;
+    private final String name;
+
+    @Override
+    public int[] array() {
+        return new int[0];
+    }
+
+}

+ 29 - 0
yudao-core-service/src/main/java/cn/iocoder/yudao/coreservice/modules/pay/enums/order/PayOrderRefundStatusEnum.java

@@ -0,0 +1,29 @@
+package cn.iocoder.yudao.coreservice.modules.pay.enums.order;
+
+import cn.iocoder.yudao.framework.common.core.IntArrayValuable;
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+
+/**
+ * 支付订单的退款状态枚举
+ *
+ * @author 芋道源码
+ */
+@Getter
+@AllArgsConstructor
+public enum PayOrderRefundStatusEnum implements IntArrayValuable {
+
+    NO(0, "未退款"),
+    SOME(10, "部分退款"),
+    ALL(20, "全部退款")
+    ;
+
+    private final Integer status;
+    private final String name;
+
+    @Override
+    public int[] array() {
+        return new int[0];
+    }
+
+}

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

@@ -12,6 +12,7 @@ import lombok.extern.slf4j.Slf4j;
 import org.springframework.scheduling.annotation.Scheduled;
 import org.springframework.stereotype.Service;
 
+import javax.annotation.PostConstruct;
 import javax.annotation.Resource;
 import javax.validation.Valid;
 
@@ -50,6 +51,7 @@ public class PayChannelCoreServiceImpl implements PayChannelCoreService {
     private PayClientFactory payClientFactory;
 
     @Override
+    @PostConstruct
     public void initPayClients() {
         // 获取支付渠道,如果有更新
         List<PayChannelDO> payChannels = this.loadPayChannelIfUpdate(maxUpdateTime);

+ 1 - 1
yudao-core-service/src/main/java/cn/iocoder/yudao/coreservice/modules/pay/service/order/PayOrderCoreService.java

@@ -28,6 +28,6 @@ public interface PayOrderCoreService {
     * @param reqDTO 提交请求
     * @return 提交结果
     */
-   PayOrderSubmitRespDTO submitPayOrder(PayOrderSubmitReqDTO reqDTO);
+   PayOrderSubmitRespDTO submitPayOrder(@Valid PayOrderSubmitReqDTO reqDTO);
 
 }

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

@@ -1,11 +1,14 @@
 package cn.iocoder.yudao.coreservice.modules.pay.service.order.dto;
 
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler;
 import lombok.Data;
 import lombok.experimental.Accessors;
 
 import javax.validation.constraints.NotEmpty;
 import javax.validation.constraints.NotNull;
 import java.io.Serializable;
+import java.util.Map;
 
 /**
  * 支付单提交 Request DTO
@@ -38,4 +41,10 @@ public class PayOrderSubmitReqDTO implements Serializable {
     @NotEmpty(message = "用户 IP 不能为空")
     private String userIp;
 
+    /**
+     * 支付渠道的额外参数
+     */
+    @TableField(typeHandler = JacksonTypeHandler.class)
+    private Map<String, String> channelExtras;
+
 }

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

@@ -9,6 +9,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.dal.mysql.order.PayOrderCoreMapper;
 import cn.iocoder.yudao.coreservice.modules.pay.dal.mysql.order.PayOrderExtensionCoreMapper;
+import cn.iocoder.yudao.coreservice.modules.pay.enums.order.PayOrderNotifyStatusEnum;
 import cn.iocoder.yudao.coreservice.modules.pay.enums.order.PayOrderStatusEnum;
 import cn.iocoder.yudao.coreservice.modules.pay.service.merchant.PayAppCoreService;
 import cn.iocoder.yudao.coreservice.modules.pay.service.merchant.PayChannelCoreService;
@@ -20,12 +21,14 @@ 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 cn.iocoder.yudao.framework.pay.core.client.dto.PayOrderUnifiedReqDTO;
 import lombok.extern.slf4j.Slf4j;
 import org.springframework.stereotype.Service;
+import org.springframework.validation.annotation.Validated;
 
 import javax.annotation.Resource;
-import javax.validation.Valid;
 import java.util.Date;
+import java.util.Objects;
 
 import static cn.iocoder.yudao.coreservice.modules.pay.enums.PayErrorCodeCoreConstants.*;
 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
@@ -36,7 +39,7 @@ import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionU
  * @author 芋道源码
  */
 @Service
-@Valid
+@Validated
 @Slf4j
 public class PayOrderCoreServiceImpl implements PayOrderCoreService {
 
@@ -68,10 +71,16 @@ public class PayOrderCoreServiceImpl implements PayOrderCoreService {
         }
 
         // 创建支付交易单
-        // TODO 芋艿:需要看看,还有啥要补全的字段
         order = PayOrderCoreConvert.INSTANCE.convert(reqDTO)
-                .setStatus(PayOrderStatusEnum.WAITING.getStatus())
-                .setNotifyUrl(app.getPayNotifyUrl());
+                .setMerchantId(app.getMerchantId()).setAppId(app.getId());
+        // 商户相关字段
+        order.setNotifyUrl(app.getPayNotifyUrl())
+                .setNotifyStatus(PayOrderNotifyStatusEnum.NO.getStatus());
+        // 订单相关字段
+        order.setStatus(PayOrderStatusEnum.WAITING.getStatus());
+        // 退款相关字段
+        order.setRefundStatus(PayOrderNotifyStatusEnum.NO.getStatus())
+                .setRefundTimes(0).setRefundAmount(0L);
         payOrderCoreMapper.insert(order);
         // 最终返回
         return order.getId();
@@ -80,9 +89,9 @@ public class PayOrderCoreServiceImpl implements PayOrderCoreService {
     @Override
     public PayOrderSubmitRespDTO submitPayOrder(PayOrderSubmitReqDTO reqDTO) {
         // 校验 App
-        PayAppDO app = payAppCoreService.validPayApp(reqDTO.getId());
+        PayAppDO app = payAppCoreService.validPayApp(reqDTO.getAppId());
         // 校验支付渠道是否有效
-        PayChannelDO channel = payChannelCoreService.validPayChannel(reqDTO.getId(), reqDTO.getChannelCode());
+        PayChannelDO channel = payChannelCoreService.validPayChannel(reqDTO.getAppId(), reqDTO.getChannelCode());
         // 校验支付客户端是否正确初始化
         PayClient client = payClientFactory.getPayClient(channel.getId());
         if (client == null) {
@@ -92,7 +101,7 @@ public class PayOrderCoreServiceImpl implements PayOrderCoreService {
 
         // 获得 PayOrderDO ,并校验其是否存在
         PayOrderDO order = payOrderCoreMapper.selectById(reqDTO.getId());
-        if (order == null || order.getAppId().equals(reqDTO.getAppId())) { // 是否存在
+        if (order == null || !Objects.equals(order.getAppId(), reqDTO.getAppId())) { // 是否存在
             throw exception(PAY_ORDER_NOT_FOUND);
         }
         if (!PayOrderStatusEnum.WAITING.getStatus().equals(order.getStatus())) { // 校验状态,必须是待支付
@@ -102,18 +111,25 @@ public class PayOrderCoreServiceImpl implements PayOrderCoreService {
         // 插入 PayOrderExtensionDO
         PayOrderExtensionDO orderExtension = PayOrderCoreConvert.INSTANCE.convert(reqDTO)
                 .setOrderId(order.getId()).setNo(generateOrderExtensionNo())
+                .setChannelId(channel.getId()).setChannelCode(channel.getCode())
                 .setStatus(PayOrderStatusEnum.WAITING.getStatus());
         payOrderExtensionCoreMapper.insert(orderExtension);
 
         // 调用三方接口
-        // TODO 暂时传入 extra = null
-        CommonResult<?> invokeResult = client.unifiedOrder(PayOrderCoreConvert.INSTANCE.convert2(reqDTO));
-        invokeResult.checkError();
+        PayOrderUnifiedReqDTO unifiedOrderReqDTO = PayOrderCoreConvert.INSTANCE.convert2(reqDTO);
+        // 商户相关字段
+        unifiedOrderReqDTO.setMerchantOrderId(order.getMerchantOrderId())
+                .setSubject(order.getSubject()).setBody(order.getBody())
+                .setNotifyUrl(app.getPayNotifyUrl());
+        // 订单相关字段
+        unifiedOrderReqDTO.setAmount(order.getAmount()).setExpireTime(order.getExpireTime());
+        CommonResult<?> unifiedOrderResult = client.unifiedOrder(unifiedOrderReqDTO);
+        unifiedOrderResult.checkError();
 
         // TODO 轮询三方接口,是否已经支付的任务
         // 返回成功
         return new PayOrderSubmitRespDTO().setExtensionId(orderExtension.getId())
-                .setInvokeResponse(JsonUtils.toJsonString(invokeResult));
+                .setInvokeResponse(JsonUtils.toJsonString(unifiedOrderResult));
     }
 
     private String generateOrderExtensionNo() {

+ 52 - 0
yudao-core-service/src/test-integration/java/cn/iocoder/yudao/coreservice/modules/pay/service/order/PayOrderCoreServiceTest.java

@@ -0,0 +1,52 @@
+package cn.iocoder.yudao.coreservice.modules.pay.service.order;
+
+import cn.iocoder.yudao.coreservice.BaseDbIntegrationTest;
+import cn.iocoder.yudao.coreservice.modules.pay.service.merchant.impl.PayAppCoreServiceImpl;
+import cn.iocoder.yudao.coreservice.modules.pay.service.merchant.impl.PayChannelCoreServiceImpl;
+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.coreservice.modules.pay.service.order.impl.PayOrderCoreServiceImpl;
+import cn.iocoder.yudao.framework.common.util.date.DateUtils;
+import cn.iocoder.yudao.framework.pay.config.YudaoPayAutoConfiguration;
+import cn.iocoder.yudao.framework.pay.core.enums.PayChannelEnum;
+import org.junit.jupiter.api.Test;
+import org.springframework.context.annotation.Import;
+
+import javax.annotation.Resource;
+import java.time.Duration;
+
+@Import({PayOrderCoreServiceImpl.class, PayAppCoreServiceImpl.class,
+        PayChannelCoreServiceImpl.class, YudaoPayAutoConfiguration.class})
+public class PayOrderCoreServiceTest extends BaseDbIntegrationTest {
+
+    @Resource
+    private PayOrderCoreService payOrderCoreService;
+
+    @Test
+    public void testCreatePayOrder() {
+        // 构造请求
+        PayOrderCreateReqDTO reqDTO = new PayOrderCreateReqDTO();
+        reqDTO.setAppId(6L);
+        reqDTO.setUserIp("127.0.0.1");
+        reqDTO.setMerchantOrderId(String.valueOf(System.currentTimeMillis()));
+        reqDTO.setSubject("标题");
+        reqDTO.setBody("内容");
+        reqDTO.setAmount(100);
+        reqDTO.setExpireTime(DateUtils.addTime(Duration.ofDays(1)));
+        // 发起请求
+        payOrderCoreService.createPayOrder(reqDTO);
+    }
+
+    @Test
+    public void testSubmitPayOrder() {
+        // 构造请求
+        PayOrderSubmitReqDTO reqDTO = new PayOrderSubmitReqDTO();
+        reqDTO.setId(10L);
+        reqDTO.setAppId(6L);
+        reqDTO.setChannelCode(PayChannelEnum.WX_PUB.getCode());
+        reqDTO.setUserIp("127.0.0.1");
+        // 发起请求
+        payOrderCoreService.submitPayOrder(reqDTO);
+    }
+
+}

+ 1 - 0
yudao-core-service/src/test-integration/java/cn/iocoder/yudao/coreservice/modules/pay/service/package-info.java

@@ -0,0 +1 @@
+package cn.iocoder.yudao.coreservice.modules.pay.service;

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

@@ -60,7 +60,7 @@ mybatis-plus:
       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
+  type-aliases-package: ${yudao.core-service.base-package}.modules.*.dal.dataobject
 
 --- #################### 定时任务相关配置 ####################
 

+ 9 - 0
yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/object/ObjectUtils.java

@@ -29,4 +29,13 @@ public class ObjectUtils {
         return obj1.compareTo(obj2) > 0 ? obj1 : obj2;
     }
 
+    public static <T> T defaultIfNull(T... array) {
+        for (T item : array) {
+            if (item != null) {
+                return item;
+            }
+        }
+        return null;
+    }
+
 }

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

@@ -1,6 +1,5 @@
 package cn.iocoder.yudao.framework.pay.core.client;
 
-import cn.iocoder.yudao.framework.common.pojo.CommonResult;
 import cn.iocoder.yudao.framework.pay.core.client.dto.PayOrderUnifiedReqDTO;
 
 /**
@@ -17,7 +16,12 @@ public interface PayClient {
      */
     Long getId();
 
-    // TODO 缺少注释
-    CommonResult<?> unifiedOrder(PayOrderUnifiedReqDTO reqDTO);
+    /**
+     * 调用支付渠道,统一下单
+     *
+     * @param reqDTO 下单信息
+     * @return 各支付渠道的返回结果
+     */
+    PayCommonResult<?> unifiedOrder(PayOrderUnifiedReqDTO reqDTO);
 
 }

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

@@ -54,7 +54,7 @@ public class PayOrderUnifiedReqDTO {
      */
     @NotNull(message = "支付金额不能为空")
     @DecimalMin(value = "0", inclusive = false, message = "支付金额必须大于零")
-    private Integer amount;
+    private Long amount;
 
     /**
      * 支付过期时间

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

@@ -1,10 +1,15 @@
 package cn.iocoder.yudao.framework.pay.core.client.impl;
 
+import cn.hutool.extra.validation.ValidationUtil;
+import cn.iocoder.yudao.framework.pay.core.client.AbstractPayCodeMapping;
 import cn.iocoder.yudao.framework.pay.core.client.PayClient;
 import cn.iocoder.yudao.framework.pay.core.client.PayClientConfig;
-import cn.iocoder.yudao.framework.pay.core.client.AbstractPayCodeMapping;
+import cn.iocoder.yudao.framework.pay.core.client.PayCommonResult;
+import cn.iocoder.yudao.framework.pay.core.client.dto.PayOrderUnifiedReqDTO;
 import lombok.extern.slf4j.Slf4j;
 
+import static cn.iocoder.yudao.framework.common.util.json.JsonUtils.toJsonString;
+
 /**
  * 支付客户端的抽象类,提供模板方法,减少子类的冗余代码
  *
@@ -30,7 +35,7 @@ public abstract class AbstractPayClient<Config extends PayClientConfig> implemen
      */
     protected Config config;
 
-    protected Double calculateAmount(Integer amount) {
+    protected Double calculateAmount(Long amount) {
         return amount / 100.0;
     }
 
@@ -70,4 +75,23 @@ public abstract class AbstractPayClient<Config extends PayClientConfig> implemen
         return channelId;
     }
 
+    @Override
+    public final PayCommonResult<?> unifiedOrder(PayOrderUnifiedReqDTO reqDTO) {
+        ValidationUtil.validate(reqDTO);
+        // 执行短信发送
+        PayCommonResult<?> result;
+        try {
+            result = doUnifiedOrder(reqDTO);
+        } catch (Throwable ex) {
+            // 打印异常日志
+            log.error("[unifiedOrder][request({}) 发起支付失败]", toJsonString(reqDTO), ex);
+            // 封装返回
+            return PayCommonResult.error(ex);
+        }
+        return result;
+    }
+
+    protected abstract PayCommonResult<?> doUnifiedOrder(PayOrderUnifiedReqDTO reqDTO)
+            throws Throwable;
+
 }

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

@@ -1,7 +1,6 @@
 package cn.iocoder.yudao.framework.pay.core.client.impl.alipay;
 
 import cn.hutool.core.bean.BeanUtil;
-import cn.iocoder.yudao.framework.common.pojo.CommonResult;
 import cn.iocoder.yudao.framework.pay.core.client.PayCommonResult;
 import cn.iocoder.yudao.framework.pay.core.client.dto.PayOrderUnifiedReqDTO;
 import cn.iocoder.yudao.framework.pay.core.client.impl.AbstractPayClient;
@@ -42,7 +41,7 @@ public class AlipayQrPayClient extends AbstractPayClient<AlipayPayClientConfig>
     }
 
     @Override
-    public CommonResult<AlipayTradePrecreateResponse> unifiedOrder(PayOrderUnifiedReqDTO reqDTO) {
+    public PayCommonResult<AlipayTradePrecreateResponse> doUnifiedOrder(PayOrderUnifiedReqDTO reqDTO) {
         // 构建 AlipayTradePrecreateModel 请求
         AlipayTradePrecreateModel model = new AlipayTradePrecreateModel();
         model.setOutTradeNo(reqDTO.getMerchantOrderId());

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

@@ -1,7 +1,6 @@
 package cn.iocoder.yudao.framework.pay.core.client.impl.alipay;
 
 import cn.hutool.core.bean.BeanUtil;
-import cn.iocoder.yudao.framework.common.pojo.CommonResult;
 import cn.iocoder.yudao.framework.pay.core.client.PayCommonResult;
 import cn.iocoder.yudao.framework.pay.core.client.dto.PayOrderUnifiedReqDTO;
 import cn.iocoder.yudao.framework.pay.core.client.impl.AbstractPayClient;
@@ -37,7 +36,7 @@ public class AlipayWapPayClient extends AbstractPayClient<AlipayPayClientConfig>
     }
 
     @Override
-    public CommonResult<AlipayTradeWapPayResponse> unifiedOrder(PayOrderUnifiedReqDTO reqDTO) {
+    public PayCommonResult<AlipayTradeWapPayResponse> doUnifiedOrder(PayOrderUnifiedReqDTO reqDTO) {
         // 构建 AlipayTradeWapPayModel 请求
         AlipayTradeWapPayModel model = new AlipayTradeWapPayModel();
         model.setOutTradeNo(reqDTO.getMerchantOrderId());

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

@@ -1,5 +1,6 @@
 package cn.iocoder.yudao.framework.pay.core.client.impl.wx;
 
+import cn.hutool.core.util.StrUtil;
 import cn.iocoder.yudao.framework.common.exception.ErrorCode;
 import cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeConstants;
 import cn.iocoder.yudao.framework.pay.core.client.AbstractPayCodeMapping;
@@ -44,6 +45,11 @@ public class WXCodeMapping extends AbstractPayCodeMapping {
                 return PAY_OPENID_ERROR;
             }
         }
+        if (Objects.equals(apiCode, "CustomErrorCode")) {
+            if (StrUtil.contains(apiMsg, "必填字段")) {
+                return PAY_PARAM_MISSING;
+            }
+        }
         return null;
     }
 

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

@@ -3,8 +3,8 @@ package cn.iocoder.yudao.framework.pay.core.client.impl.wx;
 import cn.hutool.core.bean.BeanUtil;
 import cn.hutool.core.date.DateUtil;
 import cn.hutool.core.util.StrUtil;
-import cn.iocoder.yudao.framework.common.pojo.CommonResult;
 import cn.iocoder.yudao.framework.common.util.io.FileUtils;
+import cn.iocoder.yudao.framework.common.util.object.ObjectUtils;
 import cn.iocoder.yudao.framework.pay.core.client.PayCommonResult;
 import cn.iocoder.yudao.framework.pay.core.client.dto.PayOrderUnifiedReqDTO;
 import cn.iocoder.yudao.framework.pay.core.client.impl.AbstractPayClient;
@@ -21,7 +21,6 @@ import com.github.binarywang.wxpay.service.WxPayService;
 import com.github.binarywang.wxpay.service.impl.WxPayServiceImpl;
 import lombok.extern.slf4j.Slf4j;
 
-import static cn.hutool.core.util.ObjectUtil.defaultIfNull;
 import static cn.iocoder.yudao.framework.common.util.json.JsonUtils.toJsonString;
 import static cn.iocoder.yudao.framework.pay.core.client.impl.wx.WXCodeMapping.CODE_SUCCESS;
 import static cn.iocoder.yudao.framework.pay.core.client.impl.wx.WXCodeMapping.MESSAGE_SUCCESS;
@@ -62,7 +61,7 @@ public class WXPubPayClient extends AbstractPayClient<WXPayClientConfig> {
     }
 
     @Override
-    public CommonResult<WxPayMpOrderResult> unifiedOrder(PayOrderUnifiedReqDTO reqDTO) {
+    public PayCommonResult<WxPayMpOrderResult> doUnifiedOrder(PayOrderUnifiedReqDTO reqDTO) {
         WxPayMpOrderResult response;
         try {
             switch (config.getApiVersion()) {
@@ -80,8 +79,8 @@ public class WXPubPayClient extends AbstractPayClient<WXPayClientConfig> {
             }
         } catch (WxPayException e) {
             log.error("[unifiedOrder][request({}) 发起支付失败,原因({})]", toJsonString(reqDTO), e);
-            return PayCommonResult.build(defaultIfNull(e.getErrCode(), e.getReturnCode()),
-                    defaultIfNull(e.getErrCodeDes(), e.getReturnMsg()),null, codeMapping);
+            return PayCommonResult.build(ObjectUtils.defaultIfNull(e.getErrCode(), e.getReturnCode(), "CustomErrorCode"),
+                    ObjectUtils.defaultIfNull(e.getErrCodeDes(), e.getCustomErrorMsg()),null, codeMapping);
         }
         return PayCommonResult.build(CODE_SUCCESS, MESSAGE_SUCCESS, response, codeMapping);
     }
@@ -92,9 +91,9 @@ public class WXPubPayClient extends AbstractPayClient<WXPayClientConfig> {
                 .outTradeNo(reqDTO.getMerchantOrderId())
                 // TODO 芋艿:貌似没 title?
                 .body(reqDTO.getBody())
-                .totalFee(reqDTO.getAmount()) // 单位分
+                .totalFee(reqDTO.getAmount().intValue()) // 单位分
                 .timeExpire(DateUtil.format(reqDTO.getExpireTime(), "yyyyMMddHHmmss"))
-                .spbillCreateIp(reqDTO.getClientIp())
+                .spbillCreateIp(reqDTO.getUserIp())
                 .openid("ockUAwIZ-0OeMZl9ogcZ4ILrGba0") // TODO 芋艿:先随便写死
                 .notifyUrl(reqDTO.getNotifyUrl())
                 .build();
@@ -108,10 +107,10 @@ public class WXPubPayClient extends AbstractPayClient<WXPayClientConfig> {
         request.setOutTradeNo(reqDTO.getMerchantOrderId());
         // TODO 芋艿:貌似没 title?
         request.setDescription(reqDTO.getBody());
-        request.setAmount(new WxPayUnifiedOrderV3Request.Amount().setTotal(reqDTO.getAmount())); // 单位分
+        request.setAmount(new WxPayUnifiedOrderV3Request.Amount().setTotal(reqDTO.getAmount().intValue())); // 单位分
         request.setTimeExpire(DateUtil.format(reqDTO.getExpireTime(), "yyyyMMddHHmmss"));
         request.setPayer(new WxPayUnifiedOrderV3Request.Payer().setOpenid("ockUAwIZ-0OeMZl9ogcZ4ILrGba0")); // TODO 芋艿:先随便写死
-        request.setSceneInfo(new WxPayUnifiedOrderV3Request.SceneInfo().setPayerClientIp(reqDTO.getClientIp()));
+        request.setSceneInfo(new WxPayUnifiedOrderV3Request.SceneInfo().setPayerClientIp(reqDTO.getUserIp()));
         request.setNotifyUrl(reqDTO.getNotifyUrl());
         // 执行请求
         return client.createOrderV3(TradeTypeEnum.JSAPI, request);

+ 1 - 0
yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/enums/PayFrameworkErrorCodeConstants.java

@@ -20,6 +20,7 @@ public interface PayFrameworkErrorCodeConstants {
 
     // ========== 其它相关 2002000900 开头 ==========
     ErrorCode PAY_OPENID_ERROR = new ErrorCode(2002000900, "无效的 openid"); // 例如说,微信 openid 未授权过
+    ErrorCode PAY_PARAM_MISSING = new ErrorCode(2002000901, "请求参数缺失"); // 例如说,支付少传了金额
 
     ErrorCode EXCEPTION = new ErrorCode(2002000999, "调用异常");
 

+ 2 - 0
yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/resources/META-INF/spring.factories

@@ -0,0 +1,2 @@
+org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
+  cn.iocoder.yudao.framework.pay.config.YudaoPayAutoConfiguration

+ 2 - 2
yudao-framework/yudao-spring-boot-starter-biz-pay/src/test-integration/java/cn/iocoder/yudao/framework/core/client/impl/PayClientFactoryImplTest.java

@@ -117,11 +117,11 @@ public class PayClientFactoryImplTest {
 
     private static PayOrderUnifiedReqDTO buildPayOrderUnifiedReqDTO() {
         PayOrderUnifiedReqDTO reqDTO = new PayOrderUnifiedReqDTO();
-        reqDTO.setAmount(123);
+        reqDTO.setAmount(123L);
         reqDTO.setSubject("IPhone 13");
         reqDTO.setBody("biubiubiu");
         reqDTO.setMerchantOrderId(String.valueOf(System.currentTimeMillis()));
-        reqDTO.setClientIp("127.0.0.1");
+        reqDTO.setUserIp("127.0.0.1");
         reqDTO.setNotifyUrl("http://127.0.0.1:8080");
         return reqDTO;
     }