diff --git a/yudao-core-service/src/main/java/cn/iocoder/yudao/coreservice/modules/pay/convert/order/PayOrderCoreServiceConvert.java b/yudao-core-service/src/main/java/cn/iocoder/yudao/coreservice/modules/pay/convert/order/PayOrderCoreConvert.java similarity index 50% rename from yudao-core-service/src/main/java/cn/iocoder/yudao/coreservice/modules/pay/convert/order/PayOrderCoreServiceConvert.java rename to yudao-core-service/src/main/java/cn/iocoder/yudao/coreservice/modules/pay/convert/order/PayOrderCoreConvert.java index dd6084b11..8d1623f1b 100644 --- a/yudao-core-service/src/main/java/cn/iocoder/yudao/coreservice/modules/pay/convert/order/PayOrderCoreServiceConvert.java +++ b/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); + } diff --git a/yudao-core-service/src/main/java/cn/iocoder/yudao/coreservice/modules/pay/dal/dataobject/order/PayOrderDO.java b/yudao-core-service/src/main/java/cn/iocoder/yudao/coreservice/modules/pay/dal/dataobject/order/PayOrderDO.java index 273078ac1..d4a887cf4 100644 --- a/yudao-core-service/src/main/java/cn/iocoder/yudao/coreservice/modules/pay/dal/dataobject/order/PayOrderDO.java +++ b/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 芋艿:可能要优化 /** diff --git a/yudao-core-service/src/main/java/cn/iocoder/yudao/coreservice/modules/pay/dal/dataobject/order/PayOrderExtensionDO.java b/yudao-core-service/src/main/java/cn/iocoder/yudao/coreservice/modules/pay/dal/dataobject/order/PayOrderExtensionDO.java index 6dce032c8..2165a1f90 100644 --- a/yudao-core-service/src/main/java/cn/iocoder/yudao/coreservice/modules/pay/dal/dataobject/order/PayOrderExtensionDO.java +++ b/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()} - */ - private Integer transactionId; - /** - * 选择的支付渠道 - */ - private Integer payChannel; - /** - * 生成传输给第三方的订单号 + * 订单号,根据规则生成 + * 调用支付渠道时,使用该字段作为对接的订单号。 + * 1. 调用微信支付 https://api.mch.weixin.qq.com/pay/unifiedorder 时,使用该字段作为 out_trade_no + * 2. 调用支付宝 https://opendocs.alipay.com/apis 时,使用该字段作为 out_trade_no * - * 唯一索引 + * 例如说,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 */ private Integer status; + /** + * 支付渠道异步通知的内容 + * + * 在支持成功后,会记录回调的数据 + */ + private String channelCallbackData; } diff --git a/yudao-core-service/src/main/java/cn/iocoder/yudao/coreservice/modules/pay/dal/mysql/merchant/PayChannelCoreMapper.java b/yudao-core-service/src/main/java/cn/iocoder/yudao/coreservice/modules/pay/dal/mysql/merchant/PayChannelCoreMapper.java new file mode 100644 index 000000000..153df1ec5 --- /dev/null +++ b/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 { + + default PayChannelDO selectByAppIdAndCode(Long appId, String code) { + return selectOne("app_id", appId, "code", code); + } + +} diff --git a/yudao-core-service/src/main/java/cn/iocoder/yudao/coreservice/modules/pay/dal/mysql/order/PayOrderExtensionCoreMapper.java b/yudao-core-service/src/main/java/cn/iocoder/yudao/coreservice/modules/pay/dal/mysql/order/PayOrderExtensionCoreMapper.java new file mode 100644 index 000000000..2708ee49f --- /dev/null +++ b/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 { + +} diff --git a/yudao-core-service/src/main/java/cn/iocoder/yudao/coreservice/modules/pay/enums/PayErrorCodeConstants.java b/yudao-core-service/src/main/java/cn/iocoder/yudao/coreservice/modules/pay/enums/PayErrorCodeConstants.java index affb3ddeb..8901671fe 100644 --- a/yudao-core-service/src/main/java/cn/iocoder/yudao/coreservice/modules/pay/enums/PayErrorCodeConstants.java +++ b/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, "支付订单不处于已支付"); + } diff --git a/yudao-core-service/src/main/java/cn/iocoder/yudao/coreservice/modules/pay/service/merchant/PayChannelCoreService.java b/yudao-core-service/src/main/java/cn/iocoder/yudao/coreservice/modules/pay/service/merchant/PayChannelCoreService.java new file mode 100644 index 000000000..8fb9f8858 --- /dev/null +++ b/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); + +} diff --git a/yudao-core-service/src/main/java/cn/iocoder/yudao/coreservice/modules/pay/service/merchant/impl/PayChannelCoreServiceImpl.java b/yudao-core-service/src/main/java/cn/iocoder/yudao/coreservice/modules/pay/service/merchant/impl/PayChannelCoreServiceImpl.java new file mode 100644 index 000000000..b5cd3bc1d --- /dev/null +++ b/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; + } + +} diff --git a/yudao-core-service/src/main/java/cn/iocoder/yudao/coreservice/modules/pay/service/order/dto/PayOrderSubmitReqDTO.java b/yudao-core-service/src/main/java/cn/iocoder/yudao/coreservice/modules/pay/service/order/dto/PayOrderSubmitReqDTO.java index c2d155865..c35ae23d9 100644 --- a/yudao-core-service/src/main/java/cn/iocoder/yudao/coreservice/modules/pay/service/order/dto/PayOrderSubmitReqDTO.java +++ b/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; /** * 支付单编号 diff --git a/yudao-core-service/src/main/java/cn/iocoder/yudao/coreservice/modules/pay/service/order/impl/PayOrderCoreServiceImpl.java b/yudao-core-service/src/main/java/cn/iocoder/yudao/coreservice/modules/pay/service/order/impl/PayOrderCoreServiceImpl.java index 60b2877a8..9d6b13123 100644 --- a/yudao-core-service/src/main/java/cn/iocoder/yudao/coreservice/modules/pay/service/order/impl/PayOrderCoreServiceImpl.java +++ b/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 invokeResult = thirdPayClient.submitTransaction(payTransaction, payTransactionExtensionDO, null); // TODO 暂时传入 extra = null + CommonResult 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) // 随机。为什么是这个范围,因为偷懒 + ; } } diff --git a/yudao-framework/yudao-spring-boot-starter-mybatis/src/main/java/cn/iocoder/yudao/framework/mybatis/core/mapper/BaseMapperX.java b/yudao-framework/yudao-spring-boot-starter-mybatis/src/main/java/cn/iocoder/yudao/framework/mybatis/core/mapper/BaseMapperX.java index a80d21b68..4335577d7 100644 --- a/yudao-framework/yudao-spring-boot-starter-mybatis/src/main/java/cn/iocoder/yudao/framework/mybatis/core/mapper/BaseMapperX.java +++ b/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 extends BaseMapper { return selectOne(new QueryWrapper().eq(field, value)); } + default T selectOne(String field1, Object value1, String field2, Object value2) { + return selectOne(new QueryWrapper().eq(field1, value1).eq(field2, value2)); + } + default Integer selectCount(String field, Object value) { return selectCount(new QueryWrapper().eq(field, value)); }