fix 退款修复时完成时间为空, 通道退款订单可退余额不正确问题修复

This commit is contained in:
bootx
2024-02-08 00:55:25 +08:00
parent 28b96cf822
commit 5ea91081eb
53 changed files with 924 additions and 129 deletions

View File

@@ -14,6 +14,8 @@ import java.time.LocalDateTime;
@Accessors(chain = true)
public class RequestLocal {
/** 接口信息 */
/** 客户端ip */
private String clientIp;

View File

@@ -47,13 +47,14 @@ public class AliPayRefundService {
log.error("网关返回退款失败: {}", response.getSubMsg());
throw new PayFailureException(response.getSubMsg());
}
// 接口返回fund_change=Y为退款成功fund_change=N或无此字段值返回时需通过退款查询接口进一步确认退款状态
if (response.getFundChange().equals("Y")){
// refundInfo.setStatus(RefundStatusEnum.SUCCESS)
// .setGatewayOrderNo(response.getTradeNo());
}
// 默认为退款中状态
refundInfo.setStatus(RefundStatusEnum.PROGRESS)
.setGatewayOrderNo(response.getTradeNo());
// 接口返回fund_change=Y为退款成功fund_change=N或无此字段值返回时需通过退款查询接口进一步确认退款状态
if (response.getFundChange().equals("Y")){
refundInfo.setStatus(RefundStatusEnum.SUCCESS);
}
}
catch (AlipayApiException e) {
log.error("订单退款失败:", e);

View File

@@ -1,6 +1,6 @@
package cn.bootx.platform.daxpay.service.core.order.pay.convert;
import cn.bootx.platform.daxpay.result.order.PayOrderChannelResult;
import cn.bootx.platform.daxpay.result.order.PayChannelOrderResult;
import cn.bootx.platform.daxpay.service.core.order.pay.entity.PayOrder;
import cn.bootx.platform.daxpay.service.core.order.pay.entity.PayChannelOrder;
import cn.bootx.platform.daxpay.service.core.order.pay.entity.PayOrderExtra;
@@ -25,5 +25,5 @@ public interface PayOrderConvert {
PayChannelOrderDto convert(PayChannelOrder in);
PayOrderChannelResult convertResult(PayChannelOrder in);
PayChannelOrderResult convertResult(PayChannelOrder in);
}

View File

@@ -87,12 +87,10 @@ public class PayChannelOrderService {
* 更新异步支付通道退款余额和状态
*/
public void updateAsyncPayRefund(PayChannelOrder payChannelOrder, PayRefundChannelOrder refundChannelOrder){
// 支付通道订单可退余额
int refundableBalance = payChannelOrder.getRefundableBalance() - refundChannelOrder.getAmount();
payChannelOrder.setRefundableBalance(refundableBalance);
// 支付通道订单状态
if (Objects.equals(refundChannelOrder.getStatus(), RefundStatusEnum.SUCCESS.getCode())){
PayStatusEnum status = refundableBalance == 0 ? PayStatusEnum.REFUNDED : PayStatusEnum.PARTIAL_REFUND;
// 如果可退金额为0说明已经全部退款
PayStatusEnum status = payChannelOrder.getRefundableBalance() == 0 ? PayStatusEnum.REFUNDED : PayStatusEnum.PARTIAL_REFUND;
payChannelOrder.setStatus(status.getCode());
refundChannelOrder.setRefundTime(LocalDateTime.now());
} else {

View File

@@ -7,7 +7,7 @@ import cn.bootx.platform.common.core.rest.param.PageParam;
import cn.bootx.platform.common.mybatisplus.util.MpUtil;
import cn.bootx.platform.daxpay.exception.pay.PayFailureException;
import cn.bootx.platform.daxpay.param.pay.QueryPayParam;
import cn.bootx.platform.daxpay.result.order.PayOrderChannelResult;
import cn.bootx.platform.daxpay.result.order.PayChannelOrderResult;
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.PayChannelOrderManager;
@@ -92,12 +92,13 @@ public class PayOrderQueryService {
// 查询通道数据
List<PayChannelOrder> orderChannelList = payChannelOrderManager.findAllByPaymentId(payOrder.getId());
List<PayOrderChannelResult> channels = orderChannelList.stream()
List<PayChannelOrderResult> channels = orderChannelList.stream()
.map(PayOrderConvert.CONVERT::convertResult)
.collect(Collectors.toList());
PayOrderResult payOrderResult = new PayOrderResult();
BeanUtil.copyProperties(payOrder, payOrderResult);
payOrderResult.setPaymentId(payOrder.getId());
payOrderResult.setDescription(payOrderExtra.getDescription())
.setChannels(channels);

View File

@@ -102,6 +102,7 @@ public class PayRefundOrderQueryService {
.collect(Collectors.toList());
RefundOrderResult refundOrderResult = PayRefundOrderConvert.CONVERT.convertResult(refundOrder);
refundOrderResult.setRefundId(refundOrder.getId());
refundOrderResult.setChannels(channels);
return refundOrderResult;
}

View File

@@ -1,10 +1,10 @@
package cn.bootx.platform.daxpay.service.core.payment.common.aop;
import cn.bootx.platform.common.core.util.ValidationUtil;
import cn.bootx.platform.daxpay.service.annotation.PaymentApi;
import cn.bootx.platform.daxpay.service.core.payment.common.service.PaymentSignService;
import cn.bootx.platform.daxpay.exception.pay.PayFailureException;
import cn.bootx.platform.daxpay.param.pay.PayCommonParam;
import cn.bootx.platform.daxpay.service.annotation.PaymentApi;
import cn.bootx.platform.daxpay.service.core.payment.common.service.PaymentSignService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;

View File

@@ -23,7 +23,6 @@ import java.util.Objects;
@RequiredArgsConstructor
public class PaymentSignService {
private final PaymentAssistService paymentAssistService;;
/**

View File

@@ -98,9 +98,9 @@ public class PayRefundAssistService {
if (CollUtil.isEmpty(param.getRefundChannels())) {
throw new ValidationFailedException("退款通道参数不能为空");
}
if (Objects.isNull(param.getRefundNo())) {
throw new ValidationFailedException("部分退款时退款单号必传");
}
// if (Objects.isNull(param.getRefundNo())) {
// throw new ValidationFailedException("部分退款时退款单号必传");
// }
}
// 简单退款校验

View File

@@ -152,8 +152,8 @@ public class PayRefundService {
refundStrategy.initRefundParam(payOrder, refundParam, payChannelOrder);
}
// 对通道支付订单进行预扣款
payRefundStrategies.forEach(AbsRefundStrategy::doPreDeductOrderHandler);
// 生成通道退款订单对象
payRefundStrategies.forEach(AbsRefundStrategy::generateChannelOrder);
// 退款操作的预处理, 使用独立的新事物进行发起, 返回创建成功的退款订单, 成功后才可以进行下一阶段的操作
PayRefundOrder refundOrder = SpringUtil.getBean(this.getClass()).preRefundMethod(refundParam, payOrder, payRefundStrategies);
@@ -193,7 +193,7 @@ public class PayRefundService {
public PayRefundOrder preRefundMethod(RefundParam refundParam, PayOrder payOrder, List<AbsRefundStrategy> payRefundStrategies) {
// --------------------------- 支付订单 -------------------------------------
// 预扣支付相关订单要退款的金额并进行更新
payRefundStrategies.forEach(AbsRefundStrategy::generateChannelOrder);
payRefundStrategies.forEach(AbsRefundStrategy::doPreDeductOrderHandler);
List<PayChannelOrder> channelOrders = payRefundStrategies.stream()
.map(AbsRefundStrategy::getPayChannelOrder)
.collect(Collectors.toList());

View File

@@ -1,10 +1,11 @@
package cn.bootx.platform.daxpay.service.core.payment.repair.service;
import cn.bootx.platform.common.core.function.CollectorsFunction;
import cn.bootx.platform.daxpay.code.RefundStatusEnum;
import cn.bootx.platform.daxpay.code.PayStatusEnum;
import cn.bootx.platform.daxpay.code.RefundStatusEnum;
import cn.bootx.platform.daxpay.service.code.PaymentTypeEnum;
import cn.bootx.platform.daxpay.service.code.RefundRepairWayEnum;
import cn.bootx.platform.daxpay.service.common.context.RepairLocal;
import cn.bootx.platform.daxpay.service.common.local.PaymentContextLocal;
import cn.bootx.platform.daxpay.service.core.order.pay.dao.PayChannelOrderManager;
import cn.bootx.platform.daxpay.service.core.order.pay.entity.PayChannelOrder;
@@ -102,7 +103,7 @@ public class RefundRepairService {
* 退款成功, 更新退款单和支付单
*/
private RefundRepairResult success(PayRefundOrder refundOrder, PayOrder payOrder, List<AbsRefundRepairStrategy> repairStrategies) {
RepairLocal repairInfo = PaymentContextLocal.get().getRepairInfo();
// 订单相关状态
PayStatusEnum beforePayStatus = PayStatusEnum.findByCode(refundOrder.getStatus());
PayStatusEnum afterPayRefundStatus;
@@ -114,8 +115,9 @@ public class RefundRepairService {
} else {
afterPayRefundStatus = PayStatusEnum.PARTIAL_REFUND;
}
// 设置退款为完成状态
refundOrder.setStatus(RefundStatusEnum.SUCCESS.getCode());
// 设置退款为完成状态和完成时间
refundOrder.setStatus(RefundStatusEnum.SUCCESS.getCode())
.setRefundTime(repairInfo.getFinishTime());
payOrder.setStatus(afterPayRefundStatus.getCode());
// 执行退款成功逻辑

View File

@@ -1,12 +1,15 @@
package cn.bootx.platform.daxpay.service.core.payment.repair.strategy.refund;
import cn.bootx.platform.daxpay.code.PayChannelEnum;
import cn.bootx.platform.daxpay.service.common.local.PaymentContextLocal;
import cn.bootx.platform.daxpay.service.func.AbsRefundRepairStrategy;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Service;
import java.time.LocalDateTime;
import static org.springframework.beans.factory.config.BeanDefinition.SCOPE_PROTOTYPE;
/**
@@ -27,4 +30,18 @@ public class AliRefundRepairStrategy extends AbsRefundRepairStrategy {
public PayChannelEnum getChannel() {
return PayChannelEnum.ALI;
}
/**
* 退款成功修复
*/
@Override
public void doSuccessHandler() {
LocalDateTime finishTime = PaymentContextLocal.get()
.getRepairInfo()
.getFinishTime();
// 首先执行父类的修复逻辑
super.doSuccessHandler();
// 异步支付需要追加完成时间
this.getRefundChannelOrder().setRefundTime(finishTime);
}
}

View File

@@ -1,12 +1,15 @@
package cn.bootx.platform.daxpay.service.core.payment.repair.strategy.refund;
import cn.bootx.platform.daxpay.code.PayChannelEnum;
import cn.bootx.platform.daxpay.service.common.local.PaymentContextLocal;
import cn.bootx.platform.daxpay.service.func.AbsRefundRepairStrategy;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Service;
import java.time.LocalDateTime;
import static org.springframework.beans.factory.config.BeanDefinition.SCOPE_PROTOTYPE;
/**
@@ -26,4 +29,18 @@ public class WeChatRefundRepairStrategy extends AbsRefundRepairStrategy {
public PayChannelEnum getChannel() {
return PayChannelEnum.WECHAT;
}
/**
* 退款成功修复
*/
@Override
public void doSuccessHandler() {
LocalDateTime finishTime = PaymentContextLocal.get()
.getRepairInfo()
.getFinishTime();
// 首先执行父类的修复逻辑
super.doSuccessHandler();
// 异步支付需要追加完成时间
this.getRefundChannelOrder().setRefundTime(finishTime);
}
}

View File

@@ -64,11 +64,13 @@ public class PayRefundSyncService {
}
// 如果不是异步支付, 直接返回返回
if (!refundOrder.isAsyncPay()){
return new SyncResult().setSuccess(false).setRepair(false).setErrorMsg("订单没有异步通道的退款,不需要同步");
// return new SyncResult().setSuccess(false).setRepair(false).setErrorMsg("订单没有异步通道的退款,不需要同步");
throw new PayFailureException("订单没有异步通道的退款,不需要同步");
}
// 如果订单已经关闭, 直接返回失败
if (Objects.equals(refundOrder.getStatus(), RefundStatusEnum.CLOSE.getCode())){
return new SyncResult().setSuccess(false).setRepair(false).setErrorMsg("订单已经关闭,不需要同步");
// return new SyncResult().setSuccess(false).setRepair(false). setErrorMsg("订单已经关闭,不需要同步");
throw new PayFailureException("订单已经关闭,不需要同步");
}
return this.syncRefundOrder(refundOrder);
}
@@ -95,7 +97,9 @@ public class PayRefundSyncService {
// 判断是否同步成功
if (Objects.equals(syncResult.getSyncStatus(), RefundSyncStatusEnum.FAIL)) {
// 同步失败, 返回失败响应, 同时记录失败的日志
return new SyncResult().setErrorMsg(syncResult.getErrorMsg());
// return new SyncResult().setErrorMsg(syncResult.getErrorMsg());
this.saveRecord(refundOrder, syncResult, false, null, syncResult.getErrorMsg());
throw new PayFailureException(syncResult.getErrorMsg());
}
// 支付订单的网关订单号是否一致, 不一致进行更新
if (Objects.nonNull(syncResult.getGatewayOrderNo()) && !Objects.equals(syncResult.getGatewayOrderNo(), refundOrder.getGatewayOrderNo())){
@@ -120,13 +124,12 @@ public class PayRefundSyncService {
// 同步失败, 返回失败响应, 同时记录失败的日志
syncResult.setSyncStatus(RefundSyncStatusEnum.FAIL);
this.saveRecord(refundOrder, syncResult, false, null, e.getMessage());
return new SyncResult().setErrorMsg(e.getMessage());
throw e;
}
// 同步成功记录日志
this.saveRecord(refundOrder, syncResult, !statusSync, repairResult.getRepairNo(), null);
return new SyncResult()
.setGatewayStatus(syncResult.getSyncStatus().getCode())
.setSuccess(true)
.setRepair(!statusSync)
.setRepairOrderNo(repairResult.getRepairNo());
} finally {
@@ -175,7 +178,7 @@ public class PayRefundSyncService {
repair = repairService.repair(order, RefundRepairWayEnum.SUCCESS);
break;
case PROGRESS:
// 不进行处理 TODO 添加重试
// 不进行处理
log.warn("退款状态同步接口调用出错");
break;
case FAIL: {

View File

@@ -74,7 +74,8 @@ public class PaySyncService {
}
// 如果不是异步支付, 直接返回返回
if (!payOrder.isAsyncPay()){
return new SyncResult().setSuccess(false).setRepair(false).setErrorMsg("订单没有异步支付方式,不需要同步");
// return new SyncResult().setSuccess(false).setRepair(false).setErrorMsg("订单没有异步支付方式,不需要同步");
throw new PayFailureException("订单没有异步支付方式,不需要同步");
}
// 执行订单同步逻辑
return this.syncPayOrder(payOrder);
@@ -103,7 +104,8 @@ public class PaySyncService {
if (Objects.equals(syncResult.getSyncStatus(), PaySyncStatusEnum.FAIL)){
// 同步失败, 返回失败响应, 同时记录失败的日志
this.saveRecord(payOrder, syncResult, false, null, syncResult.getErrorMsg());
return new SyncResult().setErrorMsg(syncResult.getErrorMsg());
// return new SyncResult().setErrorMsg(syncResult.getErrorMsg());
throw new PayFailureException(syncResult.getErrorMsg());
}
// 支付订单的网关订单号是否一致, 不一致进行更新
if (Objects.nonNull(syncResult.getGatewayOrderNo()) && !Objects.equals(syncResult.getGatewayOrderNo(), payOrder.getGatewayOrderNo())){
@@ -129,14 +131,14 @@ public class PaySyncService {
// 同步失败, 返回失败响应, 同时记录失败的日志
syncResult.setSyncStatus(PaySyncStatusEnum.FAIL);
this.saveRecord(payOrder, syncResult, false, null, e.getMessage());
return new SyncResult().setErrorMsg(e.getMessage());
// return new SyncResult().setErrorMsg(e.getMessage());
throw e;
}
// 同步成功记录日志
this.saveRecord( payOrder, syncResult, !statusSync, repairResult.getRepairNo(), null);
return new SyncResult()
.setGatewayStatus(syncResult.getSyncStatus().getCode())
.setSuccess(true)
.setRepair(!statusSync)
.setRepairOrderNo(repairResult.getRepairNo());
} finally {
@@ -180,7 +182,7 @@ public class PaySyncService {
return true;
}
// TODO 退款比对
// 退款比对状态不做额外处理, 需要通过退款接口进行处理
if (orderStatus.equals(PayStatusEnum.REFUNDED.getCode()) && syncStatus.equals(PaySyncStatusEnum.REFUND)){
return true;
}
@@ -221,7 +223,7 @@ public class PaySyncService {
}
// 调用出错
case FAIL: {
// 不进行处理 TODO 添加重试
// 不进行处理
log.warn("支付状态同步接口调用出错");
break;
}

View File

@@ -75,12 +75,14 @@ public abstract class AbsRefundStrategy implements PayStrategy{
* 退款发起成功操作, 异步支付通道需要进行重写
*/
public void doSuccessHandler() {
// 更新退款订单数据状态
this.refundChannelOrder.setStatus(RefundStatusEnum.SUCCESS.getCode()).setRefundTime(LocalDateTime.now());
// 更新退款订单数据状态和完成时间
this.refundChannelOrder
.setStatus(RefundStatusEnum.SUCCESS.getCode()).
setRefundTime(LocalDateTime.now());
// 支付通道订单可退余额
int refundableBalance = this.getPayChannelOrder().getRefundableBalance() - this.refundChannelOrder.getAmount();
// 支付通道订单状态
// 如果可退金额为0说明已经全部退款
PayStatusEnum status = refundableBalance == 0 ? PayStatusEnum.REFUNDED : PayStatusEnum.PARTIAL_REFUND;
this.payChannelOrder.setRefundableBalance(refundableBalance)
.setStatus(status.getCode());