Преглед на файлове

完成支付单的单体轮廓

YunaiV преди 3 години
родител
ревизия
24f4fd4fee
променени са 11 файла, в които са добавени 210 реда и са изтрити 43 реда
  1. 6 2
      yudao-core-service/src/main/java/cn/iocoder/yudao/coreservice/modules/pay/convert/order/PayOrderCoreConvert.java
  2. 7 7
      yudao-core-service/src/main/java/cn/iocoder/yudao/coreservice/modules/pay/dal/dataobject/order/PayOrderDO.java
  3. 35 19
      yudao-core-service/src/main/java/cn/iocoder/yudao/coreservice/modules/pay/dal/dataobject/order/PayOrderExtensionDO.java
  4. 14 0
      yudao-core-service/src/main/java/cn/iocoder/yudao/coreservice/modules/pay/dal/mysql/merchant/PayChannelCoreMapper.java
  5. 12 0
      yudao-core-service/src/main/java/cn/iocoder/yudao/coreservice/modules/pay/dal/mysql/order/PayOrderExtensionCoreMapper.java
  6. 14 0
      yudao-core-service/src/main/java/cn/iocoder/yudao/coreservice/modules/pay/enums/PayErrorCodeConstants.java
  7. 24 0
      yudao-core-service/src/main/java/cn/iocoder/yudao/coreservice/modules/pay/service/merchant/PayChannelCoreService.java
  8. 42 0
      yudao-core-service/src/main/java/cn/iocoder/yudao/coreservice/modules/pay/service/merchant/impl/PayChannelCoreServiceImpl.java
  9. 2 2
      yudao-core-service/src/main/java/cn/iocoder/yudao/coreservice/modules/pay/service/order/dto/PayOrderSubmitReqDTO.java
  10. 50 13
      yudao-core-service/src/main/java/cn/iocoder/yudao/coreservice/modules/pay/service/order/impl/PayOrderCoreServiceImpl.java
  11. 4 0
      yudao-framework/yudao-spring-boot-starter-mybatis/src/main/java/cn/iocoder/yudao/framework/mybatis/core/mapper/BaseMapperX.java

+ 6 - 2
yudao-core-service/src/main/java/cn/iocoder/yudao/coreservice/modules/pay/convert/order/PayOrderCoreServiceConvert.java → yudao-core-service/src/main/java/cn/iocoder/yudao/coreservice/modules/pay/convert/order/PayOrderCoreConvert.java

@@ -1,15 +1,19 @@
 package cn.iocoder.yudao.coreservice.modules.pay.convert.order;
 
 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 org.mapstruct.Mapper;
 import org.mapstruct.factory.Mappers;
 
 @Mapper
