mirror of
https://gitee.com/dromara/dax-pay.git
synced 2025-09-04 03:30:36 +00:00
feat(cashier): 实现收银台支付功能
- 新增收银台支付参数和结果对象 - 实现收银台聚合支付配置查询 - 添加收银台支付逻辑,支持多种支付方式 - 优化支付服务,增加重复支付检查 - 调整支付同步服务,增加待支付状态检查
This commit is contained in:
@@ -22,10 +22,10 @@ import java.util.Objects;
|
||||
@AllArgsConstructor
|
||||
public enum CheckoutCallTypeEnum {
|
||||
|
||||
SCAN("scan", "扫码支付"),
|
||||
BAR_CODE("barCode", "条码支付"),
|
||||
QR_CODE("qr_code", "扫码支付"),
|
||||
BAR_CODE("bar_code", "条码支付"),
|
||||
LINK("link", "跳转链接"),
|
||||
MINI_APP("miniApp", "小程序支付"),
|
||||
MINI_APP("mini_app", "小程序支付"),
|
||||
AGGREGATE("aggregate", "聚合支付"),
|
||||
APP("app", "APP支付"),
|
||||
;
|
||||
|
@@ -0,0 +1,27 @@
|
||||
package org.dromara.daxpay.core.param.checkout;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
import lombok.experimental.Accessors;
|
||||
|
||||
/**
|
||||
* 收银台聚合支付参数
|
||||
* @author xxm
|
||||
* @since 2024/11/26
|
||||
*/
|
||||
@Data
|
||||
@Accessors(chain = true)
|
||||
@Schema(title = "收银台支付参数")
|
||||
public class CheckoutAggregatePayParam {
|
||||
|
||||
@Schema(description = "要支付的订单号")
|
||||
private String orderNo;
|
||||
|
||||
@Schema(description = "聚合支付类型")
|
||||
private String aggregateType;
|
||||
|
||||
@Schema(description = "唯一标识")
|
||||
private String openId;
|
||||
|
||||
|
||||
}
|
@@ -1,4 +1,4 @@
|
||||
package org.dromara.daxpay.core.param.cashier;
|
||||
package org.dromara.daxpay.core.param.checkout;
|
||||
|
||||
import cn.hutool.core.date.DatePattern;
|
||||
import com.fasterxml.jackson.annotation.JsonFormat;
|
@@ -1,4 +1,4 @@
|
||||
package org.dromara.daxpay.core.param.cashier;
|
||||
package org.dromara.daxpay.core.param.checkout;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
@@ -5,14 +5,14 @@ import lombok.Data;
|
||||
import lombok.experimental.Accessors;
|
||||
|
||||
/**
|
||||
* 收银台聚合支付配置
|
||||
* 收银台聚合支付配置和订单信息
|
||||
* @author xxm
|
||||
* @since 2024/11/27
|
||||
*/
|
||||
@Data
|
||||
@Accessors(chain = true)
|
||||
@Schema(title = "收银台聚合支付配置")
|
||||
public class CheckoutAggregateResult {
|
||||
public class CheckoutAggregateOrderAndConfigResult {
|
||||
|
||||
/** 订单信息 */
|
||||
@Schema(description = "订单信息")
|
@@ -0,0 +1,27 @@
|
||||
package org.dromara.daxpay.core.result.checkout;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
import lombok.experimental.Accessors;
|
||||
import org.dromara.daxpay.core.enums.PayStatusEnum;
|
||||
|
||||
/**
|
||||
* 收银台支付结果
|
||||
* @author xxm
|
||||
* @since 2024/11/29
|
||||
*/
|
||||
@Data
|
||||
@Accessors(chain = true)
|
||||
@Schema(title = "收银台支付结果")
|
||||
public class CheckoutPayResult {
|
||||
|
||||
/** 链接 */
|
||||
private String url;
|
||||
|
||||
/**
|
||||
* 支付状态
|
||||
* @see PayStatusEnum
|
||||
*/
|
||||
@Schema(description = "支付结果")
|
||||
private String payStatus;
|
||||
}
|
@@ -5,10 +5,10 @@ import cn.bootx.platform.core.rest.result.Result;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.dromara.daxpay.core.param.cashier.CheckoutParam;
|
||||
import org.dromara.daxpay.core.param.cashier.CheckoutPayParam;
|
||||
import org.dromara.daxpay.core.param.checkout.CheckoutParam;
|
||||
import org.dromara.daxpay.core.param.checkout.CheckoutPayParam;
|
||||
import org.dromara.daxpay.core.result.DaxResult;
|
||||
import org.dromara.daxpay.core.result.checkout.CheckoutAggregateResult;
|
||||
import org.dromara.daxpay.core.result.checkout.CheckoutAggregateOrderAndConfigResult;
|
||||
import org.dromara.daxpay.core.result.checkout.CheckoutOrderAndConfigResult;
|
||||
import org.dromara.daxpay.core.result.checkout.CheckoutUrlResult;
|
||||
import org.dromara.daxpay.core.util.DaxRes;
|
||||
@@ -45,7 +45,7 @@ public class CheckoutController {
|
||||
|
||||
@Operation(summary = "获取聚合支付配置")
|
||||
@GetMapping("/getAggregateConfig")
|
||||
public Result<CheckoutAggregateResult> getAggregateConfig(String orderNo, String checkoutType){
|
||||
public Result<CheckoutAggregateOrderAndConfigResult> getAggregateConfig(String orderNo, String checkoutType){
|
||||
return Res.ok(checkoutQueryService.getAggregateConfig(orderNo, checkoutType));
|
||||
}
|
||||
|
||||
|
@@ -12,7 +12,7 @@ import org.dromara.daxpay.core.enums.PayRefundStatusEnum;
|
||||
import org.dromara.daxpay.core.enums.PayStatusEnum;
|
||||
import org.dromara.daxpay.core.exception.AmountExceedLimitException;
|
||||
import org.dromara.daxpay.core.exception.TradeStatusErrorException;
|
||||
import org.dromara.daxpay.core.param.cashier.CheckoutParam;
|
||||
import org.dromara.daxpay.core.param.checkout.CheckoutParam;
|
||||
import org.dromara.daxpay.core.util.PayUtil;
|
||||
import org.dromara.daxpay.core.util.TradeNoGenerateUtil;
|
||||
import org.dromara.daxpay.service.code.DaxPayCode;
|
||||
|
@@ -100,8 +100,8 @@ public class CheckoutQueryService {
|
||||
/**
|
||||
* 收银台聚合支付配置
|
||||
*/
|
||||
public CheckoutAggregateResult getAggregateConfig(String orderNo, String aggregateType){
|
||||
var checkoutInfoResult = new CheckoutAggregateResult();
|
||||
public CheckoutAggregateOrderAndConfigResult getAggregateConfig(String orderNo, String aggregateType){
|
||||
var checkoutInfoResult = new CheckoutAggregateOrderAndConfigResult();
|
||||
// 订单信息
|
||||
PayOrder payOrder = checkoutAssistService.getOrderAndCheck(orderNo);
|
||||
CheckoutOrderResult order = new CheckoutOrderResult()
|
||||
|
@@ -1,25 +1,34 @@
|
||||
package org.dromara.daxpay.service.service.cashier;
|
||||
|
||||
import cn.bootx.platform.common.spring.util.WebServletUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.hutool.extra.servlet.JakartaServletUtil;
|
||||
import com.baomidou.lock.LockInfo;
|
||||
import com.baomidou.lock.LockTemplate;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.dromara.daxpay.core.enums.CheckoutCallTypeEnum;
|
||||
import org.dromara.daxpay.core.enums.CheckoutTypeEnum;
|
||||
import org.dromara.daxpay.core.enums.PayStatusEnum;
|
||||
import org.dromara.daxpay.core.exception.TradeProcessingException;
|
||||
import org.dromara.daxpay.core.exception.UnsupportedAbilityException;
|
||||
import org.dromara.daxpay.core.param.cashier.CheckoutParam;
|
||||
import org.dromara.daxpay.core.param.cashier.CheckoutPayParam;
|
||||
import org.dromara.daxpay.core.result.checkout.CheckoutOrderAndConfigResult;
|
||||
import org.dromara.daxpay.core.param.checkout.CheckoutAggregatePayParam;
|
||||
import org.dromara.daxpay.core.param.checkout.CheckoutParam;
|
||||
import org.dromara.daxpay.core.param.checkout.CheckoutPayParam;
|
||||
import org.dromara.daxpay.core.param.trade.pay.PayParam;
|
||||
import org.dromara.daxpay.core.result.checkout.CheckoutPayResult;
|
||||
import org.dromara.daxpay.core.result.checkout.CheckoutUrlResult;
|
||||
import org.dromara.daxpay.core.result.trade.pay.PayResult;
|
||||
import org.dromara.daxpay.service.dao.config.checkout.CheckoutItemConfigManager;
|
||||
import org.dromara.daxpay.service.entity.config.PlatformConfig;
|
||||
import org.dromara.daxpay.service.entity.config.checkout.CheckoutItemConfig;
|
||||
import org.dromara.daxpay.service.entity.order.pay.PayOrder;
|
||||
import org.dromara.daxpay.service.service.assist.PaymentAssistService;
|
||||
import org.dromara.daxpay.service.service.config.PlatformConfigService;
|
||||
import org.dromara.daxpay.service.service.trade.pay.PayService;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
@@ -35,8 +44,8 @@ public class CheckoutService {
|
||||
private final LockTemplate lockTemplate;
|
||||
private final PlatformConfigService platformConfigService;
|
||||
private final PaymentAssistService paymentAssistService;
|
||||
private final CheckoutQueryService checkoutQueryService;
|
||||
private final CheckoutItemConfigManager checkoutItemConfigManager;
|
||||
private final PayService payService;
|
||||
|
||||
/**
|
||||
* 生成收银台链接
|
||||
@@ -99,30 +108,66 @@ public class CheckoutService {
|
||||
/**
|
||||
* 支付调用
|
||||
*/
|
||||
public void pay(CheckoutPayParam param){
|
||||
public CheckoutPayResult pay(CheckoutPayParam param){
|
||||
// 订单信息
|
||||
PayOrder order = checkoutAssistService.getOrderAndCheck(param.getOrderNo());
|
||||
paymentAssistService.initMchApp(order.getAppId());
|
||||
PayOrder payOrder = checkoutAssistService.getOrderAndCheck(param.getOrderNo());
|
||||
paymentAssistService.initMchApp(payOrder.getAppId());
|
||||
// 获取配置项
|
||||
var itemConfig = checkoutItemConfigManager.findByIdAndAppId(param.getItemId(),order.getAppId())
|
||||
var itemConfig = checkoutItemConfigManager.findByIdAndAppId(param.getItemId(),payOrder.getAppId())
|
||||
.orElseThrow(() -> new TradeProcessingException("支付配置项不存在"));
|
||||
// 判断支付调用类型
|
||||
CheckoutCallTypeEnum callTypeEnum = CheckoutCallTypeEnum.findBuyCode(itemConfig.getCallType());
|
||||
switch (callTypeEnum) {
|
||||
case SCAN -> {
|
||||
// 根据通道和支付方式返回扫码链接
|
||||
}
|
||||
case BAR_CODE -> {
|
||||
// 调用支付逻辑直接进行支付
|
||||
}
|
||||
case LINK -> {
|
||||
// 返回支付链接
|
||||
case QR_CODE, LINK, BAR_CODE -> {
|
||||
return this.checkoutPay(param, payOrder);
|
||||
}
|
||||
case AGGREGATE -> {
|
||||
// 直接返回手机端的聚合收银台链接, 如何判断
|
||||
PlatformConfig config = platformConfigService.getConfig();
|
||||
// 直接返回手机端的聚合收银台链接
|
||||
String url = StrUtil.format("{}/aggregate/{}", config.getGatewayPcUrl(), payOrder.getOrderNo());
|
||||
return new CheckoutPayResult().setUrl(url).setPayStatus(PayStatusEnum.WAIT.getCode());
|
||||
}
|
||||
default -> throw new UnsupportedAbilityException("不支持的支付调用类型");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理参数使用通用支付接口调起支付
|
||||
*/
|
||||
private CheckoutPayResult checkoutPay(CheckoutPayParam param, PayOrder payOrder){
|
||||
// 查询配置
|
||||
CheckoutItemConfig itemConfig = checkoutItemConfigManager.findByIdAndAppId(param.getItemId(),payOrder.getAppId())
|
||||
.orElseThrow(() -> new TradeProcessingException("支付配置项不存在"));
|
||||
paymentAssistService.initMchApp(payOrder.getAppId());
|
||||
// 构建支付参数
|
||||
String clientIP = JakartaServletUtil.getClientIP(WebServletUtil.getRequest());
|
||||
PayParam payParam = new PayParam();
|
||||
payParam.setChannel(itemConfig.getChannel());
|
||||
payParam.setMethod(itemConfig.getPayMethod());
|
||||
payParam.setAppId(itemConfig.getAppId());
|
||||
payParam.setClientIp(clientIP);
|
||||
payParam.setReqTime(LocalDateTime.now());
|
||||
|
||||
// 获取策略
|
||||
// AbsChannelCashierStrategy cashierStrategy = PaymentStrategyFactory.create(itemConfig.getChannel(), AbsChannelCashierStrategy.class);
|
||||
// 进行参数预处理
|
||||
// cashierStrategy.handlePayParam(param, payParam);
|
||||
// 发起支付
|
||||
PayResult payResult = payService.pay(payParam, payOrder);
|
||||
return new CheckoutPayResult()
|
||||
.setUrl(payResult.getPayBody())
|
||||
.setPayStatus(payResult.getStatus());
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 聚合支付
|
||||
*/
|
||||
public PayResult aggregatePay(CheckoutAggregatePayParam param){
|
||||
// 订单信息
|
||||
PayOrder payOrder = checkoutAssistService.getOrderAndCheck(param.getOrderNo());
|
||||
|
||||
return new PayResult();
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -72,6 +72,28 @@ public class PayService {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 支付入口, 内部调用时使用
|
||||
*/
|
||||
public PayResult pay(PayParam payParam, PayOrder payOrder){
|
||||
// 获取商户订单号
|
||||
String bizOrderNo = payOrder.getBizOrderNo();
|
||||
// 加锁
|
||||
LockInfo lock = lockTemplate.lock("payment:pay:" + bizOrderNo,10000,200);
|
||||
if (Objects.isNull(lock)){
|
||||
log.warn("正在支付中,请勿重复支付");
|
||||
throw new TradeProcessingException("正在支付中,请勿重复支付");
|
||||
}
|
||||
try {
|
||||
return this.repeatPay(payParam,payOrder);
|
||||
} catch (Exception e) {
|
||||
log.error("支付异常",e);
|
||||
throw e;
|
||||
} finally {
|
||||
lockTemplate.releaseLock(lock);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 首次支付 无事务
|
||||
* 拆分为多阶段,1. 保存订单记录信息 2 调起支付 3. 支付成功后操作
|
||||
|
@@ -2,12 +2,13 @@ package org.dromara.daxpay.service.service.trade.pay;
|
||||
|
||||
import cn.bootx.platform.core.exception.RepetitiveOperationException;
|
||||
import cn.bootx.platform.core.util.DateTimeUtil;
|
||||
import com.baomidou.lock.LockInfo;
|
||||
import com.baomidou.lock.LockTemplate;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.dromara.daxpay.core.enums.PayStatusEnum;
|
||||
import org.dromara.daxpay.core.enums.TradeTypeEnum;
|
||||
import org.dromara.daxpay.core.exception.OperationFailException;
|
||||
import org.dromara.daxpay.core.exception.PayFailureException;
|
||||
import org.dromara.daxpay.core.exception.SystemUnknownErrorException;
|
||||
import org.dromara.daxpay.core.exception.TradeNotExistException;
|
||||
import org.dromara.daxpay.core.exception.*;
|
||||
import org.dromara.daxpay.core.param.trade.pay.PaySyncParam;
|
||||
import org.dromara.daxpay.core.result.trade.pay.PaySyncResult;
|
||||
import org.dromara.daxpay.service.bo.sync.PaySyncResultBo;
|
||||
@@ -21,10 +22,6 @@ import org.dromara.daxpay.service.service.record.sync.TradeSyncRecordService;
|
||||
import org.dromara.daxpay.service.strategy.AbsPayCloseStrategy;
|
||||
import org.dromara.daxpay.service.strategy.AbsSyncPayOrderStrategy;
|
||||
import org.dromara.daxpay.service.util.PaymentStrategyFactory;
|
||||
import com.baomidou.lock.LockInfo;
|
||||
import com.baomidou.lock.LockTemplate;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Propagation;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
@@ -71,6 +68,11 @@ public class PaySyncService {
|
||||
*/
|
||||
@Transactional(propagation = Propagation.REQUIRES_NEW, rollbackFor = Exception.class)
|
||||
public PaySyncResult syncPayOrder(PayOrder payOrder) {
|
||||
// 待支付状态不允许同步
|
||||
if (Objects.equals(payOrder.getStatus(), WAIT.getCode())){
|
||||
throw new TradeStatusErrorException("订单未开始支付, 请重新确认支付状态");
|
||||
}
|
||||
|
||||
// 加锁
|
||||
LockInfo lock = lockTemplate.lock("sync:pay" + payOrder.getId(),10000,200);
|
||||
if (Objects.isNull(lock)){
|
||||
|
@@ -1,9 +1,14 @@
|
||||
package org.dromara.daxpay.service.service.trade.refund;
|
||||
|
||||
import cn.bootx.platform.starter.redis.delay.service.DelayJobService;
|
||||
import cn.bootx.platform.core.exception.DataNotExistException;
|
||||
import cn.bootx.platform.core.util.BigDecimalUtil;
|
||||
import cn.bootx.platform.core.util.ValidationUtil;
|
||||
import cn.bootx.platform.starter.redis.delay.service.DelayJobService;
|
||||
import cn.hutool.extra.spring.SpringUtil;
|
||||
import com.baomidou.lock.LockInfo;
|
||||
import com.baomidou.lock.LockTemplate;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.dromara.daxpay.core.enums.PayRefundStatusEnum;
|
||||
import org.dromara.daxpay.core.enums.RefundStatusEnum;
|
||||
import org.dromara.daxpay.core.exception.TradeNotExistException;
|
||||
@@ -22,11 +27,6 @@ import org.dromara.daxpay.service.service.order.pay.PayOrderQueryService;
|
||||
import org.dromara.daxpay.service.service.record.flow.TradeFlowRecordService;
|
||||
import org.dromara.daxpay.service.strategy.AbsRefundStrategy;
|
||||
import org.dromara.daxpay.service.util.PaymentStrategyFactory;
|
||||
import cn.hutool.extra.spring.SpringUtil;
|
||||
import com.baomidou.lock.LockInfo;
|
||||
import com.baomidou.lock.LockTemplate;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
@@ -91,11 +91,10 @@ public class RefundService {
|
||||
* 首次退款
|
||||
*/
|
||||
private RefundResult firstRefund(RefundParam param) {
|
||||
|
||||
// 获取支付订单
|
||||
PayOrder payOrder = payOrderQueryService.findByBizOrOrderNo(param.getOrderNo(), param.getBizOrderNo(), param.getAppId())
|
||||
.orElseThrow(() -> new DataNotExistException("支付订单不存在"));
|
||||
// 检查退款参数
|
||||
// 检查退款参数和支付订单
|
||||
refundAssistService.checkAndParam(param, payOrder);
|
||||
// 通过退款参数获取退款策略
|
||||
AbsRefundStrategy refundStrategy = PaymentStrategyFactory.create(payOrder.getChannel(), AbsRefundStrategy.class);
|
||||
|
Reference in New Issue
Block a user