From 88c7de465c8dd513fdc93d6d0fcaa239da30e6de Mon Sep 17 00:00:00 2001 From: xxm1995 Date: Fri, 22 Mar 2024 23:42:08 +0800 Subject: [PATCH] =?UTF-8?q?feat=20=E6=94=AF=E4=BB=98=E6=B5=81=E7=A8=8B?= =?UTF-8?q?=E6=8B=86=E5=88=86=E4=B8=BA=E4=B8=89=E7=B1=BB=E8=BF=9B=E8=A1=8C?= =?UTF-8?q?=E5=88=86=E5=88=AB=E5=A4=84=E7=90=86,=20=E6=9B=B4=E6=96=B0?= =?UTF-8?q?=E9=85=8D=E7=BD=AE=E6=96=87=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- _doc/Task.md | 11 +- .../service/common/context/PlatformLocal.java | 3 + .../alipay/service/AliPayTransferService.java | 4 +- .../pay/service/PayChannelOrderService.java | 10 +- .../common/service/PaymentAssistService.java | 1 + .../core/payment/pay/service/PayService.java | 403 +++++++++--------- .../payment/pay/strategy/AliPayStrategy.java | 4 +- .../pay/strategy/UnionPayStrategy.java | 4 +- .../pay/strategy/WeChatPayStrategy.java | 18 +- .../system/config/entity/PlatformConfig.java | 5 +- .../daxpay/service/func/AbsPayStrategy.java | 20 +- daxpay-single/daxpay-single-start/pom.xml | 6 - .../src/main/resources/application-dev.yml | 10 +- 13 files changed, 254 insertions(+), 245 deletions(-) diff --git a/_doc/Task.md b/_doc/Task.md index 6e2527830..dc72afd13 100644 --- a/_doc/Task.md +++ b/_doc/Task.md @@ -7,13 +7,20 @@ - [ ] 报表功能 - [ ] 各通道收入和支付情况 - [ ] 微信新增V3版本接口 - - [ ] 付款码回退到V2接口 + - [ ] 付款码支付自动路由到V2接口 - [ ] 增加转账功能 - [ ] 支付宝 - [ ] 微信 + - [ ] 云闪付 +- [ ] 增加账户余额查询功能 + - [ ] 支付宝 + - [ ] 微信 + - [ ] 云闪付 - [ ] 云闪付支持对账功能 - [x] 结算台DEMO增加云闪付示例 - +- [ ] 增加支付限额 + - [ ] 各通道支持单独的限额 + - [ ] 整个订单支持整体的限额 2.0.x 版本内容 - [ ] 统一关闭接口增加使用撤销关闭订单 - [ ] 增加各类日志记录,例如钱包的各项操作 diff --git a/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/common/context/PlatformLocal.java b/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/common/context/PlatformLocal.java index 592ca974c..36dec826e 100644 --- a/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/common/context/PlatformLocal.java +++ b/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/common/context/PlatformLocal.java @@ -33,4 +33,7 @@ public class PlatformLocal { /** 订单支付超时(分钟) */ private Integer orderTimeout; + + /** 支付限额 */ + private Integer limitAmount; } diff --git a/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/channel/alipay/service/AliPayTransferService.java b/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/channel/alipay/service/AliPayTransferService.java index 8f993f4dd..d8442d3ee 100644 --- a/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/channel/alipay/service/AliPayTransferService.java +++ b/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/channel/alipay/service/AliPayTransferService.java @@ -15,17 +15,17 @@ import org.springframework.stereotype.Service; public class AliPayTransferService { private final AliPayConfigService payConfigService; + /** * 余额查询接口 */ @SneakyThrows public void queryAccountAmount() { - AliPayConfig config = payConfigService.getAndCheckConfig(); payConfigService.initConfig(config); AlipayFundAccountQueryModel model = new AlipayFundAccountQueryModel(); model.setAccountType("ACCTRANS_ACCOUNT"); - model.setAlipayUserId(config.getAppId()); + model.setAlipayUserId("2088441532699265"); AlipayFundAccountQueryResponse response = AliPayApi.accountQueryToResponse(model, null); System.out.println(response); } diff --git a/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/order/pay/service/PayChannelOrderService.java b/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/order/pay/service/PayChannelOrderService.java index 441b671cf..7b0f80299 100644 --- a/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/order/pay/service/PayChannelOrderService.java +++ b/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/order/pay/service/PayChannelOrderService.java @@ -16,6 +16,7 @@ import cn.hutool.json.JSONUtil; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Propagation; import org.springframework.transaction.annotation.Transactional; import java.time.LocalDateTime; @@ -51,13 +52,13 @@ public class PayChannelOrderService { /** * 切换支付订单关联的异步支付通道, 同时会设置是否支付完成状态, 并返回通道订单 + * 使用单独事务 */ - @Transactional(rollbackFor = Exception.class) + @Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRES_NEW) public PayChannelOrder switchAsyncPayChannel(PayOrder payOrder, PayChannelParam payChannelParam){ PayLocal payInfo = PaymentContextLocal.get().getPayInfo(); // 是否支付完成 PayStatusEnum payStatus = payInfo.isPayComplete() ? PayStatusEnum.SUCCESS : PayStatusEnum.PROGRESS; - // 判断新发起的 Optional payOrderChannelOpt = channelOrderManager.findByPaymentIdAndChannel(payOrder.getId(), payChannelParam.getChannel()); // 扩展信息处理 Map channelParam = payChannelParam.getChannelParam(); @@ -91,7 +92,10 @@ public class PayChannelOrderService { .setChannelExtra(channelParamStr) .setStatus(payStatus.getCode()); channelOrderManager.updateById(payChannelOrder); - payInfo.getPayChannelOrders().add(payOrderChannelOpt.get()); + // 更新时一次请求有多次访问, 对上下文中的通道支付订单进行替换 + List payChannelOrders = payInfo.getPayChannelOrders(); + payChannelOrders.removeIf(o->Objects.equals(o.getChannel(),payChannelOrder.getChannel())); + payChannelOrders.add(payOrderChannelOpt.get()); return payChannelOrder; } } diff --git a/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/payment/common/service/PaymentAssistService.java b/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/payment/common/service/PaymentAssistService.java index a09d16bc2..f31a90fd3 100644 --- a/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/payment/common/service/PaymentAssistService.java +++ b/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/payment/common/service/PaymentAssistService.java @@ -41,6 +41,7 @@ public class PaymentAssistService { platform.setSignSecret(config.getSignSecret()); platform.setNotifyUrl(config.getNotifyUrl()); platform.setOrderTimeout(config.getOrderTimeout()); + platform.setLimitAmount(config.getLimitAmount()); platform.setWebsiteUrl(config.getWebsiteUrl()); } diff --git a/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/payment/pay/service/PayService.java b/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/payment/pay/service/PayService.java index e28b13ec4..374d8b713 100644 --- a/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/payment/pay/service/PayService.java +++ b/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/payment/pay/service/PayService.java @@ -1,7 +1,7 @@ package cn.bootx.platform.daxpay.service.core.payment.pay.service; import cn.bootx.platform.common.core.exception.RepetitiveOperationException; -import cn.bootx.platform.common.core.function.CollectorsFunction; +import cn.bootx.platform.daxpay.exception.pay.PayFailureException; import cn.bootx.platform.daxpay.exception.pay.PayUnsupportedMethodException; import cn.bootx.platform.daxpay.param.pay.PayChannelParam; import cn.bootx.platform.daxpay.param.pay.PayParam; @@ -10,8 +10,6 @@ import cn.bootx.platform.daxpay.result.pay.PayResult; import cn.bootx.platform.daxpay.service.common.context.PayLocal; import cn.bootx.platform.daxpay.service.common.local.PaymentContextLocal; import cn.bootx.platform.daxpay.service.core.order.pay.builder.PayBuilder; -import cn.bootx.platform.daxpay.service.core.order.pay.dao.PayChannelOrderManager; -import cn.bootx.platform.daxpay.service.core.order.pay.entity.PayChannelOrder; import cn.bootx.platform.daxpay.service.core.order.pay.entity.PayOrder; import cn.bootx.platform.daxpay.service.core.order.pay.entity.PayOrderExtra; import cn.bootx.platform.daxpay.service.core.order.pay.service.PayOrderService; @@ -34,11 +32,11 @@ import org.springframework.transaction.annotation.Transactional; import java.time.LocalDateTime; import java.util.Collections; import java.util.List; -import java.util.Map; import java.util.Objects; -import java.util.function.Function; +import java.util.Optional; import java.util.stream.Collectors; +import static cn.bootx.platform.daxpay.code.PayChannelEnum.ASYNC_TYPE; import static cn.bootx.platform.daxpay.code.PayStatusEnum.SUCCESS; @@ -57,72 +55,10 @@ public class PayService { private final PayAssistService payAssistService; - private final PayChannelOrderManager channelOrderManager; - private final ClientNoticeService clientNoticeService; private final LockTemplate lockTemplate; - /** - * 支付下单接口(同步/异步/组合支付) - * 1. 同步支付:都只会在第一次执行中就完成支付,例如钱包、储值卡都是调用完就进行了扣减,完成了支付记录 - * 2. 异步支付:例如支付宝、微信,发起支付后还需要跳转第三方平台进行支付,支付后通常需要进行回调,之后才完成支付记录 - * 3. 组合支付:主要是混合了同步支付和异步支付,同时异步支付只能有一个,在支付时先对同步支付进行扣减,然后异步支付回调结束后完成整个支付单 - * 注意: - * 组合支付在非第一次支付的时候,只对新传入的异步支付通道进行处理,该通道的价格使用第一次发起的价格,旧的同步支付如果传入后也不做处理, - * 支付单中支付通道列表将会为 旧有的同步支付+新传入的异步支付方式(在具体支付实现中处理) - * - * 订单数据会先进行入库, 才会进行发起支付, 在调用各通道支付之前发生错误, 数据不会入库 - */ - @Transactional(rollbackFor = Exception.class) - public PayResult pay(PayParam payParam) { - // 异步支付方式检查 - PayUtil.validationAsyncPay(payParam); - - String businessNo = payParam.getBusinessNo(); - // 加锁 - LockInfo lock = lockTemplate.lock("payment:pay:" + businessNo,10000,200); - if (Objects.isNull(lock)){ - throw new RepetitiveOperationException("正在支付中,请勿重复支付"); - } - try { - // 获取并校验支付订单状态, 如果超时, 触发支付单同步和修复动作 - PayOrder payOrder = payAssistService.getOrderAndCheck(payParam.getBusinessNo()); - - // 初始化上下文 - payAssistService.initPayContext(payOrder, payParam); - - // 订单为空, 创建支付订单和通道支付订单 - if (Objects.isNull(payOrder)){ - // 预支付处理, 无论请求成功还是失败, 各种订单对象都会保存 - List strategies = SpringUtil.getBean(this.getClass()).prePay(payParam); - // 执行首次支付逻辑 - return this.onePay(strategies); - } else { - // 分为同步通道全部支付成功和所有支付都是支付中状态(上次支付失败) - return this.towPay(payParam, payOrder); - } - } finally { - lockTemplate.releaseLock(lock); - } - } - - /** - * 组合支付分为同步通道全部支付成功和所有支付都是支付中状态(上次支付失败) - * 单通道支付不需要区分上次支付发起是否成功 - */ - private PayResult towPay(PayParam payParam, PayOrder payOrder) { - // 判断是否组合支付且同时包含异步支付 - if (payOrder.isCombinationPay()&&payOrder.isAsyncPay()) { - // 同步支付通道全部支付成功, 单独走异步支付支付流程 - this.onlyAsyncPay(payParam, payOrder); - // 通道订单全部支付中, 相当于上次支付异常, 重新走支付流程 - return this.allPay(payParam, payOrder); - } else { - // 单通道支付走标准支付流程 - return this.allPay(payParam, payOrder); - } - } /** * 简单下单, 可以视为不支持组合支付的下单接口 @@ -144,17 +80,143 @@ public class PayService { /** - * 预支付处理, 无论请求成功还是失败, 各种订单对象都会保存 - * 1. 校验参数 - * 2. 创建支付订单/通道支付订单/扩展信息 - * 3, 返回数据 + * 支付入口 + * 支付下单接口(同步/异步/组合支付) + * 1. 同步支付:包含一个或多个同步支付通道进行支付 + * 2. 异步支付:例如支付宝、微信,发起支付后还需要跳转第三方平台进行支付,支付后通常需要进行回调,之后才完成支付记录 + * 3. 组合支付:主要是混合了同步支付和异步支付,同时异步支付只能有一个,在支付时先对同步支付进行扣减,然后异步支付回调结束后完成整个支付单 + * 注意: + * 组合支付在非第一次支付的时候,只对新传入的异步支付通道进行处理,该通道的价格使用第一次发起的价格,旧的同步支付如果传入后也不做处理, + * 支付单中支付通道列表将会为 旧有的同步支付+新传入的异步支付方式(在具体支付实现中处理) + * + * 订单数据会先进行入库, 才会进行发起支付, 在调用各通道支付之前发生错误, 数据不会入库 */ - @Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRES_NEW) - public List prePay(PayParam payParam){ + public PayResult pay(PayParam payParam){ - // 价格检测 + // 异步支付方式检查 + PayUtil.validationAsyncPay(payParam); + // 检查支付金额 PayUtil.validationAmount(payParam.getPayChannels()); + String businessNo = payParam.getBusinessNo(); + // 加锁 + LockInfo lock = lockTemplate.lock("payment:pay:" + businessNo,10000,200); + if (Objects.isNull(lock)){ + throw new RepetitiveOperationException("正在支付中,请勿重复支付"); + } + try { + // 查询并检查订单 + PayOrder payOrder = payAssistService.getOrderAndCheck(payParam.getBusinessNo()); + // 初始化上下文 + payAssistService.initPayContext(payOrder, payParam); + // 走首次下单逻辑还是重复发起逻辑 + if (Objects.isNull(payOrder)){ + // 判断不同的支付请求类型, 然后走不同的逻辑 + List payChannels = payParam.getPayChannels(); + // 不包含异步支付通道 + if (PayUtil.isNotSync(payChannels)){ + return this.firstSyncPay(payParam); + } + // 单个异步通道支付 + else if (payChannels.size() == 1 && !PayUtil.isNotSync(payChannels)) { + return this.firstAsyncPay(payParam); + } + // 包含异步通道的组合支付 + else if (!PayUtil.isNotSync(payChannels)) { + return this.firstCombinationPay(payParam); + } else { + throw new PayFailureException("支付参数错误"); + } + } else { + // 单个异步通道支付 + + // 包含异步通道的组合支付 + + } + } catch (Exception e) { + throw new RuntimeException(e); + } + return null; + } + + /** + * 首次同步支付, 可以为一个或多个同步通道进行支付 + */ + private PayResult firstSyncPay(PayParam payParam){ + // 创建支付订单和扩展记录并返回支付订单对象 + PayOrder payOrder = payAssistService.createPayOrder(payParam); + PayLocal payInfo = PaymentContextLocal.get().getPayInfo(); + + // 获取支付方式,通过工厂生成对应的策略组 + List strategies = PayStrategyFactory.createAsyncLast(payParam.getPayChannels()); + if (CollectionUtil.isEmpty(strategies)) { + throw new PayUnsupportedMethodException(); + } + + // 初始化支付参数 + for (AbsPayStrategy strategy : strategies) { + strategy.initPayParam(payOrder, payParam); + } + + // 执行支付前处理动作 + strategies.forEach(AbsPayStrategy::doBeforePayHandler); + // 生成支付通道订单 + strategies.forEach(AbsPayStrategy::generateChannelOrder); + // 支付操作 + strategies.forEach(AbsPayStrategy::doPayHandler); + // 支付成功操作, 进行扣款、创建记录类类似的操作 + strategies.forEach(AbsPayStrategy::doSuccessHandler); + payOrder.setStatus(SUCCESS.getCode()) + .setPayTime(LocalDateTime.now()); + // 保存通道支付订单 + payAssistService.savePayChannelOrder(strategies); + // 更新订单信息 + payOrderService.updateById(payOrder); + // 支付完成 发送通知 + clientNoticeService.registerPayNotice(payOrder, payInfo.getPayOrderExtra(), payInfo.getPayChannelOrders()); + return PayBuilder.buildPayResultByPayOrder(payOrder); + } + + /** + * 首次单个异步通道支付 + */ + private PayResult firstAsyncPay(PayParam payParam){ + PayLocal payInfo = PaymentContextLocal.get().getPayInfo(); + // 开启新事务执行订单预保存操作, 并返回对应的支付策略组 + AbsPayStrategy asyncPayStrategy = SpringUtil.getBean(this.getClass()).asyncPayPreSave(payParam); + + // 支付操作 + try { + asyncPayStrategy.doPayHandler(); + } catch (Exception e) { + // 记录错误原因 + PayOrderExtra payOrderExtra = payInfo.getPayOrderExtra(); + payOrderExtra.setErrorMsg(e.getMessage()); + throw e; + } + // 支付调用成功操作, + asyncPayStrategy.doSuccessHandler(); + PayOrder payOrder = payInfo.getPayOrder(); + // 如果支付完成, 进行订单完成处理, 同时发送回调消息 + if (payInfo.isPayComplete()) { + payOrder.setGatewayOrderNo(payInfo.getGatewayOrderNo()) + .setStatus(SUCCESS.getCode()) + .setPayTime(LocalDateTime.now()); + // TODO 使用网关返回的时间 + payOrderService.updateById(payOrder); + } + // 如果支付完成 发送通知 + if (Objects.equals(payOrder.getStatus(), SUCCESS.getCode())){ + clientNoticeService.registerPayNotice(payOrder, payInfo.getPayOrderExtra(), payInfo.getPayChannelOrders()); + } + return PayBuilder.buildPayResultByPayOrder(payOrder); + } + + /** + * 首次组合支付, 包含异步支付通道 + */ + private PayResult firstCombinationPay(PayParam payParam){ + PayLocal payInfo = PaymentContextLocal.get().getPayInfo(); // 创建支付订单和扩展记录并返回支付订单对象 PayOrder payOrder = payAssistService.createPayOrder(payParam); @@ -168,179 +230,106 @@ public class PayService { strategy.initPayParam(payOrder, payParam); } - // 执行支付前处理动作 - strategies.forEach(AbsPayStrategy::doBeforePayHandler); - // 执行通道支付通道订单的生成 - strategies.forEach(AbsPayStrategy::generateChannelOrder); + // 取出同步通道 然后进行支付 + List syncStrategies = strategies.stream() + .filter(strategy -> !ASYNC_TYPE.contains(strategy.getChannel())) + .collect(Collectors.toList()); + // 初始化支付参数 + for (AbsPayStrategy strategy : syncStrategies) { + strategy.initPayParam(payOrder, payParam); + } + + // 执行支付前处理动作 + syncStrategies.forEach(AbsPayStrategy::doBeforePayHandler); + // 生成支付通道订单 + syncStrategies.forEach(AbsPayStrategy::generateChannelOrder); + // 支付操作 + syncStrategies.forEach(AbsPayStrategy::doPayHandler); + // 支付成功操作, 进行扣款、创建记录类类似的操作 + syncStrategies.forEach(AbsPayStrategy::doSuccessHandler); + payOrder.setStatus(SUCCESS.getCode()) + .setPayTime(LocalDateTime.now()); // 保存通道支付订单 payAssistService.savePayChannelOrder(strategies); - return strategies; - } + // ------------------------- 进行异步支付 ------------------------------- + // 筛选出异步通道策略类 + AbsPayStrategy asyncPayStrategy = strategies.stream() + .filter(strategy -> !ASYNC_TYPE.contains(strategy.getChannel())) + .findFirst() + .orElseThrow(() -> new PayFailureException("数据和代码异常, 请排查代码")); - /** - * 发起的第一次支付请求(同步/异步) - */ - private PayResult firstPay(PayParam payParam, PayOrder payOrder) { - - // 2. 价格检测 - PayUtil.validationAmount(payParam.getPayChannels()); - - // 3. 创建支付订单和扩展记录并返回支付订单对象 - payOrder = payAssistService.createPayOrder(payParam); - - // 4. 调用支付方法进行发起支付 - this.allPay(payParam, payOrder); - - // 5. 返回支付结果 - return PayBuilder.buildPayResultByPayOrder(payOrder); - } - - - /** - * 执行首次支付操作 - */ - public PayResult onePay(List strategies){ - PayLocal payInfo = PaymentContextLocal.get().getPayInfo(); - // 支付操作 + // 支付前准备 + asyncPayStrategy.doBeforePayHandler(); + // 设置异步支付通道订单信息 + asyncPayStrategy.generateChannelOrder(); try { - strategies.forEach(AbsPayStrategy::doPayHandler); + // 异步支付操作 + asyncPayStrategy.doPayHandler(); } catch (Exception e) { // 记录错误原因 PayOrderExtra payOrderExtra = payInfo.getPayOrderExtra(); payOrderExtra.setErrorMsg(e.getMessage()); throw e; } - PayOrder payOrder = payInfo.getPayOrder(); - // 支付调用成功操作, 进行扣款、创建记录类类似的操作 - strategies.forEach(AbsPayStrategy::doSuccessHandler); - - // 如果没有异步支付, 直接进行订单完成处理 - if (!payOrder.isAsyncPay()) { - // 修改支付订单状态为成功 - payOrder.setStatus(SUCCESS.getCode()) - .setPayTime(LocalDateTime.now()); - payOrderService.updateById(payOrder); - } - // 如果异步支付完成, 进行订单完成处理, 同时发送回调消息 - PayLocal asyncPayInfo = PaymentContextLocal.get().getPayInfo(); - if (asyncPayInfo.isPayComplete()) { - payOrder.setGatewayOrderNo(asyncPayInfo.getGatewayOrderNo()) + asyncPayStrategy.doSuccessHandler(); + // 如果支付完成, 进行订单完成处理, 同时发送回调消息 + if (payInfo.isPayComplete()) { + payOrder.setGatewayOrderNo(payInfo.getGatewayOrderNo()) .setStatus(SUCCESS.getCode()) .setPayTime(LocalDateTime.now()); + // TODO 使用网关返回的时间 payOrderService.updateById(payOrder); } - // 如果支付完成 发送通知 if (Objects.equals(payOrder.getStatus(), SUCCESS.getCode())){ clientNoticeService.registerPayNotice(payOrder, payInfo.getPayOrderExtra(), payInfo.getPayChannelOrders()); } - // 组装返回参数 return PayBuilder.buildPayResultByPayOrder(payOrder); + } - /** - * 执行所有的支付方法 + * 异步支付预报保存处理, 无论请求成功还是失败, 各种订单对象都会保存 + * 1. 创建支付订单/通道支付订单/扩展信息 + * 2, 返回支付列表记录 */ - private PayResult allPay(PayParam payParam, PayOrder payOrder) { - PayLocal payInfo = PaymentContextLocal.get().getPayInfo(); + @Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRES_NEW) + public AbsPayStrategy asyncPayPreSave(PayParam payParam){ + // 创建支付订单和扩展记录并返回支付订单对象 + PayOrder payOrder = payAssistService.createPayOrder(payParam); // 获取支付方式,通过工厂生成对应的策略组 List strategies = PayStrategyFactory.createAsyncLast(payParam.getPayChannels()); if (CollectionUtil.isEmpty(strategies)) { throw new PayUnsupportedMethodException(); } - // 查询通道支付订单 - List orders = channelOrderManager.findAllByPaymentId(payOrder.getId()); - Map channelOrderMap = orders.stream() - .collect(Collectors.toMap(PayChannelOrder::getChannel, Function.identity(), CollectorsFunction::retainFirst)); - // 初始化支付参数 - for (AbsPayStrategy strategy : strategies) { - strategy.initPayParam(payOrder, payParam); - strategy.setChannelOrder(channelOrderMap.get(strategy.getChannel().getCode())); - } + AbsPayStrategy absPayStrategy = Optional.ofNullable(strategies.get(0)) + .orElseThrow(() -> new PayFailureException("数据和代码异常, 请排查代码")); + + // 初始化支付的参数 + absPayStrategy.initPayParam(payOrder, payParam); // 执行支付前处理动作 - strategies.forEach(AbsPayStrategy::doBeforePayHandler); - // 支付操作 - - try { - strategies.forEach(AbsPayStrategy::doPayHandler); - } catch (Exception e) { - // 记录错误原因 - PayOrderExtra payOrderExtra = payInfo.getPayOrderExtra(); - payOrderExtra.setErrorMsg(e.getMessage()); - throw e; - } - - // 支付调用成功操作, 进行扣款、创建记录类类似的操作 - strategies.forEach(AbsPayStrategy::doSuccessHandler); - - // 如果没有异步支付, 直接进行订单完成处理 - if (PayUtil.isNotSync(payParam.getPayChannels())) { - // 修改支付订单状态为成功 - payOrder.setStatus(SUCCESS.getCode()) - .setPayTime(LocalDateTime.now()); - payOrderService.updateById(payOrder); - } - // 如果异步支付完成, 进行订单完成处理, 同时发送回调消息 - PayLocal asyncPayInfo = PaymentContextLocal.get().getPayInfo(); - if (asyncPayInfo.isPayComplete()) { - payOrder.setGatewayOrderNo(asyncPayInfo.getGatewayOrderNo()) - .setStatus(SUCCESS.getCode()) - .setPayTime(LocalDateTime.now()); - payOrderService.updateById(payOrder); - } - - // 如果支付完成 发送通知 - if (Objects.equals(payOrder.getStatus(), SUCCESS.getCode())){ - clientNoticeService.registerPayNotice(payOrder, payInfo.getPayOrderExtra(), payInfo.getPayChannelOrders()); - } - return PayBuilder.buildPayResultByPayOrder(payOrder); + absPayStrategy.doBeforePayHandler(); + // 执行支付通道订单的生成和保存 + absPayStrategy.generateChannelOrder(); + return absPayStrategy; } /** - * 异步支付执行(非第一次请求), 只执行异步支付策略, 因为同步支付已经支付成功. 报错不影响继续发起支付 + * 重复支付 */ - private PayResult onlyAsyncPay(PayParam payParam, PayOrder payOrder) { + public void repeatAsyncPay(){ - // 2.获取 异步支付通道,通过工厂生成对应的策略组(只包含异步支付的策略, 同步支付已经成功不再继续执行) - PayChannelParam payChannelParam = payAssistService.getAsyncPayParam(payParam, payOrder); - List payStrategyList = PayStrategyFactory.createAsyncLast(Collections.singletonList(payChannelParam)); + } - // 3.初始化支付的参数 - for (AbsPayStrategy payStrategy : payStrategyList) { - payStrategy.initPayParam(payOrder, payParam); - } - // 4.支付前准备 - payStrategyList.forEach(AbsPayStrategy::doBeforePayHandler); + /** + * + */ + public void repeatCombinationPay(){ - // 5.1发起支付 - payStrategyList.forEach(AbsPayStrategy::doPayHandler); - // 5.2支付发起成功处理 - payStrategyList.forEach(AbsPayStrategy::doSuccessHandler); - - // 6.1 如果异步支付完成, 进行订单完成处理 - PayLocal asyncPayInfo = PaymentContextLocal.get().getPayInfo(); - if (asyncPayInfo.isPayComplete()) { - payOrder.setGatewayOrderNo(asyncPayInfo.getGatewayOrderNo()) - .setStatus(SUCCESS.getCode()) - .setPayTime(LocalDateTime.now()); - } - - // 6.2 更新支付订单和扩展参数 - payOrderService.updateById(payOrder); - PayOrderExtra payOrderExtra = payAssistService.updatePayOrderExtra(payParam, payOrder.getId()); - - // 订单完成, 触发通知 - if (Objects.equals(payOrder.getStatus(), SUCCESS.getCode())) { - clientNoticeService.registerPayNotice(payOrder, payOrderExtra,null); - } - - // 7. 组装返回参数 - return PayBuilder.buildPayResultByPayOrder(payOrder); } } diff --git a/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/payment/pay/strategy/AliPayStrategy.java b/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/payment/pay/strategy/AliPayStrategy.java index 04b637078..ab27de58b 100644 --- a/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/payment/pay/strategy/AliPayStrategy.java +++ b/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/payment/pay/strategy/AliPayStrategy.java @@ -95,7 +95,8 @@ public class AliPayStrategy extends AbsPayStrategy { */ @Override public void generateChannelOrder() { - super.generateChannelOrder(); + // 创建或切换支付通道订单 + channelOrderService.switchAsyncPayChannel(this.getOrder(), this.getPayChannelParam()); } /** @@ -104,6 +105,7 @@ public class AliPayStrategy extends AbsPayStrategy { @Override public void doSuccessHandler() { PayLocal asyncPayInfo = PaymentContextLocal.get().getPayInfo(); + // 更新通道支付订单 PayChannelOrder payChannelOrder = channelOrderService.switchAsyncPayChannel(this.getOrder(), this.getPayChannelParam()); // 支付完成, 保存记录 if (asyncPayInfo.isPayComplete()) { diff --git a/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/payment/pay/strategy/UnionPayStrategy.java b/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/payment/pay/strategy/UnionPayStrategy.java index 43ec5fb81..ed66a1acf 100644 --- a/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/payment/pay/strategy/UnionPayStrategy.java +++ b/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/payment/pay/strategy/UnionPayStrategy.java @@ -98,7 +98,8 @@ public class UnionPayStrategy extends AbsPayStrategy { */ @Override public void generateChannelOrder() { - super.generateChannelOrder(); + // 创建或切换支付通道订单 + channelOrderService.switchAsyncPayChannel(this.getOrder(), this.getPayChannelParam()); } /** @@ -107,6 +108,7 @@ public class UnionPayStrategy extends AbsPayStrategy { @Override public void doSuccessHandler() { PayLocal asyncPayInfo = PaymentContextLocal.get().getPayInfo(); + // 更新支付通道信息 PayChannelOrder payChannelOrder = channelOrderService.switchAsyncPayChannel(this.getOrder(), this.getPayChannelParam()); // 支付完成, 保存记录 if (asyncPayInfo.isPayComplete()) { diff --git a/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/payment/pay/strategy/WeChatPayStrategy.java b/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/payment/pay/strategy/WeChatPayStrategy.java index 0df77c4d5..b21939b56 100644 --- a/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/payment/pay/strategy/WeChatPayStrategy.java +++ b/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/payment/pay/strategy/WeChatPayStrategy.java @@ -78,6 +78,16 @@ public class WeChatPayStrategy extends AbsPayStrategy { weChatPayService.validation(this.getPayChannelParam(), weChatPayConfig); } + + /** + * 不使用默认的生成通道支付单方法, 异步支付通道的支付订单自己管理 + */ + @Override + public void generateChannelOrder() { + // 创建或切换支付通道订单 + channelOrderService.switchAsyncPayChannel(this.getOrder(), this.getPayChannelParam()); + } + /** * 发起支付 */ @@ -100,14 +110,6 @@ public class WeChatPayStrategy extends AbsPayStrategy { } } - /** - * 不使用默认的生成通道支付单方法, 异步支付通道的支付订单自己管理 - */ - @Override - public void generateChannelOrder() { - super.generateChannelOrder(); - } - /** * 初始化微信支付 */ diff --git a/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/system/config/entity/PlatformConfig.java b/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/system/config/entity/PlatformConfig.java index 64af8bef5..b4aa8779d 100644 --- a/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/system/config/entity/PlatformConfig.java +++ b/daxpay-single/daxpay-single-service/src/main/java/cn/bootx/platform/daxpay/service/core/system/config/entity/PlatformConfig.java @@ -39,9 +39,12 @@ public class PlatformConfig extends MpBaseEntity implements EntityBaseFunction

daxpay-single-demo ${daxpay.version} - - cn.afterturn - easypoi-spring-boot-starter - 4.4.0 - - diff --git a/daxpay-single/daxpay-single-start/src/main/resources/application-dev.yml b/daxpay-single/daxpay-single-start/src/main/resources/application-dev.yml index 76d7e30e7..bb465c003 100644 --- a/daxpay-single/daxpay-single-start/src/main/resources/application-dev.yml +++ b/daxpay-single/daxpay-single-start/src/main/resources/application-dev.yml @@ -156,17 +156,17 @@ dromara: # 支付系统配置 dax-pay: # 服务地址 - server-url: http://pay1.bootx.cn + server-url: http://localhost:9000 # 前端h5地址 - front-h5-url: http://pay1.bootx.cn/h5/# + front-h5-url: http://localhost:5173/# # 前端web地址 - front-web-url: http://pay1.bootx.cn/# + front-web-url: http://localhost:9000/# # 演示模块 demo: # 网关地址 - server-url: http://pay1.bootx.cn/server + server-url: http://localhost:9000/server # 前端h5地址 - front-h5-url: http://pay1.bootx.cn/h5/# + front-h5-url: http://localhost:5173/# # 签名秘钥 sign-secret: 123456 # 签名方式