mirror of
https://gitee.com/dromara/dax-pay.git
synced 2025-09-03 02:56:20 +00:00
ref 迁移支付流程
This commit is contained in:
@@ -18,7 +18,7 @@ import java.util.List;
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@Data
|
||||
@Schema(title = "支付参数")
|
||||
public class PayParam extends PayCommonParam{
|
||||
public class PayParam extends PayCommonParam {
|
||||
|
||||
@Schema(description = "业务号")
|
||||
@NotBlank(message = "业务号不可为空")
|
||||
|
@@ -27,14 +27,14 @@ public class PayWayParam {
|
||||
*/
|
||||
@Schema(description = "支付渠道编码")
|
||||
@NotBlank(message = "支付渠道编码不可为空")
|
||||
private String payChannel;
|
||||
private String channel;
|
||||
|
||||
/**
|
||||
* @see PayWayEnum#getCode()
|
||||
*/
|
||||
@Schema(description = "支付方式编码")
|
||||
@NotBlank(message = "支付方式编码不可为空")
|
||||
private String payWay;
|
||||
private String way;
|
||||
|
||||
@Schema(description = "支付金额")
|
||||
@NotNull(message = "支付金额不可为空")
|
||||
|
@@ -0,0 +1,56 @@
|
||||
package cn.bootx.platform.daxpay.openapi.controller.channel;
|
||||
|
||||
import cn.bootx.platform.common.core.rest.Res;
|
||||
import cn.bootx.platform.common.core.rest.ResResult;
|
||||
import cn.bootx.platform.common.core.rest.dto.LabelValue;
|
||||
import cn.bootx.platform.daxpay.core.channel.alipay.service.AlipayConfigService;
|
||||
import cn.bootx.platform.daxpay.dto.channel.alipay.AlipayConfigDto;
|
||||
import cn.bootx.platform.daxpay.param.channel.alipay.AlipayConfigParam;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.SneakyThrows;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author xxm
|
||||
* @since 2021/2/26
|
||||
*/
|
||||
@Tag(name = "支付宝配置")
|
||||
@RestController
|
||||
@RequestMapping("/alipay")
|
||||
@AllArgsConstructor
|
||||
public class AlipayConfigController {
|
||||
|
||||
private final AlipayConfigService alipayConfigService;
|
||||
|
||||
@Operation(summary = "更新")
|
||||
@PostMapping("/update")
|
||||
public ResResult<Void> update(@RequestBody AlipayConfigParam param) {
|
||||
alipayConfigService.update(param);
|
||||
return Res.ok();
|
||||
}
|
||||
|
||||
@Operation(summary = "根据Id查询")
|
||||
@GetMapping("/findById")
|
||||
public ResResult<AlipayConfigDto> findById() {
|
||||
return Res.ok(alipayConfigService.getConfig().toDto());
|
||||
}
|
||||
|
||||
@Operation(summary = "支付宝支持支付方式")
|
||||
@GetMapping("/findPayWayList")
|
||||
public ResResult<List<LabelValue>> findPayWayList() {
|
||||
return Res.ok(alipayConfigService.findPayWayList());
|
||||
}
|
||||
|
||||
@SneakyThrows
|
||||
@Operation(summary = "读取证书文件内容")
|
||||
@PostMapping("/readPem")
|
||||
public ResResult<String> readPem(MultipartFile file){
|
||||
return Res.ok(new String(file.getBytes(), StandardCharsets.UTF_8));
|
||||
}
|
||||
}
|
@@ -1,6 +1,7 @@
|
||||
package cn.bootx.platform.daxpay.openapi.controller;
|
||||
|
||||
import cn.bootx.platform.common.core.annotation.IgnoreAuth;
|
||||
import cn.bootx.platform.daxpay.core.payment.pay.service.PayService;
|
||||
import cn.bootx.platform.daxpay.param.pay.*;
|
||||
import cn.bootx.platform.daxpay.result.DaxResult;
|
||||
import cn.bootx.platform.daxpay.result.pay.PayResult;
|
||||
@@ -25,10 +26,11 @@ import org.springframework.web.bind.annotation.RestController;
|
||||
@RequestMapping("/unipay")
|
||||
@RequiredArgsConstructor
|
||||
public class UniPayController {
|
||||
|
||||
private final PayService payService;
|
||||
@Operation(summary = "统一下单")
|
||||
@PostMapping("/pay")
|
||||
public DaxResult<PayResult> pay(@RequestBody PayParam payParam){
|
||||
payService.pay(payParam);
|
||||
return DaxRes.ok();
|
||||
}
|
||||
@Operation(summary = "简单下单")
|
||||
|
@@ -55,7 +55,7 @@ public class AliPayOrderService {
|
||||
refundableInfos.removeIf(payTypeInfo -> PayChannelEnum.ASYNC_TYPE_CODE.contains(payTypeInfo.getChannel()));
|
||||
// 更新支付宝支付类型信息
|
||||
payChannelInfo.add(new PayOrderChannel().setChannel(PayChannelEnum.ALI.getCode())
|
||||
.setPayWay(payWayParam.getPayWay())
|
||||
.setPayWay(payWayParam.getWay())
|
||||
.setAmount(payWayParam.getAmount())
|
||||
.setChannelExtra(payWayParam.getChannelExtra()));
|
||||
// TODO 更新支付关联信息
|
||||
|
@@ -53,7 +53,7 @@ public class AliPayService {
|
||||
throw new PayFailureException("支付宝未配置可用的支付方式");
|
||||
}
|
||||
// 发起的支付类型是否在支持的范围内
|
||||
PayWayEnum payWayEnum = Optional.ofNullable(AliPayWay.findByCode(payWayParam.getPayWay()))
|
||||
PayWayEnum payWayEnum = Optional.ofNullable(AliPayWay.findByCode(payWayParam.getWay()))
|
||||
.orElseThrow(() -> new PayFailureException("非法的支付宝支付类型"));
|
||||
if (!alipayConfig.getPayWays().contains(payWayEnum.getCode())) {
|
||||
throw new PayFailureException("该支付宝支付方式不可用");
|
||||
@@ -69,23 +69,23 @@ public class AliPayService {
|
||||
// 线程存储
|
||||
AsyncPayInfo asyncPayInfo = Optional.ofNullable(AsyncPayInfoLocal.get()).orElse(new AsyncPayInfo());
|
||||
// wap支付
|
||||
if (Objects.equals(payWayParam.getPayWay(), PayWayEnum.WAP.getCode())) {
|
||||
if (Objects.equals(payWayParam.getWay(), PayWayEnum.WAP.getCode())) {
|
||||
payBody = this.wapPay(amount, payOrder, alipayConfig, aliPayParam);
|
||||
}
|
||||
// 程序支付
|
||||
else if (Objects.equals(payWayParam.getPayWay(), PayWayEnum.APP.getCode())) {
|
||||
else if (Objects.equals(payWayParam.getWay(), PayWayEnum.APP.getCode())) {
|
||||
payBody = this.appPay(amount, payOrder, alipayConfig);
|
||||
}
|
||||
// pc支付
|
||||
else if (Objects.equals(payWayParam.getPayWay(), PayWayEnum.WEB.getCode())) {
|
||||
else if (Objects.equals(payWayParam.getWay(), PayWayEnum.WEB.getCode())) {
|
||||
payBody = this.webPay(amount, payOrder, alipayConfig, aliPayParam);
|
||||
}
|
||||
// 二维码支付
|
||||
else if (Objects.equals(payWayParam.getPayWay(), PayWayEnum.QRCODE.getCode())) {
|
||||
else if (Objects.equals(payWayParam.getWay(), PayWayEnum.QRCODE.getCode())) {
|
||||
payBody = this.qrCodePay(amount, payOrder, alipayConfig);
|
||||
}
|
||||
// 付款码支付
|
||||
else if (Objects.equals(payWayParam.getPayWay(), PayWayEnum.BARCODE.getCode())) {
|
||||
else if (Objects.equals(payWayParam.getWay(), PayWayEnum.BARCODE.getCode())) {
|
||||
String tradeNo = this.barCode(amount, payOrder, aliPayParam, alipayConfig);
|
||||
asyncPayInfo.setExpiredTime(false).setTradeNo(tradeNo);
|
||||
}
|
||||
|
@@ -2,16 +2,12 @@ package cn.bootx.platform.daxpay.core.channel.alipay.service;
|
||||
|
||||
import cn.bootx.platform.common.core.exception.BizException;
|
||||
import cn.bootx.platform.common.core.exception.DataNotExistException;
|
||||
import cn.bootx.platform.common.core.rest.PageResult;
|
||||
import cn.bootx.platform.common.core.rest.dto.LabelValue;
|
||||
import cn.bootx.platform.common.core.rest.param.PageParam;
|
||||
import cn.bootx.platform.daxpay.code.AliPayCode;
|
||||
import cn.bootx.platform.daxpay.code.AliPayWay;
|
||||
import cn.bootx.platform.daxpay.core.channel.alipay.dao.AlipayConfigManager;
|
||||
import cn.bootx.platform.daxpay.core.channel.alipay.entity.AlipayConfig;
|
||||
import cn.bootx.platform.daxpay.dto.channel.alipay.AlipayConfigDto;
|
||||
import cn.bootx.platform.daxpay.param.channel.alipay.AlipayConfigParam;
|
||||
import cn.bootx.platform.daxpay.param.channel.alipay.AlipayConfigQuery;
|
||||
import cn.hutool.core.bean.BeanUtil;
|
||||
import cn.hutool.core.bean.copier.CopyOptions;
|
||||
import cn.hutool.core.util.CharsetUtil;
|
||||
@@ -37,42 +33,20 @@ import java.util.stream.Collectors;
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class AlipayConfigService {
|
||||
|
||||
/** 默认支付包配置的主键ID */
|
||||
private final static Long ID = 0L;
|
||||
private final AlipayConfigManager alipayConfigManager;
|
||||
|
||||
/**
|
||||
* 添加支付宝配置
|
||||
*/
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void add(AlipayConfigParam param) {
|
||||
AlipayConfig alipayConfig = AlipayConfig.init(param);
|
||||
alipayConfigManager.save(alipayConfig);
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改
|
||||
*/
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void update(AlipayConfigParam param) {
|
||||
AlipayConfig alipayConfig = alipayConfigManager.findById(param.getId()).orElseThrow(DataNotExistException::new);
|
||||
AlipayConfig alipayConfig = alipayConfigManager.findById(ID).orElseThrow(() -> new DataNotExistException("支付宝配置不存在"));
|
||||
BeanUtil.copyProperties(param, alipayConfig, CopyOptions.create().ignoreNullValue());
|
||||
alipayConfigManager.updateById(alipayConfig);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取
|
||||
*/
|
||||
public AlipayConfigDto findById(Long id) {
|
||||
return alipayConfigManager.findById(id).map(AlipayConfig::toDto).orElseThrow(DataNotExistException::new);
|
||||
}
|
||||
|
||||
/**
|
||||
* 分页
|
||||
*/
|
||||
public PageResult<AlipayConfigDto> page(PageParam pageParam, AlipayConfigQuery param) {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 支付宝支持支付方式
|
||||
*/
|
||||
@@ -84,18 +58,18 @@ public class AlipayConfigService {
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化IJPay服务,通过商户应用AppCode获取支付配置
|
||||
* 获取支付配置
|
||||
*/
|
||||
public void initApiConfigByMchAppCode(String mchAppCode){
|
||||
AlipayConfig alipayConfig = null;
|
||||
this.initApiConfig(alipayConfig);
|
||||
public AlipayConfig getConfig(){
|
||||
return alipayConfigManager.findById(ID).orElseThrow(() -> new DataNotExistException("支付宝配置不存在"));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 初始化IJPay服务
|
||||
*/
|
||||
@SneakyThrows
|
||||
public void initApiConfig(AlipayConfig alipayConfig) {
|
||||
public void initConfig(AlipayConfig alipayConfig) {
|
||||
|
||||
AliPayApiConfig aliPayApiConfig;
|
||||
// 公钥
|
||||
|
@@ -68,7 +68,7 @@ public class WeChatPayService {
|
||||
throw new PayFailureException("未配置微信支付方式");
|
||||
}
|
||||
|
||||
PayWayEnum payWayEnum = Optional.ofNullable(WeChatPayWay.findByCode(payWayParam.getPayWay()))
|
||||
PayWayEnum payWayEnum = Optional.ofNullable(WeChatPayWay.findByCode(payWayParam.getWay()))
|
||||
.orElseThrow(() -> new PayFailureException("非法的微信支付类型"));
|
||||
if (!payWays.contains(payWayEnum.getCode())) {
|
||||
throw new PayFailureException("该微信支付方式不可用");
|
||||
@@ -84,7 +84,7 @@ public class WeChatPayService {
|
||||
String totalFee = String.valueOf(amount);
|
||||
AsyncPayInfo asyncPayInfo = Optional.ofNullable(AsyncPayInfoLocal.get()).orElse(new AsyncPayInfo());
|
||||
String payBody = null;
|
||||
PayWayEnum payWayEnum = PayWayEnum.findByCode(payWayParam.getPayWay());
|
||||
PayWayEnum payWayEnum = PayWayEnum.findByCode(payWayParam.getWay());
|
||||
|
||||
// wap支付
|
||||
if (payWayEnum == PayWayEnum.WAP) {
|
||||
|
@@ -51,7 +51,7 @@ public class WeChatPaymentService {
|
||||
refundableInfos.removeIf(payTypeInfo -> PayChannelEnum.ASYNC_TYPE_CODE.contains(payTypeInfo.getChannel()));
|
||||
// 添加微信支付类型信息
|
||||
payTypeInfos.add(new PayOrderChannel().setChannel(PayChannelEnum.WECHAT.getCode())
|
||||
.setPayWay(payWayParam.getPayWay())
|
||||
.setPayWay(payWayParam.getWay())
|
||||
.setAmount(payWayParam.getAmount())
|
||||
.setChannelExtra(payWayParam.getChannelExtra()));
|
||||
// TODO 更新支付方式列表
|
||||
|
@@ -1,21 +1,22 @@
|
||||
package cn.bootx.platform.daxpay.core.order.pay.builder;
|
||||
|
||||
import cn.bootx.platform.common.spring.util.WebServletUtil;
|
||||
import cn.bootx.platform.daxpay.code.PayChannelEnum;
|
||||
import cn.bootx.platform.daxpay.code.PayStatusEnum;
|
||||
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.PayOrderRefundableInfo;
|
||||
import cn.bootx.platform.daxpay.core.payment.pay.local.AsyncPayInfo;
|
||||
import cn.bootx.platform.daxpay.core.payment.pay.local.AsyncPayInfoLocal;
|
||||
import cn.bootx.platform.daxpay.param.pay.PayParam;
|
||||
import cn.bootx.platform.daxpay.param.pay.PayWayParam;
|
||||
import cn.bootx.platform.daxpay.result.pay.PayResult;
|
||||
import cn.hutool.core.collection.CollectionUtil;
|
||||
import cn.hutool.extra.servlet.ServletUtil;
|
||||
import lombok.experimental.UtilityClass;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
@@ -31,45 +32,45 @@ public class PaymentBuilder {
|
||||
* 构建payment记录
|
||||
*/
|
||||
public PayOrder buildPayOrder(PayParam payParam) {
|
||||
PayOrder payOrder = new PayOrder();
|
||||
|
||||
HttpServletRequest request = WebServletUtil.getRequest();
|
||||
String ip = ServletUtil.getClientIP(request);
|
||||
// 基础信息
|
||||
payOrder.setBusinessNo(payParam.getBusinessNo())
|
||||
.setTitle(payParam.getTitle());
|
||||
|
||||
// 支付方式
|
||||
// 可退款信息
|
||||
List<PayOrderRefundableInfo> refundableInfos = buildRefundableInfo(payParam.getPayWays());
|
||||
List<PayOrderChannel> payOrderChannels = buildPayChannel(payParam.getPayWays());
|
||||
// 计算总价
|
||||
int sumAmount = payOrderChannels.stream()
|
||||
.map(PayOrderChannel::getAmount)
|
||||
int sumAmount = payParam.getPayWays().stream()
|
||||
.map(PayWayParam::getAmount)
|
||||
.filter(Objects::nonNull)
|
||||
.reduce(Integer::sum)
|
||||
.orElse(0);
|
||||
// 支付渠道信息
|
||||
payOrder.setRefundableInfos(refundableInfos)
|
||||
// 是否有异步支付方式
|
||||
Optional<String> asyncPayMode = payParam.getPayWays().stream()
|
||||
.map(PayWayParam::getChannel)
|
||||
.filter(PayChannelEnum.ASYNC_TYPE_CODE::contains)
|
||||
.findFirst();
|
||||
// 构建支付订单对象
|
||||
return new PayOrder()
|
||||
.setBusinessNo(payParam.getBusinessNo())
|
||||
.setTitle(payParam.getTitle())
|
||||
.setRefundableInfos(refundableInfos)
|
||||
.setStatus(PayStatusEnum.PROGRESS.getCode())
|
||||
.setAmount(sumAmount)
|
||||
.setCombinationPayMode(payParam.getPayWays().size()>1)
|
||||
.setCombinationPayMode(payParam.getPayWays().size() > 1)
|
||||
.setAsyncPayMode(asyncPayMode.isPresent())
|
||||
.setAsyncPayChannel(asyncPayMode.orElse(null))
|
||||
.setRefundableBalance(sumAmount);
|
||||
return payOrder;
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建订单关联通道信息
|
||||
*/
|
||||
private List<PayOrderChannel> buildPayChannel(List<PayWayParam> payWayParamList) {
|
||||
if (CollectionUtil.isEmpty(payWayParamList)) {
|
||||
public List<PayOrderChannel> buildPayChannel(List<PayWayParam> payWayParams) {
|
||||
if (CollectionUtil.isEmpty(payWayParams)) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
return payWayParamList.stream()
|
||||
return payWayParams.stream()
|
||||
.map(o-> new PayOrderChannel()
|
||||
.setChannel(o.getPayChannel())
|
||||
.setPayWay(o.getPayWay())
|
||||
.setAmount(o.getAmount())
|
||||
.setChannelExtra(o.getChannelExtra()))
|
||||
.setChannel(o.getChannel())
|
||||
.setPayWay(o.getWay())
|
||||
.setAmount(o.getAmount())
|
||||
.setChannelExtra(o.getChannelExtra()))
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@@ -82,41 +83,50 @@ public class PaymentBuilder {
|
||||
}
|
||||
return payWayParamList.stream()
|
||||
.map(o-> new PayOrderRefundableInfo()
|
||||
.setChannel(o.getPayChannel())
|
||||
.setChannel(o.getChannel())
|
||||
.setAmount(o.getAmount()))
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 根据Payment构建PaymentResult
|
||||
* @param payment payment
|
||||
* @return paymentVO
|
||||
* 根据支付订单构建支付结果
|
||||
* @param payOrder 支付订单
|
||||
* @return PayResult 支付结果
|
||||
*/
|
||||
public PayResult buildResultByPayment(PayOrder payment) {
|
||||
// PayResult paymentResult;
|
||||
// try {
|
||||
// paymentResult = new PayResult();
|
||||
// // 异步支付信息
|
||||
// paymentResult.setAsyncPayChannel(payment.getAsyncPayChannel())
|
||||
// .setAsyncPayMode(payment.isAsyncPayMode())
|
||||
// .setStatus(payment.getStatus());
|
||||
//
|
||||
//
|
||||
// // 设置异步支付参数
|
||||
// List<PayOrderChannel> moneyPayTypeInfos = channelInfos.stream()
|
||||
// .filter(payTypeInfo -> PayChannelEnum.ASYNC_TYPE_CODE.contains(payTypeInfo.getPayChannel()))
|
||||
// .collect(Collectors.toList());
|
||||
// if (!CollUtil.isEmpty(moneyPayTypeInfos)) {
|
||||
// paymentResult.setAsyncPayInfo(AsyncPayInfoLocal.get());
|
||||
// }
|
||||
// }
|
||||
// finally {
|
||||
// // 清空线程变量
|
||||
// AsyncPayInfoLocal.clear();
|
||||
// }
|
||||
// return paymentResult;
|
||||
return null;
|
||||
public PayResult buildPayResultByPayOrder(PayOrder payOrder) {
|
||||
PayResult paymentResult;
|
||||
try {
|
||||
paymentResult = new PayResult()
|
||||
.setPaymentId(payOrder.getId())
|
||||
.setAsyncPayMode(payOrder.isAsyncPayMode())
|
||||
.setAsyncPayChannel(payOrder.getAsyncPayChannel())
|
||||
.setStatus(payOrder.getStatus());
|
||||
|
||||
// 设置异步支付参数
|
||||
AsyncPayInfo asyncPayInfo = AsyncPayInfoLocal.get();
|
||||
if (Objects.nonNull(asyncPayInfo)) {
|
||||
paymentResult.setPayBody(AsyncPayInfoLocal.get().getPayBody());
|
||||
}
|
||||
}
|
||||
finally {
|
||||
// 清空线程变量
|
||||
AsyncPayInfoLocal.clear();
|
||||
}
|
||||
return paymentResult;
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据新的新传入的支付参数和支付订单构建出当前需要支付参数
|
||||
* 主要是针对其中的异步支付参数进行处理
|
||||
*
|
||||
* @param newPayParam 新传入的参数
|
||||
* @param payOrder 支付订单
|
||||
* @return PayParam 支付参数
|
||||
*/
|
||||
public PayParam buildPayParam(PayParam newPayParam, PayOrder payOrder) {
|
||||
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
@@ -0,0 +1,52 @@
|
||||
package cn.bootx.platform.daxpay.core.order.pay.dao;
|
||||
|
||||
import cn.bootx.platform.common.core.util.LocalDateTimeUtil;
|
||||
import cn.bootx.platform.common.redis.RedisClient;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* 支付单过期处理
|
||||
*
|
||||
* @author xxm
|
||||
* @since 2022/7/12
|
||||
*/
|
||||
@Slf4j
|
||||
@Repository
|
||||
@RequiredArgsConstructor
|
||||
public class PayExpiredTimeRepository {
|
||||
|
||||
private static final String KEY = "payment:pay:expiredtime";
|
||||
|
||||
private final RedisClient redisClient;
|
||||
|
||||
/**
|
||||
* 根据 token 存储对应的 ExpiredTokenKey
|
||||
*/
|
||||
public void store(Long paymentId, LocalDateTime expiredTime) {
|
||||
long time = LocalDateTimeUtil.timestamp(expiredTime);
|
||||
redisClient.zadd(KEY, String.valueOf(paymentId), time);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取所有已过期的ExpiredTokenKey
|
||||
*/
|
||||
public Set<String> retrieveExpiredKeys(LocalDateTime expiredTime) {
|
||||
long time = LocalDateTimeUtil.timestamp(expiredTime);
|
||||
return redisClient.zrangeByScore(KEY, 0L, time);
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除指定的ExpiredTokenKey
|
||||
*/
|
||||
public void removeKeys(String... keys) {
|
||||
if (keys != null && keys.length > 0) {
|
||||
redisClient.zremByMembers(KEY, keys);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,29 @@
|
||||
package cn.bootx.platform.daxpay.core.order.pay.dao;
|
||||
|
||||
import cn.bootx.platform.common.mybatisplus.impl.BaseManager;
|
||||
import cn.bootx.platform.daxpay.core.order.pay.entity.PayOrderChannel;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
/**
|
||||
* 支付订单关联支付时通道信息
|
||||
* @author xxm
|
||||
* @since 2023/12/20
|
||||
*/
|
||||
@Slf4j
|
||||
@Repository
|
||||
@RequiredArgsConstructor
|
||||
public class PayOrderChannelManager extends BaseManager<PayOrderChannelMapper, PayOrderChannel> {
|
||||
/**
|
||||
* 根据订单id和支付渠道查询
|
||||
*/
|
||||
public Optional<PayOrderChannel> findByOderIdAndChannel(Long paymentId, String channel) {
|
||||
return lambdaQuery()
|
||||
.eq(PayOrderChannel::getPaymentId,paymentId)
|
||||
.eq(PayOrderChannel::getChannel,channel)
|
||||
.oneOpt();
|
||||
}
|
||||
}
|
@@ -0,0 +1,14 @@
|
||||
package cn.bootx.platform.daxpay.core.order.pay.dao;
|
||||
|
||||
import cn.bootx.platform.daxpay.core.order.pay.entity.PayOrderChannel;
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
|
||||
/**
|
||||
* 支付订单关联支付时通道信息
|
||||
* @author xxm
|
||||
* @since 2023/12/20
|
||||
*/
|
||||
@Mapper
|
||||
public interface PayOrderChannelMapper extends BaseMapper<PayOrderChannel> {
|
||||
}
|
@@ -0,0 +1,18 @@
|
||||
package cn.bootx.platform.daxpay.core.order.pay.dao;
|
||||
|
||||
import cn.bootx.platform.common.mybatisplus.impl.BaseManager;
|
||||
import cn.bootx.platform.daxpay.core.order.pay.entity.PayOrderExtra;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
/**
|
||||
* 支付订单扩展信息
|
||||
* @author xxm
|
||||
* @since 2023/12/20
|
||||
*/
|
||||
@Slf4j
|
||||
@Repository
|
||||
@RequiredArgsConstructor
|
||||
public class PayOrderExtraManager extends BaseManager<PayOrderExtraMapper, PayOrderExtra> {
|
||||
}
|
@@ -0,0 +1,14 @@
|
||||
package cn.bootx.platform.daxpay.core.order.pay.dao;
|
||||
|
||||
import cn.bootx.platform.daxpay.core.order.pay.entity.PayOrderExtra;
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
|
||||
/**
|
||||
* 支付订单扩展信息
|
||||
* @author xxm
|
||||
* @since 2023/12/20
|
||||
*/
|
||||
@Mapper
|
||||
public interface PayOrderExtraMapper extends BaseMapper<PayOrderExtra> {
|
||||
}
|
@@ -6,6 +6,8 @@ import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
/**
|
||||
* 支付订单
|
||||
* @author xxm
|
||||
@@ -15,4 +17,7 @@ import org.springframework.stereotype.Repository;
|
||||
@Repository
|
||||
@RequiredArgsConstructor
|
||||
public class PayOrderManager extends BaseManager<PayOrderMapper, PayOrder> {
|
||||
public Optional<PayOrder> findByBusinessNo(String businessNo) {
|
||||
return findByField(PayOrder::getBusinessNo,businessNo);
|
||||
}
|
||||
}
|
||||
|
@@ -32,7 +32,7 @@ public class PayOrder extends MpBaseEntity {
|
||||
|
||||
/** 关联的业务id */
|
||||
@DbMySqlIndex(comment = "业务业务号索引",type = MySqlIndexType.UNIQUE)
|
||||
@DbColumn(comment = "关联的业务id")
|
||||
@DbColumn(comment = "关联的业务号")
|
||||
private String businessNo;
|
||||
|
||||
/** 标题 */
|
||||
|
@@ -1,6 +1,6 @@
|
||||
package cn.bootx.platform.daxpay.core.order.pay.entity;
|
||||
|
||||
import cn.bootx.platform.common.mybatisplus.base.MpBaseEntity;
|
||||
import cn.bootx.platform.common.mybatisplus.base.MpDelEntity;
|
||||
import cn.bootx.platform.daxpay.param.channel.AliPayParam;
|
||||
import cn.bootx.platform.daxpay.param.channel.VoucherPayParam;
|
||||
import cn.bootx.platform.daxpay.param.channel.WalletPayParam;
|
||||
@@ -22,7 +22,7 @@ import lombok.experimental.Accessors;
|
||||
@Accessors(chain = true)
|
||||
@DbTable(comment = "支付订单关联支付时通道信息")
|
||||
@TableName("pay_order_channel")
|
||||
public class PayOrderChannel extends MpBaseEntity {
|
||||
public class PayOrderChannel extends MpDelEntity {
|
||||
|
||||
@DbColumn(comment = "支付id")
|
||||
private Long paymentId;
|
||||
|
@@ -5,7 +5,7 @@ import lombok.Data;
|
||||
import lombok.experimental.Accessors;
|
||||
|
||||
/**
|
||||
* 支付订单可退款信息
|
||||
* 支付订单可退款信息(不持久化,直接保存为json)
|
||||
* @author xxm
|
||||
* @since 2023/12/18
|
||||
*/
|
||||
|
@@ -1,9 +1,30 @@
|
||||
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.core.order.pay.entity.PayOrderRefundableInfo;
|
||||
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
|
||||
@@ -13,4 +34,87 @@ import org.springframework.stereotype.Service;
|
||||
@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<PayOrderRefundableInfo> refundableInfos = payment.getRefundableInfos();
|
||||
PayOrderRefundableInfo 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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -7,6 +7,7 @@ import cn.bootx.platform.daxpay.func.AbsPayStrategy;
|
||||
import cn.bootx.platform.daxpay.param.pay.PayWayParam;
|
||||
import cn.hutool.core.collection.CollectionUtil;
|
||||
import cn.hutool.extra.spring.SpringUtil;
|
||||
import lombok.experimental.UtilityClass;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
@@ -23,6 +24,7 @@ import static cn.bootx.platform.daxpay.code.PayChannelEnum.ASYNC_TYPE_CODE;
|
||||
* @author xxm
|
||||
* @since 2020/12/11
|
||||
*/
|
||||
@UtilityClass
|
||||
public class PayStrategyFactory {
|
||||
|
||||
/**
|
||||
@@ -30,10 +32,9 @@ public class PayStrategyFactory {
|
||||
* @param payWayParam 支付类型
|
||||
* @return 支付策略
|
||||
*/
|
||||
public static AbsPayStrategy create(PayWayParam payWayParam) {
|
||||
|
||||
AbsPayStrategy strategy = null;
|
||||
PayChannelEnum channelEnum = PayChannelEnum.findByCode(payWayParam.getPayChannel());
|
||||
public AbsPayStrategy create(PayWayParam payWayParam) {
|
||||
AbsPayStrategy strategy;
|
||||
PayChannelEnum channelEnum = PayChannelEnum.findByCode(payWayParam.getChannel());
|
||||
switch (channelEnum) {
|
||||
case ALI:
|
||||
strategy = SpringUtil.getBean(AliPayStrategy.class);
|
||||
@@ -56,7 +57,6 @@ public class PayStrategyFactory {
|
||||
default:
|
||||
throw new PayUnsupportedMethodException();
|
||||
}
|
||||
// noinspection ConstantConditions
|
||||
strategy.setPayWayParam(payWayParam);
|
||||
return strategy;
|
||||
}
|
||||
@@ -64,15 +64,15 @@ public class PayStrategyFactory {
|
||||
/**
|
||||
* 根据传入的支付类型批量创建策略, 异步支付在后面
|
||||
*/
|
||||
public static List<AbsPayStrategy> createDesc(List<PayWayParam> payWayParamList) {
|
||||
public static List<AbsPayStrategy> createAsyncLast(List<PayWayParam> payWayParamList) {
|
||||
return create(payWayParamList, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据传入的支付类型批量创建策略, 默认异步支付在前面
|
||||
* 根据传入的支付类型批量创建策略, 异步支付在前面
|
||||
*/
|
||||
public static List<AbsPayStrategy> create(List<PayWayParam> payWayParamList) {
|
||||
return create(payWayParamList, false);
|
||||
public List<AbsPayStrategy> create(List<PayWayParam> payWayParamList) {
|
||||
return create(payWayParamList, true);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -80,7 +80,7 @@ public class PayStrategyFactory {
|
||||
* @param payWayParamList 支付类型
|
||||
* @return 支付策略
|
||||
*/
|
||||
private static List<AbsPayStrategy> create(List<PayWayParam> payWayParamList, boolean description) {
|
||||
private List<AbsPayStrategy> create(List<PayWayParam> payWayParamList, boolean asyncFirst) {
|
||||
if (CollectionUtil.isEmpty(payWayParamList)) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
@@ -89,25 +89,24 @@ public class PayStrategyFactory {
|
||||
// 同步支付
|
||||
List<PayWayParam> syncPayWayParamList = payWayParamList.stream()
|
||||
.filter(Objects::nonNull)
|
||||
.filter(payModeParam -> !ASYNC_TYPE_CODE.contains(payModeParam.getPayChannel()))
|
||||
.filter(payModeParam -> !ASYNC_TYPE_CODE.contains(payModeParam.getChannel()))
|
||||
.collect(Collectors.toList());
|
||||
|
||||
// 异步支付
|
||||
List<PayWayParam> asyncPayWayParamList = payWayParamList.stream()
|
||||
.filter(Objects::nonNull)
|
||||
.filter(payModeParam -> ASYNC_TYPE_CODE.contains(payModeParam.getPayChannel()))
|
||||
.filter(payModeParam -> ASYNC_TYPE_CODE.contains(payModeParam.getChannel()))
|
||||
.collect(Collectors.toList());
|
||||
|
||||
List<PayWayParam> sortList = new ArrayList<>(payWayParamList.size());
|
||||
|
||||
// 异步在后面
|
||||
if (description) {
|
||||
sortList.addAll(syncPayWayParamList);
|
||||
sortList.addAll(asyncPayWayParamList);
|
||||
}
|
||||
else {
|
||||
if (asyncFirst) {
|
||||
sortList.addAll(asyncPayWayParamList);
|
||||
sortList.addAll(syncPayWayParamList);
|
||||
} else {
|
||||
sortList.addAll(syncPayWayParamList);
|
||||
sortList.addAll(asyncPayWayParamList);
|
||||
}
|
||||
|
||||
// 此处有一个根据Type的反转排序,
|
||||
|
@@ -1,11 +1,40 @@
|
||||
package cn.bootx.platform.daxpay.core.payment.pay.service;
|
||||
|
||||
import cn.bootx.platform.common.core.util.LocalDateTimeUtil;
|
||||
import cn.bootx.platform.common.core.util.ValidationUtil;
|
||||
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.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.order.pay.service.PayOrderService;
|
||||
import cn.bootx.platform.daxpay.core.payment.pay.factory.PayStrategyFactory;
|
||||
import cn.bootx.platform.daxpay.exception.pay.PayFailureException;
|
||||
import cn.bootx.platform.daxpay.exception.pay.PayUnsupportedMethodException;
|
||||
import cn.bootx.platform.daxpay.func.AbsPayStrategy;
|
||||
import cn.bootx.platform.daxpay.func.PayStrategyConsumer;
|
||||
import cn.bootx.platform.daxpay.param.pay.PayParam;
|
||||
import cn.bootx.platform.daxpay.param.pay.PayWayParam;
|
||||
import cn.bootx.platform.daxpay.result.pay.PayResult;
|
||||
import cn.bootx.platform.daxpay.util.PayWayUtil;
|
||||
import cn.hutool.core.collection.CollectionUtil;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.*;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
|
||||
/**
|
||||
* 支付流程服务
|
||||
* 支付流程
|
||||
*
|
||||
* @author xxm
|
||||
* @since 2020/12/9
|
||||
*/
|
||||
@@ -13,4 +42,224 @@ import org.springframework.stereotype.Service;
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class PayService {
|
||||
|
||||
private final PayOrderService payOrderService;
|
||||
|
||||
private final PayOrderExtraManager payOrderExtraManager;
|
||||
|
||||
private final PayOrderChannelManager payOrderChannelManager;
|
||||
|
||||
|
||||
/**
|
||||
* 支付方法(同步/异步/组合支付) 同步支付:都只会在第一次执行中就完成支付,例如钱包、积分都是调用完就进行了扣减,完成了支付记录
|
||||
* 异步支付:例如支付宝、微信,发起支付后还需要跳转第三方平台进行支付,支付后通常需要进行回调,之后才完成支付记录
|
||||
* 组合支付:主要是混合了同步支付和异步支付,同时异步支付只能有一个,在支付时先对同步支付进行扣减,然后异步支付回调结束后完成整个支付单
|
||||
* 组合支付在非第一次支付的时候,只对新传入的异步支付PayMode进行处理,PayMode的价格使用第一次发起的价格,旧的同步支付如果传入后也不做处理,
|
||||
* Payment中PayModeList将会为 旧有的同步支付+新传入的异步支付方式(在具体支付实现中处理)
|
||||
*/
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public PayResult pay(PayParam payParam) {
|
||||
// 检验参数
|
||||
ValidationUtil.validateParam(payParam);
|
||||
// 异步支付方式检查
|
||||
PayWayUtil.validationAsyncPayMode(payParam);
|
||||
// 获取并校验支付状态
|
||||
PayOrder payOrder = this.getAndCheckByBusinessId(payParam.getBusinessNo());
|
||||
|
||||
// 异步支付且非第一次支付
|
||||
if (Objects.nonNull(payOrder) && payOrder.isAsyncPayMode()) {
|
||||
return this.paySyncNotFirst(payParam, payOrder);
|
||||
}
|
||||
else {
|
||||
// 第一次发起支付或同步支付
|
||||
return this.payFirst(payParam, payOrder);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 发起的第一次支付请求(同步/异步)
|
||||
*/
|
||||
private PayResult payFirst(PayParam payParam, PayOrder payOrder) {
|
||||
// 0. 已经发起过支付情况直接返回支付结果
|
||||
if (Objects.nonNull(payOrder)) {
|
||||
return PaymentBuilder.buildPayResultByPayOrder(payOrder);
|
||||
}
|
||||
|
||||
// 1. 价格检测
|
||||
PayWayUtil.validationAmount(payParam.getPayWays());
|
||||
|
||||
// 2. 创建支付相关的记录并返回支付订单对象
|
||||
payOrder = this.createPayOrder(payParam);
|
||||
|
||||
// 3. 调用支付方法进行发起支付
|
||||
this.payFirstMethod(payParam, payOrder);
|
||||
|
||||
// 4. 获取支付记录信息
|
||||
// payOrder = payOrderService.findById(payOrder.getId()).orElseThrow(PayNotExistedException::new);
|
||||
|
||||
// 5. 返回支付结果
|
||||
return PaymentBuilder.buildPayResultByPayOrder(payOrder);
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行支付方法 (第一次支付)
|
||||
* @param payParam 支付参数
|
||||
* @param payOrder 支付订单
|
||||
*/
|
||||
private void payFirstMethod(PayParam payParam, PayOrder payOrder) {
|
||||
|
||||
// 1.获取支付方式,通过工厂生成对应的策略组
|
||||
List<AbsPayStrategy> paymentStrategyList = PayStrategyFactory.create(payParam.getPayWays());
|
||||
if (CollectionUtil.isEmpty(paymentStrategyList)) {
|
||||
throw new PayUnsupportedMethodException();
|
||||
}
|
||||
|
||||
// 2.初始化支付的参数
|
||||
for (AbsPayStrategy paymentStrategy : paymentStrategyList) {
|
||||
paymentStrategy.initPayParam(payOrder, payParam);
|
||||
}
|
||||
|
||||
// 3.支付前准备, 执行支付前处理
|
||||
this.doHandler(payOrder, paymentStrategyList, AbsPayStrategy::doBeforePayHandler, null);
|
||||
|
||||
// 4.支付操作
|
||||
this.doHandler(payOrder, paymentStrategyList, AbsPayStrategy::doPayHandler, (strategies, payOrderObj) -> {
|
||||
// 发起支付成功进行的执行方法
|
||||
strategies.forEach(AbsPayStrategy::doSuccessHandler);
|
||||
// 所有支付方式都是同步时, 对支付订单进行处理
|
||||
if (PayWayUtil.isNotSync(payParam.getPayWays())) {
|
||||
// 修改支付订单状态为成功
|
||||
payOrderObj.setStatus(PayStatusEnum.SUCCESS.getCode());
|
||||
payOrderObj.setPayTime(LocalDateTime.now());
|
||||
}
|
||||
payOrderService.updateById(payOrderObj);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 异步支付执行(非第一次请求), 只执行异步支付策略, 报错不影响继续发起支付
|
||||
*/
|
||||
private PayResult paySyncNotFirst(PayParam payParam, PayOrder payOrder) {
|
||||
|
||||
// 0. 处理支付完成情况(完成/退款)
|
||||
List<String> trades = Arrays.asList(PayStatusEnum.SUCCESS.getCode(), PayStatusEnum.CANCEL.getCode(),
|
||||
PayStatusEnum.PARTIAL_REFUND.getCode(), PayStatusEnum.REFUNDED.getCode());
|
||||
if (trades.contains(payOrder.getStatus())) {
|
||||
return PaymentBuilder.buildPayResultByPayOrder(payOrder);
|
||||
}
|
||||
|
||||
// 1.获取 异步支付 通道道,通过工厂生成对应的策略组(只包含异步支付的策略, 同步支付不再进行执行)
|
||||
PayWayParam payWayParam = this.getAsyncPayParam(payParam, payOrder);
|
||||
List<AbsPayStrategy> asyncStrategyList = PayStrategyFactory.create(Collections.singletonList(payWayParam));
|
||||
|
||||
// 2.初始化支付的参数
|
||||
for (AbsPayStrategy paymentStrategy : asyncStrategyList) {
|
||||
paymentStrategy.initPayParam(payOrder, payParam);
|
||||
}
|
||||
// 3.支付前准备
|
||||
this.doHandler(payOrder, asyncStrategyList, AbsPayStrategy::doBeforePayHandler, null);
|
||||
|
||||
// 4. 发起支付
|
||||
this.doHandler(payOrder, asyncStrategyList, AbsPayStrategy::doPayHandler, (strategyList, paymentObj) -> {
|
||||
// 发起支付成功进行的执行方法
|
||||
strategyList.forEach(AbsPayStrategy::doSuccessHandler);
|
||||
payOrderService.updateById(paymentObj);
|
||||
});
|
||||
|
||||
// 5. 获取支付记录信息
|
||||
// payOrder = payOrderService.findById(payOrder.getId()).orElseThrow(PayNotExistedException::new);
|
||||
|
||||
// 6. 组装返回参数
|
||||
return PaymentBuilder.buildPayResultByPayOrder(payOrder);
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行策略中不同的handler
|
||||
* @param payment 主支付对象
|
||||
* @param strategyList 策略列表
|
||||
* @param payMethod 执行支付的函数或者支付前的函数
|
||||
* @param successMethod 执行成功的函数
|
||||
*/
|
||||
private void doHandler(PayOrder payment, List<AbsPayStrategy> strategyList, Consumer<AbsPayStrategy> payMethod,
|
||||
PayStrategyConsumer<List<AbsPayStrategy>, PayOrder> successMethod) {
|
||||
// 执行策略操作,如支付前/支付时
|
||||
// 等同strategyList.forEach(payMethod.accept(PaymentStrategy))
|
||||
strategyList.forEach(payMethod);
|
||||
|
||||
// 执行操作成功的处理
|
||||
Optional.ofNullable(successMethod).ifPresent(fun -> fun.accept(strategyList, payment));
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取异步支付参数
|
||||
*/
|
||||
private PayWayParam getAsyncPayParam(PayParam payParam, PayOrder payOrder) {
|
||||
// 查询之前的支付方式
|
||||
String asyncPayChannel = payOrder.getAsyncPayChannel();
|
||||
PayOrderChannel payOrderChannel = payOrderChannelManager.findByOderIdAndChannel(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);
|
||||
payOrderService.saveOder(payOrder);
|
||||
// 构建支付订单扩展表并保存
|
||||
PayOrderExtra payOrderExtra = new PayOrderExtra()
|
||||
.setClientIp(payParam.getClientIp())
|
||||
.setDescription(payParam.getDescription());
|
||||
payOrderExtra.setId(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;
|
||||
}
|
||||
|
||||
/**
|
||||
* 校验支付状态,支付成功则返回,支付失败则抛出对应的异常
|
||||
*/
|
||||
private PayOrder getAndCheckByBusinessId(String businessId) {
|
||||
// 根据订单查询支付记录
|
||||
PayOrder payment = payOrderService.findByBusinessId(businessId).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;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -3,7 +3,6 @@ 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.core.channel.alipay.dao.AlipayConfigManager;
|
||||
import cn.bootx.platform.daxpay.core.channel.alipay.entity.AlipayConfig;
|
||||
import cn.bootx.platform.daxpay.core.channel.alipay.service.*;
|
||||
import cn.bootx.platform.daxpay.exception.pay.PayAmountAbnormalException;
|
||||
@@ -33,12 +32,8 @@ import static org.springframework.beans.factory.config.BeanDefinition.SCOPE_PROT
|
||||
@RequiredArgsConstructor
|
||||
public class AliPayStrategy extends AbsPayStrategy {
|
||||
|
||||
private final AlipayConfigManager alipayConfigManager;
|
||||
|
||||
private final AliPayOrderService aliPaymentService;
|
||||
|
||||
private final AlipaySyncService alipaySyncService;
|
||||
|
||||
private final AliPayService aliPayService;
|
||||
|
||||
private final AlipayConfigService alipayConfigService;
|
||||
@@ -80,12 +75,6 @@ public class AliPayStrategy extends AbsPayStrategy {
|
||||
// 检查并获取支付宝支付配置
|
||||
this.initAlipayConfig();
|
||||
aliPayService.validation(this.getPayWayParam(), alipayConfig);
|
||||
|
||||
// 如果没有显式传入同步回调地址, 使用默认配置
|
||||
// if (StrUtil.isBlank(aliPayParam.getReturnUrl())) {
|
||||
// aliPayParam.setReturnUrl(alipayConfig.getReturnUrl());
|
||||
// }
|
||||
this.initAlipayConfig();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -156,9 +145,9 @@ public class AliPayStrategy extends AbsPayStrategy {
|
||||
* 初始化支付宝配置信息
|
||||
*/
|
||||
private void initAlipayConfig() {
|
||||
// 检查并获取支付宝支付配置
|
||||
this.alipayConfig = null;
|
||||
alipayConfigService.initApiConfig(this.alipayConfig);
|
||||
// 获取并初始化支付宝支付配置
|
||||
this.alipayConfig = alipayConfigService.getConfig();
|
||||
alipayConfigService.initConfig(this.alipayConfig);
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -45,7 +45,7 @@ public abstract class AbsPayStrategy {
|
||||
}
|
||||
|
||||
/**
|
||||
* 支付前对处理 包含必要的校验以及对Payment对象的创建和保存操作
|
||||
* 支付前处理 包含必要的校验以及对当前通道支付订单的创建和保存操作
|
||||
*/
|
||||
public void doBeforePayHandler() {
|
||||
}
|
||||
@@ -56,7 +56,7 @@ public abstract class AbsPayStrategy {
|
||||
public abstract void doPayHandler();
|
||||
|
||||
/**
|
||||
* 支付成功的处理方式
|
||||
* 支付调起成功的处理方式
|
||||
*/
|
||||
public void doSuccessHandler() {
|
||||
}
|
||||
|
@@ -4,7 +4,6 @@ import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
import lombok.experimental.Accessors;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
@@ -14,20 +13,11 @@ import java.util.List;
|
||||
@Data
|
||||
@Accessors(chain = true)
|
||||
@Schema(title = "支付宝配置参数")
|
||||
public class AlipayConfigParam implements Serializable {
|
||||
|
||||
@Schema(description = "主键")
|
||||
private Long id;
|
||||
public class AlipayConfigParam {
|
||||
|
||||
@Schema(description = "名称")
|
||||
private String name;
|
||||
|
||||
@Schema(description = "商户编码")
|
||||
private String mchCode;
|
||||
|
||||
@Schema(description = "商户应用编码")
|
||||
private String mchAppCode;
|
||||
|
||||
@Schema(description = "支付宝商户appId")
|
||||
private String appId;
|
||||
|
||||
|
@@ -57,7 +57,7 @@ public class PayWayUtil {
|
||||
*/
|
||||
public boolean isNotSync(List<PayWayParam> payWayParams) {
|
||||
return payWayParams.stream()
|
||||
.map(PayWayParam::getPayChannel)
|
||||
.map(PayWayParam::getChannel)
|
||||
.noneMatch(PayChannelEnum.ASYNC_TYPE_CODE::contains);
|
||||
}
|
||||
|
||||
@@ -67,7 +67,7 @@ public class PayWayUtil {
|
||||
public PayWayParam getAsyncPayModeParam(PayParam payParam) {
|
||||
return payParam.getPayWays()
|
||||
.stream()
|
||||
.filter(payMode -> PayChannelEnum.ASYNC_TYPE_CODE.contains(payMode.getPayChannel()))
|
||||
.filter(payMode -> PayChannelEnum.ASYNC_TYPE_CODE.contains(payMode.getChannel()))
|
||||
.findFirst()
|
||||
.orElseThrow(() -> new PayFailureException("支付方式数据异常"));
|
||||
}
|
||||
@@ -129,7 +129,7 @@ public class PayWayUtil {
|
||||
public void validationAmount(List<PayWayParam> payModeList) {
|
||||
for (PayWayParam payWayParam : payModeList) {
|
||||
// 支付金额小于等于零
|
||||
if (payWayParam.getAmount() < 1) {
|
||||
if (payWayParam.getAmount() < 0) {
|
||||
throw new PayAmountAbnormalException();
|
||||
}
|
||||
}
|
||||
@@ -143,7 +143,7 @@ public class PayWayUtil {
|
||||
List<PayWayParam> payModeList = payParam.getPayWays();
|
||||
|
||||
long asyncPayModeCount = payModeList.stream()
|
||||
.map(PayWayParam::getPayChannel)
|
||||
.map(PayWayParam::getChannel)
|
||||
.map(PayChannelEnum::findByCode)
|
||||
.filter(PayChannelEnum.ASYNC_TYPE::contains)
|
||||
.count();
|
||||
|
Reference in New Issue
Block a user