mirror of
https://gitee.com/dromara/dax-pay.git
synced 2025-09-01 18:27:02 +00:00
feat 支付流程拆分为三类进行分别处理, 更新配置文件
This commit is contained in:
11
_doc/Task.md
11
_doc/Task.md
@@ -7,13 +7,20 @@
|
||||
- [ ] 报表功能
|
||||
- [ ] 各通道收入和支付情况
|
||||
- [ ] 微信新增V3版本接口
|
||||
- [ ] 付款码回退到V2接口
|
||||
- [ ] 付款码支付自动路由到V2接口
|
||||
- [ ] 增加转账功能
|
||||
- [ ] 支付宝
|
||||
- [ ] 微信
|
||||
- [ ] 云闪付
|
||||
- [ ] 增加账户余额查询功能
|
||||
- [ ] 支付宝
|
||||
- [ ] 微信
|
||||
- [ ] 云闪付
|
||||
- [ ] 云闪付支持对账功能
|
||||
- [x] 结算台DEMO增加云闪付示例
|
||||
|
||||
- [ ] 增加支付限额
|
||||
- [ ] 各通道支持单独的限额
|
||||
- [ ] 整个订单支持整体的限额
|
||||
2.0.x 版本内容
|
||||
- [ ] 统一关闭接口增加使用撤销关闭订单
|
||||
- [ ] 增加各类日志记录,例如钱包的各项操作
|
||||
|
@@ -33,4 +33,7 @@ public class PlatformLocal {
|
||||
|
||||
/** 订单支付超时(分钟) */
|
||||
private Integer orderTimeout;
|
||||
|
||||
/** 支付限额 */
|
||||
private Integer limitAmount;
|
||||
}
|
||||
|
@@ -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);
|
||||
}
|
||||
|
@@ -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<PayChannelOrder> payOrderChannelOpt = channelOrderManager.findByPaymentIdAndChannel(payOrder.getId(), payChannelParam.getChannel());
|
||||
// 扩展信息处理
|
||||
Map<String, Object> channelParam = payChannelParam.getChannelParam();
|
||||
@@ -91,7 +92,10 @@ public class PayChannelOrderService {
|
||||
.setChannelExtra(channelParamStr)
|
||||
.setStatus(payStatus.getCode());
|
||||
channelOrderManager.updateById(payChannelOrder);
|
||||
payInfo.getPayChannelOrders().add(payOrderChannelOpt.get());
|
||||
// 更新时一次请求有多次访问, 对上下文中的通道支付订单进行替换
|
||||
List<PayChannelOrder> payChannelOrders = payInfo.getPayChannelOrders();
|
||||
payChannelOrders.removeIf(o->Objects.equals(o.getChannel(),payChannelOrder.getChannel()));
|
||||
payChannelOrders.add(payOrderChannelOpt.get());
|
||||
return payChannelOrder;
|
||||
}
|
||||
}
|
||||
|
@@ -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());
|
||||
}
|
||||
|
||||
|
@@ -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<AbsPayStrategy> 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<AbsPayStrategy> 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<PayChannelParam> 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<AbsPayStrategy> 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<AbsPayStrategy> 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<AbsPayStrategy> 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<AbsPayStrategy> strategies = PayStrategyFactory.createAsyncLast(payParam.getPayChannels());
|
||||
if (CollectionUtil.isEmpty(strategies)) {
|
||||
throw new PayUnsupportedMethodException();
|
||||
}
|
||||
// 查询通道支付订单
|
||||
List<PayChannelOrder> orders = channelOrderManager.findAllByPaymentId(payOrder.getId());
|
||||
Map<String, PayChannelOrder> 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<AbsPayStrategy> 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);
|
||||
}
|
||||
}
|
||||
|
@@ -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()) {
|
||||
|
@@ -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()) {
|
||||
|
@@ -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();
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化微信支付
|
||||
*/
|
||||
|
@@ -39,9 +39,12 @@ public class PlatformConfig extends MpBaseEntity implements EntityBaseFunction<P
|
||||
@DbColumn(comment = "支付通知地址")
|
||||
private String notifyUrl;
|
||||
|
||||
@DbColumn(comment = "同步支付通知地址")
|
||||
@DbColumn(comment = "同步支付跳转地址")
|
||||
private String returnUrl;
|
||||
|
||||
@DbColumn(comment = "支付限额")
|
||||
private Integer limitAmount;
|
||||
|
||||
@DbColumn(comment = "订单默认超时时间(分钟)")
|
||||
private Integer orderTimeout;
|
||||
|
||||
|
@@ -44,11 +44,21 @@ public abstract class AbsPayStrategy implements PayStrategy{
|
||||
}
|
||||
|
||||
/**
|
||||
* 支付前处理 包含必要的校验以及对当前通道支付订单的创建和保存操作
|
||||
* 支付前处理 包含必要的校验以及对当前通道支付配置信息的初始化
|
||||
*/
|
||||
public void doBeforePayHandler() {
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成通道支付单, 如果不需要可以进行覆盖
|
||||
* 异步支付通道都进行了重新, 通道支付单由自己控制
|
||||
*/
|
||||
public void generateChannelOrder() {
|
||||
PayChannelOrder payChannelOrder = PayBuilder.buildPayChannelOrder(this.getPayChannelParam());
|
||||
payChannelOrder.setPaymentId(this.getOrder().getId());
|
||||
this.channelOrder = payChannelOrder;
|
||||
}
|
||||
|
||||
/**
|
||||
* 支付操作
|
||||
*/
|
||||
@@ -61,12 +71,4 @@ public abstract class AbsPayStrategy implements PayStrategy{
|
||||
this.channelOrder.setStatus(PayStatusEnum.SUCCESS.getCode()).setPayTime(LocalDateTime.now());
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成通道支付单, 如果不需要可以进行覆盖
|
||||
*/
|
||||
public void generateChannelOrder() {
|
||||
PayChannelOrder payChannelOrder = PayBuilder.buildPayChannelOrder(this.getPayChannelParam());
|
||||
payChannelOrder.setPaymentId(this.getOrder().getId());
|
||||
this.channelOrder = payChannelOrder;
|
||||
}
|
||||
}
|
||||
|
@@ -40,12 +40,6 @@
|
||||
<artifactId>daxpay-single-demo</artifactId>
|
||||
<version>${daxpay.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>cn.afterturn</groupId>
|
||||
<artifactId>easypoi-spring-boot-starter</artifactId>
|
||||
<version>4.4.0</version>
|
||||
</dependency>
|
||||
|
||||
|
||||
</dependencies>
|
||||
<build>
|
||||
|
@@ -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
|
||||
# 签名方式
|
||||
|
Reference in New Issue
Block a user