feat 支付对账收尾, 支付退款调整为订单+明细

This commit is contained in:
xxm1995
2024-01-23 16:48:55 +08:00
parent ec4a901d86
commit b64b481945
55 changed files with 469 additions and 264 deletions

View File

@@ -25,7 +25,7 @@ public class PaymentContext {
private final AsyncPayLocal asyncPayInfo = new AsyncPayLocal();
/** 异步退款相关信息 */
private final AsyncRefundLocal asyncRefundInfo = new AsyncRefundLocal();
private final RefundLocal refundInfo = new RefundLocal();
/** 消息通知相关信息 */
private final NoticeLocal noticeInfo = new NoticeLocal();

View File

@@ -10,10 +10,12 @@ import lombok.experimental.Accessors;
*/
@Data
@Accessors(chain = true)
public class AsyncRefundLocal {
public class RefundLocal {
/** 退款请求号(调用支付网关时用的) */
private String refundRequestNo;
/**
* 异步通道退款时发给网关的退款号, 用与将记录关联起来
*/
private String gatewayRequestNo;
/** 错误码 */
private String errorCode;

View File

@@ -7,7 +7,7 @@ import cn.bootx.platform.daxpay.service.common.local.PaymentContextLocal;
import cn.bootx.platform.daxpay.service.core.channel.alipay.dao.AliPayOrderManager;
import cn.bootx.platform.daxpay.service.core.channel.alipay.entity.AliPayOrder;
import cn.bootx.platform.daxpay.service.core.order.pay.entity.PayOrder;
import cn.bootx.platform.daxpay.service.core.order.pay.service.PayOrderChannelService;
import cn.bootx.platform.daxpay.service.core.order.pay.service.PayChannelOrderService;
import cn.bootx.platform.daxpay.param.pay.PayChannelParam;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
@@ -34,7 +34,7 @@ public class AliPayOrderService {
private final AliPayOrderManager aliPayOrderManager;
private final PayOrderChannelService payOrderChannelService;
private final PayChannelOrderService payChannelOrderService;
/**
* 支付调起成功 更新payment中异步支付类型信息, 如果支付完成, 创建支付宝支付单
@@ -42,7 +42,7 @@ public class AliPayOrderService {
public void updatePaySuccess(PayOrder payOrder, PayChannelParam payChannelParam) {
// 更新支付宝异步支付类型信息
payOrder.setAsyncPay(true).setAsyncChannel(PayChannelEnum.ALI.getCode());
payOrderChannelService.updateChannel(payChannelParam,payOrder);
payChannelOrderService.updateChannel(payChannelParam,payOrder);
// 更新支付宝可退款类型信息
List<RefundableInfo> refundableInfos = payOrder.getRefundableInfos();

View File

@@ -1,5 +1,6 @@
package cn.bootx.platform.daxpay.service.core.channel.alipay.service;
import cn.bootx.platform.daxpay.code.PayReconcileTradeEnum;
import cn.bootx.platform.daxpay.exception.pay.PayFailureException;
import cn.bootx.platform.daxpay.service.code.AliPayCode;
import cn.bootx.platform.daxpay.service.common.local.PaymentContextLocal;
@@ -138,14 +139,14 @@ public class AliPayReconcileService {
PayReconcileDetail payReconcileDetail = new PayReconcileDetail()
.setRecordOrderId(billDetail.getRecordOrderId())
.setPaymentId(billDetail.getOutTradeNo())
.setType("pay")
.setType(PayReconcileTradeEnum.PAY.getCode())
.setAmount(amount)
.setTitle(billDetail.getSubject())
.setGatewayOrderNo(billDetail.getTradeNo());
// 退款覆盖更新对应的字段
if (Objects.equals(billDetail.getTradeType(), "退款")){
payReconcileDetail.setRefundId(billDetail.getBatchNo())
.setType("refund");
.setType(PayReconcileTradeEnum.REFUND.getCode());
}
return payReconcileDetail;

View File

@@ -2,7 +2,7 @@ package cn.bootx.platform.daxpay.service.core.channel.alipay.service;
import cn.bootx.platform.daxpay.exception.pay.PayFailureException;
import cn.bootx.platform.daxpay.service.code.AliPayCode;
import cn.bootx.platform.daxpay.service.common.context.AsyncRefundLocal;
import cn.bootx.platform.daxpay.service.common.context.RefundLocal;
import cn.bootx.platform.daxpay.service.common.local.PaymentContextLocal;
import cn.bootx.platform.daxpay.service.core.order.pay.entity.PayOrder;
import cn.bootx.platform.daxpay.util.PayUtil;
@@ -37,9 +37,9 @@ public class AliPayRefundService {
refundModel.setRefundAmount(refundAmount);
// 设置退款信息
AsyncRefundLocal refundInfo = PaymentContextLocal.get().getAsyncRefundInfo();
refundInfo.setRefundRequestNo(PayUtil.getRefundNo());
refundModel.setOutRequestNo(refundInfo.getRefundRequestNo());
RefundLocal refundInfo = PaymentContextLocal.get().getRefundInfo();
refundInfo.setGatewayRequestNo(PayUtil.getRefundNo());
refundModel.setOutRequestNo(refundInfo.getGatewayRequestNo());
try {
AlipayTradeRefundResponse response = AliPayApi.tradeRefundToResponse(refundModel);
if (!Objects.equals(AliPayCode.SUCCESS, response.getCode())) {

View File

@@ -2,7 +2,7 @@ package cn.bootx.platform.daxpay.service.core.channel.wechat.service;
import cn.bootx.platform.common.spring.exception.RetryableException;
import cn.bootx.platform.daxpay.service.code.WeChatPayCode;
import cn.bootx.platform.daxpay.service.common.context.AsyncRefundLocal;
import cn.bootx.platform.daxpay.service.common.context.RefundLocal;
import cn.bootx.platform.daxpay.service.common.local.PaymentContextLocal;
import cn.bootx.platform.daxpay.service.core.channel.wechat.entity.WeChatPayConfig;
import cn.bootx.platform.daxpay.service.core.order.pay.entity.PayOrder;
@@ -62,7 +62,7 @@ public class WeChatPayCloseService {
errorMsg = result.get(WeChatPayCode.RETURN_MSG);
}
log.error("订单关闭失败 {}", errorMsg);
AsyncRefundLocal refundInfo = PaymentContextLocal.get().getAsyncRefundInfo();
RefundLocal refundInfo = PaymentContextLocal.get().getRefundInfo();
refundInfo.setErrorMsg(errorMsg);
refundInfo.setErrorCode(Optional.ofNullable(resultCode).orElse(returnCode));
throw new PayFailureException(errorMsg);

View File

@@ -2,13 +2,12 @@ package cn.bootx.platform.daxpay.service.core.channel.wechat.service;
import cn.bootx.platform.daxpay.code.PayChannelEnum;
import cn.bootx.platform.daxpay.code.PayStatusEnum;
import cn.bootx.platform.daxpay.service.common.context.AsyncPayLocal;
import cn.bootx.platform.daxpay.entity.RefundableInfo;
import cn.bootx.platform.daxpay.service.common.local.PaymentContextLocal;
import cn.bootx.platform.daxpay.service.core.channel.wechat.dao.WeChatPayOrderManager;
import cn.bootx.platform.daxpay.service.core.channel.wechat.entity.WeChatPayOrder;
import cn.bootx.platform.daxpay.service.core.order.pay.entity.PayOrder;
import cn.bootx.platform.daxpay.service.core.order.pay.service.PayOrderChannelService;
import cn.bootx.platform.daxpay.service.core.order.pay.service.PayChannelOrderService;
import cn.bootx.platform.daxpay.service.core.order.pay.service.PayOrderService;
import cn.bootx.platform.daxpay.param.pay.PayChannelParam;
import lombok.RequiredArgsConstructor;
@@ -33,7 +32,7 @@ public class WeChatPayOrderService {
private final PayOrderService payOrderService;
private final PayOrderChannelService payOrderChannelService;
private final PayChannelOrderService payChannelOrderService;
private final WeChatPayOrderManager weChatPayOrderManager;
@@ -43,7 +42,7 @@ public class WeChatPayOrderService {
public void updatePaySuccess(PayOrder payOrder, PayChannelParam payChannelParam) {
payOrder.setAsyncPay(true).setAsyncChannel(PayChannelEnum.WECHAT.getCode());
payOrderChannelService.updateChannel(payChannelParam,payOrder);
payChannelOrderService.updateChannel(payChannelParam,payOrder);
// 更新微信可退款类型信息
List<RefundableInfo> refundableInfos = payOrder.getRefundableInfos();

View File

@@ -7,9 +7,9 @@ import cn.bootx.platform.daxpay.exception.pay.PayFailureException;
import cn.bootx.platform.daxpay.service.code.WeChatPayCode;
import cn.bootx.platform.daxpay.service.common.local.PaymentContextLocal;
import cn.bootx.platform.daxpay.service.core.channel.wechat.entity.WeChatPayConfig;
import cn.bootx.platform.daxpay.service.core.order.pay.dao.PayOrderChannelManager;
import cn.bootx.platform.daxpay.service.core.order.pay.dao.PayChannelOrderManager;
import cn.bootx.platform.daxpay.service.core.order.pay.entity.PayOrder;
import cn.bootx.platform.daxpay.service.core.order.pay.entity.PayOrderChannel;
import cn.bootx.platform.daxpay.service.core.order.pay.entity.PayChannelOrder;
import cn.bootx.platform.daxpay.service.core.payment.sync.result.GatewaySyncResult;
import cn.hutool.core.date.DatePattern;
import cn.hutool.json.JSONUtil;
@@ -36,7 +36,7 @@ import java.util.Objects;
@Service
@RequiredArgsConstructor
public class WeChatPaySyncService {
private final PayOrderChannelManager payOrderChannelManager;
private final PayChannelOrderManager payChannelOrderManager;
/**
* 同步查询
@@ -102,7 +102,7 @@ public class WeChatPaySyncService {
* 退款查询
*/
private GatewaySyncResult syncRefundStatus(PayOrder order, WeChatPayConfig weChatPayConfig){
PayOrderChannel orderChannel = payOrderChannelManager.findByPaymentIdAndChannel(order.getId(), PayChannelEnum.WECHAT.getCode())
PayChannelOrder orderChannel = payChannelOrderManager.findByPaymentIdAndChannel(order.getId(), PayChannelEnum.WECHAT.getCode())
.orElseThrow(() -> new PayFailureException("支付订单通道信息不存在"));
Map<String, String> params = UnifiedOrderModel.builder()

View File

@@ -1,5 +1,6 @@
package cn.bootx.platform.daxpay.service.core.channel.wechat.service;
import cn.bootx.platform.daxpay.code.PayReconcileTradeEnum;
import cn.bootx.platform.daxpay.exception.pay.PayFailureException;
import cn.bootx.platform.daxpay.service.code.WeChatPayCode;
import cn.bootx.platform.daxpay.service.common.local.PaymentContextLocal;
@@ -126,7 +127,7 @@ public class WechatPayReconcileService{
String orderAmount = billDetail.getOrderAmount();
double v = Double.parseDouble(orderAmount) * 100;
int amount = Math.abs(((int) v));
payReconcileDetail.setType("pay")
payReconcileDetail.setType(PayReconcileTradeEnum.PAY.getCode())
.setAmount(amount);
}
// 退款
@@ -135,13 +136,13 @@ public class WechatPayReconcileService{
String refundAmount = billDetail.getApplyRefundAmount();
double v = Double.parseDouble(refundAmount) * 100;
int amount = Math.abs(((int) v));
payReconcileDetail.setType("refund")
payReconcileDetail.setType(PayReconcileTradeEnum.REFUND.getCode())
.setAmount(amount)
.setRefundId(billDetail.getMchRefundNo());
}
// TODO 已撤销, 暂时未处理
if (Objects.equals(billDetail.getStatus(), "REVOKED")){
log.warn("对账出现已撤销记录, 后续进行处理");
}
return payReconcileDetail;
}

View File

@@ -1,15 +1,14 @@
package cn.bootx.platform.daxpay.service.core.channel.wechat.service;
import cn.bootx.platform.daxpay.code.PayChannelEnum;
import cn.bootx.platform.daxpay.code.PayRefundStatusEnum;
import cn.bootx.platform.daxpay.exception.pay.PayFailureException;
import cn.bootx.platform.daxpay.service.code.WeChatPayCode;
import cn.bootx.platform.daxpay.service.common.context.AsyncRefundLocal;
import cn.bootx.platform.daxpay.service.common.context.RefundLocal;
import cn.bootx.platform.daxpay.service.common.local.PaymentContextLocal;
import cn.bootx.platform.daxpay.service.core.channel.wechat.entity.WeChatPayConfig;
import cn.bootx.platform.daxpay.service.core.order.pay.dao.PayOrderChannelManager;
import cn.bootx.platform.daxpay.service.core.order.pay.dao.PayChannelOrderManager;
import cn.bootx.platform.daxpay.service.core.order.pay.entity.PayOrder;
import cn.bootx.platform.daxpay.service.core.order.pay.entity.PayOrderChannel;
import cn.bootx.platform.daxpay.service.core.order.pay.entity.PayChannelOrder;
import cn.bootx.platform.daxpay.util.PayUtil;
import cn.hutool.core.codec.Base64;
import cn.hutool.core.util.StrUtil;
@@ -35,26 +34,24 @@ import java.util.Optional;
@RequiredArgsConstructor
public class WechatRefundService {
private final PayOrderChannelManager payOrderChannelManager;
private final PayChannelOrderManager payChannelOrderManager;
/**
* 退款方法
* 微信需要同时传输订单金额或退款金额
*/
public void refund(PayOrder payOrder, int amount, WeChatPayConfig weChatPayConfig) {
PayOrderChannel orderChannel = payOrderChannelManager.findByPaymentIdAndChannel(payOrder.getId(), PayChannelEnum.WECHAT.getCode())
.orElseThrow(() -> new PayFailureException("未找到微信支付的详细信息"));
public void refund(PayOrder payOrder, int amount, PayChannelOrder orderChannel, WeChatPayConfig weChatPayConfig) {
String refundFee = String.valueOf(amount);
String totalFee = String.valueOf(orderChannel.getAmount());
// 设置退款信息
AsyncRefundLocal refundInfo = PaymentContextLocal.get().getAsyncRefundInfo();
refundInfo.setRefundRequestNo(PayUtil.getRefundNo());
RefundLocal refundInfo = PaymentContextLocal.get().getRefundInfo();
refundInfo.setGatewayRequestNo(PayUtil.getRefundNo());
Map<String, String> params = RefundModel.builder()
.appid(weChatPayConfig.getWxAppId())
.mch_id(weChatPayConfig.getWxMchId())
.out_trade_no(String.valueOf(payOrder.getId()))
.out_refund_no(refundInfo.getRefundRequestNo())
.out_refund_no(refundInfo.getGatewayRequestNo())
.total_fee(totalFee)
.refund_fee(refundFee)
.nonce_str(WxPayKit.generateStr())
@@ -87,7 +84,7 @@ public class WechatRefundService {
errorMsg = result.get(WeChatPayCode.RETURN_MSG);
}
log.error("订单退款失败 {}", errorMsg);
AsyncRefundLocal refundInfo = PaymentContextLocal.get().getAsyncRefundInfo();
RefundLocal refundInfo = PaymentContextLocal.get().getRefundInfo();
refundInfo.setErrorMsg(errorMsg);
refundInfo.setErrorCode(Optional.ofNullable(resultCode).orElse(returnCode));
throw new PayFailureException(errorMsg);

View File

@@ -7,7 +7,7 @@ import cn.bootx.platform.daxpay.service.common.context.PlatformLocal;
import cn.bootx.platform.daxpay.service.common.local.PaymentContextLocal;
import cn.bootx.platform.daxpay.service.core.notice.result.PayChannelResult;
import cn.bootx.platform.daxpay.service.core.notice.result.PayNoticeResult;
import cn.bootx.platform.daxpay.service.core.order.pay.dao.PayOrderChannelManager;
import cn.bootx.platform.daxpay.service.core.order.pay.dao.PayChannelOrderManager;
import cn.bootx.platform.daxpay.service.core.order.pay.dao.PayOrderExtraManager;
import cn.bootx.platform.daxpay.service.core.order.pay.dao.PayOrderManager;
import cn.bootx.platform.daxpay.service.core.order.pay.entity.PayOrder;
@@ -37,7 +37,7 @@ import java.util.stream.Collectors;
public class PayNoticeService {
private final PayOrderManager payOrderManager;
private final PayOrderExtraManager payOrderExtraManager;
private final PayOrderChannelManager payOrderChannelManager;
private final PayChannelOrderManager payChannelOrderManager;
/**
* 发送支付完成回调通知
@@ -59,7 +59,7 @@ public class PayNoticeService {
if (payOrderExtra.isNotNotify()){
return;
}
List<PayChannelResult> orderChannels = payOrderChannelManager.findAllByPaymentId(paymentId).stream()
List<PayChannelResult> orderChannels = payChannelOrderManager.findAllByPaymentId(paymentId).stream()
.map(o->new PayChannelResult().setChannel(o.getChannel()).setWay(o.getPayWay()).setAmount(o.getAmount()))
.collect(Collectors.toList());
// 组装内容

View File

@@ -6,8 +6,8 @@ import cn.bootx.platform.daxpay.service.common.context.AsyncPayLocal;
import cn.bootx.platform.daxpay.service.common.context.NoticeLocal;
import cn.bootx.platform.daxpay.service.common.context.PlatformLocal;
import cn.bootx.platform.daxpay.service.common.local.PaymentContextLocal;
import cn.bootx.platform.daxpay.service.core.order.pay.entity.PayChannelOrder;
import cn.bootx.platform.daxpay.service.core.order.pay.entity.PayOrder;
import cn.bootx.platform.daxpay.service.core.order.pay.entity.PayOrderChannel;
import cn.bootx.platform.daxpay.service.core.order.pay.entity.PayOrderExtra;
import cn.bootx.platform.daxpay.entity.RefundableInfo;
import cn.bootx.platform.daxpay.param.pay.PayParam;
@@ -93,12 +93,12 @@ public class PaymentBuilder {
/**
* 构建订单关联通道信息
*/
public List<PayOrderChannel> buildPayChannel(List<PayChannelParam> payChannelParams) {
public List<PayChannelOrder> buildPayChannel(List<PayChannelParam> payChannelParams) {
if (CollectionUtil.isEmpty(payChannelParams)) {
return Collections.emptyList();
}
return payChannelParams.stream()
.map(o-> new PayOrderChannel()
.map(o-> new PayChannelOrder()
.setChannel(o.getChannel())
.setPayWay(o.getWay())
.setAmount(o.getAmount())

View File

@@ -2,9 +2,9 @@ package cn.bootx.platform.daxpay.service.core.order.pay.convert;
import cn.bootx.platform.daxpay.result.order.PayOrderChannelResult;
import cn.bootx.platform.daxpay.service.core.order.pay.entity.PayOrder;
import cn.bootx.platform.daxpay.service.core.order.pay.entity.PayOrderChannel;
import cn.bootx.platform.daxpay.service.core.order.pay.entity.PayChannelOrder;
import cn.bootx.platform.daxpay.service.core.order.pay.entity.PayOrderExtra;
import cn.bootx.platform.daxpay.service.dto.order.pay.PayOrderChannelDto;
import cn.bootx.platform.daxpay.service.dto.order.pay.PayChanneOrderlDto;
import cn.bootx.platform.daxpay.service.dto.order.pay.PayOrderDto;
import cn.bootx.platform.daxpay.service.dto.order.pay.PayOrderExtraDto;
import org.mapstruct.Mapper;
@@ -23,7 +23,7 @@ public interface PayOrderConvert {
PayOrderDto convert(PayOrder in);
PayOrderChannelDto convert(PayOrderChannel in);
PayChanneOrderlDto convert(PayChannelOrder in);
PayOrderChannelResult convertResult(PayOrderChannel in);
PayOrderChannelResult convertResult(PayChannelOrder in);
}

View File

@@ -1,7 +1,7 @@
package cn.bootx.platform.daxpay.service.core.order.pay.dao;
import cn.bootx.platform.common.mybatisplus.impl.BaseManager;
import cn.bootx.platform.daxpay.service.core.order.pay.entity.PayOrderChannel;
import cn.bootx.platform.daxpay.service.core.order.pay.entity.PayChannelOrder;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Repository;
@@ -17,22 +17,22 @@ import java.util.Optional;
@Slf4j
@Repository
@RequiredArgsConstructor
public class PayOrderChannelManager extends BaseManager<PayOrderChannelMapper, PayOrderChannel> {
public class PayChannelOrderManager extends BaseManager<PayChannelOrderMapper, PayChannelOrder> {
/**
* 根据订单查找
*/
public List<PayOrderChannel> findAllByPaymentId(Long paymentId){
return findAllByField(PayOrderChannel::getPaymentId,paymentId);
public List<PayChannelOrder> findAllByPaymentId(Long paymentId){
return findAllByField(PayChannelOrder::getPaymentId,paymentId);
}
/**
* 根据订单id和支付通道查询
*/
public Optional<PayOrderChannel> findByPaymentIdAndChannel(Long paymentId, String channel) {
public Optional<PayChannelOrder> findByPaymentIdAndChannel(Long paymentId, String channel) {
return lambdaQuery()
.eq(PayOrderChannel::getPaymentId,paymentId)
.eq(PayOrderChannel::getChannel,channel)
.eq(PayChannelOrder::getPaymentId,paymentId)
.eq(PayChannelOrder::getChannel,channel)
.oneOpt();
}
@@ -41,8 +41,8 @@ public class PayOrderChannelManager extends BaseManager<PayOrderChannelMapper,
*/
public void deleteByPaymentIdAndAsync(Long paymentId){
lambdaUpdate()
.eq(PayOrderChannel::getPaymentId,paymentId)
.eq(PayOrderChannel::isAsync,true)
.eq(PayChannelOrder::getPaymentId,paymentId)
.eq(PayChannelOrder::isAsync,true)
.remove();
}

View File

@@ -1,14 +1,14 @@
package cn.bootx.platform.daxpay.service.core.order.pay.dao;
import cn.bootx.platform.daxpay.service.core.order.pay.entity.PayOrderChannel;
import cn.bootx.platform.daxpay.service.core.order.pay.entity.PayChannelOrder;
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> {
public interface PayChannelOrderMapper extends BaseMapper<PayChannelOrder> {
}

View File

@@ -7,7 +7,7 @@ import cn.bootx.platform.daxpay.param.channel.VoucherPayParam;
import cn.bootx.platform.daxpay.param.channel.WalletPayParam;
import cn.bootx.platform.daxpay.param.channel.WeChatPayParam;
import cn.bootx.platform.daxpay.service.core.order.pay.convert.PayOrderConvert;
import cn.bootx.platform.daxpay.service.dto.order.pay.PayOrderChannelDto;
import cn.bootx.platform.daxpay.service.dto.order.pay.PayChanneOrderlDto;
import cn.bootx.table.modify.annotation.DbColumn;
import cn.bootx.table.modify.annotation.DbTable;
import com.baomidou.mybatisplus.annotation.TableName;
@@ -25,7 +25,7 @@ import lombok.experimental.Accessors;
@Accessors(chain = true)
@DbTable(comment = "支付订单关联支付时通道信息")
@TableName("pay_order_channel")
public class PayOrderChannel extends MpCreateEntity implements EntityBaseFunction<PayOrderChannelDto> {
public class PayChannelOrder extends MpCreateEntity implements EntityBaseFunction<PayChanneOrderlDto> {
@DbColumn(comment = "支付id")
private Long paymentId;
@@ -55,7 +55,7 @@ public class PayOrderChannel extends MpCreateEntity implements EntityBaseFunctio
* 转换
*/
@Override
public PayOrderChannelDto toDto() {
public PayChanneOrderlDto toDto() {
return PayOrderConvert.CONVERT.convert(this);
}
}

View File

@@ -3,10 +3,10 @@ package cn.bootx.platform.daxpay.service.core.order.pay.service;
import cn.bootx.platform.common.core.util.ResultConvertUtil;
import cn.bootx.platform.daxpay.code.PayChannelEnum;
import cn.bootx.platform.daxpay.param.pay.PayChannelParam;
import cn.bootx.platform.daxpay.service.core.order.pay.dao.PayOrderChannelManager;
import cn.bootx.platform.daxpay.service.core.order.pay.dao.PayChannelOrderManager;
import cn.bootx.platform.daxpay.service.core.order.pay.entity.PayOrder;
import cn.bootx.platform.daxpay.service.core.order.pay.entity.PayOrderChannel;
import cn.bootx.platform.daxpay.service.dto.order.pay.PayOrderChannelDto;
import cn.bootx.platform.daxpay.service.core.order.pay.entity.PayChannelOrder;
import cn.bootx.platform.daxpay.service.dto.order.pay.PayChanneOrderlDto;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
@@ -23,14 +23,14 @@ import java.util.Optional;
@Slf4j
@Service
@RequiredArgsConstructor
public class PayOrderChannelService {
private final PayOrderChannelManager payOrderChannelManager;
public class PayChannelOrderService {
private final PayChannelOrderManager payChannelOrderManager;
/**
* 根据支付ID查询列表
*/
public List<PayOrderChannelDto> findAllByPaymentId(Long paymentId){
return ResultConvertUtil.dtoListConvert(payOrderChannelManager.findAllByPaymentId(paymentId));
public List<PayChanneOrderlDto> findAllByPaymentId(Long paymentId){
return ResultConvertUtil.dtoListConvert(payChannelOrderManager.findAllByPaymentId(paymentId));
}
/**
@@ -38,10 +38,10 @@ public class PayOrderChannelService {
*/
@Transactional(rollbackFor = Exception.class)
public void updateChannel(PayChannelParam payChannelParam, PayOrder payOrder){
Optional<PayOrderChannel> payOrderChannelOpt = payOrderChannelManager.findByPaymentIdAndChannel(payOrder.getId(), PayChannelEnum.WECHAT.getCode());
Optional<PayChannelOrder> payOrderChannelOpt = payChannelOrderManager.findByPaymentIdAndChannel(payOrder.getId(), PayChannelEnum.WECHAT.getCode());
if (!payOrderChannelOpt.isPresent()){
payOrderChannelManager.deleteByPaymentIdAndAsync(payOrder.getId());
payOrderChannelManager.save(new PayOrderChannel()
payChannelOrderManager.deleteByPaymentIdAndAsync(payOrder.getId());
payChannelOrderManager.save(new PayChannelOrder()
.setPaymentId(payOrder.getId())
.setChannel(PayChannelEnum.ALI.getCode())
.setAmount(payChannelParam.getAmount())
@@ -53,7 +53,7 @@ public class PayOrderChannelService {
payOrderChannelOpt.get()
.setChannelExtra(payChannelParam.getChannelExtra())
.setPayWay(payChannelParam.getWay());
payOrderChannelManager.updateById(payOrderChannelOpt.get());
payChannelOrderManager.updateById(payOrderChannelOpt.get());
}
}
}

View File

@@ -10,11 +10,11 @@ import cn.bootx.platform.daxpay.param.pay.QueryPayParam;
import cn.bootx.platform.daxpay.result.order.PayOrderChannelResult;
import cn.bootx.platform.daxpay.result.order.PayOrderResult;
import cn.bootx.platform.daxpay.service.core.order.pay.convert.PayOrderConvert;
import cn.bootx.platform.daxpay.service.core.order.pay.dao.PayOrderChannelManager;
import cn.bootx.platform.daxpay.service.core.order.pay.dao.PayChannelOrderManager;
import cn.bootx.platform.daxpay.service.core.order.pay.dao.PayOrderExtraManager;
import cn.bootx.platform.daxpay.service.core.order.pay.dao.PayOrderManager;
import cn.bootx.platform.daxpay.service.core.order.pay.entity.PayChannelOrder;
import cn.bootx.platform.daxpay.service.core.order.pay.entity.PayOrder;
import cn.bootx.platform.daxpay.service.core.order.pay.entity.PayOrderChannel;
import cn.bootx.platform.daxpay.service.core.order.pay.entity.PayOrderExtra;
import cn.bootx.platform.daxpay.service.dto.order.pay.PayOrderDto;
import cn.bootx.platform.daxpay.service.param.order.PayOrderQuery;
@@ -41,7 +41,7 @@ import java.util.stream.Collectors;
public class PayOrderQueryService {
private final PayOrderManager payOrderManager;
private final PayOrderExtraManager payOrderExtraManager;
private final PayOrderChannelManager payOrderChannelManager;
private final PayChannelOrderManager payChannelOrderManager;
/**
* 分页
@@ -90,7 +90,7 @@ public class PayOrderQueryService {
.orElseThrow(() -> new PayFailureException("支付订单不完整"));
// 查询通道数据
List<PayOrderChannel> orderChannelList = payOrderChannelManager.findAllByPaymentId(payOrder.getId());
List<PayChannelOrder> orderChannelList = payChannelOrderManager.findAllByPaymentId(payOrder.getId());
List<PayOrderChannelResult> channels = orderChannelList.stream()
.map(PayOrderConvert.CONVERT::convertResult)

View File

@@ -2,6 +2,7 @@ package cn.bootx.platform.daxpay.service.core.order.reconcile.entity;
import cn.bootx.platform.common.core.function.EntityBaseFunction;
import cn.bootx.platform.common.mybatisplus.base.MpCreateEntity;
import cn.bootx.platform.daxpay.code.PayReconcileTradeEnum;
import cn.bootx.platform.daxpay.service.core.order.reconcile.conver.PayReconcileConvert;
import cn.bootx.platform.daxpay.service.dto.order.reconcile.PayReconcileDetailDto;
import cn.bootx.table.modify.annotation.DbColumn;
@@ -35,7 +36,10 @@ public class PayReconcileDetail extends MpCreateEntity implements EntityBaseFunc
@DbColumn(comment = "交易金额")
private Integer amount;
/** 交易类型 pay/refund */
/**
* 交易类型
* @see PayReconcileTradeEnum
*/
@DbColumn(comment = "交易类型")
private String type;

View File

@@ -1,8 +1,8 @@
package cn.bootx.platform.daxpay.service.core.order.refund.convert;
import cn.bootx.platform.daxpay.result.order.RefundOrderChannelResult;
import cn.bootx.platform.daxpay.service.core.order.refund.entity.PayRefundOrderChannel;
import cn.bootx.platform.daxpay.service.dto.order.refund.RefundOrderChannelDto;
import cn.bootx.platform.daxpay.result.order.RefundChannelOrderResult;
import cn.bootx.platform.daxpay.service.core.order.refund.entity.PayRefundChannelOrder;
import cn.bootx.platform.daxpay.service.dto.order.refund.RefundChannelOrderDto;
import org.mapstruct.Mapper;
import org.mapstruct.factory.Mappers;
@@ -16,8 +16,8 @@ public interface RefundOrderChannelConvert {
RefundOrderChannelConvert CONVERT = Mappers.getMapper(RefundOrderChannelConvert.class);
RefundOrderChannelDto convert(PayRefundOrderChannel in);
RefundChannelOrderDto convert(PayRefundChannelOrder in);
RefundOrderChannelResult convertResult(PayRefundOrderChannel in);
RefundChannelOrderResult convertResult(PayRefundChannelOrder in);
}

View File

@@ -1,7 +1,7 @@
package cn.bootx.platform.daxpay.service.core.order.refund.dao;
import cn.bootx.platform.common.mybatisplus.impl.BaseManager;
import cn.bootx.platform.daxpay.service.core.order.refund.entity.PayRefundOrderChannel;
import cn.bootx.platform.daxpay.service.core.order.refund.entity.PayRefundChannelOrder;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Repository;
@@ -10,30 +10,29 @@ import java.util.List;
import java.util.Optional;
/**
*
* 通道退款订单
* @author xxm
* @since 2024/1/17
*/
@Slf4j
@Repository
@RequiredArgsConstructor
public class PayRefundOrderChannelManager extends BaseManager<PayRefundOrderChannelMapper, PayRefundOrderChannel> {
public class PayRefundChannelOrderManager extends BaseManager<PayRefundChannelOrderMapper, PayRefundChannelOrder> {
/**
* 根据退款单ID查找
*/
public List<PayRefundOrderChannel> findAllByRefundId(Long paymentId){
return findAllByField(PayRefundOrderChannel::getRefundId,paymentId);
public List<PayRefundChannelOrder> findAllByRefundId(Long paymentId){
return findAllByField(PayRefundChannelOrder::getRefundId,paymentId);
}
/**
* 根据退款单ID和退款通道查询
*/
public Optional<PayRefundOrderChannel> findByPaymentIdAndChannel(Long paymentId, String channel) {
public Optional<PayRefundChannelOrder> findByPaymentIdAndChannel(Long paymentId, String channel) {
return lambdaQuery()
.eq(PayRefundOrderChannel::getRefundId,paymentId)
.eq(PayRefundOrderChannel::getChannel,channel)
.eq(PayRefundChannelOrder::getRefundId,paymentId)
.eq(PayRefundChannelOrder::getChannel,channel)
.oneOpt();
}
}

View File

@@ -1,6 +1,6 @@
package cn.bootx.platform.daxpay.service.core.order.refund.dao;
import cn.bootx.platform.daxpay.service.core.order.refund.entity.PayRefundOrderChannel;
import cn.bootx.platform.daxpay.service.core.order.refund.entity.PayRefundChannelOrder;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Mapper;
@@ -10,5 +10,5 @@ import org.apache.ibatis.annotations.Mapper;
* @since 2024/1/17
*/
@Mapper
public interface PayRefundOrderChannelMapper extends BaseMapper<PayRefundOrderChannel> {
public interface PayRefundChannelOrderMapper extends BaseMapper<PayRefundChannelOrder> {
}

View File

@@ -0,0 +1,57 @@
package cn.bootx.platform.daxpay.service.core.order.refund.entity;
import cn.bootx.platform.common.core.function.EntityBaseFunction;
import cn.bootx.platform.common.mybatisplus.base.MpBaseEntity;
import cn.bootx.platform.daxpay.service.core.order.refund.convert.RefundOrderChannelConvert;
import cn.bootx.platform.daxpay.service.dto.order.refund.RefundChannelOrderDto;
import cn.bootx.table.modify.annotation.DbColumn;
import cn.bootx.table.modify.annotation.DbTable;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
/**
* 支付退款订单关联通道信息
* @author xxm
* @since 2024/1/17
*/
@EqualsAndHashCode(callSuper = true)
@Data
@DbTable(comment = "支付退款通道订单")
@Accessors(chain = true)
@TableName("pay_refund_channel_order")
public class PayRefundChannelOrder extends MpBaseEntity implements EntityBaseFunction<RefundChannelOrderDto> {
@DbColumn(comment = "关联退款id")
private Long refundId;
@DbColumn(comment = "通道支付单id")
private Long payChannelId;
/**
* 异步支付通道发给网关的退款号, 用与将记录关联起来
*/
@DbColumn(comment = "关联网关退款号")
private String gatewayRequestNo;
@DbColumn(comment = "通道")
private String channel;
@DbColumn(comment = "异步支付方式")
private boolean async;
@DbColumn(comment = "订单金额")
private Integer totalAmount;
@DbColumn(comment = "退款金额")
private Integer amount;
/**
* 转换
*/
@Override
public RefundChannelOrderDto toDto() {
return RefundOrderChannelConvert.CONVERT.convert(this);
}
}

View File

@@ -47,10 +47,6 @@ public class PayRefundOrder extends MpBaseEntity implements EntityBaseFunction<P
@DbColumn(comment = "退款号")
private String refundNo;
/** 异步方式关联退款请求号(部分退款情况) */
@DbColumn(comment = "异步方式关联退款请求号(部分退款情况)")
private String refundRequestNo;
/** 标题 */
@DbColumn(comment = "标题")
private String title;

View File

@@ -1,39 +0,0 @@
package cn.bootx.platform.daxpay.service.core.order.refund.entity;
import cn.bootx.platform.common.mybatisplus.base.MpBaseEntity;
import cn.bootx.table.modify.annotation.DbColumn;
import cn.bootx.table.modify.annotation.DbTable;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
/**
* 支付退款订单关联通道信息
* @author xxm
* @since 2024/1/17
*/
@EqualsAndHashCode(callSuper = true)
@Data
@DbTable(comment = "支付退款通道订单")
@Accessors(chain = true)
@TableName("pay_refund_order_channel")
public class PayRefundOrderChannel extends MpBaseEntity {
@DbColumn(comment = "关联退款id")
private Long refundId;
@DbColumn(comment = "通道")
private String channel;
/** 关联支付网关退款请求号 */
@DbColumn(comment = "异步方式关联退款请求号(部分退款情况)")
private String refundRequestNo;
@DbColumn(comment = "异步支付方式")
private boolean async;
@DbColumn(comment = "退款金额")
private Integer amount;
}

View File

@@ -6,15 +6,16 @@ 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.param.pay.QueryRefundParam;
import cn.bootx.platform.daxpay.result.order.RefundOrderChannelResult;
import cn.bootx.platform.daxpay.result.order.RefundChannelOrderResult;
import cn.bootx.platform.daxpay.result.order.RefundOrderResult;
import cn.bootx.platform.daxpay.service.core.order.refund.convert.PayRefundOrderConvert;
import cn.bootx.platform.daxpay.service.core.order.refund.convert.RefundOrderChannelConvert;
import cn.bootx.platform.daxpay.service.core.order.refund.dao.PayRefundOrderChannelManager;
import cn.bootx.platform.daxpay.service.core.order.refund.dao.PayRefundChannelOrderManager;
import cn.bootx.platform.daxpay.service.core.order.refund.dao.PayRefundOrderManager;
import cn.bootx.platform.daxpay.service.core.order.refund.entity.PayRefundChannelOrder;
import cn.bootx.platform.daxpay.service.core.order.refund.entity.PayRefundOrder;
import cn.bootx.platform.daxpay.service.core.order.refund.entity.PayRefundOrderChannel;
import cn.bootx.platform.daxpay.service.dto.order.refund.PayRefundOrderDto;
import cn.bootx.platform.daxpay.service.dto.order.refund.RefundChannelOrderDto;
import cn.bootx.platform.daxpay.service.param.order.PayRefundOrderQuery;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
@@ -35,10 +36,10 @@ import java.util.stream.Collectors;
@Slf4j
@Service
@RequiredArgsConstructor
public class PayRefundOrderService {
public class PayRefundQueryService {
private final PayRefundOrderManager refundOrderManager;
private final PayRefundOrderChannelManager refundOrderChannelManager;
private final PayRefundChannelOrderManager refundOrderChannelManager;
/**
* 分页查询
@@ -52,7 +53,27 @@ public class PayRefundOrderService {
* 根据id查询
*/
public PayRefundOrderDto findById(Long id) {
return refundOrderManager.findById(id).map(PayRefundOrder::toDto).orElseThrow(DataNotExistException::new);
return refundOrderManager.findById(id).map(PayRefundOrder::toDto)
.orElseThrow(() -> new DataNotExistException("退款订单不存在"));
}
/**
* 通道退款订单列表查询
*/
public List<RefundChannelOrderDto> listByChannel(Long refundId){
List<PayRefundChannelOrder> refundOrderChannels = refundOrderChannelManager.findAllByRefundId(refundId);
return refundOrderChannels.stream()
.map(RefundOrderChannelConvert.CONVERT::convert)
.collect(Collectors.toList());
}
/**
* 查询通道退款订单详情
*/
public RefundChannelOrderDto findChannelById(Long id) {
return refundOrderChannelManager.findById(id)
.map(PayRefundChannelOrder::toDto)
.orElseThrow(() -> new DataNotExistException("通道退款订单不存在"));
}
/**
@@ -75,8 +96,8 @@ public class PayRefundOrderService {
.orElseThrow(() -> new DataNotExistException("未查询到支付订单"));
}
// 查询退款明细
List<PayRefundOrderChannel> refundOrderChannels = refundOrderChannelManager.findAllByRefundId(refundOrder.getId());
List<RefundOrderChannelResult> channels = refundOrderChannels.stream()
List<PayRefundChannelOrder> refundOrderChannels = refundOrderChannelManager.findAllByRefundId(refundOrder.getId());
List<RefundChannelOrderResult> channels = refundOrderChannels.stream()
.map(RefundOrderChannelConvert.CONVERT::convertResult)
.collect(Collectors.toList());

View File

@@ -14,10 +14,10 @@ import cn.bootx.platform.daxpay.service.common.local.PaymentContextLocal;
import cn.bootx.platform.daxpay.service.core.order.pay.service.PayOrderQueryService;
import cn.bootx.platform.daxpay.service.core.payment.sync.service.PaySyncService;
import cn.bootx.platform.daxpay.service.core.order.pay.builder.PaymentBuilder;
import cn.bootx.platform.daxpay.service.core.order.pay.dao.PayOrderChannelManager;
import cn.bootx.platform.daxpay.service.core.order.pay.dao.PayChannelOrderManager;
import cn.bootx.platform.daxpay.service.core.order.pay.dao.PayOrderExtraManager;
import cn.bootx.platform.daxpay.service.core.order.pay.entity.PayOrder;
import cn.bootx.platform.daxpay.service.core.order.pay.entity.PayOrderChannel;
import cn.bootx.platform.daxpay.service.core.order.pay.entity.PayChannelOrder;
import cn.bootx.platform.daxpay.service.core.order.pay.entity.PayOrderExtra;
import cn.bootx.platform.daxpay.service.core.order.pay.service.PayOrderService;
import cn.bootx.platform.daxpay.util.PayUtil;
@@ -50,7 +50,7 @@ public class PayAssistService {
private final PayOrderExtraManager payOrderExtraManager;
private final PayOrderChannelManager payOrderChannelManager;
private final PayChannelOrderManager payChannelOrderManager;
/**
* 初始化支付相关上下文
@@ -117,7 +117,7 @@ public class PayAssistService {
public PayChannelParam getAsyncPayParam(PayParam payParam, PayOrder payOrder) {
// 查询之前的支付方式
String asyncPayChannel = payOrder.getAsyncChannel();
PayOrderChannel payOrderChannel = payOrderChannelManager.findByPaymentIdAndChannel(payOrder.getId(), asyncPayChannel)
PayChannelOrder payChannelOrder = payChannelOrderManager.findByPaymentIdAndChannel(payOrder.getId(), asyncPayChannel)
.orElseThrow(() -> new PayFailureException("支付方式数据异常"));
// 新的异步支付方式
@@ -127,7 +127,7 @@ public class PayAssistService {
.findFirst()
.orElseThrow(() -> new PayFailureException("支付方式数据异常"));
// 新传入的金额是否一致
if (!Objects.equals(payOrderChannel.getAmount(), payChannelParam.getAmount())){
if (!Objects.equals(payChannelOrder.getAmount(), payChannelParam.getAmount())){
throw new PayFailureException("传入的支付金额非法!与订单金额不一致");
}
return payChannelParam;
@@ -144,11 +144,11 @@ public class PayAssistService {
PayOrderExtra payOrderExtra = PaymentBuilder.buildPayOrderExtra(payParam, payOrder.getId());
payOrderExtraManager.save(payOrderExtra);
// 构建支付通道表并保存
List<PayOrderChannel> payOrderChannels = PaymentBuilder.buildPayChannel(payParam.getPayChannels())
List<PayChannelOrder> payChannelOrders = PaymentBuilder.buildPayChannel(payParam.getPayChannels())
.stream()
.peek(o -> o.setPaymentId(payOrder.getId()).setAsync(payOrder.isAsyncPay()))
.collect(Collectors.toList());
payOrderChannelManager.saveAll(payOrderChannels);
payChannelOrderManager.saveAll(payChannelOrders);
return payOrder;
}

View File

@@ -54,7 +54,7 @@ public class PayRefundStrategyFactory {
default:
throw new PayUnsupportedMethodException();
}
strategy.setChannelParam(refundChannelParam);
strategy.setRefundChannelParam(refundChannelParam);
return strategy;
}

View File

@@ -7,14 +7,16 @@ import cn.bootx.platform.daxpay.code.PayStatusEnum;
import cn.bootx.platform.daxpay.exception.pay.PayFailureException;
import cn.bootx.platform.daxpay.param.pay.RefundChannelParam;
import cn.bootx.platform.daxpay.param.pay.RefundParam;
import cn.bootx.platform.daxpay.service.common.context.AsyncRefundLocal;
import cn.bootx.platform.daxpay.service.common.context.RefundLocal;
import cn.bootx.platform.daxpay.service.common.context.NoticeLocal;
import cn.bootx.platform.daxpay.service.common.context.PlatformLocal;
import cn.bootx.platform.daxpay.service.common.local.PaymentContextLocal;
import cn.bootx.platform.daxpay.service.core.order.pay.entity.PayOrder;
import cn.bootx.platform.daxpay.service.core.order.pay.service.PayOrderQueryService;
import cn.bootx.platform.daxpay.service.core.order.refund.dao.PayRefundChannelOrderManager;
import cn.bootx.platform.daxpay.service.core.order.refund.dao.PayRefundOrderManager;
import cn.bootx.platform.daxpay.service.core.order.refund.entity.PayRefundOrder;
import cn.bootx.platform.daxpay.service.core.order.refund.entity.PayRefundChannelOrder;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.StrUtil;
import lombok.RequiredArgsConstructor;
@@ -40,6 +42,7 @@ public class PayRefundAssistService {
private final PayOrderQueryService payOrderQueryService;
private final PayRefundOrderManager payRefundOrderManager;
private final PayRefundChannelOrderManager payRefundChannelOrderManager;
/**
* 初始化上下文
@@ -123,9 +126,9 @@ public class PayRefundAssistService {
* 保存退款记录 成不成功都记录
*/
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void saveOrder(RefundParam refundParam, PayOrder payOrder){
AsyncRefundLocal asyncRefundInfo = PaymentContextLocal.get().getAsyncRefundInfo();
// 退款金额
public PayRefundOrder generateRefundOrder(RefundParam refundParam, PayOrder payOrder){
RefundLocal asyncRefundInfo = PaymentContextLocal.get().getRefundInfo();
// 退款金额
Integer amount = refundParam.getRefundChannels()
.stream()
.map(RefundChannelParam::getAmount)
@@ -137,7 +140,6 @@ public class PayRefundAssistService {
.setRefundNo(refundParam.getRefundNo())
.setAmount(amount)
.setRefundableBalance(payOrder.getRefundableBalance())
.setRefundRequestNo(asyncRefundInfo.getRefundRequestNo())
.setRefundTime(LocalDateTime.now())
.setTitle(payOrder.getTitle())
.setErrorCode(asyncRefundInfo.getErrorCode())
@@ -151,6 +153,26 @@ public class PayRefundAssistService {
refundOrder.setRefundNo(String.valueOf(id))
.setId(id);
}
return refundOrder;
}
/**
* 保存退款记录和对应的通道记录
*/
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void saveOrderAndChannels(PayRefundOrder refundOrder,List<PayRefundChannelOrder> refundChannelOrders){
payRefundOrderManager.save(refundOrder);
for (PayRefundChannelOrder refundOrderChannel : refundChannelOrders) {
refundOrderChannel.setRefundId(refundOrder.getId());
}
payRefundChannelOrderManager.saveAll(refundChannelOrders);
}
/**
* 保存退款记录, 开启新事物
*/
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void saveOrder(PayRefundOrder refundOrder){
payRefundOrderManager.save(refundOrder);
}
}

View File

@@ -1,17 +1,23 @@
package cn.bootx.platform.daxpay.service.core.payment.refund.service;
import cn.bootx.platform.common.core.exception.RepetitiveOperationException;
import cn.bootx.platform.common.core.function.CollectorsFunction;
import cn.bootx.platform.common.core.util.ValidationUtil;
import cn.bootx.platform.daxpay.code.PayStatusEnum;
import cn.bootx.platform.daxpay.service.core.payment.refund.factory.PayRefundStrategyFactory;
import cn.bootx.platform.daxpay.service.core.order.pay.entity.PayOrder;
import cn.bootx.platform.daxpay.service.core.order.pay.service.PayOrderService;
import cn.bootx.platform.daxpay.exception.pay.PayFailureException;
import cn.bootx.platform.daxpay.exception.pay.PayUnsupportedMethodException;
import cn.bootx.platform.daxpay.service.func.AbsPayRefundStrategy;
import cn.bootx.platform.daxpay.param.pay.RefundChannelParam;
import cn.bootx.platform.daxpay.param.pay.RefundParam;
import cn.bootx.platform.daxpay.param.pay.SimpleRefundParam;
import cn.bootx.platform.daxpay.result.pay.RefundResult;
import cn.bootx.platform.daxpay.service.core.order.pay.dao.PayChannelOrderManager;
import cn.bootx.platform.daxpay.service.core.order.pay.entity.PayChannelOrder;
import cn.bootx.platform.daxpay.service.core.order.pay.entity.PayOrder;
import cn.bootx.platform.daxpay.service.core.order.pay.service.PayOrderService;
import cn.bootx.platform.daxpay.service.core.order.refund.entity.PayRefundChannelOrder;
import cn.bootx.platform.daxpay.service.core.order.refund.entity.PayRefundOrder;
import cn.bootx.platform.daxpay.service.core.payment.refund.factory.PayRefundStrategyFactory;
import cn.bootx.platform.daxpay.service.func.AbsPayRefundStrategy;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollectionUtil;
import com.baomidou.lock.LockInfo;
@@ -23,7 +29,9 @@ import org.springframework.transaction.annotation.Transactional;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.function.Function;
import java.util.stream.Collectors;
/**
@@ -40,6 +48,8 @@ public class PayRefundService {
private final PayOrderService payOrderService;
private final PayChannelOrderManager payChannelOrderManager;
private final LockTemplate lockTemplate;
/**
@@ -97,6 +107,7 @@ public class PayRefundService {
.collect(Collectors.toList());
param.setRefundChannels(channelParams);
}
// 分支付通道进行退款
return this.refundByChannel(param,payOrder);
} finally {
lockTemplate.releaseLock(lock);
@@ -107,7 +118,16 @@ public class PayRefundService {
* 分支付通道进行退款
*/
public RefundResult refundByChannel(RefundParam refundParam, PayOrder payOrder){
// 0.基础数据准备
List<RefundChannelParam> refundChannels = refundParam.getRefundChannels();
Map<String, PayChannelOrder> orderChannelMap = payChannelOrderManager.findAllByPaymentId(payOrder.getId())
.stream()
.collect(Collectors.toMap(PayChannelOrder::getChannel, Function.identity(), CollectorsFunction::retainLatest));
// 比对通道支付单是否与可退款记录数量一致
if (orderChannelMap.size() != refundChannels.size()){
throw new PayFailureException("通道支付单是否与可退款记录数量不一致");
}
// 1.获取退款参数方式,通过工厂生成对应的策略组
List<AbsPayRefundStrategy> payRefundStrategies = PayRefundStrategyFactory.createAsyncLast(refundChannels);
if (CollectionUtil.isEmpty(payRefundStrategies)) {
@@ -115,47 +135,65 @@ public class PayRefundService {
}
// 2.初始化退款策略的参数
payRefundStrategies.forEach(refundStrategy -> refundStrategy.initRefundParam(payOrder, refundParam));
for (AbsPayRefundStrategy refundStrategy : payRefundStrategies) {
refundStrategy.initRefundParam(payOrder, refundParam, orderChannelMap.get(refundStrategy.getType().getCode()));
}
try {
// 3.退款前准备
// 3.退款前准备操作
payRefundStrategies.forEach(AbsPayRefundStrategy::doBeforeRefundHandler);
// 4.执行退款策略
payRefundStrategies.forEach(AbsPayRefundStrategy::doRefundHandler);
}
catch (Exception e) {
// 记录退款失败的记录
payRefundAssistService.saveOrder(refundParam,payOrder);
// 5失败处理
this.errorHandler(refundParam, payOrder, e);
throw e;
}
// 5.退款成功后处理
// 5 生成退款相关订单并保存
List<PayRefundChannelOrder> refundChannelOrders = payRefundStrategies.stream()
.map(AbsPayRefundStrategy::generateChannelOrder)
.collect(Collectors.toList());
PayRefundOrder refundOrder = payRefundAssistService.generateRefundOrder(refundParam, payOrder);
payRefundAssistService.saveOrderAndChannels(refundOrder,refundChannelOrders);
// 6.退款成功后支付单处理
this.successHandler(refundParam, payOrder);
// 返回结果
return new RefundResult();
return new RefundResult()
.setRefundId(refundOrder.getId())
.setRefundNo(refundParam.getRefundNo());
}
/**
* 支付订单处理
* 退款订单成功处理, 保存退房订单, 通道退款订单, 更新支付订单
*/
private void successHandler(RefundParam refundParam, PayOrder payOrder) {
// 退款金额
Integer amount = refundParam.getRefundChannels().stream()
.map(RefundChannelParam::getAmount)
.reduce(0, Integer::sum);
// 剩余可退款余额
int refundableBalance = payOrder.getRefundableBalance() - amount;
// 退款完成
if (refundableBalance == 0) {
payOrder.setStatus(PayStatusEnum.REFUNDED.getCode());
}
else {
} else {
payOrder.setStatus(PayStatusEnum.PARTIAL_REFUND.getCode());
}
payOrder.setRefundableBalance(refundableBalance);
payOrderService.updateById(payOrder);
payRefundAssistService.saveOrder(refundParam,payOrder);
}
/**
* 失败处理
*/
private void errorHandler(RefundParam refundParam, PayOrder payOrder, Exception e) {
// 记录退款失败的记录
PayRefundOrder refundOrder = payRefundAssistService.generateRefundOrder(refundParam, payOrder);
payRefundAssistService.saveOrder(refundOrder);
}
}

View File

@@ -1,11 +1,13 @@
package cn.bootx.platform.daxpay.service.core.payment.refund.strategy;
import cn.bootx.platform.daxpay.code.PayChannelEnum;
import cn.bootx.platform.daxpay.service.common.local.PaymentContextLocal;
import cn.bootx.platform.daxpay.service.core.channel.alipay.entity.AliPayConfig;
import cn.bootx.platform.daxpay.service.core.channel.alipay.service.AliPayConfigService;
import cn.bootx.platform.daxpay.service.core.channel.alipay.service.AliPayOrderService;
import cn.bootx.platform.daxpay.service.core.channel.alipay.service.AliPayRefundService;
import cn.bootx.platform.daxpay.service.core.channel.alipay.service.AliPayConfigService;
import cn.bootx.platform.daxpay.service.core.order.pay.service.PayOrderService;
import cn.bootx.platform.daxpay.service.core.order.refund.entity.PayRefundChannelOrder;
import cn.bootx.platform.daxpay.service.func.AbsPayRefundStrategy;
import lombok.RequiredArgsConstructor;
import org.springframework.context.annotation.Scope;
@@ -53,10 +55,21 @@ public class AliPayRefundStrategy extends AbsPayRefundStrategy {
*/
@Override
public void doRefundHandler() {
aliRefundService.refund(this.getOrder(), this.getChannelParam().getAmount());
aliPayOrderService.updateRefund(this.getOrder().getId(), this.getChannelParam().getAmount());
payOrderService.updateRefundSuccess(this.getOrder(), this.getChannelParam().getAmount(), PayChannelEnum.ALI);
aliRefundService.refund(this.getPayOrder(), this.getRefundChannelParam().getAmount());
aliPayOrderService.updateRefund(this.getPayOrder().getId(), this.getRefundChannelParam().getAmount());
payOrderService.updateRefundSuccess(this.getPayOrder(), this.getRefundChannelParam().getAmount(), PayChannelEnum.ALI);
}
/**
* 生成通道退款订单对象
*/
@Override
public PayRefundChannelOrder generateChannelOrder() {
PayRefundChannelOrder payRefundChannelOrder = super.generateChannelOrder();
// 追加关联对款请求号
String refundRequestNo = PaymentContextLocal.get()
.getRefundInfo()
.getGatewayRequestNo();
return payRefundChannelOrder.setGatewayRequestNo(refundRequestNo);
}
}

View File

@@ -38,7 +38,7 @@ public class CashPayRefundStrategy extends AbsPayRefundStrategy {
*/
@Override
public void doRefundHandler() {
cashService.refund(this.getOrder().getId(), this.getChannelParam().getAmount());
paymentService.updateRefundSuccess(this.getOrder(), this.getChannelParam().getAmount(), PayChannelEnum.CASH);
cashService.refund(this.getPayOrder().getId(), this.getRefundChannelParam().getAmount());
paymentService.updateRefundSuccess(this.getPayOrder(), this.getRefundChannelParam().getAmount(), PayChannelEnum.CASH);
}
}

View File

@@ -34,4 +34,5 @@ public class UnionPayRefundStrategy extends AbsPayRefundStrategy {
public void doRefundHandler() {
}
}

View File

@@ -39,8 +39,9 @@ public class VoucherPayRefundStrategy extends AbsPayRefundStrategy {
*/
@Override
public void doRefundHandler() {
voucherPayService.refund(this.getOrder().getId(), this.getChannelParam().getAmount());
voucherPayOrderService.updateRefund(this.getOrder().getId(), this.getChannelParam().getAmount());
payOrderService.updateRefundSuccess(this.getOrder(), this.getChannelParam().getAmount(), PayChannelEnum.VOUCHER);
voucherPayService.refund(this.getPayOrder().getId(), this.getRefundChannelParam().getAmount());
voucherPayOrderService.updateRefund(this.getPayOrder().getId(), this.getRefundChannelParam().getAmount());
payOrderService.updateRefundSuccess(this.getPayOrder(), this.getRefundChannelParam().getAmount(), PayChannelEnum.VOUCHER);
}
}

View File

@@ -38,8 +38,8 @@ public class WalletPayRefundStrategy extends AbsPayRefundStrategy {
*/
@Override
public void doRefundHandler() {
walletPayService.refund(this.getOrder().getId(), this.getChannelParam().getAmount());
walletPayOrderService.updateRefund(this.getOrder().getId(), this.getChannelParam().getAmount());
payOrderService.updateRefundSuccess(this.getOrder(), this.getChannelParam().getAmount(), PayChannelEnum.WALLET);
walletPayService.refund(this.getPayOrder().getId(), this.getRefundChannelParam().getAmount());
walletPayOrderService.updateRefund(this.getPayOrder().getId(), this.getRefundChannelParam().getAmount());
payOrderService.updateRefundSuccess(this.getPayOrder(), this.getRefundChannelParam().getAmount(), PayChannelEnum.WALLET);
}
}

View File

@@ -1,11 +1,13 @@
package cn.bootx.platform.daxpay.service.core.payment.refund.strategy;
import cn.bootx.platform.daxpay.code.PayChannelEnum;
import cn.bootx.platform.daxpay.service.common.local.PaymentContextLocal;
import cn.bootx.platform.daxpay.service.core.channel.wechat.entity.WeChatPayConfig;
import cn.bootx.platform.daxpay.service.core.channel.wechat.service.WeChatPayConfigService;
import cn.bootx.platform.daxpay.service.core.channel.wechat.service.WeChatPayOrderService;
import cn.bootx.platform.daxpay.service.core.channel.wechat.service.WechatRefundService;
import cn.bootx.platform.daxpay.service.core.order.pay.service.PayOrderService;
import cn.bootx.platform.daxpay.service.core.order.refund.entity.PayRefundChannelOrder;
import cn.bootx.platform.daxpay.service.func.AbsPayRefundStrategy;
import lombok.RequiredArgsConstructor;
import org.springframework.context.annotation.Scope;
@@ -54,9 +56,22 @@ public class WeChatPayRefundStrategy extends AbsPayRefundStrategy {
*/
@Override
public void doRefundHandler() {
wechatRefundService.refund(this.getOrder(), this.getChannelParam().getAmount(), this.weChatPayConfig);
weChatPayOrderService.updateRefund(this.getOrder().getId(), this.getChannelParam().getAmount());
payOrderService.updateRefundSuccess(this.getOrder(), this.getChannelParam().getAmount(), PayChannelEnum.WECHAT);
wechatRefundService.refund(this.getPayOrder(), this.getRefundChannelParam().getAmount(), this.getPayChannelOrder(), this.weChatPayConfig);
weChatPayOrderService.updateRefund(this.getPayOrder().getId(), this.getRefundChannelParam().getAmount());
payOrderService.updateRefundSuccess(this.getPayOrder(), this.getRefundChannelParam().getAmount(), PayChannelEnum.WECHAT);
}
/**
* 生成通道退款订单对象
*/
@Override
public PayRefundChannelOrder generateChannelOrder() {
PayRefundChannelOrder payRefundChannelOrder = super.generateChannelOrder();
// 追加关联对款请求号
String refundRequestNo = PaymentContextLocal.get()
.getRefundInfo()
.getGatewayRequestNo();
return payRefundChannelOrder.setGatewayRequestNo(refundRequestNo);
}
}

View File

@@ -7,8 +7,8 @@ import cn.bootx.platform.daxpay.service.core.channel.alipay.entity.AliPayConfig;
import cn.bootx.platform.daxpay.service.core.channel.alipay.service.AliPayCloseService;
import cn.bootx.platform.daxpay.service.core.channel.alipay.service.AliPayConfigService;
import cn.bootx.platform.daxpay.service.core.channel.alipay.service.AliPayOrderService;
import cn.bootx.platform.daxpay.service.core.order.pay.dao.PayOrderChannelManager;
import cn.bootx.platform.daxpay.service.core.order.pay.entity.PayOrderChannel;
import cn.bootx.platform.daxpay.service.core.order.pay.dao.PayChannelOrderManager;
import cn.bootx.platform.daxpay.service.core.order.pay.entity.PayChannelOrder;
import cn.bootx.platform.daxpay.service.func.AbsPayRepairStrategy;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
@@ -31,7 +31,7 @@ public class AliPayRepairStrategy extends AbsPayRepairStrategy {
private final AliPayCloseService closeService;
private final AliPayConfigService aliPayConfigService;
private final PayOrderChannelManager orderChannelManager;
private final PayChannelOrderManager orderChannelManager;
/**
* 修复前处理
@@ -46,7 +46,7 @@ public class AliPayRepairStrategy extends AbsPayRepairStrategy {
*/
@Override
public void doSuccessHandler() {
PayOrderChannel orderChannel = orderChannelManager.findByPaymentIdAndChannel(this.getOrder().getId(), PayChannelEnum.ALI.getCode())
PayChannelOrder orderChannel = orderChannelManager.findByPaymentIdAndChannel(this.getOrder().getId(), PayChannelEnum.ALI.getCode())
.orElseThrow(() -> new PayFailureException("支付宝订单不存在"));
// 将支付方式写入上下文
PaymentContextLocal.get().getAsyncPayInfo().setPayWay(orderChannel.getPayWay());

View File

@@ -6,8 +6,8 @@ import cn.bootx.platform.daxpay.service.core.channel.wechat.entity.WeChatPayConf
import cn.bootx.platform.daxpay.service.core.channel.wechat.service.WeChatPayCloseService;
import cn.bootx.platform.daxpay.service.core.channel.wechat.service.WeChatPayConfigService;
import cn.bootx.platform.daxpay.service.core.channel.wechat.service.WeChatPayOrderService;
import cn.bootx.platform.daxpay.service.core.order.pay.dao.PayOrderChannelManager;
import cn.bootx.platform.daxpay.service.core.order.pay.entity.PayOrderChannel;
import cn.bootx.platform.daxpay.service.core.order.pay.dao.PayChannelOrderManager;
import cn.bootx.platform.daxpay.service.core.order.pay.entity.PayChannelOrder;
import cn.bootx.platform.daxpay.service.func.AbsPayRepairStrategy;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
@@ -28,7 +28,7 @@ import static org.springframework.beans.factory.config.BeanDefinition.SCOPE_PROT
public class WeChatPayRepairStrategy extends AbsPayRepairStrategy {
private final WeChatPayCloseService closeService;
private final WeChatPayOrderService orderService;
private final PayOrderChannelManager payOrderChannelManager;
private final PayChannelOrderManager payChannelOrderManager;
private final WeChatPayConfigService weChatPayConfigService;
@@ -47,7 +47,7 @@ public class WeChatPayRepairStrategy extends AbsPayRepairStrategy {
*/
@Override
public void doSuccessHandler() {
PayOrderChannel orderChannel = payOrderChannelManager.findByPaymentIdAndChannel(this.getOrder().getId(), PayChannelEnum.WECHAT.getCode())
PayChannelOrder orderChannel = payChannelOrderManager.findByPaymentIdAndChannel(this.getOrder().getId(), PayChannelEnum.WECHAT.getCode())
.orElseThrow(() -> new PayFailureException("支付宝订单不存在"));
orderService.updateAsyncSuccess(this.getOrder(), orderChannel.getAmount());
}

View File

@@ -15,7 +15,7 @@ import lombok.experimental.Accessors;
@Data
@Accessors(chain = true)
@Schema(title = "支付订单关联通道信息")
public class PayOrderChannelDto extends BaseDto {
public class PayChanneOrderlDto extends BaseDto {
@Schema(description = "支付id")
private Long paymentId;

View File

@@ -33,9 +33,6 @@ public class PayRefundOrderDto extends BaseDto {
@Schema(description = "退款号")
private String refundNo;
@Schema(description = "异步方式关联退款请求号(部分退款情况)")
private String refundRequestNo;
@Schema(description = "标题")
private String title;

View File

@@ -0,0 +1,42 @@
package cn.bootx.platform.daxpay.service.dto.order.refund;
import cn.bootx.platform.common.core.rest.dto.BaseDto;
import cn.bootx.table.modify.annotation.DbColumn;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
/**
* 支付退款通道订单
* @author xxm
* @since 2024/1/17
*/
@EqualsAndHashCode(callSuper = true)
@Data
@Accessors(chain = true)
@Schema(title = "支付退款通道订单")
public class RefundChannelOrderDto extends BaseDto {
@DbColumn(comment = "关联退款id")
@Schema(description = "关联退款id")
private Long refundId;
@Schema(description = "通道")
private String channel;
@Schema(description = "通道支付单id")
private Long payChannelId;
@Schema(description = "支付网关订单号")
private String gatewayOrderNo;
@Schema(description = "异步支付方式")
private boolean async;
@Schema(description = "订单金额")
private Integer totalAmount;
@Schema(description = "退款金额")
private Integer amount;
}

View File

@@ -1,19 +0,0 @@
package cn.bootx.platform.daxpay.service.dto.order.refund;
import cn.bootx.platform.common.core.rest.dto.BaseDto;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
/**
* 支付退款通道订单
* @author xxm
* @since 2024/1/17
*/
@EqualsAndHashCode(callSuper = true)
@Data
@Accessors(chain = true)
@Schema(title = "支付退款通道订单")
public class RefundOrderChannelDto extends BaseDto {
}

View File

@@ -1,10 +1,11 @@
package cn.bootx.platform.daxpay.service.func;
import cn.bootx.platform.daxpay.code.PayChannelEnum;
import cn.bootx.platform.daxpay.service.common.exception.ExceptionInfo;
import cn.bootx.platform.daxpay.service.core.order.pay.entity.PayOrder;
import cn.bootx.platform.daxpay.param.pay.RefundChannelParam;
import cn.bootx.platform.daxpay.param.pay.RefundParam;
import cn.bootx.platform.daxpay.service.core.order.pay.entity.PayChannelOrder;
import cn.bootx.platform.daxpay.service.core.order.pay.entity.PayOrder;
import cn.bootx.platform.daxpay.service.core.order.refund.entity.PayRefundChannelOrder;
import lombok.Getter;
import lombok.Setter;
@@ -19,13 +20,16 @@ import lombok.Setter;
public abstract class AbsPayRefundStrategy implements PayStrategy{
/** 支付对象 */
private PayOrder order = null;
private PayOrder payOrder = null;
/** 支付通道对象 */
private PayChannelOrder payChannelOrder = null;
/** 退款参数 */
private RefundParam refundParam = null;
/** 当前支付通道退款参数 退款参数中的与这个不一致, 以这个为准 */
private RefundChannelParam channelParam = null;
private RefundChannelParam refundChannelParam = null;
/**
* 策略标识
@@ -36,8 +40,9 @@ public abstract class AbsPayRefundStrategy implements PayStrategy{
/**
* 初始化参数
*/
public void initRefundParam(PayOrder payOrder, RefundParam refundParam) {
this.order = payOrder;
public void initRefundParam(PayOrder payOrder, RefundParam refundParam, PayChannelOrder payChannelOrder) {
this.payOrder = payOrder;
this.payChannelOrder = payChannelOrder;
this.refundParam = refundParam;
}
@@ -53,9 +58,15 @@ public abstract class AbsPayRefundStrategy implements PayStrategy{
public abstract void doRefundHandler();
/**
* 退款失败的处理方式
* 生成通道退款订单对象
*/
public void doErrorHandler(ExceptionInfo exceptionInfo) {
public PayRefundChannelOrder generateChannelOrder() {
return new PayRefundChannelOrder()
.setChannel(this.getPayChannelOrder().getChannel())
.setTotalAmount(this.getPayChannelOrder().getAmount())
.setAmount(this.getRefundChannelParam().getAmount())
.setAsync(this.getPayChannelOrder().isAsync())
.setPayChannelId(this.getPayChannelOrder().getId());
}
}

View File

@@ -30,9 +30,6 @@ public class PayRefundOrderQuery extends QueryOrder {
@QueryParam(type = QueryParam.CompareTypeEnum.LIKE)
private String businessNo;
@Schema(description = "外部网关请求号")
private String refundRequestNo;
@Schema(description = "标题")
@QueryParam(type = QueryParam.CompareTypeEnum.LIKE)
private String title;

View File

@@ -1,6 +1,7 @@
package cn.bootx.platform.daxpay.service.param.reconcile;
import cn.bootx.platform.common.core.annotation.QueryParam;
import cn.bootx.platform.daxpay.code.PayReconcileTradeEnum;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.experimental.Accessors;
@@ -19,12 +20,15 @@ public class ReconcileDetailQuery {
@Schema(description = "关联对账订单ID")
private Long recordOrderId;
/** 交易类型 支付/退款 */
/**
* 交易类型
* @see PayReconcileTradeEnum
*/
@Schema(description = "交易类型")
private String type;
/** 订单id - 支付ID/退款ID等 */
@Schema(description = "订单id")
/** 本地支付ID */
@Schema(description = "本地支付ID")
private String paymentId;
/** 本地退款ID */
@@ -39,8 +43,8 @@ public class ReconcileDetailQuery {
@Schema(description = "交易金额")
private Integer amount;
/** 商品名称 */
/** 订单名称 */
@QueryParam(type = QueryParam.CompareTypeEnum.LIKE)
@Schema(description = "商品名称")
@Schema(description = "订单名称")
private String title;
}