ref 从支付策略中拆分出退款操作

This commit is contained in:
xxm1995
2023-07-05 17:06:07 +08:00
parent 79956ba3a8
commit d5b3dbfa62
38 changed files with 719 additions and 288 deletions

View File

@@ -34,7 +34,7 @@ public interface VoucherCode {
String LOG_UNFREEZE_BALANCE = "unfreeze";
/**
* 钱包日志-解冻并扣减余额
* 钱包日志-扣减并解冻余额
*/
String LOG_REDUCE_AND_UNFREEZE_BALANCE = "reduceAndUnfreeze";

View File

@@ -41,11 +41,6 @@ public interface WalletCode {
*/
String LOG_FREEZE_BALANCE = "freeze";
/**
* 钱包日志-解冻额度
*/
String LOG_UNFREEZE_BALANCE = "unfreeze";
/**
* 钱包日志-解冻并扣减余额
*/

View File

@@ -3,7 +3,7 @@ package cn.bootx.platform.daxpay.controller;
import cn.bootx.platform.common.core.rest.Res;
import cn.bootx.platform.common.core.rest.ResResult;
import cn.bootx.platform.daxpay.core.pay.service.PayCancelService;
import cn.bootx.platform.daxpay.core.pay.service.PayRefundService;
import cn.bootx.platform.daxpay.core.refund.service.PayRefundService;
import cn.bootx.platform.daxpay.core.pay.service.PayService;
import cn.bootx.platform.daxpay.core.pay.service.PaySyncService;
import cn.bootx.platform.daxpay.dto.pay.PayResult;

View File

@@ -4,7 +4,7 @@ import cn.bootx.platform.common.core.rest.PageResult;
import cn.bootx.platform.common.core.rest.Res;
import cn.bootx.platform.common.core.rest.ResResult;
import cn.bootx.platform.common.core.rest.param.PageParam;
import cn.bootx.platform.daxpay.core.refund.service.RefundRecordService;
import cn.bootx.platform.daxpay.core.refund.record.service.RefundRecordService;
import cn.bootx.platform.daxpay.dto.refund.RefundRecordDto;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;

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.paymodel.AliPayCode;
import cn.bootx.platform.daxpay.core.pay.local.AsyncRefundLocal;
import cn.bootx.platform.daxpay.core.refund.local.AsyncRefundLocal;
import cn.bootx.platform.daxpay.core.payment.entity.Payment;
import cn.bootx.platform.daxpay.exception.payment.PayFailureException;
import cn.hutool.core.util.IdUtil;

View File

@@ -8,6 +8,7 @@ import cn.bootx.platform.common.core.rest.param.PageParam;
import cn.bootx.platform.common.mybatisplus.util.MpUtil;
import cn.bootx.platform.daxpay.code.MchAndAppCode;
import cn.bootx.platform.daxpay.code.pay.PayChannelEnum;
import cn.bootx.platform.daxpay.code.paymodel.AliPayCode;
import cn.bootx.platform.daxpay.code.paymodel.AliPayWay;
import cn.bootx.platform.daxpay.core.channel.alipay.dao.AlipayConfigManager;
import cn.bootx.platform.daxpay.core.channel.alipay.entity.AlipayConfig;
@@ -20,12 +21,18 @@ 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.collection.CollUtil;
import cn.hutool.core.util.CharsetUtil;
import com.ijpay.alipay.AliPayApiConfig;
import com.ijpay.alipay.AliPayApiConfigKit;
import lombok.RequiredArgsConstructor;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import lombok.val;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;
/**
@@ -107,4 +114,50 @@ public class AlipayConfigService {
.collect(Collectors.toList());
}
/**
* 初始化IJPay服务,通过商户应用AppCode获取支付配置
*/
public void initApiConfigByMchAppCode(String mchAppCode){
val alipayConfig = alipayConfigManager.findByMchAppCode(mchAppCode)
.orElseThrow(() -> new DataNotExistException("支付配置不存在"));
this.initApiConfig(alipayConfig);
}
/**
* 初始化IJPay服务
*/
@SneakyThrows
public void initApiConfig(AlipayConfig alipayConfig) {
AliPayApiConfig aliPayApiConfig;
// 公钥
if (Objects.equals(alipayConfig.getAuthType(), AliPayCode.AUTH_TYPE_KEY)) {
aliPayApiConfig = AliPayApiConfig.builder()
.setAppId(alipayConfig.getAppId())
.setPrivateKey(alipayConfig.getPrivateKey())
.setAliPayPublicKey(alipayConfig.getAlipayPublicKey())
.setCharset(CharsetUtil.UTF_8)
.setServiceUrl(alipayConfig.getServerUrl())
.setSignType(alipayConfig.getSignType())
.build();
}
// 证书
else if (Objects.equals(alipayConfig.getAuthType(), AliPayCode.AUTH_TYPE_CART)) {
aliPayApiConfig = AliPayApiConfig.builder()
.setAppId(alipayConfig.getAppId())
.setPrivateKey(alipayConfig.getPrivateKey())
.setAppCertContent(alipayConfig.getAppCert())
.setAliPayCertContent(alipayConfig.getAlipayCert())
.setAliPayRootCertContent(alipayConfig.getAlipayRootCert())
.setCharset(CharsetUtil.UTF_8)
.setServiceUrl(alipayConfig.getServerUrl())
.setSignType(alipayConfig.getSignType())
.buildByCertContent();
}
else {
throw new BizException("支付宝认证方式不可为空");
}
AliPayApiConfigKit.setThreadLocalAliPayApiConfig(aliPayApiConfig);
}
}

View File

@@ -1,17 +1,20 @@
package cn.bootx.platform.daxpay.core.channel.voucher.dao;
import cn.bootx.platform.common.core.rest.param.PageParam;
import cn.bootx.platform.common.mybatisplus.base.MpDelEntity;
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.channel.voucher.entity.Voucher;
import cn.bootx.platform.daxpay.param.channel.voucher.VoucherParam;
import cn.bootx.platform.starter.auth.util.SecurityUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Repository;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
@@ -57,16 +60,24 @@ public class VoucherManager extends BaseManager<VoucherMapper, Voucher> {
/**
* 更改状态
*/
public void changeStatus(Long id, int status) {
this.lambdaUpdate().eq(MpIdEntity::getId, id).set(Voucher::getStatus, status).update();
public void changeStatus(Long id, String status) {
Long userIdOrDefaultId = SecurityUtil.getUserIdOrDefaultId();
this.lambdaUpdate().eq(MpIdEntity::getId, id)
.set(MpDelEntity::getLastModifiedTime, LocalDateTime.now())
.set(MpDelEntity::getLastModifier,userIdOrDefaultId)
.set(Voucher::getStatus, status).update();
}
/**
* 批量更改状态
*/
public void changeStatusBatch(List<Long> ids, int status) {
this.lambdaUpdate().in(MpIdEntity::getId, ids).set(Voucher::getStatus, status).update();
public void changeStatusBatch(List<Long> ids, String status) {
Long userIdOrDefaultId = SecurityUtil.getUserIdOrDefaultId();
this.lambdaUpdate().in(MpIdEntity::getId, ids)
.set(MpDelEntity::getLastModifiedTime, LocalDateTime.now())
.set(MpDelEntity::getLastModifier,userIdOrDefaultId)
.set(Voucher::getStatus, status).update();
}
}

View File

@@ -1,6 +1,5 @@
package cn.bootx.platform.daxpay.core.channel.voucher.service;
import cn.bootx.platform.common.core.exception.DataNotExistException;
import cn.bootx.platform.common.core.function.CollectorsFunction;
import cn.bootx.platform.common.core.util.BigDecimalUtil;
import cn.bootx.platform.common.core.util.LocalDateTimeUtil;
@@ -31,7 +30,6 @@ import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.function.Function;
import java.util.stream.Collectors;
/**
@@ -90,7 +88,7 @@ public class VoucherPayService {
}
/**
* 支付前冻结余额
* 支付前冻结余额 (异步组合支付环境下)
*/
public List<VoucherRecord> freezeBalance(BigDecimal amount, Payment payment, List<Voucher> vouchers){
List<VoucherLog> voucherLogs = new ArrayList<>();
@@ -106,19 +104,23 @@ public class VoucherPayService {
.setPaymentId(payment.getId())
.setBusinessId(payment.getBusinessId())
.setType(VoucherCode.LOG_FREEZE_BALANCE)
.setRemark(String.format("钱包预冻结金额 %.2f ", amount))
.setVoucherId(voucher.getId())
.setVoucherNo(voucher.getCardNo());
// 待支付额大于储值卡余额. 全冻结
// 待支付额大于储值卡余额. 储值卡的金额全冻结
if (BigDecimalUtil.compareTo(amount, balance) == 1) {
amount = amount.subtract(balance);
voucher.setFreezeBalance(balance);
voucherLog.setAmount(balance);
// 剩余待支付的金额
amount = amount.subtract(balance);
}
else {
amount = BigDecimal.ZERO;
// 待支付额小于或储值卡余额, 冻结待支付的金额
voucher.setFreezeBalance(amount);
voucherLog.setAmount(amount);
// 待支付的金额扣减完毕, 值设置为零
amount = BigDecimal.ZERO;
}
voucherRecords.add(new VoucherRecord().setCardNo(voucher.getCardNo()).setAmount(voucher.getFreezeBalance()));
voucherLogs.add(voucherLog);
@@ -129,35 +131,30 @@ public class VoucherPayService {
}
/**
* 支付成功, 对冻结的金额进行扣款
* 支付成功, 对冻结的金额进行扣款 (异步组合支付环境下)
*/
public void paySuccess(Long paymentId){
voucherPaymentManager.findByPaymentId(paymentId).ifPresent(voucherPayment -> {
List<VoucherRecord> voucherRecords = voucherPayment.getVoucherRecords();
List<String> cardNoList = voucherRecords.stream()
.map(VoucherRecord::getCardNo)
.collect(Collectors.toList());
List<Voucher> vouchers = voucherManager.findByCardNoList(cardNoList);
List<VoucherLog> voucherLogs = new ArrayList<>();
for (Voucher voucher : vouchers) {
// 待支付余额为零, 不在处理后面的储值卡
if (BigDecimalUtil.compareTo(amount, BigDecimal.ZERO) < 1) {
break;
}
BigDecimal balance = voucher.getBalance();
// 余额扣减
voucher.setBalance(voucher.getBalance().subtract(voucher.getFreezeBalance()))
.setFreezeBalance(BigDecimal.ZERO);
// 日志
VoucherLog voucherLog = new VoucherLog()
.setPaymentId(payment.getId())
.setBusinessId(payment.getBusinessId())
.setType(VoucherCode.LOG_FREEZE_BALANCE)
.setPaymentId(voucherPayment.getPaymentId())
.setBusinessId(voucherPayment.getBusinessId())
.setType(VoucherCode.LOG_REDUCE_AND_UNFREEZE_BALANCE)
.setRemark(String.format("钱包扣款金额 %.2f ", voucherPayment.getAmount()))
.setVoucherId(voucher.getId())
.setAmount(voucher.getFreezeBalance())
.setVoucherNo(voucher.getCardNo());
// 待支付额大于储值卡余额. 全冻结
if (BigDecimalUtil.compareTo(amount, balance) == 1) {
amount = amount.subtract(balance);
voucher.setFreezeBalance(balance);
voucherLog.setAmount(balance);
} else {
voucher.setFreezeBalance(balance.subtract(amount));
voucherLog.setAmount(amount);
}
voucherLogs.add(voucherLog);
}
voucherManager.updateAllById(vouchers);
@@ -183,23 +180,26 @@ public class VoucherPayService {
.setPaymentId(payment.getId())
.setBusinessId(payment.getBusinessId())
.setType(VoucherCode.LOG_PAY)
.setRemark(String.format("钱包支付金额 %.2f ", amount))
.setVoucherId(voucher.getId())
.setVoucherNo(voucher.getCardNo());
// 记录当前卡扣了多少钱
BigDecimal amountRecord;
// 待支付额大于储值卡余额. 全扣光
// 待支付额大于储值卡余额. 储值卡金额全扣光
if (BigDecimalUtil.compareTo(amount, balance) == 1) {
amountRecord = voucher.getBalance();
amount = amount.subtract(balance);
voucher.setBalance(BigDecimal.ZERO);
voucherLog.setAmount(balance);
amount = amount.subtract(balance);
}
else {
// 待支付额小于或储值卡余额, 储值卡余额-待支付额
amountRecord = balance.subtract(amount);
amount = BigDecimal.ZERO;
voucher.setBalance(balance.subtract(amount));
voucherLog.setAmount(amount);
// 支付完毕, 待支付金额归零
amount = BigDecimal.ZERO;
}
voucherLogs.add(voucherLog);
voucherRecords.add(new VoucherRecord().setCardNo(voucher.getCardNo()).setAmount(amountRecord));
@@ -211,35 +211,29 @@ public class VoucherPayService {
/**
* 取消支付, 可配置解除冻结金额
* @param freeze 是否是冻结模式, 是的话对冻结金额进行解冻, 不是的话返还扣减的金额
* 取消支付,解除冻结的额度, 只有在异步支付中才会发生取消操作
*/
public void close(Long paymentId, boolean freeze) {
VoucherPayment voucherPayment = voucherPaymentManager.findByPaymentId(paymentId).orElseThrow(DataNotExistException::new);
// 查找支付记录日志
List<VoucherLog> voucherLogs = voucherLogManager.findByPaymentIdAndType(paymentId, VoucherCode.LOG_PAY);
// 查出关联的储值卡
Map<Long, VoucherLog> voucherLogMap = voucherLogs.stream()
.collect(Collectors.toMap(VoucherLog::getVoucherId, Function.identity()));
List<Voucher> vouchers = voucherManager.findAllByIds(voucherLogMap.keySet());
// 执行退款并记录日志
List<VoucherLog> logs = new ArrayList<>();
for (Voucher voucher : vouchers) {
VoucherLog voucherLog = voucherLogMap.get(voucher.getId());
voucher.setBalance(voucher.getBalance().add(voucherLog.getAmount()));
VoucherLog log = new VoucherLog().setAmount(voucherLog.getAmount())
.setPaymentId(paymentId)
.setBusinessId(voucherLog.getBusinessId())
.setVoucherId(voucher.getId())
.setVoucherNo(voucher.getCardNo())
.setType(VoucherCode.LOG_CLOSE_PAY);
logs.add(log);
}
voucherManager.updateAllById(vouchers);
voucherLogManager.saveAll(logs);
public void close(Long paymentId) {
voucherPaymentManager.findByPaymentId(paymentId).ifPresent(voucherPayment -> {
List<String> cardNoList = voucherPayment.getVoucherRecords().stream()
.map(VoucherRecord::getCardNo)
.collect(Collectors.toList());
List<Voucher> vouchers = voucherManager.findByCardNoList(cardNoList);
// 解冻额度和记录日志
List<VoucherLog> logs = new ArrayList<>();
for (Voucher voucher : vouchers) {
VoucherLog log = new VoucherLog().setAmount(voucher.getFreezeBalance())
.setPaymentId(paymentId)
.setBusinessId(voucherPayment.getBusinessId())
.setVoucherId(voucher.getId())
.setRemark(String.format("取消支付金额 %.2f ", voucher.getFreezeBalance()))
.setVoucherNo(voucher.getCardNo())
.setType(VoucherCode.LOG_CLOSE_PAY);
logs.add(log);
}
voucherManager.updateAllById(vouchers);
voucherLogManager.saveAll(logs);
});
}
/**

View File

@@ -119,10 +119,9 @@ public class WalletPayService {
}
/**
* 取消支付, 可配置解除冻结金额
* @param freeze 是否是冻结模式, 是的话对冻结金额进行解冻, 不是的话返还扣减的金额
* 取消支付,解除冻结的额度, 只有在异步支付中才会发生取消操作
*/
public void close(Long paymentId,boolean freeze) {
public void close(Long paymentId) {
// 钱包支付记录
walletPaymentManager.findByPaymentId(paymentId).ifPresent(walletPayment -> {
Optional<Wallet> walletOpt = walletManager.findById(walletPayment.getWalletId());
@@ -134,21 +133,14 @@ public class WalletPayService {
walletPayment.setPayStatus(PayStatusCode.TRADE_CANCEL);
walletPaymentManager.save(walletPayment);
// 是否在进行了额度冻结
if (freeze){
// 解冻金额
walletManager.unfreezeBalance(wallet.getId(), walletPayment.getAmount());
} else {
// 金额返还
walletManager.increaseBalance(wallet.getId(), walletPayment.getAmount());
}
// 记录日志
WalletLog walletLog = new WalletLog().setAmount(walletPayment.getAmount())
.setPaymentId(walletPayment.getPaymentId())
.setWalletId(wallet.getId())
.setUserId(wallet.getUserId())
.setType(freeze?WalletCode.LOG_CLOSE_PAY:WalletCode.LOG_UNFREEZE_BALANCE)
.setType(WalletCode.LOG_CLOSE_PAY)
.setRemark(String.format("取消支付金额 %.2f ", walletPayment.getAmount()))
.setOperationSource(WalletCode.OPERATION_SOURCE_SYSTEM)
.setBusinessId(walletPayment.getBusinessId());

View File

@@ -2,7 +2,7 @@ package cn.bootx.platform.daxpay.core.channel.wechat.service;
import cn.bootx.platform.common.spring.exception.RetryableException;
import cn.bootx.platform.daxpay.code.paymodel.WeChatPayCode;
import cn.bootx.platform.daxpay.core.pay.local.AsyncRefundLocal;
import cn.bootx.platform.daxpay.core.refund.local.AsyncRefundLocal;
import cn.bootx.platform.daxpay.core.payment.entity.Payment;
import cn.bootx.platform.daxpay.core.channel.wechat.entity.WeChatPayConfig;
import cn.bootx.platform.daxpay.core.channel.wechat.entity.WeChatPayment;

View File

@@ -8,8 +8,8 @@ import cn.bootx.platform.daxpay.core.payment.entity.Payment;
import cn.bootx.platform.daxpay.dto.pay.PayResult;
import cn.bootx.platform.daxpay.dto.payment.PayChannelInfo;
import cn.bootx.platform.daxpay.dto.payment.RefundableInfo;
import cn.bootx.platform.daxpay.param.pay.PayWayParam;
import cn.bootx.platform.daxpay.param.pay.PayParam;
import cn.bootx.platform.daxpay.param.pay.PayWayParam;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.extra.servlet.ServletUtil;

View File

@@ -32,7 +32,7 @@ public class PayStrategyFactory {
public static AbsPayStrategy create(PayWayParam payWayParam) {
AbsPayStrategy strategy = null;
PayChannelEnum channelEnum = findByCode(payWayParam.getPayChannel());
PayChannelEnum channelEnum = PayChannelEnum.findByCode(payWayParam.getPayChannel());
switch (channelEnum) {
case ALI:
strategy = SpringUtil.getBean(AliPayStrategy.class);
@@ -50,7 +50,7 @@ public class PayStrategyFactory {
strategy = SpringUtil.getBean(WalletPayStrategy.class);
break;
case VOUCHER:
strategy = SpringUtil.getBean(VoucherStrategy.class);
strategy = SpringUtil.getBean(VoucherPayStrategy.class);
break;
case CREDIT_CARD:
break;
@@ -92,13 +92,13 @@ public class PayStrategyFactory {
// 同步支付
List<PayWayParam> syncPayWayParamList = payWayParamList.stream()
.filter(Objects::nonNull)
.filter(payModeParam -> !ASYNC_TYPE.contains(payModeParam.getPayChannel()))
.filter(payModeParam -> !ASYNC_TYPE_CODE.contains(payModeParam.getPayChannel()))
.collect(Collectors.toList());
// 异步支付
List<PayWayParam> asyncPayWayParamList = payWayParamList.stream()
.filter(Objects::nonNull)
.filter(payModeParam -> ASYNC_TYPE.contains(payModeParam.getPayChannel()))
.filter(payModeParam -> ASYNC_TYPE_CODE.contains(payModeParam.getPayChannel()))
.collect(Collectors.toList());
List<PayWayParam> sortList = new ArrayList<>(payWayParamList.size());

View File

@@ -93,11 +93,6 @@ public abstract class AbsPayStrategy {
*/
public abstract void doCloseHandler();
/**
* 退款
*/
public abstract void doRefundHandler();
/**
* 异步支付单与支付网关进行状态比对
* @see PaySyncStatus

View File

@@ -1,37 +1,26 @@
package cn.bootx.platform.daxpay.core.pay.strategy;
import cn.bootx.platform.common.core.exception.BizException;
import cn.bootx.platform.common.core.util.BigDecimalUtil;
import cn.bootx.platform.daxpay.code.pay.PayChannelEnum;
import cn.bootx.platform.daxpay.code.paymodel.AliPayCode;
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.pay.exception.ExceptionInfo;
import cn.bootx.platform.daxpay.core.pay.func.AbsPayStrategy;
import cn.bootx.platform.daxpay.core.pay.result.PaySyncResult;
import cn.bootx.platform.daxpay.core.payment.service.PaymentService;
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.AliPayCancelService;
import cn.bootx.platform.daxpay.core.channel.alipay.service.AliPayService;
import cn.bootx.platform.daxpay.core.channel.alipay.service.AliPaymentService;
import cn.bootx.platform.daxpay.core.channel.alipay.service.AlipaySyncService;
import cn.bootx.platform.daxpay.exception.payment.PayAmountAbnormalException;
import cn.bootx.platform.daxpay.exception.payment.PayFailureException;
import cn.bootx.platform.daxpay.param.pay.PayWayParam;
import cn.bootx.platform.daxpay.param.channel.alipay.AliPayParam;
import cn.hutool.core.util.CharsetUtil;
import cn.bootx.platform.daxpay.param.pay.PayWayParam;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONException;
import cn.hutool.json.JSONUtil;
import com.ijpay.alipay.AliPayApiConfig;
import com.ijpay.alipay.AliPayApiConfigKit;
import lombok.RequiredArgsConstructor;
import lombok.SneakyThrows;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
import java.math.BigDecimal;
import java.util.Map;
import java.util.Objects;
import static org.springframework.beans.factory.config.BeanDefinition.SCOPE_PROTOTYPE;
@@ -52,12 +41,10 @@ public class AliPayStrategy extends AbsPayStrategy {
private final AliPayService aliPayService;
private final AlipayConfigService alipayConfigService;
private final AliPayCancelService aliPayCancelService;
private final AlipayConfigManager alipayConfigManager;
private final PaymentService paymentService;
private AlipayConfig alipayConfig;
private AliPayParam aliPayParam;
@@ -163,17 +150,6 @@ public class AliPayStrategy extends AbsPayStrategy {
aliPaymentService.updateClose(this.getPayment().getId());
}
/**
* 退款
*/
@Override
public void doRefundHandler() {
this.initAlipayConfig(this.getPayParam().getMchAppCode());
aliPayCancelService.refund(this.getPayment(), this.getPayWayParam().getAmount());
aliPaymentService.updatePayRefund(this.getPayment().getId(), this.getPayWayParam().getAmount());
paymentService.updateRefundSuccess(this.getPayment(), this.getPayWayParam().getAmount(), PayChannelEnum.ALI);
}
/**
* 异步支付单与支付网关进行状态比对
*/
@@ -188,46 +164,7 @@ public class AliPayStrategy extends AbsPayStrategy {
*/
private void initAlipayConfig(String mchAppCode) {
// 检查并获取支付宝支付配置
this.alipayConfig = alipayConfigManager.findByMchAppCode(mchAppCode)
.orElseThrow(() -> new PayFailureException("支付配置不存在"));
this.initApiConfig(this.alipayConfig);
}
/**
* 初始化IJPay 服务
*/
@SneakyThrows
private void initApiConfig(AlipayConfig alipayConfig) {
AliPayApiConfig aliPayApiConfig;
// 公钥
if (Objects.equals(alipayConfig.getAuthType(), AliPayCode.AUTH_TYPE_KEY)) {
aliPayApiConfig = AliPayApiConfig.builder()
.setAppId(alipayConfig.getAppId())
.setPrivateKey(alipayConfig.getPrivateKey())
.setAliPayPublicKey(alipayConfig.getAlipayPublicKey())
.setCharset(CharsetUtil.UTF_8)
.setServiceUrl(alipayConfig.getServerUrl())
.setSignType(alipayConfig.getSignType())
.build();
}
// 证书
else if (Objects.equals(alipayConfig.getAuthType(), AliPayCode.AUTH_TYPE_CART)) {
aliPayApiConfig = AliPayApiConfig.builder()
.setAppId(alipayConfig.getAppId())
.setPrivateKey(alipayConfig.getPrivateKey())
.setAppCertContent(alipayConfig.getAppCert())
.setAliPayCertContent(alipayConfig.getAlipayCert())
.setAliPayRootCertContent(alipayConfig.getAlipayRootCert())
.setCharset(CharsetUtil.UTF_8)
.setServiceUrl(alipayConfig.getServerUrl())
.setSignType(alipayConfig.getSignType())
.buildByCertContent();
}
else {
throw new BizException("支付宝认证方式不可为空");
}
AliPayApiConfigKit.setThreadLocalAliPayApiConfig(aliPayApiConfig);
alipayConfigService.initApiConfigByMchAppCode(mchAppCode);
}
}

View File

@@ -68,13 +68,4 @@ public class CashPayStrategy extends AbsPayStrategy {
cashService.close(this.getPayment().getId());
}
/**
* 退款
*/
@Override
public void doRefundHandler() {
cashService.refund(this.getPayment().getId(), this.getPayWayParam().getAmount());
paymentService.updateRefundSuccess(this.getPayment(), this.getPayWayParam().getAmount(), PayChannelEnum.CASH);
}
}

View File

@@ -42,9 +42,4 @@ public class UnionPayStrategy extends AbsPayStrategy {
}
@Override
public void doRefundHandler() {
}
}

View File

@@ -1,12 +1,11 @@
package cn.bootx.platform.daxpay.core.pay.strategy;
import cn.bootx.platform.daxpay.code.pay.PayChannelEnum;
import cn.bootx.platform.daxpay.core.channel.voucher.entity.VoucherRecord;
import cn.bootx.platform.daxpay.core.pay.func.AbsPayStrategy;
import cn.bootx.platform.daxpay.core.payment.service.PaymentService;
import cn.bootx.platform.daxpay.core.channel.voucher.entity.Voucher;
import cn.bootx.platform.daxpay.core.channel.voucher.entity.VoucherRecord;
import cn.bootx.platform.daxpay.core.channel.voucher.service.VoucherPayService;
import cn.bootx.platform.daxpay.core.channel.voucher.service.VoucherPaymentService;
import cn.bootx.platform.daxpay.core.pay.func.AbsPayStrategy;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Scope;
@@ -26,14 +25,12 @@ import static org.springframework.beans.factory.config.BeanDefinition.SCOPE_PROT
@Scope(SCOPE_PROTOTYPE)
@Service
@RequiredArgsConstructor
public class VoucherStrategy extends AbsPayStrategy {
public class VoucherPayStrategy extends AbsPayStrategy {
private final VoucherPayService voucherPayService;
private final VoucherPaymentService voucherPaymentService;
private final PaymentService paymentService;
private List<Voucher> vouchers;
@Override
@@ -80,19 +77,8 @@ public class VoucherStrategy extends AbsPayStrategy {
*/
@Override
public void doCloseHandler() {
voucherPayService.close(this.getPayment().getId(), this.getPayment().isAsyncPayMode());
voucherPayService.close(this.getPayment().getId());
voucherPaymentService.updateClose(this.getPayment().getId());
}
/**
* 退款
*/
@Override
public void doRefundHandler() {
voucherPayService.refund(this.getPayment().getId(), this.getPayWayParam().getAmount(),null);
voucherPaymentService.updateRefund(this.getPayment().getId(), this.getPayWayParam().getAmount());
paymentService.updateRefundSuccess(this.getPayment(), this.getPayWayParam().getAmount(),
PayChannelEnum.VOUCHER);
}
}

View File

@@ -8,7 +8,6 @@ import cn.bootx.platform.daxpay.core.channel.wallet.service.WalletPayService;
import cn.bootx.platform.daxpay.core.channel.wallet.service.WalletPaymentService;
import cn.bootx.platform.daxpay.core.channel.wallet.service.WalletQueryService;
import cn.bootx.platform.daxpay.core.pay.func.AbsPayStrategy;
import cn.bootx.platform.daxpay.core.payment.service.PaymentService;
import cn.bootx.platform.daxpay.exception.payment.PayFailureException;
import cn.bootx.platform.daxpay.exception.waller.WalletBannedException;
import cn.bootx.platform.daxpay.exception.waller.WalletLackOfBalanceException;
@@ -41,8 +40,6 @@ public class WalletPayStrategy extends AbsPayStrategy {
private final WalletQueryService walletQueryService;
private final PaymentService paymentService;
private Wallet wallet;
@Override
@@ -107,21 +104,8 @@ public class WalletPayStrategy extends AbsPayStrategy {
*/
@Override
public void doCloseHandler() {
if (this.getPayment().isAsyncPayMode()){
walletPayService.paySuccess(this.getPayment().getId());
}
walletPayService.close(this.getPayment().getId(),this.getPayment().isAsyncPayMode());
walletPayService.close(this.getPayment().getId());
walletPaymentService.updateClose(this.getPayment().getId());
}
/**
* 退款
*/
@Override
public void doRefundHandler() {
walletPayService.refund(this.getPayment().getId(), this.getPayWayParam().getAmount());
walletPaymentService.updateRefund(this.getPayment().getId(), this.getPayWayParam().getAmount());
paymentService.updateRefundSuccess(this.getPayment(), this.getPayWayParam().getAmount(), PayChannelEnum.WALLET);
}
}

View File

@@ -3,22 +3,19 @@ package cn.bootx.platform.daxpay.core.pay.strategy;
import cn.bootx.platform.common.core.util.BigDecimalUtil;
import cn.bootx.platform.daxpay.code.pay.PayChannelEnum;
import cn.bootx.platform.daxpay.code.paymodel.WeChatPayCode;
import cn.bootx.platform.daxpay.core.pay.exception.ExceptionInfo;
import cn.bootx.platform.daxpay.core.pay.func.AbsPayStrategy;
import cn.bootx.platform.daxpay.core.pay.result.PaySyncResult;
import cn.bootx.platform.daxpay.core.payment.service.PaymentService;
import cn.bootx.platform.daxpay.core.channel.wechat.dao.WeChatPayConfigManager;
import cn.bootx.platform.daxpay.core.channel.wechat.dao.WeChatPaymentManager;
import cn.bootx.platform.daxpay.core.channel.wechat.entity.WeChatPayConfig;
import cn.bootx.platform.daxpay.core.channel.wechat.entity.WeChatPayment;
import cn.bootx.platform.daxpay.core.channel.wechat.service.WeChatPayCancelService;
import cn.bootx.platform.daxpay.core.channel.wechat.service.WeChatPayService;
import cn.bootx.platform.daxpay.core.channel.wechat.service.WeChatPaySyncService;
import cn.bootx.platform.daxpay.core.channel.wechat.service.WeChatPaymentService;
import cn.bootx.platform.daxpay.core.pay.exception.ExceptionInfo;
import cn.bootx.platform.daxpay.core.pay.func.AbsPayStrategy;
import cn.bootx.platform.daxpay.core.pay.result.PaySyncResult;
import cn.bootx.platform.daxpay.exception.payment.PayAmountAbnormalException;
import cn.bootx.platform.daxpay.exception.payment.PayFailureException;
import cn.bootx.platform.daxpay.param.pay.PayWayParam;
import cn.bootx.platform.daxpay.param.channel.wechat.WeChatPayParam;
import cn.bootx.platform.daxpay.param.pay.PayWayParam;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONException;
import cn.hutool.json.JSONUtil;
@@ -49,14 +46,10 @@ public class WeChatPayStrategy extends AbsPayStrategy {
private final WeChatPaymentService weChatPaymentService;
private final WeChatPaymentManager weChatPaymentManager;
private final WeChatPayCancelService weChatPayCancelService;
private final WeChatPaySyncService weChatPaySyncService;
private final PaymentService paymentService;
private WeChatPayConfig weChatPayConfig;
private WeChatPayParam weChatPayParam;
@@ -162,20 +155,6 @@ public class WeChatPayStrategy extends AbsPayStrategy {
weChatPaymentService.updateClose(this.getPayment().getId());
}
/**
* 退款
*/
@Override
public void doRefundHandler() {
this.initWeChatPayConfig(this.getPayParam().getMchAppCode());
WeChatPayment weChatPayment = weChatPaymentManager.findByPaymentId(this.getPayment().getId())
.orElseThrow(() -> new PayFailureException("微信支付记录不存在"));
weChatPayCancelService.refund(this.getPayment(), weChatPayment, this.getPayWayParam().getAmount(),
this.weChatPayConfig);
weChatPaymentService.updatePayRefund(weChatPayment, this.getPayWayParam().getAmount());
paymentService.updateRefundSuccess(this.getPayment(), this.getPayWayParam().getAmount(), PayChannelEnum.WECHAT);
}
/**
* 异步支付单与支付网关进行状态比对
*/

View File

@@ -0,0 +1,119 @@
package cn.bootx.platform.daxpay.core.refund.factory;
import cn.bootx.platform.daxpay.code.pay.PayChannelEnum;
import cn.bootx.platform.daxpay.core.refund.func.AbsPayRefundStrategy;
import cn.bootx.platform.daxpay.core.refund.strategy.*;
import cn.bootx.platform.daxpay.exception.payment.PayUnsupportedMethodException;
import cn.bootx.platform.daxpay.param.refund.RefundModeParam;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.extra.spring.SpringUtil;
import lombok.val;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;
import static cn.bootx.platform.daxpay.code.pay.PayChannelEnum.ASYNC_TYPE_CODE;
/**
* 退款策略工厂
* @author xxm
* @since 2023/7/4
*/
public class PayRefundStrategyFactory {
/**
* 根据传入的支付通道创建策略
* @return 支付策略
*/
public static AbsPayRefundStrategy create(RefundModeParam refundModeParam) {
AbsPayRefundStrategy strategy = null;
PayChannelEnum channelEnum = PayChannelEnum.findByCode(refundModeParam.getPayChannel());
switch (channelEnum) {
case ALI:
strategy = SpringUtil.getBean(AliPayRefundStrategy.class);
break;
case WECHAT:
strategy = SpringUtil.getBean(WeChatPayRefundStrategy.class);
break;
case UNION_PAY:
strategy = SpringUtil.getBean(UnionPayRefundStrategy.class);
break;
case CASH:
strategy = SpringUtil.getBean(CashPayRefundStrategy.class);
break;
case WALLET:
strategy = SpringUtil.getBean(WalletPayRefundStrategy.class);
break;
case VOUCHER:
strategy = SpringUtil.getBean(VoucherPayRefundStrategy.class);
break;
case CREDIT_CARD:
break;
case APPLE_PAY:
break;
default:
throw new PayUnsupportedMethodException();
}
// noinspection ConstantConditions
strategy.setRefundModeParam(refundModeParam);
return strategy;
}
/**
* 根据传入的支付类型批量创建策略, 异步支付在后面
*/
public static List<AbsPayRefundStrategy> createDesc(List<RefundModeParam> refundModeParams) {
return create(refundModeParams, true);
}
/**
* 根据传入的支付类型批量创建策略, 默认异步支付在前面
*/
public static List<AbsPayRefundStrategy> create(List<RefundModeParam> refundModeParams) {
return create(refundModeParams, false);
}
/**
* 根据传入的支付类型批量创建策略
* @param refundModeParams 支付类型
* @return 支付策略
*/
private static List<AbsPayRefundStrategy> create(List<RefundModeParam> refundModeParams, boolean description) {
if (CollectionUtil.isEmpty(refundModeParams)) {
return Collections.emptyList();
}
List<AbsPayRefundStrategy> list = new ArrayList<>(refundModeParams.size());
// 同步支付
val syncRefundModeParams = refundModeParams.stream()
.filter(Objects::nonNull)
.filter(payModeParam -> !ASYNC_TYPE_CODE.contains(payModeParam.getPayChannel()))
.collect(Collectors.toList());
// 异步支付
val asyncRefundModeParams = refundModeParams.stream()
.filter(Objects::nonNull)
.filter(payModeParam -> ASYNC_TYPE_CODE.contains(payModeParam.getPayChannel()))
.collect(Collectors.toList());
List<RefundModeParam> sortList = new ArrayList<>(refundModeParams.size());
// 异步在后面
if (description) {
sortList.addAll(syncRefundModeParams);
sortList.addAll(asyncRefundModeParams);
}
else {
sortList.addAll(asyncRefundModeParams);
sortList.addAll(syncRefundModeParams);
}
// 此处有一个根据Type的反转排序
sortList.stream().filter(Objects::nonNull).forEach(payMode -> list.add(create(payMode)));
return list;
}
}

View File

@@ -0,0 +1,61 @@
package cn.bootx.platform.daxpay.core.refund.func;
import cn.bootx.platform.daxpay.code.pay.PayChannelEnum;
import cn.bootx.platform.daxpay.core.pay.exception.ExceptionInfo;
import cn.bootx.platform.daxpay.core.payment.entity.Payment;
import cn.bootx.platform.daxpay.param.refund.RefundModeParam;
import cn.bootx.platform.daxpay.param.refund.RefundParam;
import lombok.Getter;
import lombok.Setter;
/**
* 抽象支付退款策略基类
*
* @author xxm
* @since 2020/12/11
*/
@Getter
@Setter
public abstract class AbsPayRefundStrategy {
/** 支付对象 */
private Payment payment = null;
/** 退款参数 */
private RefundParam refundParam = null;
/** 当前支付方式退款参数 退款参参数中的与这个不一致, 以这个为准 */
private RefundModeParam refundModeParam = null;
/**
* 策略标识
* @see PayChannelEnum
*/
public abstract PayChannelEnum getType();
/**
* 初始化支付的参数
*/
public void initPayParam(Payment payment, RefundParam refundParam) {
this.payment = payment;
this.refundParam = refundParam;
}
/**
* 退款前对处理 包含必要的校验以及对Payment对象的创建和保存操作
*/
public void doBeforeRefundHandler() {
}
/**
* 退款操作
*/
public abstract void doRefundHandler();
/**
* 退款失败的处理方式
*/
public void doErrorHandler(ExceptionInfo exceptionInfo) {
}
}

View File

@@ -0,0 +1,17 @@
package cn.bootx.platform.daxpay.core.refund.func;
import cn.bootx.platform.daxpay.core.payment.entity.Payment;
import java.util.List;
/**
* 支付退款策略接口
* @author xxm
* @since 2023/7/5
*/
@FunctionalInterface
public interface PayRefundStrategyConsumer<T extends List<AbsPayRefundStrategy>, S extends Payment> {
void accept(T t, S s);
}

View File

@@ -1,4 +1,4 @@
package cn.bootx.platform.daxpay.core.pay.local;
package cn.bootx.platform.daxpay.core.refund.local;
import com.alibaba.ttl.TransmittableThreadLocal;

View File

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

View File

@@ -1,10 +1,10 @@
package cn.bootx.platform.daxpay.core.refund.dao;
package cn.bootx.platform.daxpay.core.refund.record.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.refund.entity.RefundRecord;
import cn.bootx.platform.daxpay.core.refund.record.entity.RefundRecord;
import cn.bootx.platform.daxpay.dto.refund.RefundRecordDto;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import lombok.RequiredArgsConstructor;

View File

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

View File

@@ -1,10 +1,10 @@
package cn.bootx.platform.daxpay.core.refund.entity;
package cn.bootx.platform.daxpay.core.refund.record.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.pay.PayStatusCode;
import cn.bootx.platform.daxpay.core.refund.convert.RefundConvert;
import cn.bootx.platform.daxpay.core.refund.record.convert.RefundConvert;
import cn.bootx.platform.daxpay.dto.payment.RefundableInfo;
import cn.bootx.platform.daxpay.dto.refund.RefundRecordDto;
import com.baomidou.mybatisplus.annotation.TableField;

View File

@@ -1,11 +1,11 @@
package cn.bootx.platform.daxpay.core.refund.service;
package cn.bootx.platform.daxpay.core.refund.record.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.refund.dao.RefundRecordManager;
import cn.bootx.platform.daxpay.core.refund.entity.RefundRecord;
import cn.bootx.platform.daxpay.core.refund.record.dao.RefundRecordManager;
import cn.bootx.platform.daxpay.core.refund.record.entity.RefundRecord;
import cn.bootx.platform.daxpay.dto.refund.RefundRecordDto;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import lombok.RequiredArgsConstructor;

View File

@@ -1,24 +1,21 @@
package cn.bootx.platform.daxpay.core.pay.service;
package cn.bootx.platform.daxpay.core.refund.service;
import cn.bootx.platform.common.core.util.BigDecimalUtil;
import cn.bootx.platform.common.spring.util.WebServletUtil;
import cn.bootx.platform.daxpay.code.pay.PayStatusCode;
import cn.bootx.platform.daxpay.core.pay.builder.PaymentBuilder;
import cn.bootx.platform.daxpay.core.pay.factory.PayStrategyFactory;
import cn.bootx.platform.daxpay.core.pay.func.AbsPayStrategy;
import cn.bootx.platform.daxpay.core.pay.func.PayStrategyConsumer;
import cn.bootx.platform.daxpay.core.pay.local.AsyncRefundLocal;
import cn.bootx.platform.daxpay.core.payment.dao.PaymentManager;
import cn.bootx.platform.daxpay.core.payment.entity.Payment;
import cn.bootx.platform.daxpay.core.payment.service.PaymentService;
import cn.bootx.platform.daxpay.core.refund.dao.RefundRecordManager;
import cn.bootx.platform.daxpay.core.refund.entity.RefundRecord;
import cn.bootx.platform.daxpay.core.refund.factory.PayRefundStrategyFactory;
import cn.bootx.platform.daxpay.core.refund.func.AbsPayRefundStrategy;
import cn.bootx.platform.daxpay.core.refund.func.PayRefundStrategyConsumer;
import cn.bootx.platform.daxpay.core.refund.local.AsyncRefundLocal;
import cn.bootx.platform.daxpay.core.refund.record.dao.RefundRecordManager;
import cn.bootx.platform.daxpay.core.refund.record.entity.RefundRecord;
import cn.bootx.platform.daxpay.dto.payment.RefundableInfo;
import cn.bootx.platform.daxpay.exception.payment.PayAmountAbnormalException;
import cn.bootx.platform.daxpay.exception.payment.PayFailureException;
import cn.bootx.platform.daxpay.exception.payment.PayUnsupportedMethodException;
import cn.bootx.platform.daxpay.param.pay.PayWayParam;
import cn.bootx.platform.daxpay.param.pay.PayParam;
import cn.bootx.platform.daxpay.param.refund.RefundModeParam;
import cn.bootx.platform.daxpay.param.refund.RefundParam;
import cn.bootx.platform.starter.auth.util.SecurityUtil;
@@ -67,13 +64,12 @@ public class PayRefundService {
@Transactional(rollbackFor = Exception.class)
public void refund(RefundParam refundParam) {
Payment payment = paymentService.findByBusinessId(refundParam.getBusinessId())
.orElseThrow(() -> new PayFailureException("未找到支付单"));
this.refundPayment(payment, refundParam.getRefundModeParams());
.orElseThrow(() -> new PayFailureException("未找到支付单"));
this.refundPayment(payment, refundParam);
}
/**
* 根据业务id取消支付记录
* 根据业务ID全额退款
*/
@Transactional(rollbackFor = Exception.class)
public void refundByBusinessId(String businessId) {
@@ -83,14 +79,19 @@ public class PayRefundService {
.stream()
.map(o -> new RefundModeParam().setPayChannel(o.getPayChannel()).setAmount(o.getAmount()))
.collect(Collectors.toList());
this.refundPayment(payment, refundModeParams);
RefundParam refundParam = new RefundParam()
.setBusinessId(businessId)
.setRefundModeParams(refundModeParams);
this.refundPayment(payment, refundParam);
}
/**
* 退款
*/
private void refundPayment(Payment payment, List<RefundModeParam> refundModeParams) {
private void refundPayment(Payment payment,RefundParam refundParam) {
List<RefundModeParam> refundModeParams = refundParam.getRefundModeParams();
// 状态判断, 支付中/失败/撤销不处理
List<String> tradesStatus = Arrays.asList(TRADE_PROGRESS, TRADE_CANCEL, TRADE_FAIL);
if (tradesStatus.contains(payment.getPayStatus())) {
@@ -98,32 +99,26 @@ public class PayRefundService {
}
// 过滤退款金额为0的支付通道参数
refundModeParams
.removeIf(refundModeParam -> BigDecimalUtil.compareTo(refundModeParam.getAmount(), BigDecimal.ZERO) == 0);
// 获取 paymentParam
PayParam payParam = PaymentBuilder.buildPayParamByPayment(payment);
refundModeParams.removeIf(refundModeParam -> BigDecimalUtil.compareTo(refundModeParam.getAmount(), BigDecimal.ZERO) == 0);
// 退款参数检查
this.payModeCheck(refundModeParams, payment.getRefundableInfo());
// 1.获取退款参数方式通过工厂生成对应的策略组
List<PayWayParam> payWayParams = refundModeParams.stream()
.map(RefundModeParam::toPayModeParam)
.collect(Collectors.toList());
List<AbsPayStrategy> paymentStrategyList = PayStrategyFactory.create(payWayParams);
if (CollectionUtil.isEmpty(paymentStrategyList)) {
List<AbsPayRefundStrategy> payRefundStrategies = PayRefundStrategyFactory.create(refundModeParams);
if (CollectionUtil.isEmpty(payRefundStrategies)) {
throw new PayUnsupportedMethodException();
}
// 2.初始化支付的参数
for (AbsPayStrategy paymentStrategy : paymentStrategyList) {
paymentStrategy.initPayParam(payment, payParam);
for (AbsPayRefundStrategy refundStrategy : payRefundStrategies) {
refundStrategy.initPayParam(payment, refundParam);
}
// 3.执行退款
this.doHandler(payment, paymentStrategyList, (strategyList, paymentObj) -> {
this.doHandler(payment, payRefundStrategies, (strategyList, paymentObj) -> {
// 发起支付成功进行的执行方法
try {
strategyList.forEach(AbsPayStrategy::doRefundHandler);
strategyList.forEach(AbsPayRefundStrategy::doRefundHandler);
}
catch (Exception e) {
// 记录退款失败的记录
@@ -165,12 +160,11 @@ public class PayRefundService {
/**
* 处理方法
* @param payment 支付记录
* @param strategyList 支付策略
* @param strategyList 退款策略
* @param successCallback 成功操作
*/
private void doHandler(Payment payment, List<AbsPayStrategy> strategyList,
PayStrategyConsumer<List<AbsPayStrategy>, Payment> successCallback) {
private void doHandler(Payment payment, List<AbsPayRefundStrategy> strategyList,
PayRefundStrategyConsumer<List<AbsPayRefundStrategy>, Payment> successCallback) {
try {
// 执行
successCallback.accept(strategyList, payment);

View File

@@ -0,0 +1,60 @@
package cn.bootx.platform.daxpay.core.refund.strategy;
import cn.bootx.platform.daxpay.code.pay.PayChannelEnum;
import cn.bootx.platform.daxpay.core.channel.alipay.service.AliPayCancelService;
import cn.bootx.platform.daxpay.core.channel.alipay.service.AliPaymentService;
import cn.bootx.platform.daxpay.core.channel.alipay.service.AlipayConfigService;
import cn.bootx.platform.daxpay.core.payment.service.PaymentService;
import cn.bootx.platform.daxpay.core.refund.func.AbsPayRefundStrategy;
import lombok.RequiredArgsConstructor;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
import static org.springframework.beans.factory.config.BeanDefinition.SCOPE_PROTOTYPE;
/**
* 支付宝退款
* @author xxm
* @since 2023/7/4
*/
@Scope(SCOPE_PROTOTYPE)
@Component
@RequiredArgsConstructor
public class AliPayRefundStrategy extends AbsPayRefundStrategy {
private final AlipayConfigService alipayConfigService;
private final AliPaymentService aliPaymentService;
private final AliPayCancelService aliPayCancelService;
private final PaymentService paymentService;
/**
* 策略标识
*
* @see PayChannelEnum
*/
@Override
public PayChannelEnum getType() {
return PayChannelEnum.ALI;
}
/**
* 退款前前操作
*/
@Override
public void doBeforeRefundHandler() {
alipayConfigService.initApiConfigByMchAppCode(this.getPayment().getMchAppCode());
}
/**
* 退款
*/
@Override
public void doRefundHandler() {
aliPayCancelService.refund(this.getPayment(), this.getRefundModeParam().getAmount());
aliPaymentService.updatePayRefund(this.getPayment().getId(), this.getRefundModeParam().getAmount());
paymentService.updateRefundSuccess(this.getPayment(), this.getRefundModeParam().getAmount(), PayChannelEnum.ALI);
}
}

View File

@@ -0,0 +1,44 @@
package cn.bootx.platform.daxpay.core.refund.strategy;
import cn.bootx.platform.daxpay.code.pay.PayChannelEnum;
import cn.bootx.platform.daxpay.core.channel.cash.service.CashService;
import cn.bootx.platform.daxpay.core.payment.service.PaymentService;
import cn.bootx.platform.daxpay.core.refund.func.AbsPayRefundStrategy;
import lombok.RequiredArgsConstructor;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
import static org.springframework.beans.factory.config.BeanDefinition.SCOPE_PROTOTYPE;
/**
* 现金支付退款
* @author xxm
* @since 2023/7/5
*/
@Scope(SCOPE_PROTOTYPE)
@Component
@RequiredArgsConstructor
public class CashPayRefundStrategy extends AbsPayRefundStrategy {
private final CashService cashService;
private final PaymentService paymentService;
/**
* 策略标识
*
* @see PayChannelEnum
*/
@Override
public PayChannelEnum getType() {
return PayChannelEnum.CASH;
}
/**
* 退款
*/
@Override
public void doRefundHandler() {
cashService.refund(this.getPayment().getId(), this.getRefundModeParam().getAmount());
paymentService.updateRefundSuccess(this.getPayment(), this.getRefundModeParam().getAmount(), PayChannelEnum.CASH);
}
}

View File

@@ -0,0 +1,37 @@
package cn.bootx.platform.daxpay.core.refund.strategy;
import cn.bootx.platform.daxpay.code.pay.PayChannelEnum;
import cn.bootx.platform.daxpay.core.refund.func.AbsPayRefundStrategy;
import lombok.RequiredArgsConstructor;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
import static org.springframework.beans.factory.config.BeanDefinition.SCOPE_PROTOTYPE;
/**
* 云闪付支付退款
* @author xxm
* @since 2023/7/5
*/
@Scope(SCOPE_PROTOTYPE)
@Component
@RequiredArgsConstructor
public class UnionPayRefundStrategy extends AbsPayRefundStrategy {
/**
* 策略标识
*
* @see PayChannelEnum
*/
@Override
public PayChannelEnum getType() {
return PayChannelEnum.UNION_PAY;
}
/**
* 退款
*/
@Override
public void doRefundHandler() {
}
}

View File

@@ -0,0 +1,65 @@
package cn.bootx.platform.daxpay.core.refund.strategy;
import cn.bootx.platform.daxpay.code.pay.PayChannelEnum;
import cn.bootx.platform.daxpay.core.channel.voucher.service.VoucherPayService;
import cn.bootx.platform.daxpay.core.channel.voucher.service.VoucherPaymentService;
import cn.bootx.platform.daxpay.core.payment.service.PaymentService;
import cn.bootx.platform.daxpay.core.refund.func.AbsPayRefundStrategy;
import cn.bootx.platform.daxpay.param.channel.voucher.VoucherRefundParam;
import cn.bootx.platform.daxpay.param.refund.RefundModeParam;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import lombok.RequiredArgsConstructor;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
import static org.springframework.beans.factory.config.BeanDefinition.SCOPE_PROTOTYPE;
/**
* 储值卡支付退款
* @author xxm
* @since 2023/7/5
*/
@Scope(SCOPE_PROTOTYPE)
@Component
@RequiredArgsConstructor
public class VoucherPayRefundStrategy extends AbsPayRefundStrategy {
private final VoucherPayService voucherPayService;
private final VoucherPaymentService voucherPaymentService;
private final PaymentService paymentService;
private VoucherRefundParam voucherRefundParam;
/**
* 策略标识
*
* @see PayChannelEnum
*/
@Override
public PayChannelEnum getType() {
return PayChannelEnum.VOUCHER;
}
/**
* 退款前对处理
*/
@Override
public void doBeforeRefundHandler() {
RefundModeParam refundModeParam = this.getRefundModeParam();
this.voucherRefundParam = new VoucherRefundParam();
if (StrUtil.isNotBlank(refundModeParam.getExtraParamsJson())){
this.voucherRefundParam = JSONUtil.toBean(refundModeParam.getExtraParamsJson(), VoucherRefundParam.class);
}
}
/**
* 退款
*/
@Override
public void doRefundHandler() {
voucherPayService.refund(this.getPayment().getId(), this.getRefundModeParam().getAmount(),voucherRefundParam);
voucherPaymentService.updateRefund(this.getPayment().getId(), this.getRefundModeParam().getAmount());
paymentService.updateRefundSuccess(this.getPayment(), this.getRefundModeParam().getAmount(),
PayChannelEnum.VOUCHER);
}
}

View File

@@ -0,0 +1,45 @@
package cn.bootx.platform.daxpay.core.refund.strategy;
import cn.bootx.platform.daxpay.code.pay.PayChannelEnum;
import cn.bootx.platform.daxpay.core.channel.wallet.service.WalletPayService;
import cn.bootx.platform.daxpay.core.channel.wallet.service.WalletPaymentService;
import cn.bootx.platform.daxpay.core.payment.service.PaymentService;
import cn.bootx.platform.daxpay.core.refund.func.AbsPayRefundStrategy;
import lombok.RequiredArgsConstructor;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
import static org.springframework.beans.factory.config.BeanDefinition.SCOPE_PROTOTYPE;
/**
* 钱包支付退款
* @author xxm
* @since 2023/7/5
*/
@Scope(SCOPE_PROTOTYPE)
@Component
@RequiredArgsConstructor
public class WalletPayRefundStrategy extends AbsPayRefundStrategy {
private final WalletPayService walletPayService;
private final WalletPaymentService walletPaymentService;
private final PaymentService paymentService;
/**
* 策略标识
*
* @see PayChannelEnum
*/
@Override
public PayChannelEnum getType() {
return PayChannelEnum.WALLET;
}
/**
* 退款
*/
@Override
public void doRefundHandler() {
walletPayService.refund(this.getPayment().getId(), this.getRefundModeParam().getAmount());
walletPaymentService.updateRefund(this.getPayment().getId(), this.getRefundModeParam().getAmount());
paymentService.updateRefundSuccess(this.getPayment(), this.getRefundModeParam().getAmount(), PayChannelEnum.WALLET);
}
}

View File

@@ -0,0 +1,77 @@
package cn.bootx.platform.daxpay.core.refund.strategy;
import cn.bootx.platform.daxpay.code.pay.PayChannelEnum;
import cn.bootx.platform.daxpay.core.channel.wechat.dao.WeChatPayConfigManager;
import cn.bootx.platform.daxpay.core.channel.wechat.dao.WeChatPaymentManager;
import cn.bootx.platform.daxpay.core.channel.wechat.entity.WeChatPayConfig;
import cn.bootx.platform.daxpay.core.channel.wechat.entity.WeChatPayment;
import cn.bootx.platform.daxpay.core.channel.wechat.service.WeChatPayCancelService;
import cn.bootx.platform.daxpay.core.channel.wechat.service.WeChatPaymentService;
import cn.bootx.platform.daxpay.core.payment.service.PaymentService;
import cn.bootx.platform.daxpay.core.refund.func.AbsPayRefundStrategy;
import cn.bootx.platform.daxpay.exception.payment.PayFailureException;
import lombok.RequiredArgsConstructor;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
import static org.springframework.beans.factory.config.BeanDefinition.SCOPE_PROTOTYPE;
/**
* 微信支付退款
* @author xxm
* @since 2023/7/5
*/
@Scope(SCOPE_PROTOTYPE)
@Component
@RequiredArgsConstructor
public class WeChatPayRefundStrategy extends AbsPayRefundStrategy {
private final WeChatPaymentManager weChatPaymentManager;
private final WeChatPayConfigManager weChatPayConfigManager;
private final WeChatPayCancelService weChatPayCancelService;
private final WeChatPaymentService weChatPaymentService;
private final PaymentService paymentService;
private WeChatPayConfig weChatPayConfig;
/**
* 策略标识
*
* @see PayChannelEnum
*/
@Override
public PayChannelEnum getType() {
return PayChannelEnum.WECHAT;
}
/**
* 退款前对处理 包含必要的校验以及对Payment对象的创建和保存操作
*/
@Override
public void doBeforeRefundHandler() {
this.weChatPayConfig = weChatPayConfigManager.findByMchAppCode(this.getPayment().getMchAppCode())
.orElseThrow(() -> new PayFailureException("支付配置不存在"));
}
/**
* 退款
*/
@Override
public void doRefundHandler() {
WeChatPayment weChatPayment = weChatPaymentManager.findByPaymentId(this.getPayment().getId())
.orElseThrow(() -> new PayFailureException("微信支付记录不存在"));
weChatPayCancelService.refund(this.getPayment(), weChatPayment, this.getRefundModeParam().getAmount(),
this.weChatPayConfig);
weChatPaymentService.updatePayRefund(weChatPayment, this.getRefundModeParam().getAmount());
paymentService.updateRefundSuccess(this.getPayment(), this.getRefundModeParam().getAmount(), PayChannelEnum.WECHAT);
}
/**
* 初始化微信支付
*/
private void initWeChatPayConfig(String appCode) {
// 检查并获取微信支付配置
}
}

View File

@@ -37,9 +37,9 @@ public class VoucherGenerationParam {
private LocalDateTime endTime;
/**
* @see VoucherCode
* @see VoucherCode#STATUS_NORMAL
*/
@Schema(description = "默认状态")
private Integer status;
private String status;
}

View File

@@ -4,7 +4,6 @@ import cn.bootx.platform.daxpay.code.pay.PayChannelEnum;
import cn.bootx.platform.daxpay.dto.payment.RefundableInfo;
import cn.bootx.platform.daxpay.param.channel.voucher.VoucherRefundParam;
import cn.bootx.platform.daxpay.param.channel.wallet.WalletRefundParam;
import cn.bootx.platform.daxpay.param.pay.PayWayParam;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.experimental.Accessors;
@@ -38,12 +37,6 @@ public class RefundModeParam {
@Schema(description = "扩展参数的json字符串")
private String extraParamsJson;
/**
* 转换成支付方式参数
*/
public PayWayParam toPayModeParam() {
return new PayWayParam().setPayChannel(getPayChannel()).setAmount(getAmount());
}
/**
* 转换成退款方式记录对象

View File

@@ -20,6 +20,13 @@ spring:
host: 127.0.0.1
port: 6379
database: 1
lettuce:
cluster:
refresh:
# 自适应拓扑刷新
adaptive: true
# 群集拓扑刷新周期
period: 60s
# 如果不使用MQ的话, 可以将项目中 @EnableRabbit 给注释掉, 就不会一直报错了
rabbitmq:
# 虚拟主机