ref 支付回调逻辑重构, 接入支付订单修复逻辑, 超时任务处理逻辑, 定时同步支付中订单状态

This commit is contained in:
nws
2024-01-02 00:03:19 +08:00
parent ec22293a1c
commit 722f8a08f4
84 changed files with 697 additions and 743 deletions

View File

@@ -4,38 +4,17 @@
- 增加回调机制
- 增加消息通知机制
- 支付超时取消支付单
- 支付单回调处理实现
- 支付同步相关逻辑
- 新增支付单预警功能, 处理支付单与网关状态不一致且无法自动修复的情况
- 记录支付修复单的情况, 主要分为自动修复, 人工介入
- 2023-12-31:
- 支付关闭相关逻辑
- 各支付通道补充相关未实现的逻辑
- 支付订单修复逻辑, 用于回调和支付同步后不一致的情况处理
1.0.1
- x 钱包支持多商户和多应用
- x 储值卡支持多商户和多应用
- x 拆分网关同步相关代码
- x 记录网关同步记录
- x 重构支付消息通知结构, 支持多种消息中间件
- x 保存各渠道的支付单
- x 钱包支持设置开通时的默认金额
- x 储值卡多卡支付和退款演示
- x 储值卡批量导入
- x 储值卡多卡退款到单卡, 不传卡号自动设置为默认卡
1.0.2
- 微信V3支付接口
- 钱包设置支持配置是否创建用户时自动开通
- 支付超时逻辑重构
- 支付单增加类型记录, 如是否单渠道支付/单渠道支付类型等信息
- 各种子支付单记录商户号和应用号等信息, 后期进行统计时使用
- 增加手续费配置管理
- 各种支付方式配置可以记录手续费比例, 支持简易模式和关联支付配置模式
- 储值卡支持多卡合一
- 储值卡信息调整
- 删除应用或商户是做校验, 级联删除对应的支付配置
- 用户体系调整
- 拆分出来用户和顾客
- 增加可同时用于多商户的钱包
- 增加类似京东E卡的支付工具
- 2024-01-01:
- 支付订单修复逻辑, 用于回调和支付同步后不一致的情况处理
- 2024-01-02:
- 支付配置支持数据库配置和配置文件配置
- 支付订单的各类操作接入订单超时任务处理
- 超时任务处理支持轮训表+Redis过期事件
- 支持定时同步支付中订单状态, 借助订单超时任务
- 订单取消/修复/取消/同步添加分布式锁, 防止操作订单时出现重复操作

View File

@@ -20,7 +20,7 @@
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>5.8.18</version>
<version>5.8.24</version>
</dependency>
</dependencies>

View File