-public interface PayOrderCoreServiceConvert {
+public interface PayOrderCoreConvert {
 
-    PayOrderCoreServiceConvert INSTANCE = Mappers.getMapper(PayOrderCoreServiceConvert.class);
+    PayOrderCoreConvert INSTANCE = Mappers.getMapper(PayOrderCoreConvert.class);
 
     PayOrderDO convert(PayOrderCreateReqDTO bean);
 
+    PayOrderExtensionDO convert(PayOrderSubmitReqDTO bean);
+
 }

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

@@ -28,12 +28,6 @@ public class PayOrderDO extends BaseDO {
      * 订单编号,数据库自增
      */
     private Long id;
-//    /**
-//     * 订单号,根据规则生成
-//     *
-//     * 例如说,P202110132239124200055
-//     */
-//    private String no;
     /**
      * 商户编号
      *
@@ -53,7 +47,7 @@ public class PayOrderDO extends BaseDO {
      */
     private Long channelId;
     /**
-     * 商户编码
+     * 渠道编码
      *
      * 枚举 {@link PayChannelCodeEnum}
      */
@@ -132,6 +126,12 @@ public class PayOrderDO extends BaseDO {
      * 页面跳转地址
      */
     private String returnUrl;
+    /**
+     * 支付成功的订单拓展单编号
+     *
+     * 关联 {@link PayOrderDO#getId()}
+     */
+    private Long successExtensionId;
 
     // TODO 芋艿:可能要优化
     /**

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

@@ -1,5 +1,7 @@
 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.TableName;
 import lombok.Data;
@@ -7,48 +9,62 @@ import lombok.EqualsAndHashCode;
 import lombok.experimental.Accessors;
 
 /**
- * 交易扩展表
+ * 支付订单拓展 DO
+ *
+ *
+ * @author 芋道源码
  */
-@TableName("pay_transaction_extension")
+@TableName("pay_order_extension")
 @Data
 @EqualsAndHashCode(callSuper = true)
 @Accessors(chain = true)
 public class PayOrderExtensionDO extends BaseDO {
 
     /**
-     * 编号,自增
+     * 订单拓展编号,数据库自增
      */
-    private Integer id;
+    private Long id;
     /**
-     * 交易编号 {@link PayTransactionDO#getId()}
+     * 订单号,根据规则生成
+     * 调用支付渠道时,使用该字段作为对接的订单号。
+     * 1. 调用微信支付 https://api.mch.weixin.qq.com/pay/unifiedorder 时,使用该字段作为 out_trade_no
+     * 2. 调用支付宝 https://opendocs.alipay.com/apis 时,使用该字段作为 out_trade_no
+     *
+     * 例如说,P202110132239124200055
      */
-    private Integer transactionId;
+    private String no;
     /**
-     * 选择的支付渠道
+     * 订单号
+     *
+     * 关联 {@link PayOrderDO#getId()}
      */
-    private Integer payChannel;
+    private Long orderId;
     /**
-     * 生成传输给第三方的订单
+     * 渠道编
      *
-     * 唯一索引
+     * 关联 {@link PayChannelDO#getId()}
      */
-    private String transactionCode;
+    private Long channelId;
     /**
-     * 扩展内容
-     *
-     * 异步通知的时候填充回调的数据
+     * 渠道编码
      */
-    private String extensionData;
+    private Integer channelCode;
     /**
-     * 发起交易的 IP
+     * 客户端 IP
      */
-    private String createIp;
+    private String clientIp;
     /**
-     * 状态
+     * 支付状态
      *
-     * @see cn.iocoder.mall.payservice.enums.transaction.PayTransactionStatusEnum
+     * 枚举 {@link PayOrderStatusEnum}
      * 注意,只包含上述枚举的 WAITING 和 SUCCESS
      */
     private Integer status;
+    /**
+     * 支付渠道异步通知的内容
+     *
+     * 在支持成功后,会记录回调的数据
+     */
+    private String channelCallbackData;
 
 }

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

@@ -0,0 +1,14 @@
+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;
+
+@Mapper
+public interface PayChannelCoreMapper extends BaseMapperX<PayChannelDO> {
+
+    default PayChannelDO selectByAppIdAndCode(Long appId, String code) {
+        return selectOne("app_id", appId, "code", code);
+    }
+
+}

+ 12 - 0
yudao-core-service/src/main/java/cn/iocoder/yudao/coreservice/modules/pay/dal/mysql/order/PayOrderExtensionCoreMapper.java

@@ -0,0 +1,12 @@
+package cn.iocoder.yudao.coreservice.modules.pay.dal.mysql.order;
+
+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.framework.mybatis.core.mapper.BaseMapperX;
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import org.apache.ibatis.annotations.Mapper;
+
+@Mapper
+public interface PayOrderExtensionCoreMapper extends BaseMapperX<PayOrderExtensionDO> {
+
+}

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

@@ -13,4 +13,18 @@ public interface PayErrorCodeConstants {
     ErrorCode PAY_APP_NOT_FOUND = new ErrorCode(1007000000, "App 不存在");
     ErrorCode PAY_APP_IS_DISABLE = new ErrorCode(1007000002, "App 已经被禁用");
 
+    // ========== CHANNEL 模块 1-007-001-000 ==========
+    ErrorCode PAY_CHANNEL_NOT_FOUND = new ErrorCode(1007001000, "支付渠道的配置不存在");
+    ErrorCode PAY_CHANNEL_IS_DISABLE = new ErrorCode(1007001000, "支付渠道已经禁用");
+
+    // ========== ORDER 模块 1-007-002-000 ==========
+    ErrorCode PAY_ORDER_NOT_FOUND = new ErrorCode(100401000, "支付订单不存在");
+    ErrorCode PAY_ORDER_STATUS_IS_NOT_WAITING = new ErrorCode(100401001, "支付订单不处于待支付");
+    ErrorCode PAY_ORDER_STATUS_IS_NOT_SUCCESS = new ErrorCode(100401002, "支付订单不处于已支付");
+    ErrorCode PAY_ORDER_ERROR_USER = new ErrorCode(100401003, "支付订单用户不正确");
+
+    ErrorCode PAY_ORDER_EXTENSION_NOT_FOUND = new ErrorCode(100401050, "支付交易拓展单不存在");
+    ErrorCode PAY_ORDER_EXTENSION_STATUS_IS_NOT_WAITING = new ErrorCode(100401051, "支付交易拓展单不处于待支付");
+    ErrorCode PAY_ORDER_EXTENSION_STATUS_IS_NOT_SUCCESS = new ErrorCode(100401052, "支付订单不处于已支付");
+
 }

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

@@ -0,0 +1,24 @@
+package cn.iocoder.yudao.coreservice.modules.pay.service.merchant;
+
+import cn.iocoder.yudao.coreservice.modules.pay.dal.dataobject.merchant.PayChannelDO;
+import cn.iocoder.yudao.framework.common.exception.ServiceException;
+
+/**
+ * 支付渠道 Core Service 接口
+ *
+ * @author 芋道源码
+ */
+public interface PayChannelCoreService {
+
+    /**
+     * 支付渠道的合法性
+     *
+     * 如果不合法,抛出 {@link ServiceException} 业务异常
+     *
+     * @param appId 应用编号
+     * @param code 支付渠道
+     * @return 渠道信息
+     */
+    PayChannelDO validPayChannel(Long appId, String code);
+
+}

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

@@ -0,0 +1,42 @@
+package cn.iocoder.yudao.coreservice.modules.pay.service.merchant.impl;
+
+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.service.merchant.PayChannelCoreService;
+import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
+import lombok.extern.slf4j.Slf4j;
+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.framework.common.exception.util.ServiceExceptionUtil.exception;
+
+/**
+ * 支付渠道 Core Service 实现类
+ *
+ * @author 芋道源码
+ */
+@Service
+@Valid
+@Slf4j
+public class PayChannelCoreServiceImpl implements PayChannelCoreService {
+
+    @Resource
+    private PayChannelCoreMapper payChannelCoreMapper;
+
+    @Override
+    public PayChannelDO validPayChannel(Long appId, String code) {
+        PayChannelDO channel = payChannelCoreMapper.selectByAppIdAndCode(appId, code);
+        if (channel == null) {
+            throw exception(PAY_CHANNEL_NOT_FOUND);
+        }
+        if (CommonStatusEnum.DISABLE.getStatus().equals(channel.getStatus())) {
+            throw exception(PayErrorCodeConstants.PAY_CHANNEL_IS_DISABLE);
+        }
+        return channel;
+    }
+
+}

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

@@ -17,8 +17,8 @@ public class PayOrderSubmitReqDTO implements Serializable {
     /**
      * 应用编号
      */
-    @NotEmpty(message = "应用编号不能为空")
-    private String appId;
+    @NotNull(message = "应用编号不能为空")
+    private Long appId;
 
     /**
      * 支付单编号

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

@@ -1,11 +1,17 @@
 package cn.iocoder.yudao.coreservice.modules.pay.service.order.impl;
 
-import cn.iocoder.yudao.coreservice.modules.pay.convert.order.PayOrderCoreServiceConvert;
+import cn.hutool.core.date.DateUtil;
+import cn.hutool.core.util.RandomUtil;
+import cn.iocoder.yudao.coreservice.modules.pay.convert.order.PayOrderCoreConvert;
 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.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.PayOrderStatusEnum;
 import cn.iocoder.yudao.coreservice.modules.pay.service.merchant.PayAppCoreService;
+import cn.iocoder.yudao.coreservice.modules.pay.service.merchant.PayChannelCoreService;
 import cn.iocoder.yudao.coreservice.modules.pay.service.order.PayOrderCoreService;
 import cn.iocoder.yudao.coreservice.modules.pay.service.order.dto.PayOrderCreateReqDTO;
 import cn.iocoder.yudao.coreservice.modules.pay.service.order.dto.PayOrderSubmitReqDTO;
@@ -17,9 +23,16 @@ import org.springframework.stereotype.Service;
 
 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.framework.common.exception.util.ServiceExceptionUtil.exception;
 
 /**
  * 支付订单 Core Service 实现类
+ *
+ * @author 芋道源码
  */
 @Service
 @Valid
@@ -28,9 +41,13 @@ public class PayOrderCoreServiceImpl implements PayOrderCoreService {
 
     @Resource
     private PayAppCoreService payAppCoreService;
+    @Resource
+    private PayChannelCoreService payChannelCoreService;
 
     @Resource
     private PayOrderCoreMapper payOrderCoreMapper;
+    @Resource
+    private PayOrderExtensionCoreMapper payOrderExtensionCoreMapper;
 
     @Override
     public Long createPayOrder(PayOrderCreateReqDTO reqDTO) {
@@ -47,7 +64,8 @@ public class PayOrderCoreServiceImpl implements PayOrderCoreService {
         }
 
         // 创建支付交易单
-        order = PayOrderCoreServiceConvert.INSTANCE.convert(reqDTO)
+        // TODO 芋艿:需要看看,还有啥要补全的字段
+        order = PayOrderCoreConvert.INSTANCE.convert(reqDTO)
                 .setStatus(PayOrderStatusEnum.WAITING.getStatus())
                 .setNotifyUrl(app.getPayNotifyUrl());
         payOrderCoreMapper.insert(order);
@@ -59,31 +77,50 @@ public class PayOrderCoreServiceImpl implements PayOrderCoreService {
     public PayOrderSubmitRespDTO submitPayOrder(PayOrderSubmitReqDTO reqDTO) {
         // 校验 App
         PayAppDO app = payAppCoreService.validPayApp(reqDTO.getId());
-        // TODO 校验支付渠道是否有效
+        // 校验支付渠道是否有效
+        PayChannelDO channel = payChannelCoreService.validPayChannel(reqDTO.getId(), reqDTO.getChannelCode());
 
         // 获得 PayOrderDO ,并校验其是否存在
         PayOrderDO order = payOrderCoreMapper.selectById(reqDTO.getId());
-        if (order == null) { // 是否存在
-            throw exception(PAY_TRANSACTION_NOT_FOUND);
+        if (order == null || order.getAppId().equals(reqDTO.getAppId())) { // 是否存在
+            throw exception(PAY_ORDER_NOT_FOUND);
         }
         if (!PayOrderStatusEnum.WAITING.getStatus().equals(order.getStatus())) { // 校验状态,必须是待支付
-            throw exception(PAY_TRANSACTION_STATUS_IS_NOT_WAITING);
+            throw exception(PAY_ORDER_STATUS_IS_NOT_WAITING);
         }
 
-        // 插入 PayTransactionExtensionDO
-        PayTransactionExtensionDO payTransactionExtensionDO = PayTransactionConvert.INSTANCE.convert(submitReqDTO)
-                .setTransactionId(payTransaction.getId()).setTransactionCode(generateTransactionCode())
-                .setStatus(PayTransactionStatusEnum.WAITING.getStatus());
-        payTransactionExtensionMapper.insert(payTransactionExtensionDO);
+        // 插入 PayOrderExtensionDO
+        PayOrderExtensionDO orderExtension = PayOrderCoreConvert.INSTANCE.convert(reqDTO)
+                .setOrderId(order.getId()).setNo(generateOrderExtensionNo())
+                .setStatus(PayOrderStatusEnum.WAITING.getStatus());
+        payOrderExtensionCoreMapper.insert(orderExtension);
 
         // 调用三方接口
         AbstractThirdPayClient thirdPayClient = ThirdPayClientFactory.getThirdPayClient(submitReqDTO.getPayChannel());
-        CommonResult<String> invokeResult = thirdPayClient.submitTransaction(payTransaction, payTransactionExtensionDO, null); // TODO 暂时传入 extra = null
+        CommonResult<String> invokeResult = thirdPayClient.submitTransaction(payTransaction, orderExtension, null); // TODO 暂时传入 extra = null
         invokeResult.checkError();
 
         // TODO 轮询三方接口,是否已经支付的任务
         // 返回成功
-        return new PayTransactionSubmitRespDTO().setId(payTransactionExtensionDO.getId()).setInvokeResponse(invokeResult.getData());
+        return new PayOrderSubmitRespDTO().setExtensionId(orderExtension.getId()).setInvokeResponse(invokeResult.getData());
+    }
+
+    private String generateOrderExtensionNo() {
+//    wx
+//    2014
+//    10
+//    27
+//    20
+//    09
+//    39
+//    5522657
+//    a690389285100
+        // 目前的算法
+        // 时间序列,年月日时分秒 14 位
+        // 纯随机,6 位 TODO 芋艿:此处估计是会有问题的,后续在调整
+        return DateUtil.format(new Date(), "yyyyMMddHHmmss") + // 时间序列
+                RandomUtil.randomInt(100000, 999999) // 随机。为什么是这个范围,因为偷懒
+                ;
     }
 
 }

+ 4 - 0
yudao-framework/yudao-spring-boot-starter-mybatis/src/main/java/cn/iocoder/yudao/framework/mybatis/core/mapper/BaseMapperX.java

@@ -28,6 +28,10 @@ public interface BaseMapperX<T> extends BaseMapper<T> {
         return selectOne(new QueryWrapper<T>().eq(field, value));
     }
 
+    default T selectOne(String field1, Object value1, String field2, Object value2) {
+        return selectOne(new QueryWrapper<T>().eq(field1, value1).eq(field2, value2));
+    }
+
     default Integer selectCount(String field, Object value) {
         return selectCount(new QueryWrapper<T>().eq(field, value));
     }