完成支付单的单体轮廓

This commit is contained in:
YunaiV 2021-10-19 07:41:45 +08:00
parent 81126b2b4b
commit 24f4fd4fee
11 changed files with 215 additions and 48 deletions

View File

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

View File

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

View File

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

View File

@ -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);
}
}

View File

@ -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> {
}

View File

@ -13,4 +13,18 @@ public interface PayErrorCodeConstants {
ErrorCode PAY_APP_NOT_FOUND = new ErrorCode(1007000000, "App 不存在"); ErrorCode PAY_APP_NOT_FOUND = new ErrorCode(1007000000, "App 不存在");
ErrorCode PAY_APP_IS_DISABLE = new ErrorCode(1007000002, "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, "支付订单不处于已支付");
} }

View File

@ -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);
}

View File

@ -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;
}
}

View File

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

View File

@ -1,11 +1,17 @@
package cn.iocoder.yudao.coreservice.modules.pay.service.order.impl; 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.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.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.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.enums.order.PayOrderStatusEnum;
import cn.iocoder.yudao.coreservice.modules.pay.service.merchant.PayAppCoreService; 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.PayOrderCoreService;
import cn.iocoder.yudao.coreservice.modules.pay.service.order.dto.PayOrderCreateReqDTO; 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.dto.PayOrderSubmitReqDTO;
@ -17,9 +23,16 @@ import org.springframework.stereotype.Service;
import javax.annotation.Resource; import javax.annotation.Resource;
import javax.validation.Valid; 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 实现类 * 支付订单 Core Service 实现类
*
* @author 芋道源码
*/ */
@Service @Service
@Valid @Valid
@ -28,9 +41,13 @@ public class PayOrderCoreServiceImpl implements PayOrderCoreService {
@Resource @Resource
private PayAppCoreService payAppCoreService; private PayAppCoreService payAppCoreService;
@Resource
private PayChannelCoreService payChannelCoreService;
@Resource @Resource
private PayOrderCoreMapper payOrderCoreMapper; private PayOrderCoreMapper payOrderCoreMapper;
@Resource
private PayOrderExtensionCoreMapper payOrderExtensionCoreMapper;
@Override @Override
public Long createPayOrder(PayOrderCreateReqDTO reqDTO) { 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()) .setStatus(PayOrderStatusEnum.WAITING.getStatus())
.setNotifyUrl(app.getPayNotifyUrl()); .setNotifyUrl(app.getPayNotifyUrl());
payOrderCoreMapper.insert(order); payOrderCoreMapper.insert(order);
@ -59,31 +77,50 @@ public class PayOrderCoreServiceImpl implements PayOrderCoreService {
public PayOrderSubmitRespDTO submitPayOrder(PayOrderSubmitReqDTO reqDTO) { public PayOrderSubmitRespDTO submitPayOrder(PayOrderSubmitReqDTO reqDTO) {
// 校验 App // 校验 App
PayAppDO app = payAppCoreService.validPayApp(reqDTO.getId()); PayAppDO app = payAppCoreService.validPayApp(reqDTO.getId());
// TODO 校验支付渠道是否有效 // 校验支付渠道是否有效
PayChannelDO channel = payChannelCoreService.validPayChannel(reqDTO.getId(), reqDTO.getChannelCode());
// 获得 PayOrderDO 并校验其是否存在 // 获得 PayOrderDO 并校验其是否存在
PayOrderDO order = payOrderCoreMapper.selectById(reqDTO.getId()); PayOrderDO order = payOrderCoreMapper.selectById(reqDTO.getId());
if (order == null) { // 是否存在 if (order == null || order.getAppId().equals(reqDTO.getAppId())) { // 是否存在
throw exception(PAY_TRANSACTION_NOT_FOUND); throw exception(PAY_ORDER_NOT_FOUND);
} }
if (!PayOrderStatusEnum.WAITING.getStatus().equals(order.getStatus())) { // 校验状态必须是待支付 if (!PayOrderStatusEnum.WAITING.getStatus().equals(order.getStatus())) { // 校验状态必须是待支付
throw exception(PAY_TRANSACTION_STATUS_IS_NOT_WAITING); throw exception(PAY_ORDER_STATUS_IS_NOT_WAITING);
} }
// 插入 PayTransactionExtensionDO // 插入 PayOrderExtensionDO
PayTransactionExtensionDO payTransactionExtensionDO = PayTransactionConvert.INSTANCE.convert(submitReqDTO) PayOrderExtensionDO orderExtension = PayOrderCoreConvert.INSTANCE.convert(reqDTO)
.setTransactionId(payTransaction.getId()).setTransactionCode(generateTransactionCode()) .setOrderId(order.getId()).setNo(generateOrderExtensionNo())
.setStatus(PayTransactionStatusEnum.WAITING.getStatus()); .setStatus(PayOrderStatusEnum.WAITING.getStatus());
payTransactionExtensionMapper.insert(payTransactionExtensionDO); payOrderExtensionCoreMapper.insert(orderExtension);
// 调用三方接口 // 调用三方接口
AbstractThirdPayClient thirdPayClient = ThirdPayClientFactory.getThirdPayClient(submitReqDTO.getPayChannel()); 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(); invokeResult.checkError();
// TODO 轮询三方接口是否已经支付的任务 // 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) // 随机为什么是这个范围因为偷懒
;
} }
} }

View File

@ -28,6 +28,10 @@ public interface BaseMapperX<T> extends BaseMapper<T> {
return selectOne(new QueryWrapper<T>().eq(field, value)); 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) { default Integer selectCount(String field, Object value) {
return selectCount(new QueryWrapper<T>().eq(field, value)); return selectCount(new QueryWrapper<T>().eq(field, value));
} }