feat 支持支付限额配置, 修改支付方法适配限额校验

This commit is contained in:
xxm1995
2024-03-24 15:25:26 +08:00
parent 659413357c
commit c290c1ea0f
25 changed files with 107 additions and 98 deletions

View File

@@ -43,6 +43,7 @@ public class PayUtil {
* 检查支付金额
*/
public void validationAmount(List<PayChannelParam> payModeList) {
// 验证支付金额
for (PayChannelParam payChannelParam : payModeList) {
// 支付金额小于等于零
if (payChannelParam.getAmount() < 0) {

View File

@@ -35,5 +35,5 @@ public class PlatformLocal {
private Integer orderTimeout;
/** 支付限额 */
private Integer singleLimit;
private Integer limitAmount;
}

View File

@@ -41,7 +41,7 @@ public class PaymentAssistService {
platform.setSignSecret(config.getSignSecret());
platform.setNotifyUrl(config.getNotifyUrl());
platform.setOrderTimeout(config.getOrderTimeout());
platform.setSingleLimit(config.getSingleLimit());
platform.setLimitAmount(config.getLimitAmount());
platform.setWebsiteUrl(config.getWebsiteUrl());
}

View File

@@ -219,4 +219,19 @@ public class PayAssistService {
}
return null;
}
/**
* 检验订单是否超过限额
*/
public void validationLimitAmount(PayParam payParam) {
// 总额校验
PlatformLocal platformInfo = PaymentContextLocal.get().getPlatformInfo();
Integer amount = payParam.getPayChannels()
.stream()
.map(PayChannelParam::getAmount)
.reduce(0, Integer::sum);
if (amount > platformInfo.getLimitAmount()) {
throw new PayFailureException("支付金额超过限额");
}
}
}

View File

@@ -101,6 +101,8 @@ public class PayService {
PayUtil.validationAsyncPay(payParam);
// 检查支付金额
PayUtil.validationAmount(payParam.getPayChannels());
// 校验支付限额
payAssistService.validationLimitAmount(payParam);
String businessNo = payParam.getBusinessNo();
// 加锁
@@ -161,8 +163,6 @@ public class PayService {
*/
@Transactional(rollbackFor = Exception.class)
public PayResult firstSyncPay(PayParam payParam){
// 创建支付订单和扩展记录并返回支付订单对象
PayOrder payOrder = payAssistService.createPayOrder(payParam);
PayLocal payInfo = PaymentContextLocal.get().getPayInfo();
// 获取支付方式,通过工厂生成对应的策略组
@@ -172,12 +172,15 @@ public class PayService {
}
// 初始化支付参数
for (AbsPayStrategy strategy : strategies) {
strategy.initPayParam(payOrder, payParam);
}
strategies.forEach(strategy -> strategy.setPayParam(payParam));
// 执行支付前处理动作
// 执行支付前处理动作, 进行校验
strategies.forEach(AbsPayStrategy::doBeforePayHandler);
// 创建支付订单和扩展记录并返回支付订单对象
PayOrder payOrder = payAssistService.createPayOrder(payParam);
strategies.forEach(strategy -> strategy.setOrder(payOrder));
// 生成支付通道订单
strategies.forEach(AbsPayStrategy::generateChannelOrder);
// 支付操作
@@ -201,8 +204,6 @@ public class PayService {
*/
private PayResult firstAsyncPay(PayParam payParam){
PayLocal payInfo = PaymentContextLocal.get().getPayInfo();
// 开启新事务执行订单预保存操作
PayOrder payOrder = payAssistService.createPayOrder(payParam);
// 获取支付方式,通过工厂生成对应的策略组
List<AbsPayStrategy> strategies = PayStrategyFactory.createAsyncLast(payParam.getPayChannels());
@@ -214,10 +215,13 @@ public class PayService {
.orElseThrow(() -> new PayFailureException("数据和代码异常, 请排查代码"));
// 初始化支付的参数
asyncPayStrategy.initPayParam(payOrder, payParam);
asyncPayStrategy.setPayParam(payParam);
// 执行支付前处理动作
// 执行支付前处理动作, 进行各种校验
asyncPayStrategy.doBeforePayHandler();
// 创建支付订单和扩展记录并返回支付订单对象
PayOrder payOrder = payAssistService.createPayOrder(payParam);
asyncPayStrategy.setOrder(payOrder);
// 支付操作
try {
@@ -262,20 +266,9 @@ public class PayService {
*/
private PayResult firstCombinationPay(PayParam payParam){
PayLocal payInfo = PaymentContextLocal.get().getPayInfo();
// ------------------------- 进行同步支付 -------------------------------
PayOrder payOrder = SpringUtil.getBean(this.getClass()).firstCombinationSyncPay(payParam);
// ------------------------- 进行同步支付, 成功后返回异步通道策略类 -------------------------------
AbsPayStrategy asyncPayStrategy = SpringUtil.getBean(this.getClass()).firstCombinationSyncPay(payParam);
// ------------------------- 进行异步支付 -------------------------------
// 创建异步通道策略类
//获取异步支付通道参数并构建支付策略
PayChannelParam payChannelParam = payAssistService.getAsyncPayParam(payParam, payOrder);
AbsPayStrategy asyncPayStrategy = PayStrategyFactory.create(payChannelParam);
// 初始化支付的参数
asyncPayStrategy.initPayParam(payOrder, payParam);
// 支付前准备
asyncPayStrategy.doBeforePayHandler();
// 设置异步支付通道订单信息
asyncPayStrategy.generateChannelOrder();
try {
// 异步支付操作
asyncPayStrategy.doPayHandler();
@@ -291,13 +284,11 @@ public class PayService {
}
/**
* 首次组合支付, 先进行同步支付, 如果成功返回支付订单
* 注意: 同时也执行异步支付通道订单的保存, 保证订单操作的原子性
* 首次组合支付, 先进行同步支付, 如果成功返回异步支付策略
* 注意: 同时也异步支付通道进行校验和订单的保存, 保证整个订单操作的原子性
*/
@Transactional(rollbackFor = Exception.class)
public PayOrder firstCombinationSyncPay(PayParam payParam){
// 创建支付订单和扩展记录并返回支付订单对象
PayOrder payOrder = payAssistService.createPayOrder(payParam);
public AbsPayStrategy firstCombinationSyncPay(PayParam payParam){
// 获取支付方式,通过工厂生成对应的策略组
List<AbsPayStrategy> strategies = PayStrategyFactory.createAsyncLast(payParam.getPayChannels());
@@ -306,30 +297,38 @@ public class PayService {
}
// 初始化支付的参数
for (AbsPayStrategy strategy : strategies) {
strategy.initPayParam(payOrder, payParam);
strategy.setPayParam(payParam);
}
// 取出同步通道 然后进行同步通道的支付, 使用单独事务
// 初始化支付参数
strategies.forEach(strategy -> strategy.setPayParam(payParam));
// 执行支付前处理动作, 对各通道进行检验
strategies.forEach(AbsPayStrategy::doBeforePayHandler);
// 创建支付订单和扩展记录并返回支付订单对象
PayOrder payOrder = payAssistService.createPayOrder(payParam);
strategies.forEach(strategy -> strategy.setOrder(payOrder));
// 生成支付通道订单, 同时也执行异步支付通道订单的保存, 保证订单操作的原子性
strategies.forEach(AbsPayStrategy::generateChannelOrder);
// 取出同步通道 然后进行同步通道的支付操作
List<AbsPayStrategy> syncStrategies = strategies.stream()
.filter(strategy -> !ASYNC_TYPE.contains(strategy.getChannel()))
.collect(Collectors.toList());
syncStrategies.forEach(strategy -> strategy.setPayParam(payParam));
// 初始化支付参数
for (AbsPayStrategy strategy : syncStrategies) {
strategy.initPayParam(payOrder, payParam);
}
// 执行支付前处理动作
syncStrategies.forEach(AbsPayStrategy::doBeforePayHandler);
// 生成支付通道订单, 同时也执行异步支付通道订单的保存, 保证订单操作的原子性
strategies.forEach(AbsPayStrategy::generateChannelOrder);
// 支付操作
syncStrategies.forEach(AbsPayStrategy::doPayHandler);
// 支付调起成功操作, 进行扣款、创建记录类类似的操作
syncStrategies.forEach(AbsPayStrategy::doSuccessHandler);
// 保存通道支付订单
payAssistService.savePayChannelOrder(strategies);
return payOrder;
return strategies.stream()
.filter(o-> ASYNC_TYPE.contains(o.getChannel()))
.findFirst()
.orElseThrow(()->new PayFailureException("支付代码异常,请检查"));
}
/**

View File

@@ -78,6 +78,7 @@ public class AliPayStrategy extends AbsPayStrategy {
}
// 检查并获取支付宝支付配置
this.initAlipayConfig();
// 校验
aliPayService.validation(this.getPayChannelParam(), alipayConfig);
}

View File

@@ -78,7 +78,6 @@ public class WeChatPayStrategy extends AbsPayStrategy {
weChatPayService.validation(this.getPayChannelParam(), weChatPayConfig);
}
/**
* 不使用默认的生成通道支付单方法, 异步支付通道的支付订单自己管理
*/

View File

@@ -43,12 +43,11 @@ public class PlatformConfig extends MpBaseEntity implements EntityBaseFunction<P
private String returnUrl;
@DbColumn(comment = "支付限额")
private Integer singleLimit;
private Integer limitAmount;
@DbColumn(comment = "订单默认超时时间(分钟)")
private Integer orderTimeout;
/**
* 转换
*/

View File

@@ -2,7 +2,6 @@ package cn.bootx.platform.daxpay.service.dto.channel.alipay;
import cn.bootx.platform.common.core.rest.dto.BaseDto;
import cn.bootx.platform.starter.data.perm.sensitive.SensitiveInfo;
import cn.bootx.table.modify.annotation.DbColumn;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
@@ -27,7 +26,7 @@ public class AliPayConfigDto extends BaseDto implements Serializable {
@SensitiveInfo
private String appId;
@DbColumn(comment = "是否启用")
@Schema(description = "是否启用")
private Boolean enable;
@Schema(description = "支付限额")

View File

@@ -24,7 +24,7 @@ public class CashPayConfigDto extends BaseDto {
private Boolean enable;
/** 可用支付方式 */
@DbColumn(comment = "可用支付方式")
@Schema(description = "可用支付方式")
private List<String> payWays;
/** 单次支持支持多少钱 */

View File

@@ -1,7 +1,6 @@
package cn.bootx.platform.daxpay.service.dto.channel.voucher;
import cn.bootx.platform.common.core.rest.dto.BaseDto;
import cn.bootx.table.modify.annotation.DbColumn;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
@@ -25,7 +24,7 @@ public class VoucherConfigDto extends BaseDto {
private Boolean enable;
/** 可用支付方式 */
@DbColumn(comment = "可用支付方式")
@Schema(description = "可用支付方式")
private List<String> payWays;
/** 单次支持支持多少钱 */

View File

@@ -25,7 +25,7 @@ public class WalletConfigDto extends BaseDto {
private Boolean enable;
/** 可用支付方式 */
@DbColumn(comment = "可用支付方式")
@Schema(description = "可用支付方式")
private List<String> payWays;
/** 单次支持支持多少钱 */

View File

@@ -2,7 +2,6 @@ package cn.bootx.platform.daxpay.service.dto.channel.wechat;
import cn.bootx.platform.common.core.rest.dto.BaseDto;
import cn.bootx.platform.starter.data.perm.sensitive.SensitiveInfo;
import cn.bootx.table.modify.annotation.DbColumn;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
@@ -29,7 +28,7 @@ public class WeChatPayConfigDto extends BaseDto implements Serializable {
@SensitiveInfo
private String wxAppId;
@DbColumn(comment = "是否启用")
@Schema(description = "是否启用")
private Boolean enable;
@Schema(description = "支付限额")

View File

@@ -2,7 +2,6 @@ package cn.bootx.platform.daxpay.service.dto.order.refund;
import cn.bootx.platform.common.core.rest.dto.BaseDto;
import cn.bootx.platform.daxpay.code.RefundStatusEnum;
import cn.bootx.table.modify.annotation.DbColumn;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
@@ -19,7 +18,6 @@ import lombok.experimental.Accessors;
@Schema(title = "支付退款通道订单")
public class RefundChannelOrderDto extends BaseDto {
@DbColumn(comment = "关联退款id")
@Schema(description = "关联退款id")
private Long refundId;

View File

@@ -2,7 +2,6 @@ package cn.bootx.platform.daxpay.service.dto.record.close;
import cn.bootx.platform.common.core.rest.dto.BaseDto;
import cn.bootx.platform.daxpay.code.PayChannelEnum;
import cn.bootx.table.modify.annotation.DbColumn;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
@@ -45,10 +44,10 @@ public class PayCloseRecordDto extends BaseDto {
private String errorMsg;
/** 客户端IP */
@DbColumn(comment = "客户端IP")
@Schema(description = "客户端IP")
private String clientIp;
/** 请求链路ID */
@DbColumn(comment = "请求链路ID")
@Schema(description = "请求链路ID")
private String reqId;
}

View File

@@ -5,7 +5,6 @@ import cn.bootx.platform.daxpay.code.PayStatusEnum;
import cn.bootx.platform.daxpay.service.code.PayRepairSourceEnum;
import cn.bootx.platform.daxpay.service.code.PayRepairWayEnum;
import cn.bootx.platform.daxpay.service.code.RefundRepairWayEnum;
import cn.bootx.table.modify.annotation.DbColumn;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
@@ -58,7 +57,7 @@ public class PayRepairRecordDto extends BaseDto {
private String repairWay;
/** 修复的异步通道 */
@DbColumn(comment = "修复的异步通道")
@Schema(description = "修复的异步通道")
private String asyncChannel;
/**

View File

@@ -27,7 +27,7 @@ public class PayChannelConfigDto extends BaseDto {
private String name;
/** 是否启用 */
@DbColumn(comment = "是否启用")
@Schema(description = "是否启用")
private Boolean enable;
/** logo图片 */

View File

@@ -15,24 +15,27 @@ import lombok.experimental.Accessors;
@Schema(title = "平台配置")
public class PlatformConfigDto {
@Schema(description ="网站地址")
@Schema(description = "网站地址")
private String websiteUrl;
/**
* @see PaySignTypeEnum
*/
@Schema(description ="签名方式")
@Schema(description = "签名方式")
private String signType;
@Schema(description ="签名秘钥")
@Schema(description = "签名秘钥")
private String signSecret;
@Schema(description ="异步支付通知地址")
@Schema(description = "异步支付通知地址")
private String notifyUrl;
@Schema(description ="同步支付跳转地址")
@Schema(description = "同步支付跳转地址")
private String returnUrl;
@Schema(description ="订单默认超时时间(分钟)")
@Schema(description = "订单默认超时时间(分钟)")
private Integer orderTimeout;
@Schema(description = "订单默认超时时间(分钟)")
private Integer limitAmount;
}

View File

@@ -35,7 +35,7 @@ public class WeChatPayConfigParam {
@Schema(description = "异步通知地址")
private String notifyUrl;
@Schema(description ="同步通知地址")
@Schema(description = "同步通知地址")
private String returnUrl;
/** 接口版本, 使用v2还是v3接口 */

View File

@@ -20,18 +20,18 @@ import lombok.experimental.Accessors;
public class ClientNoticeRecordQuery extends QueryOrder {
/** 任务ID */
@Schema(description ="任务ID")
@Schema(description = "任务ID")
private Long taskId;
/** 请求次数 */
@Schema(description ="请求次数")
@Schema(description = "请求次数")
private Integer reqCount;
/** 发送是否成功 */
@Schema(description ="发送是否成功")
@Schema(description = "发送是否成功")
private Boolean success;
/** 错误信息 */
@Schema(description ="错误信息")
@Schema(description = "错误信息")
private String errorMsg;
}

View File

@@ -21,29 +21,29 @@ import lombok.experimental.Accessors;
public class ClientNoticeTaskQuery extends QueryOrder {
/** 本地订单ID */
@Schema(description ="本地订单ID")
@Schema(description = "本地订单ID")
private Long orderId;
/**
* 通知类型
* @see ClientNoticeTypeEnum
*/
@Schema(description ="通知类型")
@Schema(description = "通知类型")
private String type;
/** 消息内容 */
@Schema(description ="消息内容")
@Schema(description = "消息内容")
private String content;
/** 是否发送成功 */
@Schema(description ="是否发送成功")
@Schema(description = "是否发送成功")
private Boolean success;
/** 发送次数 */
@Schema(description ="发送次数")
@Schema(description = "发送次数")
private Integer sendCount;
/** 发送地址 */
@Schema(description ="发送地址")
@Schema(description = "发送地址")
private String url;
}

View File

@@ -18,17 +18,17 @@ import lombok.experimental.Accessors;
@Schema(title = "支付关闭记录")
public class PayCloseRecordQuery extends QueryOrder {
@Schema(description ="支付记录id")
@Schema(description = "支付记录id")
private Long paymentId;
@Schema(description ="业务号")
@Schema(description = "业务号")
private String businessNo;
/**
* 关闭的异步支付通道, 可以为空
* @see PayChannelEnum#getCode()
*/
@Schema(description ="关闭的异步支付通道")
@Schema(description = "关闭的异步支付通道")
private String asyncChannel;
@Schema(description = "请求链路ID")

View File

@@ -1,7 +1,6 @@
package cn.bootx.platform.daxpay.service.param.system.config;
import cn.bootx.platform.daxpay.code.PaySignTypeEnum;
import cn.bootx.table.modify.annotation.DbColumn;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.experimental.Accessors;
@@ -16,24 +15,27 @@ import lombok.experimental.Accessors;
@Schema(title = "平台配置")
public class PlatformConfigParam {
@DbColumn(comment = "网站地址")
@Schema(description = "网站地址")
private String websiteUrl;
/**
* @see PaySignTypeEnum
*/
@DbColumn(comment = "签名方式")
@Schema(description = "签名方式")
private String signType;
@DbColumn(comment = "签名秘钥")
@Schema(description = "签名秘钥")
private String signSecret;
@DbColumn(comment = "支付通知地址")
@Schema(description = "支付通知地址")
private String notifyUrl;
@Schema(description ="同步支付跳转地址")
@Schema(description = "同步支付跳转地址")
private String returnUrl;
@DbColumn(comment = "订单默认超时时间")
@Schema(description = "订单默认超时时间(分钟)")
private Integer orderTimeout;
@Schema(description = "订单支付限额(分)")
private Integer limitAmount;
}