@@ -34,7 +34,7 @@ public class PayParam extends PayCommonParam{
@Schema(description = "支付描述")
private String description;
@Schema(description = "过期时间")
@Schema(description = "过期时间, 多次传输以第一次为准")
@JsonDeserialize(using = TimestampToLocalDateTimeDeserializer.class)
private LocalDateTime expiredTime;

View File

@@ -12,24 +12,6 @@
<artifactId>daxpay-single-service</artifactId>
<dependencies>
<!-- lombok -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<scope>provided</scope>
</dependency>
<!-- lombok 配合 mapstruct -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok-mapstruct-binding</artifactId>
<scope>provided</scope>
</dependency>
<!-- 类型转换处理器 -->
<dependency>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct-processor</artifactId>
<scope>provided</scope>
</dependency>
<!-- 类型转换 -->
<dependency>
<groupId>org.mapstruct</groupId>

View File

@@ -20,7 +20,13 @@ public enum PayNotifyStatusEnum {
SUCCESS("success","成功"),
/** 忽略 */
IGNORE("ignore","忽略");
IGNORE("ignore","忽略"),
/** 超时 */
TIMEOUT("timeout","超时"),
/** 未找到本地订单 需要行进订单撤销 */
NOT_FOUND("not_found","未找到");
/** 状态 */
private final String code;

View File

@@ -7,7 +7,7 @@ import lombok.experimental.Accessors;
import java.time.LocalDateTime;
/**
* 异步支付信息
* 异步支付信息,不只局限在支付流程,同步、退款、回调中都会用到
* @author xxm
* @since 2021/2/28
*/

View File

@@ -21,7 +21,7 @@ public class PaymentContext {
/** 平台全局配置 */
private final PlatformLocal platform = new PlatformLocal();
/** 异步支付相关信息 */
/** 异步支付相关信息, 不只局限在支付流程,同步、退款、回调中都会用到 */
private final AsyncPayLocal asyncPayInfo = new AsyncPayLocal();
/** 异步退款相关信息 */

View File

@@ -1,14 +0,0 @@
package cn.bootx.platform.daxpay.core.callback.convert;
import org.mapstruct.Mapper;
import org.mapstruct.factory.Mappers;
/**
* 回调通知转换
* @author xxm
* @since 2023/12/18
*/
@Mapper
public interface CallbackNotifyMConvert {
CallbackNotifyMConvert CONVERT = Mappers.getMapper(CallbackNotifyMConvert.class);
}

View File

@@ -1,14 +0,0 @@
package cn.bootx.platform.daxpay.core.callback.dao;
import cn.bootx.platform.daxpay.core.callback.entity.CallbackNotify;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Mapper;
/**
*
* @author xxm
* @since 2023/12/18
*/
@Mapper
public interface CallbackNotifyMapper extends BaseMapper<CallbackNotify> {
}

View File

@@ -6,7 +6,7 @@ import cn.bootx.platform.daxpay.code.AliPayCode;
import cn.bootx.platform.daxpay.code.PayChannelEnum;
import cn.bootx.platform.daxpay.code.PayStatusEnum;
import cn.bootx.platform.daxpay.common.local.PaymentContextLocal;
import cn.bootx.platform.daxpay.core.callback.dao.CallbackNotifyManager;
import cn.bootx.platform.daxpay.core.record.callback.dao.CallbackRecordManager;
import cn.bootx.platform.daxpay.core.channel.alipay.entity.AliPayConfig;
import cn.bootx.platform.daxpay.core.payment.callback.service.PayCallbackService;
import cn.bootx.platform.daxpay.func.AbsPayCallbackStrategy;
@@ -35,17 +35,23 @@ public class AliPayCallbackService extends AbsPayCallbackStrategy {
private final AliPayConfigService aliasConfigService;
public AliPayCallbackService(RedisClient redisClient, CallbackNotifyManager callbackNotifyManager,
public AliPayCallbackService(RedisClient redisClient, CallbackRecordManager callbackRecordManager,
PayCallbackService payCallbackService, AliPayConfigService aliasConfigService) {
super(redisClient, callbackNotifyManager, payCallbackService);
super(redisClient, callbackRecordManager, payCallbackService);
this.aliasConfigService = aliasConfigService;
}
/**
* 获取支付通道
*/
@Override
public PayChannelEnum getPayChannel() {
return PayChannelEnum.ALI;
}
/**
* 获取交易状态
*/
@Override
public String getTradeStatus() {
Map<String, String> params = PaymentContextLocal.get().getCallbackParam();
@@ -74,29 +80,40 @@ public class AliPayCallbackService extends AbsPayCallbackStrategy {
log.error("支付宝支付配置不存在: {}", callReq);
return false;
}
try {
if (Objects.equals(alipayConfig.getAuthType(), AliPayCode.AUTH_TYPE_KEY)) {
return AlipaySignature.rsaCheckV1(params, alipayConfig.getAlipayPublicKey(), CharsetUtil.UTF_8,
AlipayConstants.SIGN_TYPE_RSA2);
return AlipaySignature.rsaCheckV1(params, alipayConfig.getAlipayPublicKey(), CharsetUtil.UTF_8, AlipayConstants.SIGN_TYPE_RSA2);
}
else {
return AlipaySignature.verifyV1(params, CertUtil.getCertByContent(alipayConfig.getAlipayCert()),
CharsetUtil.UTF_8, AlipayConstants.SIGN_TYPE_RSA2);
return AlipaySignature.verifyV1(params, CertUtil.getCertByContent(alipayConfig.getAlipayCert()), CharsetUtil.UTF_8, AlipayConstants.SIGN_TYPE_RSA2);
}
}
catch (AlipayApiException e) {
} catch (AlipayApiException e) {
log.error("支付宝验签失败", e);
return false;
}
}
/**
* 分通道特殊处理, 如将解析的数据放到上下文中
*/
@Override
public void initContext() {
Map<String, String> callbackParam = PaymentContextLocal.get().getCallbackParam();
PaymentContextLocal.get().getAsyncPayInfo().setTradeNo(callbackParam.get(AliPayCode.TRADE_NO));
}
/**
* 获取支付id
*/
@Override
public Long getPaymentId() {
Map<String, String> params = PaymentContextLocal.get().getCallbackParam();
return Long.valueOf(params.get(AliPayCode.OUT_TRADE_NO));
}
/**
* 获取返回信息
*/
@Override
public String getReturnMsg() {
return "success";

View File

@@ -2,7 +2,7 @@ package cn.bootx.platform.daxpay.core.channel.alipay.service;
import cn.bootx.platform.common.spring.exception.RetryableException;
import cn.bootx.platform.daxpay.code.AliPayCode;
import cn.bootx.platform.daxpay.core.order.pay.entity.PayOrder;
import cn.bootx.platform.daxpay.core.record.pay.entity.PayOrder;
import cn.bootx.platform.daxpay.exception.pay.PayFailureException;
import com.alipay.api.AlipayApiException;
import com.alipay.api.domain.AlipayTradeCloseModel;

View File

@@ -6,9 +6,8 @@ import cn.bootx.platform.daxpay.common.entity.OrderRefundableInfo;
import cn.bootx.platform.daxpay.common.local.PaymentContextLocal;
import cn.bootx.platform.daxpay.core.channel.alipay.dao.AliPayOrderManager;
import cn.bootx.platform.daxpay.core.channel.alipay.entity.AliPayOrder;
import cn.bootx.platform.daxpay.core.order.pay.dao.PayOrderManager;
import cn.bootx.platform.daxpay.core.order.pay.entity.PayOrder;
import cn.bootx.platform.daxpay.core.order.pay.service.PayOrderChannelService;
import cn.bootx.platform.daxpay.core.record.pay.entity.PayOrder;
import cn.bootx.platform.daxpay.core.record.pay.service.PayOrderChannelService;
import cn.bootx.platform.daxpay.param.pay.PayWayParam;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
@@ -35,8 +34,6 @@ public class AliPayOrderService {
private final AliPayOrderManager aliPayOrderManager;
private final PayOrderManager payOrderManager;
private final PayOrderChannelService payOrderChannelService;
/**

View File

@@ -3,7 +3,7 @@ package cn.bootx.platform.daxpay.core.channel.alipay.service;
import cn.bootx.platform.daxpay.code.AliPayCode;
import cn.bootx.platform.daxpay.common.context.AsyncRefundLocal;
import cn.bootx.platform.daxpay.common.local.PaymentContextLocal;
import cn.bootx.platform.daxpay.core.order.pay.entity.PayOrder;
import cn.bootx.platform.daxpay.core.record.pay.entity.PayOrder;
import cn.bootx.platform.daxpay.exception.pay.PayFailureException;
import cn.hutool.core.util.IdUtil;
import com.alipay.api.AlipayApiException;

View File

@@ -8,7 +8,7 @@ import cn.bootx.platform.daxpay.common.context.AsyncPayLocal;
import cn.bootx.platform.daxpay.common.context.NoticeLocal;
import cn.bootx.platform.daxpay.common.local.PaymentContextLocal;
import cn.bootx.platform.daxpay.core.channel.alipay.entity.AliPayConfig;
import cn.bootx.platform.daxpay.core.order.pay.entity.PayOrder;
import cn.bootx.platform.daxpay.core.record.pay.entity.PayOrder;
import cn.bootx.platform.daxpay.exception.pay.PayFailureException;
import cn.bootx.platform.daxpay.param.channel.AliPayParam;
import cn.bootx.platform.daxpay.param.pay.PayWayParam;
@@ -97,15 +97,15 @@ public class AliPayService {
/**
* wap支付
*/
public String wapPay(int amount, PayOrder payment, AliPayConfig alipayConfig) {
public String wapPay(int amount, PayOrder payOrder, AliPayConfig alipayConfig) {
NoticeLocal noticeInfo = PaymentContextLocal.get().getNoticeInfo();
AsyncPayLocal asyncPayInfo = PaymentContextLocal.get().getAsyncPayInfo();
AlipayTradeWapPayModel model = new AlipayTradeWapPayModel();
model.setSubject(payment.getTitle());
model.setOutTradeNo(String.valueOf(payment.getId()));
model.setSubject(payOrder.getTitle());
model.setOutTradeNo(String.valueOf(payOrder.getId()));
model.setTotalAmount(String.valueOf(amount*0.01));
// 过期时间
model.setTimeExpire(PayUtil.getAliTimeExpire(asyncPayInfo.getExpiredTime()));
model.setTimeExpire(PayUtil.getAliTimeExpire(payOrder.getExpiredTime()));
model.setProductCode(AliPayCode.QUICK_WAP_PAY);
// 中途退出地址
model.setQuitUrl(noticeInfo.getQuitUrl());
@@ -131,15 +131,14 @@ public class AliPayService {
/**
* app支付
*/
public String appPay(int amount, PayOrder payment, AliPayConfig alipayConfig) {
AsyncPayLocal asyncPayInfo = PaymentContextLocal.get().getAsyncPayInfo();
public String appPay(int amount, PayOrder payOrder, AliPayConfig alipayConfig) {
AlipayTradeAppPayModel model = new AlipayTradeAppPayModel();
model.setSubject(payment.getTitle());
model.setSubject(payOrder.getTitle());
model.setProductCode(QUICK_MSECURITY_PAY);
model.setOutTradeNo(String.valueOf(payment.getId()));
model.setOutTradeNo(String.valueOf(payOrder.getId()));
// 过期时间
model.setTimeExpire(PayUtil.getAliTimeExpire(asyncPayInfo.getExpiredTime()));
model.setTimeExpire(PayUtil.getAliTimeExpire(payOrder.getExpiredTime()));
model.setTotalAmount(String.valueOf(amount*0.01));
try {
@@ -156,15 +155,14 @@ public class AliPayService {
/**
* PC支付
*/
public String webPay(int amount, PayOrder payment, AliPayConfig alipayConfig) {
public String webPay(int amount, PayOrder payOrder, AliPayConfig alipayConfig) {
NoticeLocal noticeInfo = PaymentContextLocal.get().getNoticeInfo();
AsyncPayLocal asyncPayInfo = PaymentContextLocal.get().getAsyncPayInfo();
AlipayTradePagePayModel model = new AlipayTradePagePayModel();
model.setSubject(payment.getTitle());
model.setOutTradeNo(String.valueOf(payment.getId()));
model.setSubject(payOrder.getTitle());
model.setOutTradeNo(String.valueOf(payOrder.getId()));
// 过期时间
model.setTimeExpire(PayUtil.getAliTimeExpire(asyncPayInfo.getExpiredTime()));
model.setTimeExpire(PayUtil.getAliTimeExpire(payOrder.getExpiredTime()));
model.setTotalAmount(String.valueOf(amount*0.01));
// 目前仅支持FAST_INSTANT_TRADE_PAY
model.setProductCode(AliPayCode.FAST_INSTANT_TRADE_PAY);
@@ -189,15 +187,14 @@ public class AliPayService {
/**
* 二维码支付(扫码支付)
*/
public String qrCodePay(int amount, PayOrder payment, AliPayConfig alipayConfig) {
AsyncPayLocal asyncPayInfo = PaymentContextLocal.get().getAsyncPayInfo();
public String qrCodePay(int amount, PayOrder payOrder, AliPayConfig alipayConfig) {
AlipayTradePrecreateModel model = new AlipayTradePrecreateModel();
model.setSubject(payment.getTitle());
model.setOutTradeNo(String.valueOf(payment.getId()));
model.setSubject(payOrder.getTitle());
model.setOutTradeNo(String.valueOf(payOrder.getId()));
model.setTotalAmount(String.valueOf(amount*0.01));
// 过期时间
model.setTimeExpire(PayUtil.getAliTimeExpire(asyncPayInfo.getExpiredTime()));
model.setTimeExpire(PayUtil.getAliTimeExpire(payOrder.getExpiredTime()));
try {
AlipayTradePrecreateResponse response = AliPayApi.tradePrecreatePayToResponse(model, alipayConfig.getNotifyUrl());
@@ -213,24 +210,23 @@ public class AliPayService {
/**
* 付款码支付
*/
public String barCode(int amount, PayOrder payment, AliPayParam aliPayParam, AliPayConfig alipayConfig) {
AsyncPayLocal asyncPayInfo = PaymentContextLocal.get().getAsyncPayInfo();
public String barCode(int amount, PayOrder payOrder, AliPayParam aliPayParam, AliPayConfig alipayConfig) {
AlipayTradePayModel model = new AlipayTradePayModel();
model.setSubject(payment.getTitle());
model.setOutTradeNo(String.valueOf(payment.getId()));
model.setSubject(payOrder.getTitle());
model.setOutTradeNo(String.valueOf(payOrder.getId()));
model.setScene(AliPayCode.BAR_CODE);
model.setAuthCode(aliPayParam.getAuthCode());
// 过期时间
model.setTimeExpire(PayUtil.getAliTimeExpire(asyncPayInfo.getExpiredTime()));
model.setTimeExpire(PayUtil.getAliTimeExpire(payOrder.getExpiredTime()));
model.setTotalAmount(String.valueOf(amount*0.01));
try {
AlipayTradePayResponse response = AliPayApi.tradePayToResponse(model, alipayConfig.getNotifyUrl());
// 支付成功处理 金额2000以下免密支付
if (Objects.equals(response.getCode(), AliPayCode.SUCCESS)) {
payment.setStatus(PayStatusEnum.SUCCESS.getCode()).setPayTime(LocalDateTime.now());
payOrder.setStatus(PayStatusEnum.SUCCESS.getCode()).setPayTime(LocalDateTime.now());
return response.getTradeNo();
}
// 非支付中响应码, 进行错误处理

View File

@@ -5,7 +5,7 @@ import cn.bootx.platform.daxpay.code.PayStatusEnum;
import cn.bootx.platform.daxpay.code.PaySyncStatusEnum;
import cn.bootx.platform.daxpay.common.local.PaymentContextLocal;
import cn.bootx.platform.daxpay.core.channel.alipay.dao.AliPayOrderManager;
import cn.bootx.platform.daxpay.core.order.pay.entity.PayOrder;
import cn.bootx.platform.daxpay.core.record.pay.entity.PayOrder;
import cn.bootx.platform.daxpay.core.payment.sync.result.GatewaySyncResult;
import cn.hutool.json.JSONUtil;
import com.alipay.api.AlipayApiException;

View File

@@ -3,7 +3,7 @@ package cn.bootx.platform.daxpay.core.channel.cash.service;
import cn.bootx.platform.daxpay.code.PayStatusEnum;
import cn.bootx.platform.daxpay.core.channel.cash.dao.CashPayOrderManager;
import cn.bootx.platform.daxpay.core.channel.cash.entity.CashPayOrder;
import cn.bootx.platform.daxpay.core.order.pay.entity.PayOrder;
import cn.bootx.platform.daxpay.core.record.pay.entity.PayOrder;
import cn.bootx.platform.daxpay.param.pay.PayParam;
import cn.bootx.platform.daxpay.param.pay.PayWayParam;
import lombok.RequiredArgsConstructor;

View File

@@ -5,7 +5,7 @@ import cn.bootx.platform.daxpay.code.PayStatusEnum;
import cn.bootx.platform.daxpay.core.channel.voucher.dao.VoucherPaymentManager;
import cn.bootx.platform.daxpay.core.channel.voucher.entity.VoucherPayOrder;
import cn.bootx.platform.daxpay.core.channel.voucher.entity.VoucherRecord;
import cn.bootx.platform.daxpay.core.order.pay.entity.PayOrder;
import cn.bootx.platform.daxpay.core.record.pay.entity.PayOrder;
import cn.bootx.platform.daxpay.param.pay.PayParam;
import cn.bootx.platform.daxpay.param.pay.PayWayParam;
import lombok.RequiredArgsConstructor;

View File

@@ -8,7 +8,7 @@ import cn.bootx.platform.daxpay.core.channel.voucher.entity.Voucher;
import cn.bootx.platform.daxpay.core.channel.voucher.entity.VoucherLog;
import cn.bootx.platform.daxpay.core.channel.voucher.entity.VoucherPayOrder;
import cn.bootx.platform.daxpay.core.channel.voucher.entity.VoucherRecord;
import cn.bootx.platform.daxpay.core.order.pay.entity.PayOrder;
import cn.bootx.platform.daxpay.core.record.pay.entity.PayOrder;
import cn.bootx.platform.daxpay.exception.pay.PayFailureException;
import cn.bootx.platform.daxpay.param.channel.VoucherPayParam;
import cn.bootx.platform.daxpay.param.pay.PayWayParam;

View File

@@ -5,7 +5,7 @@ import cn.bootx.platform.daxpay.code.PayStatusEnum;
import cn.bootx.platform.daxpay.core.channel.wallet.dao.WalletPaymentManager;
import cn.bootx.platform.daxpay.core.channel.wallet.entity.Wallet;
import cn.bootx.platform.daxpay.core.channel.wallet.entity.WalletPayOrder;
import cn.bootx.platform.daxpay.core.order.pay.entity.PayOrder;
import cn.bootx.platform.daxpay.core.record.pay.entity.PayOrder;
import cn.bootx.platform.daxpay.param.pay.PayParam;
import cn.bootx.platform.daxpay.param.pay.PayWayParam;
import lombok.RequiredArgsConstructor;

View File

@@ -9,7 +9,7 @@ import cn.bootx.platform.daxpay.core.channel.wallet.dao.WalletPaymentManager;
import cn.bootx.platform.daxpay.core.channel.wallet.entity.Wallet;
import cn.bootx.platform.daxpay.core.channel.wallet.entity.WalletLog;
import cn.bootx.platform.daxpay.core.channel.wallet.entity.WalletPayOrder;
import cn.bootx.platform.daxpay.core.order.pay.entity.PayOrder;
import cn.bootx.platform.daxpay.core.record.pay.entity.PayOrder;
import cn.bootx.platform.daxpay.exception.waller.WalletLackOfBalanceException;
import cn.bootx.platform.daxpay.exception.waller.WalletNotExistsException;
import lombok.RequiredArgsConstructor;

View File

@@ -5,7 +5,7 @@ import cn.bootx.platform.daxpay.code.PayChannelEnum;
import cn.bootx.platform.daxpay.code.PayStatusEnum;
import cn.bootx.platform.daxpay.code.WeChatPayCode;
import cn.bootx.platform.daxpay.common.local.PaymentContextLocal;
import cn.bootx.platform.daxpay.core.callback.dao.CallbackNotifyManager;
import cn.bootx.platform.daxpay.core.record.callback.dao.CallbackRecordManager;
import cn.bootx.platform.daxpay.core.channel.wechat.entity.WeChatPayConfig;
import cn.bootx.platform.daxpay.core.payment.callback.service.PayCallbackService;
import cn.bootx.platform.daxpay.func.AbsPayCallbackStrategy;
@@ -34,12 +34,15 @@ import static cn.bootx.platform.daxpay.code.WeChatPayCode.APPID;
public class WeChatPayCallbackService extends AbsPayCallbackStrategy {
private final WeChatPayConfigService weChatPayConfigService;
public WeChatPayCallbackService(RedisClient redisClient, CallbackNotifyManager callbackNotifyManager,
public WeChatPayCallbackService(RedisClient redisClient, CallbackRecordManager callbackRecordManager,
PayCallbackService payCallbackService, WeChatPayConfigService weChatPayConfigService) {
super(redisClient, callbackNotifyManager, payCallbackService);
super(redisClient, callbackRecordManager, payCallbackService);
this.weChatPayConfigService = weChatPayConfigService;
}
/**
* 支付通道
*/
@Override
public PayChannelEnum getPayChannel() {
return PayChannelEnum.WECHAT;
@@ -92,6 +95,18 @@ public class WeChatPayCallbackService extends AbsPayCallbackStrategy {
return WxPayKit.verifyNotify(params, weChatPayConfig.getApiKeyV2(), SignType.HMACSHA256, null);
}
/**
* 分通道特殊处理, 如将解析的数据放到上下文中
*/
@Override
public void initContext() {
Map<String, String> callbackParam = PaymentContextLocal.get().getCallbackParam();
PaymentContextLocal.get().getAsyncPayInfo().setTradeNo(callbackParam.get(WeChatPayCode.TRANSACTION_ID));
}
/**
* 返回响应结果
*/
@Override
public String getReturnMsg() {
Map<String, String> xml = new HashMap<>(4);

View File

@@ -5,7 +5,7 @@ import cn.bootx.platform.daxpay.code.WeChatPayCode;
import cn.bootx.platform.daxpay.common.context.AsyncRefundLocal;
import cn.bootx.platform.daxpay.common.local.PaymentContextLocal;
import cn.bootx.platform.daxpay.core.channel.wechat.entity.WeChatPayConfig;
import cn.bootx.platform.daxpay.core.order.pay.entity.PayOrder;
import cn.bootx.platform.daxpay.core.record.pay.entity.PayOrder;
import cn.bootx.platform.daxpay.exception.pay.PayFailureException;
import cn.hutool.core.util.StrUtil;
import com.ijpay.core.enums.SignType;

View File

@@ -8,9 +8,9 @@ import cn.bootx.platform.daxpay.common.entity.OrderRefundableInfo;
import cn.bootx.platform.daxpay.common.local.PaymentContextLocal;
import cn.bootx.platform.daxpay.core.channel.wechat.dao.WeChatPayOrderManager;
import cn.bootx.platform.daxpay.core.channel.wechat.entity.WeChatPayOrder;
import cn.bootx.platform.daxpay.core.order.pay.dao.PayOrderManager;
import cn.bootx.platform.daxpay.core.order.pay.entity.PayOrder;
import cn.bootx.platform.daxpay.core.order.pay.service.PayOrderChannelService;
import cn.bootx.platform.daxpay.core.record.pay.entity.PayOrder;
import cn.bootx.platform.daxpay.core.record.pay.service.PayOrderChannelService;
import cn.bootx.platform.daxpay.core.record.pay.service.PayOrderService;
import cn.bootx.platform.daxpay.param.pay.PayWayParam;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
@@ -32,7 +32,7 @@ import java.util.Optional;
@RequiredArgsConstructor
public class WeChatPayOrderService {
private final PayOrderManager paymentService;
private final PayOrderService payOrderService;
private final PayOrderChannelService payOrderChannelService;
@@ -65,7 +65,7 @@ public class WeChatPayOrderService {
* 异步支付成功, 更新支付记录成功状态, 并创建微信支付记录
*/
public void updateAsyncSuccess(Long id, PayWayParam payWayParam) {
PayOrder payOrder = paymentService.findById(id).orElseThrow(() -> new BizException("支付记录不存在"));
PayOrder payOrder = payOrderService.findById(id).orElseThrow(() -> new BizException("支付记录不存在"));
this.createWeChatOrder(payOrder, payWayParam.getAmount());
}

View File

@@ -11,7 +11,7 @@ import cn.bootx.platform.daxpay.code.WeChatPayWay;
import cn.bootx.platform.daxpay.common.context.AsyncPayLocal;
import cn.bootx.platform.daxpay.common.local.PaymentContextLocal;
import cn.bootx.platform.daxpay.core.channel.wechat.entity.WeChatPayConfig;
import cn.bootx.platform.daxpay.core.order.pay.entity.PayOrder;
import cn.bootx.platform.daxpay.core.record.pay.entity.PayOrder;
import cn.bootx.platform.daxpay.core.payment.sync.service.PaySyncService;
import cn.bootx.platform.daxpay.exception.pay.PayFailureException;
import cn.bootx.platform.daxpay.param.channel.wechat.WeChatPayParam;
@@ -231,20 +231,17 @@ public class WeChatPayService {
/**
* 构建参数
*/
private UnifiedOrderModelBuilder buildParams(String amount, PayOrder payment, WeChatPayConfig weChatPayConfig, String tradeType) {
private UnifiedOrderModelBuilder buildParams(String amount, PayOrder payOrder, WeChatPayConfig weChatPayConfig, String tradeType) {
LocalDateTime expiredTime = PaymentContextLocal.get()
.getAsyncPayInfo()
.getExpiredTime();
return builder()
.appid(weChatPayConfig.getWxAppId())
.mch_id(weChatPayConfig.getWxMchId())
.nonce_str(WxPayKit.generateStr())
.time_start(LocalDateTimeUtil.format(LocalDateTime.now(), DatePattern.PURE_DATETIME_PATTERN))
// 反正v2版本的超时时间无效
.time_expire(PayUtil.getWxExpiredTime(expiredTime))
.body(payment.getTitle())
.out_trade_no(String.valueOf(payment.getId()))
.time_expire(PayUtil.getWxExpiredTime(payOrder.getExpiredTime()))
.body(payOrder.getTitle())
.out_trade_no(String.valueOf(payOrder.getId()))
.total_fee(amount)
.spbill_create_ip(NetUtil.getLocalhostStr())
.notify_url(weChatPayConfig.getNotifyUrl())

View File

@@ -7,7 +7,7 @@ import cn.bootx.platform.daxpay.common.context.AsyncRefundLocal;
import cn.bootx.platform.daxpay.common.entity.OrderRefundableInfo;
import cn.bootx.platform.daxpay.common.local.PaymentContextLocal;
import cn.bootx.platform.daxpay.core.channel.wechat.entity.WeChatPayConfig;
import cn.bootx.platform.daxpay.core.order.pay.entity.PayOrder;
import cn.bootx.platform.daxpay.core.record.pay.entity.PayOrder;
import cn.bootx.platform.daxpay.exception.pay.PayFailureException;
import cn.hutool.core.codec.Base64;
import cn.hutool.core.util.IdUtil;

View File

@@ -1,120 +0,0 @@
package cn.bootx.platform.daxpay.core.order.pay.service;
import cn.bootx.platform.common.core.util.LocalDateTimeUtil;
import cn.bootx.platform.common.spring.exception.RetryableException;
import cn.bootx.platform.daxpay.code.PayChannelEnum;
import cn.bootx.platform.daxpay.code.PayStatusEnum;
import cn.bootx.platform.daxpay.core.order.pay.dao.PayExpiredTimeRepository;
import cn.bootx.platform.daxpay.core.order.pay.dao.PayOrderChannelManager;
import cn.bootx.platform.daxpay.core.order.pay.dao.PayOrderExtraManager;
import cn.bootx.platform.daxpay.core.order.pay.dao.PayOrderManager;
import cn.bootx.platform.daxpay.core.order.pay.entity.PayOrder;
import cn.bootx.platform.daxpay.core.order.pay.entity.PayOrderChannel;
import cn.bootx.platform.daxpay.common.entity.OrderRefundableInfo;
import cn.bootx.platform.daxpay.exception.pay.PayFailureException;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.retry.annotation.Retryable;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import java.io.Serializable;
import java.time.LocalDateTime;
import java.time.temporal.ChronoUnit;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
/**
* 支付订单服务
* @author xxm
* @since 2023/12/18
*/
@Slf4j
@Service
@RequiredArgsConstructor
public class PayOrderService {
private final PayOrderManager orderManager;
private final PayOrderChannelManager orderChannelManager;
private final PayOrderExtraManager orderExtraManager;
private final PayExpiredTimeRepository expiredTimeRepository;
/**
* 保存
*/
public PayOrder saveOder(PayOrder payment) {
return orderManager.save(payment);
}
/**
* 保存支付通道信息列表
*/
public void saveOrderChannels(List<PayOrderChannel> payOrderChannels){
orderChannelManager.saveAll(payOrderChannels);
}
/**
* 更新支付记录
*/
public PayOrder updateById(PayOrder payment) {
// 超时注册
this.registerExpiredTime(payment);
return orderManager.updateById(payment);
}
/**
* 根据id查询
*/
public Optional<PayOrder> findById(Serializable id) {
return orderManager.findById(id);
}
/**
* 根据BusinessId查询
*/
public Optional<PayOrder> findByBusinessId(String businessNo) {
return orderManager.findByBusinessNo(businessNo);
}
/**
* 退款成功处理, 更新可退款信息 不要进行持久化
*/
public void updateRefundSuccess(PayOrder payment, int amount, PayChannelEnum payChannelEnum) {
// 删除旧有的退款记录, 替换退款完的新的
List<OrderRefundableInfo> refundableInfos = payment.getRefundableInfos();
OrderRefundableInfo refundableInfo = refundableInfos.stream()
.filter(o -> Objects.equals(o.getChannel(), payChannelEnum.getCode()))
.findFirst()
.orElseThrow(() -> new PayFailureException("退款数据不存在"));
refundableInfos.remove(refundableInfo);
refundableInfo.setAmount(refundableInfo.getAmount() - amount);
refundableInfos.add(refundableInfo);
payment.setRefundableInfos(refundableInfos);
}
/**
* 支付单超时关闭事件注册, 失败重试3次, 间隔一秒
*/
@Async("bigExecutor")
@Retryable(value = RetryableException.class)
public void registerExpiredTime(PayOrder payOrder) {
LocalDateTime expiredTime = payOrder.getExpiredTime();
// 支付中且有超时时间才会注册超时关闭时间
if (Objects.equals(payOrder.getStatus(), PayStatusEnum.PROGRESS.getCode()) && Objects.nonNull(expiredTime)) {
try {
// 将过期时间添加到redis中, 往后延时一分钟
expiredTime = LocalDateTimeUtil.offset(expiredTime, 1, ChronoUnit.MINUTES);
expiredTimeRepository.store(payOrder.getId(), expiredTime);
}
catch (Exception e) {
log.error("注册支付单超时关闭失败");
throw new RetryableException();
}
}
}
}

View File

@@ -1,19 +0,0 @@
package cn.bootx.platform.daxpay.core.order.sync.convert;
import cn.bootx.platform.daxpay.core.order.sync.entity.PaySyncOrder;
import cn.bootx.platform.daxpay.dto.order.sync.PaySyncOrderDto;
import org.mapstruct.Mapper;
import org.mapstruct.factory.Mappers;
/**
* 支付同步记录同步
* @author xxm
* @since 2023/7/14
*/
@Mapper
public interface PaySyncOrderConvert {
PaySyncOrderConvert CONVERT = Mappers.getMapper(PaySyncOrderConvert.class);
PaySyncOrderDto convert(PaySyncOrder in);
}

View File

@@ -1,14 +0,0 @@
package cn.bootx.platform.daxpay.core.order.sync.dao;
import cn.bootx.platform.daxpay.core.order.sync.entity.PaySyncOrder;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Mapper;
/**
* 支付同步记录
* @author xxm
* @since 2023/7/14
*/
@Mapper
public interface PaySyncOrderMapper extends BaseMapper<PaySyncOrder> {
}

View File

@@ -1,27 +1,21 @@
package cn.bootx.platform.daxpay.core.payment.callback.service;
import cn.bootx.platform.common.core.util.LocalDateTimeUtil;
import cn.bootx.platform.daxpay.code.PayChannelEnum;
import cn.bootx.platform.daxpay.code.PayNotifyStatusEnum;
import cn.bootx.platform.daxpay.code.PayRepairSourceEnum;
import cn.bootx.platform.daxpay.code.PayRepairTypeEnum;
import cn.bootx.platform.daxpay.code.PayStatusEnum;
import cn.bootx.platform.daxpay.common.exception.ExceptionInfo;
import cn.bootx.platform.daxpay.core.order.pay.dao.PayOrderManager;
import cn.bootx.platform.daxpay.core.order.pay.entity.PayOrder;
import cn.bootx.platform.daxpay.core.payment.callback.result.PayCallbackResult;
import cn.bootx.platform.daxpay.core.payment.pay.factory.PayStrategyFactory;
import cn.bootx.platform.daxpay.func.AbsPayStrategy;
import cn.bootx.platform.daxpay.func.PayStrategyConsumer;
import cn.bootx.platform.daxpay.param.pay.PayParam;
import cn.hutool.core.collection.CollectionUtil;
import cn.bootx.platform.daxpay.core.payment.repair.param.PayRepairParam;
import cn.bootx.platform.daxpay.core.payment.repair.service.PayRepairService;
import cn.bootx.platform.daxpay.core.record.pay.entity.PayOrder;
import cn.bootx.platform.daxpay.core.record.pay.service.PayOrderService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;
/**
* 支付回调处理
@@ -34,184 +28,86 @@ import java.util.stream.Collectors;
@RequiredArgsConstructor
public class PayCallbackService {
private final PayOrderManager payOrderService;
private final PayOrderService payOrderService;
private final PayRepairService payRepairService;
/**
* 统一回调处理
* @param tradeStatus 支付状态
*/
public PayCallbackResult callback(Long paymentId, String tradeStatus, Map<String, String> map) {
public PayCallbackResult callback(Long paymentId, String tradeStatus) {
// 获取支付单
PayOrder payOrder = payOrderService.findById(paymentId).orElse(null);
// 支付单不存在,记录回调记录
// 支付单不存在,记录回调记录, TODO 取消支付网关的订单支付情况
if (Objects.isNull(payOrder)) {
return new PayCallbackResult().setStatus(PayNotifyStatusEnum.FAIL.getCode()).setMsg("支付单不存在,记录回调记录");
return new PayCallbackResult().setStatus(PayNotifyStatusEnum.NOT_FOUND.getCode()).setMsg("支付单不存在,记录回调记录");
}
// 回调时间超出了支付单超时时间, 记录一下, 不做处理 TODO 这块应该把订单给正常处理了,
// 回调时间超出了支付单超时时间, 记录一下, 不做处理 TODO 这块应该把订单给当成正常结束给处理了,
if (Objects.nonNull(payOrder.getExpiredTime())
&& LocalDateTimeUtil.ge(LocalDateTime.now(), payOrder.getExpiredTime())) {
return new PayCallbackResult().setStatus(PayNotifyStatusEnum.FAIL.getCode()).setMsg("回调时间超出了支付单支付有效时间");
return new PayCallbackResult().setStatus(PayNotifyStatusEnum.TIMEOUT.getCode()).setMsg("回调时间超出了支付单支付有效时间");
}
// 成功状态
if (Objects.equals(PayNotifyStatusEnum.SUCCESS.getCode(), tradeStatus)) {
return this.success(payOrder, map);
return this.success(payOrder);
}
else {
// 失败状态
return this.fail(payOrder, map);
return this.fail(payOrder);
}
}
/**
* 成功处理
* 成功处理 使用补偿
*/
private PayCallbackResult success(PayOrder payOrder, Map<String, String> map) {
private PayCallbackResult success(PayOrder payOrder) {
PayCallbackResult result = new PayCallbackResult().setStatus(PayNotifyStatusEnum.SUCCESS.getCode());
// payment已经被支付,不需要重复处理
// 支付单已经被支付,不需要重复处理
if (Objects.equals(payOrder.getStatus(), PayStatusEnum.SUCCESS.getCode())) {
return result.setStatus(PayNotifyStatusEnum.IGNORE.getCode()).setMsg("支付单已经是支付成功状态,不进行处理");
}
// payment已被取消,记录回调记录
// 支付单已被取消,记录回调记录 TODO 考虑不全, 需要做支付订单的取消处理
if (!Objects.equals(payOrder.getStatus(), PayStatusEnum.PROGRESS.getCode())) {
return result.setStatus(PayNotifyStatusEnum.FAIL.getCode()).setMsg("支付单不是待支付状态,记录回调记录");
}
// 2.通过工厂生成对应的策略组
PayParam payParam = null;
List<AbsPayStrategy> paymentStrategyList = PayStrategyFactory.createAsyncFront(payParam.getPayWays());
if (CollectionUtil.isEmpty(paymentStrategyList)) {
return result.setStatus(PayStatusEnum.FAIL.getCode()).setMsg("支付单数据非法,未找到对应的支付方式");
}
// 3.初始化支付的参数
for (AbsPayStrategy paymentStrategy : paymentStrategyList) {
paymentStrategy.initPayParam(payOrder, payParam);
}
// 4.处理方法, 支付时只有一种payModel(异步支付), 失败时payment的所有payModel都会生效
boolean handlerFlag = this.doHandler(payOrder, paymentStrategyList, (strategyList, paymentObj) -> {
// 执行异步支付方式的成功回调(不会有同步payModel)
strategyList.forEach(absPaymentStrategy -> absPaymentStrategy.doAsyncSuccessHandler(map));
// 修改payment支付状态为成功
paymentObj.setStatus(PayStatusEnum.SUCCESS.getCode());
paymentObj.setPayTime(LocalDateTime.now());
payOrderService.updateById(paymentObj);
});
if (handlerFlag) {
// 5. 发送成功事件
// eventSender.sendPayComplete(PayEventBuilder.buildPayComplete(payOrder));
}
else {
return result.setStatus(PayStatusEnum.FAIL.getCode()).setMsg("回调处理过程报错");
}
// 执行支付完成修复逻辑
PayRepairParam payRepairParam = new PayRepairParam()
.setRepairSource(PayRepairSourceEnum.CALLBACK)
.setRepairType(PayRepairTypeEnum.SUCCESS);
payRepairService.repair(payOrder, payRepairParam);
return result;
}
/**
* 失败处理, 关闭并退款 按说这块不会发生
* 失败处理, 关闭并退款
*/
private PayCallbackResult fail(PayOrder payOrder, Map<String, String> map) {
private PayCallbackResult fail(PayOrder payOrder) {
PayCallbackResult result = new PayCallbackResult().setStatus(PayStatusEnum.SUCCESS.getCode());
// payment已被取消,记录回调记录,后期处理
// payment已被取消,记录回调记录,后期处理 TODO 考虑不完善, 后续优化
if (!Objects.equals(payOrder.getStatus(), PayStatusEnum.PROGRESS.getCode())) {
return result.setStatus(PayNotifyStatusEnum.IGNORE.getCode()).setMsg("支付单已经取消,记录回调记录");
}
// payment支付成功, 状态非法
// payment支付成功, 状态非法 TODO 考虑不完善, 后续优化
if (!Objects.equals(payOrder.getStatus(), PayStatusEnum.SUCCESS.getCode())) {
return result.setStatus(PayNotifyStatusEnum.FAIL.getCode()).setMsg("支付单状态非法,支付网关状态为失败,但支付单状态为已完成");
}
// 2.通过工厂生成对应的策略组
PayParam payParam = null;
List<AbsPayStrategy> paymentStrategyList = PayStrategyFactory.createAsyncFront(payParam.getPayWays());
if (CollectionUtil.isEmpty(paymentStrategyList)) {
return result.setStatus(PayNotifyStatusEnum.FAIL.getCode()).setMsg("支付单数据非法,未找到对应的支付方式");
}
// 3.初始化支付关闭的参数
for (AbsPayStrategy paymentStrategy : paymentStrategyList) {
paymentStrategy.initPayParam(payOrder, payParam);
}
// 4.处理方法, 支付时只有一种payModel(异步支付), 失败时payment的所有payModel都会生效
boolean handlerFlag = this.doHandler(payOrder, paymentStrategyList, (strategyList, paymentObj) -> {
// 执行异步支付方式的失败回调(不会有同步payModel)
strategyList.forEach(AbsPayStrategy::doCloseHandler);
// 修改payment支付状态为撤销
paymentObj.setStatus(PayStatusEnum.CLOSE.getCode());
payOrderService.updateById(paymentObj);
});
if (handlerFlag) {
// 5. 发送退款事件
// eventSender.sendPayRefund(PayEventBuilder.buildPayRefund(payOrder));
}
else {
return result.setStatus(PayNotifyStatusEnum.FAIL.getCode()).setMsg("回调处理过程报错");
}
// 执行支付关闭修复逻辑
PayRepairParam payRepairParam = new PayRepairParam()
.setRepairSource(PayRepairSourceEnum.CALLBACK)
.setRepairType(PayRepairTypeEnum.CLOSE);
payRepairService.repair(payOrder, payRepairParam);
return result;
}
/**
* 处理方法
* @param payOrder 支付订单
* @param strategyList 支付策略
* @param successCallback 成功操作
*/
private boolean doHandler(PayOrder payOrder, List<AbsPayStrategy> strategyList,
PayStrategyConsumer<List<AbsPayStrategy>, PayOrder> successCallback) {
try {
// 1.获取异步支付方式,通过工厂生成对应的策略组
List<AbsPayStrategy> syncPaymentStrategyList = strategyList.stream()
.filter(paymentStrategy -> PayChannelEnum.ASYNC_TYPE.contains(paymentStrategy.getType()))
.collect(Collectors.toList());
// 执行成功方法
successCallback.accept(syncPaymentStrategyList, payOrder);
}
catch (Exception e) {
// error事件的处理
this.asyncErrorHandler(payOrder, strategyList, e);
return false;
}
return true;
}
/**
* 对Error的处理
*/
private void asyncErrorHandler(PayOrder payOrder, List<AbsPayStrategy> strategyList, Exception e) {
// 默认的错误信息
ExceptionInfo exceptionInfo = new ExceptionInfo(PayStatusEnum.FAIL.getCode(), e.getMessage());
// if (e instanceof BaseException) {
// exceptionInfo = ((BaseException) e).getExceptionInfo();
// }
// else if (e instanceof ErrorCodeRuntimeException) {
// ErrorCodeRuntimeException ex = (ErrorCodeRuntimeException) e;
// exceptionInfo = new ExceptionInfo(String.valueOf(ex.getCode()), ex.getMessage());
// }
// 更新Payment的状态
// payOrder.setErrorCode(String.valueOf(exceptionInfo.getErrorCode()));
// payOrder.setErrorMsg(String.valueOf(exceptionInfo.getErrorMsg()));
// payOrder.setPayStatus(PayStatusCode.TRADE_FAIL);
payOrderService.updateById(payOrder);
// 调用失败处理
for (AbsPayStrategy paymentStrategy : strategyList) {
paymentStrategy.doAsyncErrorHandler(exceptionInfo);
}
}
}

View File

@@ -2,9 +2,9 @@ package cn.bootx.platform.daxpay.core.payment.close.service;
import cn.bootx.platform.daxpay.code.PayStatusEnum;
import cn.bootx.platform.daxpay.common.entity.OrderRefundableInfo;
import cn.bootx.platform.daxpay.core.order.pay.dao.PayOrderManager;
import cn.bootx.platform.daxpay.core.order.pay.entity.PayOrder;
import cn.bootx.platform.daxpay.core.record.pay.entity.PayOrder;
import cn.bootx.platform.daxpay.core.payment.close.factory.PayCloseStrategyFactory;
import cn.bootx.platform.daxpay.core.record.pay.service.PayOrderService;
import cn.bootx.platform.daxpay.exception.pay.PayFailureException;
import cn.bootx.platform.daxpay.exception.pay.PayUnsupportedMethodException;
import cn.bootx.platform.daxpay.func.AbsPayCloseStrategy;
@@ -28,7 +28,7 @@ import java.util.stream.Collectors;
@Service
@RequiredArgsConstructor
public class PayCloseService {
private final PayOrderManager payOrderManager;
private final PayOrderService payOrderService;
/**
* 关闭支付
@@ -37,11 +37,11 @@ public class PayCloseService {
public void close(PayCloseParam param){
PayOrder payOrder = null;
if (Objects.nonNull(param.getPaymentId())){
payOrder = payOrderManager.findById(param.getPaymentId())
payOrder = payOrderService.findById(param.getPaymentId())
.orElseThrow(() -> new PayFailureException("未查询到支付订单"));
}
if (Objects.isNull(payOrder)){
payOrder = payOrderManager.findByBusinessNo(param.getBusinessNo())
payOrder = payOrderService.findByBusinessNo(param.getBusinessNo())
.orElseThrow(() -> new PayFailureException("未查询到支付订单"));
}
this.close(payOrder);
@@ -91,6 +91,6 @@ public class PayCloseService {
private void successHandler(PayOrder payOrder){
// 取消订单
payOrder.setStatus(PayStatusEnum.CLOSE.getCode());
payOrderManager.updateById(payOrder);
payOrderService.updateById(payOrder);
}
}

View File

@@ -1,11 +1,23 @@
package cn.bootx.platform.daxpay.core.payment.pay.service;
import cn.bootx.platform.common.core.exception.DataNotExistException;
import cn.bootx.platform.common.core.util.LocalDateTimeUtil;
import cn.bootx.platform.daxpay.code.PayChannelEnum;
import cn.bootx.platform.daxpay.code.PayStatusEnum;
import cn.bootx.platform.daxpay.common.context.AsyncPayLocal;
import cn.bootx.platform.daxpay.common.context.NoticeLocal;
import cn.bootx.platform.daxpay.common.context.PlatformLocal;
import cn.bootx.platform.daxpay.common.local.PaymentContextLocal;
import cn.bootx.platform.daxpay.core.order.pay.entity.PayOrder;
import cn.bootx.platform.daxpay.core.record.pay.builder.PaymentBuilder;
import cn.bootx.platform.daxpay.core.record.pay.dao.PayOrderChannelManager;
import cn.bootx.platform.daxpay.core.record.pay.dao.PayOrderExtraManager;
import cn.bootx.platform.daxpay.core.record.pay.entity.PayOrder;
import cn.bootx.platform.daxpay.core.record.pay.entity.PayOrderChannel;
import cn.bootx.platform.daxpay.core.record.pay.entity.PayOrderExtra;
import cn.bootx.platform.daxpay.core.record.pay.service.PayOrderService;
import cn.bootx.platform.daxpay.exception.pay.PayFailureException;
import cn.bootx.platform.daxpay.param.pay.PayParam;
import cn.bootx.platform.daxpay.param.pay.PayWayParam;
import cn.bootx.platform.daxpay.util.PayUtil;
import cn.hutool.core.util.StrUtil;
import lombok.RequiredArgsConstructor;
@@ -13,7 +25,10 @@ import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import java.time.LocalDateTime;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;
/**
* 支付支持服务
@@ -25,6 +40,10 @@ import java.util.Objects;
@RequiredArgsConstructor
public class PayAssistService {
private final PayOrderService payOrderService;
private final PayOrderExtraManager payOrderExtraManager;
private final PayOrderChannelManager payOrderChannelManager;
/**
* 初始化支付相关上下文
*/
@@ -83,4 +102,89 @@ public class PayAssistService {
noticeInfo.setQuitUrl(payParam.getQuitUrl());
}
/**
* 获取异步支付参数
*/
public PayWayParam getAsyncPayParam(PayParam payParam, PayOrder payOrder) {
// 查询之前的支付方式
String asyncPayChannel = payOrder.getAsyncPayChannel();
PayOrderChannel payOrderChannel = payOrderChannelManager.findByPaymentIdAndChannel(payOrder.getId(), asyncPayChannel)
.orElseThrow(() -> new PayFailureException("支付方式数据异常"));
// 新的异步支付方式
PayWayParam payWayParam = payParam.getPayWays()
.stream()
.filter(payMode -> PayChannelEnum.ASYNC_TYPE_CODE.contains(payMode.getChannel()))
.findFirst()
.orElseThrow(() -> new PayFailureException("支付方式数据异常"));
// 新传入的金额是否一致
if (!Objects.equals(payOrderChannel.getAmount(), payWayParam.getAmount())){
throw new PayFailureException("传入的支付金额非法!与订单金额不一致");
}
return payWayParam;
}
/**
* 创建支付订单/附加表/支付通道表并保存,返回支付订单
*/
public PayOrder createPayOrder(PayParam payParam) {
// 构建支付订单并保存
PayOrder payOrder = PaymentBuilder.buildPayOrder(payParam);
payOrderService.save(payOrder);
// 构建支付订单扩展表并保存
PayOrderExtra payOrderExtra = PaymentBuilder.buildPayOrderExtra(payParam, payOrder.getId());
payOrderExtraManager.save(payOrderExtra);
// 构建支付通道表并保存
List<PayOrderChannel> payOrderChannels = PaymentBuilder.buildPayChannel(payParam.getPayWays())
.stream()
.peek(o -> o.setPaymentId(payOrder.getId()))
.collect(Collectors.toList());
payOrderChannelManager.saveAll(payOrderChannels);
return payOrder;
}
/**
* 更新支付订单扩展参数
* @param payParam 支付参数
* @param paymentId 支付订单id
*/
public void updatePayOrderExtra(PayParam payParam,Long paymentId){
PayOrderExtra payOrderExtra = payOrderExtraManager.findById(paymentId)
.orElseThrow(() -> new DataNotExistException("支付订单不存在"));
payOrderExtra.setReqTime(payParam.getReqTime())
.setSign(payParam.getSign())
.setNotNotify(payParam.isNotNotify())
.setNotifyUrl(payParam.getNotifyUrl())
.setClientIp(payParam.getClientIp());
payOrderExtraManager.updateById(payOrderExtra);
}
/**
* 校验支付状态,支付成功则返回,支付失败则抛出对应的异常
*/
public PayOrder getOrderAndCheck(String businessNo) {
// 根据订单查询支付记录
PayOrder payOrder = payOrderService.findByBusinessNo(businessNo).orElse(null);
if (Objects.nonNull(payOrder)) {
// 支付失败类型状态
List<String> tradesStatus = Arrays.asList(PayStatusEnum.FAIL.getCode(), PayStatusEnum.CANCEL.getCode(),
PayStatusEnum.CLOSE.getCode());
if (tradesStatus.contains(payOrder.getStatus())) {
throw new PayFailureException("支付失败或已经被撤销");
}
// 退款类型状态
tradesStatus = Arrays.asList(PayStatusEnum.REFUNDED.getCode(), PayStatusEnum.PARTIAL_REFUND.getCode());
if (tradesStatus.contains(payOrder.getStatus())) {
throw new PayFailureException("支付失败或已经被撤销");
}
// 支付超时状态
if (Objects.nonNull(payOrder.getExpiredTime())
&& LocalDateTimeUtil.ge(LocalDateTime.now(), payOrder.getExpiredTime())) {
throw new PayFailureException("支付已超时");
}
}
return payOrder;
}
}

View File

@@ -1,18 +1,10 @@
package cn.bootx.platform.daxpay.core.payment.pay.service;
import cn.bootx.platform.common.core.exception.DataNotExistException;
import cn.bootx.platform.common.core.util.LocalDateTimeUtil;
import cn.bootx.platform.daxpay.code.PayChannelEnum;
import cn.bootx.platform.daxpay.code.PayStatusEnum;
import cn.bootx.platform.daxpay.core.order.pay.builder.PaymentBuilder;
import cn.bootx.platform.daxpay.core.order.pay.dao.PayOrderChannelManager;
import cn.bootx.platform.daxpay.core.order.pay.dao.PayOrderExtraManager;
import cn.bootx.platform.daxpay.core.order.pay.dao.PayOrderManager;
import cn.bootx.platform.daxpay.core.order.pay.entity.PayOrder;
import cn.bootx.platform.daxpay.core.order.pay.entity.PayOrderChannel;
import cn.bootx.platform.daxpay.core.order.pay.entity.PayOrderExtra;
import cn.bootx.platform.daxpay.core.payment.pay.factory.PayStrategyFactory;
import cn.bootx.platform.daxpay.exception.pay.PayFailureException;
import cn.bootx.platform.daxpay.core.record.pay.builder.PaymentBuilder;
import cn.bootx.platform.daxpay.core.record.pay.entity.PayOrder;
import cn.bootx.platform.daxpay.core.record.pay.service.PayOrderService;
import cn.bootx.platform.daxpay.exception.pay.PayUnsupportedMethodException;
import cn.bootx.platform.daxpay.func.AbsPayStrategy;
import cn.bootx.platform.daxpay.func.PayStrategyConsumer;
@@ -32,7 +24,6 @@ import org.springframework.transaction.annotation.Transactional;
import java.time.LocalDateTime;
import java.util.*;
import java.util.function.Consumer;
import java.util.stream.Collectors;
/**
@@ -46,13 +37,9 @@ import java.util.stream.Collectors;
@RequiredArgsConstructor
public class PayService {
private final PayOrderManager payOrderManager;
private final PayOrderService payOrderService;
private final PayAssistService payAssistService;;
private final PayOrderExtraManager payOrderExtraManager;
private final PayOrderChannelManager payOrderChannelManager;
private final PayAssistService payAssistService;
/**
* 支付下单接口(同步/异步/组合支付)
@@ -69,7 +56,7 @@ public class PayService {
PayUtil.validationAsyncPayMode(payParam);
// 获取并校验支付订单状态
PayOrder payOrder = this.getAndCheckByBusinessNo(payParam.getBusinessNo());
PayOrder payOrder = payAssistService.getOrderAndCheck(payParam.getBusinessNo());
// 初始化上下文
payAssistService.initPayContext(payOrder, payParam);
@@ -114,7 +101,7 @@ public class PayService {
PayUtil.validationAmount(payParam.getPayWays());
// 3. 创建支付相关的记录并返回支付订单对象
payOrder = this.createPayOrder(payParam);
payOrder = payAssistService.createPayOrder(payParam);
// 4. 调用支付方法进行发起支付
this.firstPayHandler(payParam, payOrder);
@@ -152,7 +139,7 @@ public class PayService {
payOrderObj.setStatus(PayStatusEnum.SUCCESS.getCode());
payOrderObj.setPayTime(LocalDateTime.now());
}
payOrderManager.updateById(payOrderObj);
payOrderService.updateById(payOrderObj);
});
}
@@ -162,14 +149,14 @@ public class PayService {
private PayResult paySyncNotFirst(PayParam payParam, PayOrder payOrder) {
// 1. 处理支付完成情况(完成/退款)
List<String> trades = Arrays.asList(PayStatusEnum.SUCCESS.getCode(), PayStatusEnum.CANCEL.getCode(),PayStatusEnum.CLOSE.getCode(),
List<String> trades = Arrays.asList(PayStatusEnum.SUCCESS.getCode(), PayStatusEnum.CLOSE.getCode(),
PayStatusEnum.PARTIAL_REFUND.getCode(), PayStatusEnum.REFUNDED.getCode());
if (trades.contains(payOrder.getStatus())) {
return PaymentBuilder.buildPayResultByPayOrder(payOrder);
}
// 2.获取 异步支付通道,通过工厂生成对应的策略组(只包含异步支付的策略, 同步支付相关逻辑不再进行执行)
PayWayParam payWayParam = this.getAsyncPayParam(payParam, payOrder);
PayWayParam payWayParam = payAssistService.getAsyncPayParam(payParam, payOrder);
List<AbsPayStrategy> asyncStrategyList = PayStrategyFactory.createAsyncLast(Collections.singletonList(payWayParam));
// 3.初始化支付的参数
@@ -183,13 +170,16 @@ public class PayService {
this.doHandler(payOrder, asyncStrategyList, AbsPayStrategy::doPayHandler, (strategyList, paymentObj) -> {
// 发起支付成功进行的执行方法
strategyList.forEach(AbsPayStrategy::doSuccessHandler);
payOrderManager.updateById(paymentObj);
payOrderService.updateById(paymentObj);
});
// 6. 更新支付订单扩展参数
updatePayOrderExtra(payParam,payOrder.getId());
payAssistService.updatePayOrderExtra(payParam,payOrder.getId());
// 7. 组装返回参数
// 7. 更新订单过期时间
// 8. 组装返回参数
return PaymentBuilder.buildPayResultByPayOrder(payOrder);
}
@@ -210,87 +200,4 @@ public class PayService {
Optional.ofNullable(successMethod).ifPresent(fun -> fun.accept(strategyList, payment));
}
/**
* 获取异步支付参数
*/
private PayWayParam getAsyncPayParam(PayParam payParam, PayOrder payOrder) {
// 查询之前的支付方式
String asyncPayChannel = payOrder.getAsyncPayChannel();
PayOrderChannel payOrderChannel = payOrderChannelManager.findByPaymentIdAndChannel(payOrder.getId(), asyncPayChannel)
.orElseThrow(() -> new PayFailureException("支付方式数据异常"));
// 新的异步支付方式
PayWayParam payWayParam = payParam.getPayWays()
.stream()
.filter(payMode -> PayChannelEnum.ASYNC_TYPE_CODE.contains(payMode.getChannel()))
.findFirst()
.orElseThrow(() -> new PayFailureException("支付方式数据异常"));
// 新传入的金额是否一致
if (!Objects.equals(payOrderChannel.getAmount(), payWayParam.getAmount())){
throw new PayFailureException("传入的支付金额非法!与订单金额不一致");
}
return payWayParam;
}
/**
* 创建支付订单/附加表/支付通道表并保存,返回支付订单
*/
private PayOrder createPayOrder(PayParam payParam) {
// 构建支付订单并保存
PayOrder payOrder = PaymentBuilder.buildPayOrder(payParam);
payOrderManager.save(payOrder);
// 构建支付订单扩展表并保存
PayOrderExtra payOrderExtra = PaymentBuilder.buildPayOrderExtra(payParam, payOrder.getId());
payOrderExtraManager.save(payOrderExtra);
// 构建支付通道表并保存
List<PayOrderChannel> payOrderChannels = PaymentBuilder.buildPayChannel(payParam.getPayWays())
.stream()
.peek(o -> o.setPaymentId(payOrder.getId()))
.collect(Collectors.toList());
payOrderChannelManager.saveAll(payOrderChannels);
return payOrder;
}
/**
* 更新支付订单扩展参数
* @param payParam 支付参数
* @param paymentId 支付订单id
*/
private void updatePayOrderExtra(PayParam payParam,Long paymentId){
PayOrderExtra payOrderExtra = payOrderExtraManager.findById(paymentId)
.orElseThrow(() -> new DataNotExistException("支付订单不存在"));
payOrderExtra.setReqTime(payParam.getReqTime())
.setSign(payParam.getSign())
.setNotNotify(payParam.isNotNotify())
.setNotifyUrl(payParam.getNotifyUrl())
.setClientIp(payParam.getClientIp());
payOrderExtraManager.updateById(payOrderExtra);
}
/**
* 校验支付状态,支付成功则返回,支付失败则抛出对应的异常
*/
private PayOrder getAndCheckByBusinessNo(String businessNo) {
// 根据订单查询支付记录
PayOrder payment = payOrderManager.findByBusinessNo(businessNo).orElse(null);
if (Objects.nonNull(payment)) {
// 支付失败类型状态
List<String> tradesStatus = Arrays.asList(PayStatusEnum.FAIL.getCode(), PayStatusEnum.CANCEL.getCode(),
PayStatusEnum.CLOSE.getCode());
if (tradesStatus.contains(payment.getStatus())) {
throw new PayFailureException("支付失败或已经被撤销");
}
// 退款类型状态
tradesStatus = Arrays.asList(PayStatusEnum.REFUNDED.getCode(), PayStatusEnum.PARTIAL_REFUND.getCode());
if (tradesStatus.contains(payment.getStatus())) {
throw new PayFailureException("支付失败或已经被撤销");
}
// 支付超时状态
if (Objects.nonNull(payment.getExpiredTime())
&& LocalDateTimeUtil.ge(LocalDateTime.now(), payment.getExpiredTime())) {
throw new PayFailureException("支付已超时");
}
}
return payment;
}
}

View File

@@ -1,11 +1,12 @@
package cn.bootx.platform.daxpay.core.payment.pay.strategy;
import cn.bootx.platform.daxpay.code.AliPayCode;
import cn.bootx.platform.daxpay.code.PayChannelEnum;
import cn.bootx.platform.daxpay.common.exception.ExceptionInfo;
import cn.bootx.platform.daxpay.common.local.PaymentContextLocal;
import cn.bootx.platform.daxpay.core.channel.alipay.entity.AliPayConfig;
import cn.bootx.platform.daxpay.core.channel.alipay.service.*;
import cn.bootx.platform.daxpay.core.channel.alipay.service.AliPayCloseService;
import cn.bootx.platform.daxpay.core.channel.alipay.service.AliPayConfigService;
import cn.bootx.platform.daxpay.core.channel.alipay.service.AliPayOrderService;
import cn.bootx.platform.daxpay.core.channel.alipay.service.AliPayService;
import cn.bootx.platform.daxpay.exception.pay.PayAmountAbnormalException;
import cn.bootx.platform.daxpay.exception.pay.PayFailureException;
import cn.bootx.platform.daxpay.func.AbsPayStrategy;
@@ -18,8 +19,6 @@ import lombok.RequiredArgsConstructor;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
import java.util.Map;
import static org.springframework.beans.factory.config.BeanDefinition.SCOPE_PROTOTYPE;
/**
@@ -102,24 +101,6 @@ public class AliPayStrategy extends AbsPayStrategy {
this.doCloseHandler();
}
/**
* 异步支付成功
*/
@Override
public void doAsyncSuccessHandler(Map<String, String> map) {
PaymentContextLocal.get().getAsyncPayInfo().setTradeNo(map.get(AliPayCode.TRADE_NO));
aliPaymentService.updateAsyncSuccess(this.getOrder(), this.getPayWayParam().getAmount());
}
/**
* 异步支付失败
*/
@Override
public void doAsyncErrorHandler(ExceptionInfo exceptionInfo) {
// 调用撤销支付
this.doCloseHandler();
}
/**
* 关闭支付记录
*/

View File

@@ -2,7 +2,7 @@ package cn.bootx.platform.daxpay.core.payment.pay.strategy;
import cn.bootx.platform.daxpay.code.PayChannelEnum;
import cn.bootx.platform.daxpay.core.channel.cash.service.CashService;
import cn.bootx.platform.daxpay.core.order.pay.service.PayOrderService;
import cn.bootx.platform.daxpay.core.record.pay.service.PayOrderService;
import cn.bootx.platform.daxpay.exception.pay.PayAmountAbnormalException;
import cn.bootx.platform.daxpay.func.AbsPayStrategy;
import cn.bootx.platform.daxpay.param.pay.PayWayParam;

View File

@@ -1,9 +1,7 @@
package cn.bootx.platform.daxpay.core.payment.pay.strategy;
import cn.bootx.platform.daxpay.code.PayChannelEnum;
import cn.bootx.platform.daxpay.code.WeChatPayCode;
import cn.bootx.platform.daxpay.common.exception.ExceptionInfo;
import cn.bootx.platform.daxpay.common.local.PaymentContextLocal;
import cn.bootx.platform.daxpay.core.channel.wechat.dao.WeChatPayConfigManager;
import cn.bootx.platform.daxpay.core.channel.wechat.entity.WeChatPayConfig;
import cn.bootx.platform.daxpay.core.channel.wechat.service.WeChatPayCloseService;
@@ -22,8 +20,6 @@ import lombok.RequiredArgsConstructor;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
import java.util.Map;
import static org.springframework.beans.factory.config.BeanDefinition.SCOPE_PROTOTYPE;
/**
@@ -113,24 +109,6 @@ public class WeChatPayStrategy extends AbsPayStrategy {
this.doCloseHandler();
}
/**
* 异步支付成功
*/
@Override
public void doAsyncSuccessHandler(Map<String, String> map) {
PaymentContextLocal.get().getAsyncPayInfo().setTradeNo(map.get(WeChatPayCode.TRANSACTION_ID));
weChatPayOrderService.updateAsyncSuccess(this.getOrder().getId(), this.getPayWayParam());
}
/**
* 异步支付失败
*/
@Override
public void doAsyncErrorHandler(ExceptionInfo exceptionInfo) {
// 调用关闭支付
this.doCloseHandler();
}
/**
* 关闭本地支付记录
*/

View File

@@ -8,10 +8,10 @@ import cn.bootx.platform.daxpay.common.context.AsyncRefundLocal;
import cn.bootx.platform.daxpay.common.context.NoticeLocal;
import cn.bootx.platform.daxpay.common.context.PlatformLocal;
import cn.bootx.platform.daxpay.common.local.PaymentContextLocal;
import cn.bootx.platform.daxpay.core.order.pay.dao.PayOrderManager;
import cn.bootx.platform.daxpay.core.order.pay.entity.PayOrder;
import cn.bootx.platform.daxpay.core.order.refund.dao.PayRefundOrderManager;
import cn.bootx.platform.daxpay.core.order.refund.entity.PayRefundOrder;
import cn.bootx.platform.daxpay.core.record.pay.entity.PayOrder;
import cn.bootx.platform.daxpay.core.record.pay.service.PayOrderService;
import cn.bootx.platform.daxpay.core.record.refund.dao.PayRefundOrderManager;
import cn.bootx.platform.daxpay.core.record.refund.entity.PayRefundOrder;
import cn.bootx.platform.daxpay.exception.pay.PayFailureException;
import cn.bootx.platform.daxpay.param.pay.RefundChannelParam;
import cn.bootx.platform.daxpay.param.pay.RefundParam;
@@ -36,7 +36,7 @@ import java.util.Objects;
@Service
@RequiredArgsConstructor
public class PayRefundAssistService {
private final PayOrderManager payOrderManager;
private final PayOrderService payOrderService;
private final PayRefundOrderManager payRefundOrderManager;
@@ -79,11 +79,11 @@ public class PayRefundAssistService {
PayOrder payOrder = null;
if (Objects.nonNull(param.getPaymentId())){
payOrder = payOrderManager.findById(param.getPaymentId())
payOrder = payOrderService.findById(param.getPaymentId())
.orElseThrow(() -> new PayFailureException("未查询到支付订单"));
}
if (Objects.isNull(payOrder)){
payOrder = payOrderManager.findByBusinessNo(param.getBusinessNo())
payOrder = payOrderService.findByBusinessNo(param.getBusinessNo())
.orElseThrow(() -> new PayFailureException("未查询到支付订单"));
}

View File

@@ -2,11 +2,11 @@ package cn.bootx.platform.daxpay.core.payment.refund.service;
import cn.bootx.platform.common.core.util.ValidationUtil;
import cn.bootx.platform.daxpay.code.PayStatusEnum;
import cn.bootx.platform.daxpay.core.order.pay.dao.PayOrderManager;
import cn.bootx.platform.daxpay.core.order.pay.entity.PayOrder;
import cn.bootx.platform.daxpay.core.payment.refund.factory.PayRefundStrategyFactory;
import cn.bootx.platform.daxpay.func.AbsPayRefundStrategy;
import cn.bootx.platform.daxpay.core.record.pay.entity.PayOrder;
import cn.bootx.platform.daxpay.core.record.pay.service.PayOrderService;
import cn.bootx.platform.daxpay.exception.pay.PayUnsupportedMethodException;
import cn.bootx.platform.daxpay.func.AbsPayRefundStrategy;
import cn.bootx.platform.daxpay.param.pay.RefundChannelParam;
import cn.bootx.platform.daxpay.param.pay.RefundParam;
import cn.bootx.platform.daxpay.param.pay.SimpleRefundParam;
@@ -34,7 +34,7 @@ public class PayRefundService {
private final PayRefundAssistService payRefundAssistService;;
private final PayOrderManager payOrderManager;
private final PayOrderService payOrderService;
/**
* 支付退款
*/
@@ -136,7 +136,7 @@ public class PayRefundService {
payOrder.setStatus(PayStatusEnum.PARTIAL_REFUND.getCode());
}
payOrder.setRefundableBalance(refundableBalance);
payOrderManager.updateById(payOrder);
payOrderService.updateById(payOrder);
payRefundAssistService.saveRefundOrder(refundParam,payOrder);
}
}

View File

@@ -5,7 +5,7 @@ import cn.bootx.platform.daxpay.core.channel.alipay.entity.AliPayConfig;
import cn.bootx.platform.daxpay.core.channel.alipay.service.AliPayOrderService;
import cn.bootx.platform.daxpay.core.channel.alipay.service.AliPayRefundService;
import cn.bootx.platform.daxpay.core.channel.alipay.service.AliPayConfigService;
import cn.bootx.platform.daxpay.core.order.pay.service.PayOrderService;
import cn.bootx.platform.daxpay.core.record.pay.service.PayOrderService;
import cn.bootx.platform.daxpay.func.AbsPayRefundStrategy;
import lombok.RequiredArgsConstructor;
import org.springframework.context.annotation.Scope;

View File

@@ -2,7 +2,7 @@ package cn.bootx.platform.daxpay.core.payment.refund.strategy;
import cn.bootx.platform.daxpay.code.PayChannelEnum;
import cn.bootx.platform.daxpay.core.channel.cash.service.CashService;
import cn.bootx.platform.daxpay.core.order.pay.service.PayOrderService;
import cn.bootx.platform.daxpay.core.record.pay.service.PayOrderService;
import cn.bootx.platform.daxpay.func.AbsPayRefundStrategy;
import lombok.RequiredArgsConstructor;
import org.springframework.context.annotation.Scope;

View File

@@ -3,7 +3,7 @@ package cn.bootx.platform.daxpay.core.payment.refund.strategy;
import cn.bootx.platform.daxpay.code.PayChannelEnum;
import cn.bootx.platform.daxpay.core.channel.voucher.service.VoucherPayService;
import cn.bootx.platform.daxpay.core.channel.voucher.service.VoucherPayOrderService;
import cn.bootx.platform.daxpay.core.order.pay.service.PayOrderService;
import cn.bootx.platform.daxpay.core.record.pay.service.PayOrderService;
import cn.bootx.platform.daxpay.func.AbsPayRefundStrategy;
import lombok.RequiredArgsConstructor;
import org.springframework.context.annotation.Scope;

View File

@@ -3,7 +3,7 @@ package cn.bootx.platform.daxpay.core.payment.refund.strategy;
import cn.bootx.platform.daxpay.code.PayChannelEnum;
import cn.bootx.platform.daxpay.core.channel.wallet.service.WalletPayService;
import cn.bootx.platform.daxpay.core.channel.wallet.service.WalletPayOrderService;
import cn.bootx.platform.daxpay.core.order.pay.service.PayOrderService;
import cn.bootx.platform.daxpay.core.record.pay.service.PayOrderService;
import cn.bootx.platform.daxpay.func.AbsPayRefundStrategy;
import lombok.RequiredArgsConstructor;
import org.springframework.context.annotation.Scope;

View File

@@ -5,7 +5,7 @@ import cn.bootx.platform.daxpay.core.channel.wechat.entity.WeChatPayConfig;
import cn.bootx.platform.daxpay.core.channel.wechat.service.WeChatPayConfigService;
import cn.bootx.platform.daxpay.core.channel.wechat.service.WeChatPayOrderService;
import cn.bootx.platform.daxpay.core.channel.wechat.service.WechatRefundService;
import cn.bootx.platform.daxpay.core.order.pay.service.PayOrderService;
import cn.bootx.platform.daxpay.core.record.pay.service.PayOrderService;
import cn.bootx.platform.daxpay.func.AbsPayRefundStrategy;
import lombok.RequiredArgsConstructor;
import org.springframework.context.annotation.Scope;

View File

@@ -2,10 +2,10 @@ package cn.bootx.platform.daxpay.core.payment.repair.service;
import cn.bootx.platform.daxpay.code.PayStatusEnum;
import cn.bootx.platform.daxpay.common.entity.OrderRefundableInfo;
import cn.bootx.platform.daxpay.core.order.pay.dao.PayOrderManager;
import cn.bootx.platform.daxpay.core.order.pay.entity.PayOrder;
import cn.bootx.platform.daxpay.core.payment.repair.factory.PayRepairStrategyFactory;
import cn.bootx.platform.daxpay.core.payment.repair.param.PayRepairParam;
import cn.bootx.platform.daxpay.core.record.pay.entity.PayOrder;
import cn.bootx.platform.daxpay.core.record.pay.service.PayOrderService;
import cn.bootx.platform.daxpay.func.AbsPayRepairStrategy;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
@@ -26,7 +26,7 @@ import java.util.stream.Collectors;
@RequiredArgsConstructor
public class PayRepairService {
private final PayOrderManager payOrderManager;
private final PayOrderService payOrderService;
/**
* 修复支付单
@@ -40,9 +40,7 @@ public class PayRepairService {
.collect(Collectors.toList());
// 初始化修复参数
List<AbsPayRepairStrategy> repairStrategies = PayRepairStrategyFactory.createAsyncLast(channels);
for (AbsPayRepairStrategy repairStrategy : repairStrategies) {
repairStrategy.initRepairParam(order, repairParam.getRepairSource());
}
repairStrategies.forEach(repairStrategy -> repairStrategy.initRepairParam(order, repairParam.getRepairSource()));
// 根据不同的类型执行对应的修复逻辑
switch (repairParam.getRepairType()) {
case SUCCESS:
@@ -72,7 +70,7 @@ public class PayRepairService {
// 修改订单支付状态为成功
payment.setStatus(PayStatusEnum.SUCCESS.getCode());
payment.setPayTime(LocalDateTime.now());
payOrderManager.updateById(payment);
payOrderService.updateById(payment);
}
/**
@@ -84,7 +82,7 @@ public class PayRepairService {
// 执行策略的关闭方法
absPayStrategies.forEach(AbsPayRepairStrategy::doCloseHandler);
payOrder.setStatus(PayStatusEnum.CLOSE.getCode());
payOrderManager.updateById(payOrder);
payOrderService.updateById(payOrder);
}
/**

View File

@@ -6,8 +6,8 @@ import cn.bootx.platform.daxpay.core.channel.alipay.entity.AliPayConfig;
import cn.bootx.platform.daxpay.core.channel.alipay.service.AliPayCloseService;
import cn.bootx.platform.daxpay.core.channel.alipay.service.AliPayOrderService;
import cn.bootx.platform.daxpay.core.channel.alipay.service.AliPayConfigService;
import cn.bootx.platform.daxpay.core.order.pay.dao.PayOrderChannelManager;
import cn.bootx.platform.daxpay.core.order.pay.entity.PayOrderChannel;
import cn.bootx.platform.daxpay.core.record.pay.dao.PayOrderChannelManager;
import cn.bootx.platform.daxpay.core.record.pay.entity.PayOrderChannel;
import cn.bootx.platform.daxpay.exception.pay.PayFailureException;
import cn.bootx.platform.daxpay.func.AbsPayRepairStrategy;
import lombok.RequiredArgsConstructor;
@@ -40,7 +40,6 @@ public class AliPayRepairStrategy extends AbsPayRepairStrategy {
public void doBeforeHandler() {
AliPayConfig config = aliPayConfigService.getConfig();
aliPayConfigService.initConfig(config);
}
/**
* 支付成功处理

View File

@@ -5,13 +5,13 @@ import cn.bootx.platform.daxpay.code.PayRepairSourceEnum;
import cn.bootx.platform.daxpay.code.PayRepairTypeEnum;
import cn.bootx.platform.daxpay.code.PayStatusEnum;
import cn.bootx.platform.daxpay.code.PaySyncStatusEnum;
import cn.bootx.platform.daxpay.core.order.pay.dao.PayOrderManager;
import cn.bootx.platform.daxpay.core.order.pay.entity.PayOrder;
import cn.bootx.platform.daxpay.core.order.sync.service.PaySyncOrderService;
import cn.bootx.platform.daxpay.core.payment.repair.param.PayRepairParam;
import cn.bootx.platform.daxpay.core.payment.repair.service.PayRepairService;
import cn.bootx.platform.daxpay.core.payment.sync.factory.PaySyncStrategyFactory;
import cn.bootx.platform.daxpay.core.payment.sync.result.GatewaySyncResult;
import cn.bootx.platform.daxpay.core.record.pay.entity.PayOrder;
import cn.bootx.platform.daxpay.core.record.pay.service.PayOrderService;
import cn.bootx.platform.daxpay.core.record.sync.service.PaySyncOrderService;
import cn.bootx.platform.daxpay.exception.pay.PayFailureException;
import cn.bootx.platform.daxpay.func.AbsPaySyncStrategy;
import cn.bootx.platform.daxpay.param.pay.PaySyncParam;
@@ -31,7 +31,7 @@ import java.util.Objects;
@Service
@RequiredArgsConstructor
public class PaySyncService {
private final PayOrderManager payOrderManager;
private final PayOrderService payOrderService;
private final PaySyncOrderService syncOrderService;
@@ -43,11 +43,11 @@ public class PaySyncService {
public PaySyncResult sync(PaySyncParam param) {
PayOrder payOrder = null;
if (Objects.nonNull(param.getPaymentId())){
payOrder = payOrderManager.findById(param.getPaymentId())
payOrder = payOrderService.findById(param.getPaymentId())
.orElseThrow(() -> new PayFailureException("未查询到支付订单"));
}
if (Objects.isNull(payOrder)){
payOrder = payOrderManager.findByBusinessNo(param.getBusinessNo())
payOrder = payOrderService.findByBusinessNo(param.getBusinessNo())
.orElseThrow(() -> new PayFailureException("未查询到支付订单"));
}
// 如果不是异步支付, 直接返回返回

View File

@@ -0,0 +1,40 @@
package cn.bootx.platform.daxpay.core.payment.sync.task;
import cn.bootx.platform.daxpay.core.payment.sync.service.PaySyncService;
import cn.bootx.platform.daxpay.core.timeout.dao.PayExpiredTimeRepository;
import cn.bootx.platform.daxpay.param.pay.PaySyncParam;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
/**
* 对未过期的支付中订单进行状态同步
* @author xxm
* @since 2024/1/1
*/
@Slf4j
@Service
@RequiredArgsConstructor
public class PayOrderSyncTaskService {
private final PayExpiredTimeRepository PayExpiredTimeRepository;
private final PaySyncService paySyncService;
/**
* 同步支付订单任务
*/
public void syncTask() {
log.info("开始同步支付订单");
// 1. 从超时订单列表中获取到未超时的订单号
for (String s : PayExpiredTimeRepository.getNormalKeysBy7Day()) {
try {
Long paymentId = Long.parseLong(s);
PaySyncParam paySyncParam = new PaySyncParam();
paySyncParam.setPaymentId(paymentId);
paySyncService.sync(paySyncParam);
} catch (Exception e) {
log.error("同步支付订单异常", e);
}
}
}
}

View File

@@ -0,0 +1,14 @@
package cn.bootx.platform.daxpay.core.record.callback.convert;
import org.mapstruct.Mapper;
import org.mapstruct.factory.Mappers;
/**
* 回调通知转换
* @author xxm
* @since 2023/12/18
*/
@Mapper
public interface CallbackRecordMConvert {
CallbackRecordMConvert CONVERT = Mappers.getMapper(CallbackRecordMConvert.class);
}

View File

@@ -1,7 +1,7 @@
package cn.bootx.platform.daxpay.core.callback.dao;
package cn.bootx.platform.daxpay.core.record.callback.dao;
import cn.bootx.platform.common.mybatisplus.impl.BaseManager;
import cn.bootx.platform.daxpay.core.callback.entity.CallbackNotify;
import cn.bootx.platform.daxpay.core.record.callback.entity.CallbackRecord;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Repository;
@@ -12,5 +12,5 @@ import org.springframework.stereotype.Repository;
*/
@Slf4j
@Repository
public class CallbackNotifyManager extends BaseManager<CallbackNotifyMapper, CallbackNotify> {
public class CallbackRecordManager extends BaseManager<CallbackRecordMapper, CallbackRecord> {
}

View File

@@ -0,0 +1,14 @@
package cn.bootx.platform.daxpay.core.record.callback.dao;
import cn.bootx.platform.daxpay.core.record.callback.entity.CallbackRecord;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Mapper;
/**
*
* @author xxm
* @since 2023/12/18
*/
@Mapper
public interface CallbackRecordMapper extends BaseMapper<CallbackRecord> {
}

View File

@@ -1,4 +1,4 @@
package cn.bootx.platform.daxpay.core.callback.entity;
package cn.bootx.platform.daxpay.core.record.callback.entity;
import cn.bootx.platform.common.mybatisplus.base.MpCreateEntity;
import cn.bootx.platform.daxpay.code.PayChannelEnum;
@@ -20,7 +20,7 @@ import java.time.LocalDateTime;
@EqualsAndHashCode(callSuper = true)
@Data
@Accessors(chain = true)
public class CallbackNotify extends MpCreateEntity {
public class CallbackRecord extends MpCreateEntity {
/** 支付记录id */
@DbComment("支付记录id")
private Long paymentId;

View File

@@ -1,4 +1,4 @@
package cn.bootx.platform.daxpay.core.callback.service;
package cn.bootx.platform.daxpay.core.record.callback.service;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
@@ -12,5 +12,5 @@ import org.springframework.stereotype.Service;
@Slf4j
@Service
@RequiredArgsConstructor
public class CallbackNotifyService {
public class CallbackRecordService {
}

View File

@@ -1,4 +1,4 @@
package cn.bootx.platform.daxpay.core.order.pay.builder;
package cn.bootx.platform.daxpay.core.record.pay.builder;
import cn.bootx.platform.daxpay.code.PayChannelEnum;
import cn.bootx.platform.daxpay.code.PayStatusEnum;
@@ -6,9 +6,9 @@ import cn.bootx.platform.daxpay.common.context.AsyncPayLocal;
import cn.bootx.platform.daxpay.common.context.NoticeLocal;
import cn.bootx.platform.daxpay.common.context.PlatformLocal;
import cn.bootx.platform.daxpay.common.local.PaymentContextLocal;
import cn.bootx.platform.daxpay.core.order.pay.entity.PayOrder;
import cn.bootx.platform.daxpay.core.order.pay.entity.PayOrderChannel;
import cn.bootx.platform.daxpay.core.order.pay.entity.PayOrderExtra;
import cn.bootx.platform.daxpay.core.record.pay.entity.PayOrder;
import cn.bootx.platform.daxpay.core.record.pay.entity.PayOrderChannel;
import cn.bootx.platform.daxpay.core.record.pay.entity.PayOrderExtra;
import cn.bootx.platform.daxpay.common.entity.OrderRefundableInfo;
import cn.bootx.platform.daxpay.param.pay.PayParam;
import cn.bootx.platform.daxpay.param.pay.PayWayParam;
@@ -17,6 +17,7 @@ import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.StrUtil;
import lombok.experimental.UtilityClass;
import java.time.LocalDateTime;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
@@ -36,6 +37,10 @@ public class PaymentBuilder {
* 构建payment记录
*/
public PayOrder buildPayOrder(PayParam payParam) {
// 订单超时时间
LocalDateTime expiredTime = PaymentContextLocal.get()
.getAsyncPayInfo()
.getExpiredTime();
// 可退款信息
List<OrderRefundableInfo> refundableInfos = buildRefundableInfo(payParam.getPayWays());
// 计算总价
@@ -56,6 +61,7 @@ public class PaymentBuilder {
.setRefundableInfos(refundableInfos)
.setStatus(PayStatusEnum.PROGRESS.getCode())
.setAmount(sumAmount)
.setExpiredTime(expiredTime)
.setCombinationPay(payParam.getPayWays().size() > 1)
.setAsyncPay(asyncPayMode.isPresent())
.setAsyncPayChannel(asyncPayMode.orElse(null))

View File

@@ -1,7 +1,7 @@
package cn.bootx.platform.daxpay.core.order.pay.dao;
package cn.bootx.platform.daxpay.core.record.pay.dao;
import cn.bootx.platform.common.mybatisplus.impl.BaseManager;
import cn.bootx.platform.daxpay.core.order.pay.entity.PayOrderChannel;
import cn.bootx.platform.daxpay.core.record.pay.entity.PayOrderChannel;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Repository;

View File

@@ -1,6 +1,6 @@
package cn.bootx.platform.daxpay.core.order.pay.dao;
package cn.bootx.platform.daxpay.core.record.pay.dao;
import cn.bootx.platform.daxpay.core.order.pay.entity.PayOrderChannel;
import cn.bootx.platform.daxpay.core.record.pay.entity.PayOrderChannel;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Mapper;

View File

@@ -1,7 +1,7 @@
package cn.bootx.platform.daxpay.core.order.pay.dao;
package cn.bootx.platform.daxpay.core.record.pay.dao;
import cn.bootx.platform.common.mybatisplus.impl.BaseManager;
import cn.bootx.platform.daxpay.core.order.pay.entity.PayOrderExtra;
import cn.bootx.platform.daxpay.core.record.pay.entity.PayOrderExtra;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Repository;

View File

@@ -1,6 +1,6 @@
package cn.bootx.platform.daxpay.core.order.pay.dao;
package cn.bootx.platform.daxpay.core.record.pay.dao;
import cn.bootx.platform.daxpay.core.order.pay.entity.PayOrderExtra;
import cn.bootx.platform.daxpay.core.record.pay.entity.PayOrderExtra;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Mapper;

View File

@@ -1,7 +1,7 @@
package cn.bootx.platform.daxpay.core.order.pay.dao;
package cn.bootx.platform.daxpay.core.record.pay.dao;
import cn.bootx.platform.common.mybatisplus.impl.BaseManager;
import cn.bootx.platform.daxpay.core.order.pay.entity.PayOrder;
import cn.bootx.platform.daxpay.core.record.pay.entity.PayOrder;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Repository;

View File

@@ -1,6 +1,6 @@
package cn.bootx.platform.daxpay.core.order.pay.dao;
package cn.bootx.platform.daxpay.core.record.pay.dao;
import cn.bootx.platform.daxpay.core.order.pay.entity.PayOrder;
import cn.bootx.platform.daxpay.core.record.pay.entity.PayOrder;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Mapper;

View File

@@ -1,4 +1,4 @@
package cn.bootx.platform.daxpay.core.order.pay.entity;
package cn.bootx.platform.daxpay.core.record.pay.entity;
import cn.bootx.platform.common.core.annotation.BigField;
import cn.bootx.platform.common.mybatisplus.base.MpBaseEntity;

View File

@@ -1,4 +1,4 @@
package cn.bootx.platform.daxpay.core.order.pay.entity;
package cn.bootx.platform.daxpay.core.record.pay.entity;
import cn.bootx.platform.common.mybatisplus.base.MpCreateEntity;
import cn.bootx.platform.daxpay.param.channel.AliPayParam;

View File

@@ -1,4 +1,4 @@
package cn.bootx.platform.daxpay.core.order.pay.entity;
package cn.bootx.platform.daxpay.core.record.pay.entity;
import cn.bootx.platform.common.mybatisplus.base.MpBaseEntity;

View File

@@ -1,9 +1,9 @@
package cn.bootx.platform.daxpay.core.order.pay.service;
package cn.bootx.platform.daxpay.core.record.pay.service;
import cn.bootx.platform.daxpay.code.PayChannelEnum;
import cn.bootx.platform.daxpay.core.order.pay.dao.PayOrderChannelManager;
import cn.bootx.platform.daxpay.core.order.pay.entity.PayOrder;
import cn.bootx.platform.daxpay.core.order.pay.entity.PayOrderChannel;
import cn.bootx.platform.daxpay.core.record.pay.dao.PayOrderChannelManager;
import cn.bootx.platform.daxpay.core.record.pay.entity.PayOrder;
import cn.bootx.platform.daxpay.core.record.pay.entity.PayOrderChannel;
import cn.bootx.platform.daxpay.param.pay.PayWayParam;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;

View File

@@ -0,0 +1,86 @@
package cn.bootx.platform.daxpay.core.record.pay.service;
import cn.bootx.platform.daxpay.code.PayChannelEnum;
import cn.bootx.platform.daxpay.code.PayStatusEnum;
import cn.bootx.platform.daxpay.common.entity.OrderRefundableInfo;
import cn.bootx.platform.daxpay.core.record.pay.dao.PayOrderManager;
import cn.bootx.platform.daxpay.core.record.pay.entity.PayOrder;
import cn.bootx.platform.daxpay.core.timeout.service.PayExpiredTimeService;
import cn.bootx.platform.daxpay.exception.pay.PayFailureException;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
/**
* 支付订单服务
* @author xxm
* @since 2023/12/18
*/
@Slf4j
@Service
@RequiredArgsConstructor
public class PayOrderService {
private final PayOrderManager payOrderManager;
private final PayExpiredTimeService expiredTimeService;
private final List<String> ORDER_FINISH = Arrays.asList(PayStatusEnum.CLOSE.getCode(),PayStatusEnum.TIMEOUT.getCode(), PayStatusEnum.SUCCESS.getCode());
/**
* 根据id查询
*/
public Optional<PayOrder> findById(Long paymentId) {
return payOrderManager.findById(paymentId);
}
/**
* 根据业务号查询
*/
public Optional<PayOrder> findByBusinessNo(String businessNo) {
return payOrderManager.findByBusinessNo(businessNo);
}
/**
* 新增
*/
public void save(PayOrder payOrder){
// 异步支付需要添加订单超时任务记录
if (payOrder.isAsyncPay()){
expiredTimeService.registerExpiredTime(payOrder);
}
payOrderManager.save(payOrder);
}
/**
* 更新
*/
public void updateById(PayOrder payOrder){
// 如果是异步支付且支付订单完成, 需要删除订单超时任务记录
if (payOrder.isAsyncPay() && ORDER_FINISH.contains(payOrder.getStatus())){
expiredTimeService.cancelExpiredTime(payOrder.getId());
}
payOrderManager.updateById(payOrder);
}
/**
* 退款成功处理, 更新可退款信息 不要进行持久化
*/
public void updateRefundSuccess(PayOrder payment, int amount, PayChannelEnum payChannelEnum) {
// 删除旧有的退款记录, 替换退款完的新的
List<OrderRefundableInfo> refundableInfos = payment.getRefundableInfos();
OrderRefundableInfo refundableInfo = refundableInfos.stream()
.filter(o -> Objects.equals(o.getChannel(), payChannelEnum.getCode()))
.findFirst()
.orElseThrow(() -> new PayFailureException("退款数据不存在"));
refundableInfos.remove(refundableInfo);
refundableInfo.setAmount(refundableInfo.getAmount() - amount);
refundableInfos.add(refundableInfo);
payment.setRefundableInfos(refundableInfos);
}
}

View File

@@ -1,6 +1,6 @@
package cn.bootx.platform.daxpay.core.order.refund.convert;
package cn.bootx.platform.daxpay.core.record.refund.convert;
import cn.bootx.platform.daxpay.core.order.refund.entity.PayRefundOrder;
import cn.bootx.platform.daxpay.core.record.refund.entity.PayRefundOrder;
import cn.bootx.platform.daxpay.dto.order.refund.PayRefundOrderDto;
import org.mapstruct.Mapper;
import org.mapstruct.factory.Mappers;

View File

@@ -1,10 +1,10 @@
package cn.bootx.platform.daxpay.core.order.refund.dao;
package cn.bootx.platform.daxpay.core.record.refund.dao;
import cn.bootx.platform.common.core.rest.param.PageParam;
import cn.bootx.platform.common.mybatisplus.base.MpIdEntity;
import cn.bootx.platform.common.mybatisplus.impl.BaseManager;
import cn.bootx.platform.common.mybatisplus.util.MpUtil;
import cn.bootx.platform.daxpay.core.order.refund.entity.PayRefundOrder;
import cn.bootx.platform.daxpay.core.record.refund.entity.PayRefundOrder;
import cn.bootx.platform.daxpay.dto.order.refund.PayRefundOrderDto;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import lombok.RequiredArgsConstructor;

View File

@@ -1,6 +1,6 @@
package cn.bootx.platform.daxpay.core.order.refund.dao;
package cn.bootx.platform.daxpay.core.record.refund.dao;
import cn.bootx.platform.daxpay.core.order.refund.entity.PayRefundOrder;
import cn.bootx.platform.daxpay.core.record.refund.entity.PayRefundOrder;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Mapper;

View File

@@ -1,11 +1,11 @@
package cn.bootx.platform.daxpay.core.order.refund.entity;
package cn.bootx.platform.daxpay.core.record.refund.entity;
import cn.bootx.platform.common.core.function.EntityBaseFunction;
import cn.bootx.platform.common.mybatisplus.base.MpBaseEntity;
import cn.bootx.platform.common.mybatisplus.handler.JacksonRawTypeHandler;
import cn.bootx.platform.daxpay.code.PayRefundStatusEnum;
import cn.bootx.platform.daxpay.common.entity.OrderRefundableInfo;
import cn.bootx.platform.daxpay.core.order.refund.convert.RefundConvert;
import cn.bootx.platform.daxpay.core.record.refund.convert.RefundConvert;
import cn.bootx.platform.daxpay.dto.order.refund.PayRefundOrderDto;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableName;

View File

@@ -1,11 +1,11 @@
package cn.bootx.platform.daxpay.core.order.refund.service;
package cn.bootx.platform.daxpay.core.record.refund.service;
import cn.bootx.platform.common.core.exception.DataNotExistException;
import cn.bootx.platform.common.core.rest.PageResult;
import cn.bootx.platform.common.core.rest.param.PageParam;
import cn.bootx.platform.common.mybatisplus.util.MpUtil;
import cn.bootx.platform.daxpay.core.order.refund.dao.PayRefundOrderManager;
import cn.bootx.platform.daxpay.core.order.refund.entity.PayRefundOrder;
import cn.bootx.platform.daxpay.core.record.refund.dao.PayRefundOrderManager;
import cn.bootx.platform.daxpay.core.record.refund.entity.PayRefundOrder;
import cn.bootx.platform.daxpay.dto.order.refund.PayRefundOrderDto;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import lombok.RequiredArgsConstructor;

View File

@@ -0,0 +1,19 @@
package cn.bootx.platform.daxpay.core.record.sync.convert;
import cn.bootx.platform.daxpay.core.record.sync.entity.PaySyncRecord;
import cn.bootx.platform.daxpay.dto.order.sync.PaySyncRecordDto;
import org.mapstruct.Mapper;
import org.mapstruct.factory.Mappers;
/**
* 支付同步记录同步
* @author xxm
* @since 2023/7/14
*/
@Mapper
public interface PaySyncRecordConvert {
PaySyncRecordConvert CONVERT = Mappers.getMapper(PaySyncRecordConvert.class);
PaySyncRecordDto convert(PaySyncRecord in);
}

View File

@@ -1,11 +1,11 @@
package cn.bootx.platform.daxpay.core.order.sync.dao;
package cn.bootx.platform.daxpay.core.record.sync.dao;
import cn.bootx.platform.common.core.rest.param.PageParam;
import cn.bootx.platform.common.mybatisplus.base.MpIdEntity;
import cn.bootx.platform.common.mybatisplus.impl.BaseManager;
import cn.bootx.platform.common.mybatisplus.util.MpUtil;
import cn.bootx.platform.daxpay.core.order.sync.entity.PaySyncOrder;
import cn.bootx.platform.daxpay.dto.order.sync.PaySyncOrderDto;
import cn.bootx.platform.daxpay.core.record.sync.entity.PaySyncRecord;
import cn.bootx.platform.daxpay.dto.order.sync.PaySyncRecordDto;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
@@ -21,14 +21,14 @@ import java.util.Objects;
@Slf4j
@Repository
@RequiredArgsConstructor
public class PaySyncOrderManager extends BaseManager<PaySyncOrderMapper, PaySyncOrder> {
public class PaySyncRecordManager extends BaseManager<PaySyncRecordMapper, PaySyncRecord> {
public Page<PaySyncOrder> page(PageParam pageParam, PaySyncOrderDto param) {
Page<PaySyncOrder> mpPage = MpUtil.getMpPage(pageParam, PaySyncOrder.class);
public Page<PaySyncRecord> page(PageParam pageParam, PaySyncRecordDto param) {
Page<PaySyncRecord> mpPage = MpUtil.getMpPage(pageParam, PaySyncRecord.class);
return lambdaQuery().orderByDesc(MpIdEntity::getId)
.like(Objects.nonNull(param.getPaymentId()), PaySyncOrder::getPaymentId, param.getPaymentId())
.eq(Objects.nonNull(param.getChannel()), PaySyncOrder::getChannel, param.getChannel())
.eq(Objects.nonNull(param.getStatus()), PaySyncOrder::getStatus, param.getStatus())
.like(Objects.nonNull(param.getPaymentId()), PaySyncRecord::getPaymentId, param.getPaymentId())
.eq(Objects.nonNull(param.getChannel()), PaySyncRecord::getChannel, param.getChannel())
.eq(Objects.nonNull(param.getStatus()), PaySyncRecord::getStatus, param.getStatus())
.page(mpPage);
}

View File

@@ -0,0 +1,14 @@
package cn.bootx.platform.daxpay.core.record.sync.dao;
import cn.bootx.platform.daxpay.core.record.sync.entity.PaySyncRecord;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Mapper;
/**
* 支付同步记录
* @author xxm
* @since 2023/7/14
*/
@Mapper
public interface PaySyncRecordMapper extends BaseMapper<PaySyncRecord> {
}

View File

@@ -1,11 +1,11 @@
package cn.bootx.platform.daxpay.core.order.sync.entity;
package cn.bootx.platform.daxpay.core.record.sync.entity;
import cn.bootx.platform.common.core.function.EntityBaseFunction;
import cn.bootx.platform.common.mybatisplus.base.MpCreateEntity;
import cn.bootx.platform.daxpay.code.PayChannelEnum;
import cn.bootx.platform.daxpay.code.PaySyncStatusEnum;
import cn.bootx.platform.daxpay.core.order.sync.convert.PaySyncOrderConvert;
import cn.bootx.platform.daxpay.dto.order.sync.PaySyncOrderDto;
import cn.bootx.platform.daxpay.core.record.sync.convert.PaySyncRecordConvert;
import cn.bootx.platform.daxpay.dto.order.sync.PaySyncRecordDto;
import cn.bootx.table.modify.annotation.DbComment;
import cn.bootx.table.modify.annotation.DbTable;
import cn.bootx.table.modify.mysql.annotation.DbMySqlFieldType;
@@ -26,8 +26,8 @@ import java.time.LocalDateTime;
@Data
@DbTable(comment = "支付同步订单")
@Accessors(chain = true)
@TableName("pay_sync_order")
public class PaySyncOrder extends MpCreateEntity implements EntityBaseFunction<PaySyncOrderDto> {
@TableName("pay_sync_record")
public class PaySyncRecord extends MpCreateEntity implements EntityBaseFunction<PaySyncRecordDto> {
/** 支付记录id */
@DbComment("支付记录id")
@@ -69,7 +69,7 @@ public class PaySyncOrder extends MpCreateEntity implements EntityBaseFunction<P
* 转换
*/
@Override
public PaySyncOrderDto toDto() {
return PaySyncOrderConvert.CONVERT.convert(this);
public PaySyncRecordDto toDto() {
return PaySyncRecordConvert.CONVERT.convert(this);
}
}

View File

@@ -1,14 +1,14 @@
package cn.bootx.platform.daxpay.core.order.sync.service;
package cn.bootx.platform.daxpay.core.record.sync.service;
import cn.bootx.platform.common.core.exception.DataNotExistException;
import cn.bootx.platform.common.core.rest.PageResult;
import cn.bootx.platform.common.core.rest.param.PageParam;
import cn.bootx.platform.common.mybatisplus.util.MpUtil;
import cn.bootx.platform.daxpay.core.order.pay.entity.PayOrder;
import cn.bootx.platform.daxpay.core.order.sync.dao.PaySyncOrderManager;
import cn.bootx.platform.daxpay.core.order.sync.entity.PaySyncOrder;
import cn.bootx.platform.daxpay.core.record.pay.entity.PayOrder;
import cn.bootx.platform.daxpay.core.record.sync.dao.PaySyncRecordManager;
import cn.bootx.platform.daxpay.core.record.sync.entity.PaySyncRecord;
import cn.bootx.platform.daxpay.core.payment.sync.result.GatewaySyncResult;
import cn.bootx.platform.daxpay.dto.order.sync.PaySyncOrderDto;
import cn.bootx.platform.daxpay.dto.order.sync.PaySyncRecordDto;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
@@ -27,35 +27,35 @@ import java.time.LocalDateTime;
@Service
@RequiredArgsConstructor
public class PaySyncOrderService {
private final PaySyncOrderManager orderManager;
private final PaySyncRecordManager orderManager;
/**
* 记录同步记录
*/
@Transactional(propagation= Propagation.REQUIRES_NEW)
public void saveRecord(GatewaySyncResult paySyncResult, PayOrder payment){
PaySyncOrder paySyncOrder = new PaySyncOrder()
PaySyncRecord paySyncRecord = new PaySyncRecord()
.setPaymentId(payment.getId())
.setChannel(payment.getAsyncPayChannel())
.setSyncInfo(paySyncResult.getJson())
.setStatus(paySyncResult.getSyncStatus())
.setMsg(paySyncResult.getMsg())
.setSyncTime(LocalDateTime.now());
orderManager.save(paySyncOrder);
orderManager.save(paySyncRecord);
}
/**
* 分页查询
*/
public PageResult<PaySyncOrderDto> page(PageParam pageParam, PaySyncOrderDto param) {
Page<PaySyncOrder> page = orderManager.page(pageParam, param);
public PageResult<PaySyncRecordDto> page(PageParam pageParam, PaySyncRecordDto param) {
Page<PaySyncRecord> page = orderManager.page(pageParam, param);
return MpUtil.convert2DtoPageResult(page);
}
/**
* 根据id查询
*/
public PaySyncOrderDto findById(Long id) {
return orderManager.findById(id).map(PaySyncOrder::toDto).orElseThrow(DataNotExistException::new);
public PaySyncRecordDto findById(Long id) {
return orderManager.findById(id).map(PaySyncRecord::toDto).orElseThrow(DataNotExistException::new);
}
}

View File

@@ -0,0 +1,35 @@
package cn.bootx.platform.daxpay.core.system.entity;
import cn.bootx.platform.common.mybatisplus.base.MpBaseEntity;
import cn.bootx.table.modify.annotation.DbTable;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
/**
* 订单超时配置
* @author xxm
* @since 2024/1/1
*/
@EqualsAndHashCode(callSuper = true)
@Data
@Accessors(chain = true)
@DbTable(comment = "订单超时配置")
@TableName("pay_order_timeout_config")
public class OrderTimeoutConfig extends MpBaseEntity {
/**
* 超时时间
*/
private Integer timeout;
/**
* 是否开启定时任务
*/
private boolean cron;
/**
*
*/
}

View File

@@ -1,4 +1,4 @@
package cn.bootx.platform.daxpay.core.order.pay.dao;
package cn.bootx.platform.daxpay.core.timeout.dao;
import cn.bootx.platform.common.core.util.LocalDateTimeUtil;
import cn.bootx.platform.common.redis.RedisClient;
@@ -33,15 +33,25 @@ public class PayExpiredTimeRepository {
}
/**
* 获取所有已过期的ExpiredTokenKey
* 获取所有已过期的订单ID
*/
public Set<String> retrieveExpiredKeys(LocalDateTime expiredTime) {
public Set<String> getExpiredKeys(LocalDateTime expiredTime) {
long time = LocalDateTimeUtil.timestamp(expiredTime);
return redisClient.zrangeByScore(KEY, 0L, time);
}
/**
* 删除指定的ExpiredTokenKey
* 获取所有未过期的订单ID. (7天内的订单)
*/
public Set<String> getNormalKeysBy7Day(){
LocalDateTime now = LocalDateTime.now();
long start = LocalDateTimeUtil.timestamp(now);
long end = LocalDateTimeUtil.timestamp(now.plusDays(7));
return redisClient.zrangeByScore(KEY, start, end);
}
/**
* 支付完成或者取消支付后, 需要调用这个方法来清除列表里的订单ID
*/
public void removeKeys(String... keys) {
if (keys != null && keys.length > 0) {

View File

@@ -0,0 +1,57 @@
package cn.bootx.platform.daxpay.core.timeout.service;
import cn.bootx.platform.common.core.util.LocalDateTimeUtil;
import cn.bootx.platform.common.spring.exception.RetryableException;
import cn.bootx.platform.daxpay.code.PayStatusEnum;
import cn.bootx.platform.daxpay.core.record.pay.entity.PayOrder;
import cn.bootx.platform.daxpay.core.timeout.dao.PayExpiredTimeRepository;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.retry.annotation.Retryable;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import java.time.LocalDateTime;
import java.time.temporal.ChronoUnit;
import java.util.Objects;
/**
* 支付超时处理
* @author xxm
* @since 2024/1/1
*/
@Slf4j
@Service
@RequiredArgsConstructor
public class PayExpiredTimeService {
private final PayExpiredTimeRepository repository;
/**
* 支付单超时关闭事件注册, 失败重试3次, 间隔一秒
*/
@Async("bigExecutor")
@Retryable(value = RetryableException.class)
public void registerExpiredTime(PayOrder payOrder) {
LocalDateTime expiredTime = payOrder.getExpiredTime();
// 支付中且有超时时间才会注册超时关闭时间
if (Objects.equals(payOrder.getStatus(), PayStatusEnum.PROGRESS.getCode()) && Objects.nonNull(expiredTime)) {
try {
// 将过期时间添加到redis中, 往后延时一分钟
expiredTime = LocalDateTimeUtil.offset(expiredTime, 1, ChronoUnit.MINUTES);
repository.store(payOrder.getId(), expiredTime);
}
catch (Exception e) {
log.error("注册支付单超时关闭失败");
throw new RetryableException();
}
}
}
/**
* 取消支付单超时关闭事件
*/
public void cancelExpiredTime(Long paymentId) {
repository.removeKeys(String.valueOf(paymentId));
}
}

View File

@@ -19,7 +19,7 @@ import java.time.LocalDateTime;
@Data
@Accessors(chain = true)
@Schema(title = "支付同步订单")
public class PaySyncOrderDto extends BaseDto {
public class PaySyncRecordDto extends BaseDto {
/** 支付记录id */
@Schema(description = "支付记录id")

View File

@@ -3,12 +3,11 @@ package cn.bootx.platform.daxpay.func;
import cn.bootx.platform.common.redis.RedisClient;
import cn.bootx.platform.daxpay.code.PayChannelEnum;
import cn.bootx.platform.daxpay.common.local.PaymentContextLocal;
import cn.bootx.platform.daxpay.core.callback.dao.CallbackNotifyManager;
import cn.bootx.platform.daxpay.core.callback.entity.CallbackNotify;
import cn.bootx.platform.daxpay.core.record.callback.dao.CallbackRecordManager;
import cn.bootx.platform.daxpay.core.record.callback.entity.CallbackRecord;
import cn.bootx.platform.daxpay.core.payment.callback.result.PayCallbackResult;
import cn.bootx.platform.daxpay.core.payment.callback.service.PayCallbackService;
import cn.hutool.json.JSONUtil;
import com.alibaba.ttl.TransmittableThreadLocal;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
@@ -25,11 +24,9 @@ import java.util.Map;
@RequiredArgsConstructor
public abstract class AbsPayCallbackStrategy implements PayStrategy {
protected static final ThreadLocal<Map<String, String>> PARAMS = new TransmittableThreadLocal<>();
private final RedisClient redisClient;
private final CallbackNotifyManager callbackNotifyManager;
private final CallbackRecordManager callbackRecordManager;
private final PayCallbackService payCallbackService;
@@ -47,15 +44,18 @@ public abstract class AbsPayCallbackStrategy implements PayStrategy {
if (!this.duplicateChecker()) {
return this.getReturnMsg();
}
// 分通道特殊处理, 如将解析的数据放到上下文中
this.initContext();
// 调用统一回调处理
PayCallbackResult result = payCallbackService.callback(this.getPaymentId(), this.getTradeStatus(), params);
PayCallbackResult result = payCallbackService.callback(this.getPaymentId(), this.getTradeStatus());
// 记录回调记录
this.saveNotifyRecord(result);
return this.getReturnMsg();
}
/**
* 支付类型
* 支付通道
* @see PayChannelEnum
*/
public abstract PayChannelEnum getPayChannel();
@@ -75,7 +75,12 @@ public abstract class AbsPayCallbackStrategy implements PayStrategy {
public abstract boolean verifyNotify();
/**
* 获取paymentId
* 分通道特殊处理, 如将解析的数据放到上下文中
*/
public abstract void initContext();
/**
* 获取支付单Id
*/
public abstract Long getPaymentId();
@@ -95,14 +100,14 @@ public abstract class AbsPayCallbackStrategy implements PayStrategy {
*/
public void saveNotifyRecord(PayCallbackResult result) {
Map<String, String> callbackParam = PaymentContextLocal.get().getCallbackParam();
CallbackNotify payNotifyRecord = new CallbackNotify()
CallbackRecord payNotifyRecord = new CallbackRecord()
.setNotifyInfo(JSONUtil.toJsonStr(callbackParam))
.setNotifyTime(LocalDateTime.now())
.setPaymentId(this.getPaymentId())
.setPayChannel(this.getPayChannel().getCode())
.setPayStatus(result.getStatus())
.setMsg(result.getMsg());
callbackNotifyManager.save(payNotifyRecord);
callbackRecordManager.save(payNotifyRecord);
}
}

View File

@@ -1,7 +1,7 @@
package cn.bootx.platform.daxpay.func;
import cn.bootx.platform.daxpay.common.exception.ExceptionInfo;
import cn.bootx.platform.daxpay.core.order.pay.entity.PayOrder;
import cn.bootx.platform.daxpay.core.record.pay.entity.PayOrder;
import lombok.Getter;
import lombok.Setter;

View File

@@ -2,7 +2,7 @@ package cn.bootx.platform.daxpay.func;
import cn.bootx.platform.daxpay.code.PayChannelEnum;
import cn.bootx.platform.daxpay.common.exception.ExceptionInfo;
import cn.bootx.platform.daxpay.core.order.pay.entity.PayOrder;
import cn.bootx.platform.daxpay.core.record.pay.entity.PayOrder;
import cn.bootx.platform.daxpay.param.pay.RefundChannelParam;
import cn.bootx.platform.daxpay.param.pay.RefundParam;
import lombok.Getter;

View File

@@ -1,7 +1,7 @@
package cn.bootx.platform.daxpay.func;
import cn.bootx.platform.daxpay.code.PayRepairSourceEnum;
import cn.bootx.platform.daxpay.core.order.pay.entity.PayOrder;
import cn.bootx.platform.daxpay.core.record.pay.entity.PayOrder;
import lombok.Getter;
import lombok.Setter;

View File

@@ -2,14 +2,12 @@ package cn.bootx.platform.daxpay.func;
import cn.bootx.platform.daxpay.code.PayChannelEnum;
import cn.bootx.platform.daxpay.common.exception.ExceptionInfo;
import cn.bootx.platform.daxpay.core.order.pay.entity.PayOrder;
import cn.bootx.platform.daxpay.core.record.pay.entity.PayOrder;
import cn.bootx.platform.daxpay.param.pay.PayParam;
import cn.bootx.platform.daxpay.param.pay.PayWayParam;
import lombok.Getter;
import lombok.Setter;
import java.util.Map;
/**
* 抽象支付策略基类 同步支付 异步支付 错误处理 关闭支付 撤销支付 支付网关同步 退款
*
@@ -63,26 +61,11 @@ public abstract class AbsPayStrategy implements PayStrategy{
}
/**
* 支付失败的处理方式
* 支付失败的处理方式 TODO 后期考虑如何进行错误处理
*/
public void doErrorHandler(ExceptionInfo exceptionInfo) {
}
/**
* 异步支付成功的处理方式
*/
@Deprecated
public void doAsyncSuccessHandler(Map<String, String> map) {
}
/**
* 异步支付失败的处理方式, 默认使用支付失败的处理方式 同步支付方式调用时同 this#doErrorHandler
*/
@Deprecated
public void doAsyncErrorHandler(ExceptionInfo exceptionInfo) {
this.doErrorHandler(exceptionInfo);
}
/**
* 关闭支付. 支付交易返回失败或支付系统超时调通该接口关闭支付
*/

View File

@@ -1,7 +1,7 @@
package cn.bootx.platform.daxpay.func;
import cn.bootx.platform.daxpay.code.PaySyncStatusEnum;
import cn.bootx.platform.daxpay.core.order.pay.entity.PayOrder;
import cn.bootx.platform.daxpay.core.record.pay.entity.PayOrder;
import cn.bootx.platform.daxpay.core.payment.sync.result.GatewaySyncResult;
import lombok.Getter;
import lombok.Setter;

View File

@@ -1,6 +1,6 @@
package cn.bootx.platform.daxpay.func;
import cn.bootx.platform.daxpay.core.order.pay.entity.PayOrder;
import cn.bootx.platform.daxpay.core.record.pay.entity.PayOrder;
import java.util.List;