feat 退款回调联调

This commit is contained in:
bootx
2024-01-27 01:42:29 +08:00
parent 7b6dbe8d05
commit 7fa88d527a
35 changed files with 137 additions and 402 deletions

View File

@@ -45,8 +45,8 @@ public class PayRefundOrderController {
@Operation(summary = "查询单条")
@GetMapping("/findById")
public ResResult<PayRefundOrderDto> findById(Long paymentId){
return Res.ok(payRefundQueryService.findById(paymentId));
public ResResult<PayRefundOrderDto> findById(Long id){
return Res.ok(payRefundQueryService.findById(id));
}
@Operation(summary = "通道退款订单列表查询")

View File

@@ -20,6 +20,8 @@ public enum PayRefundStatusEnum {
* 接口调用成功不代表成功
*/
PROGRESS("progress","退款中"),
/** 部分成功 */
PART_SUCCESS("part_success","部分成功"),
SUCCESS("success","成功"),
FAIL("fail","失败");

View File

@@ -20,9 +20,6 @@ public class RefundChannelOrderResult {
@Schema(description = "通道支付单id")
private Long payChannelId;
@Schema(description = "支付网关订单号")
private String gatewayOrderNo;
@Schema(description = "异步支付方式")
private boolean async;

View File

@@ -36,15 +36,21 @@ public class RefundOrderResult {
@Schema(description = "剩余可退")
private BigDecimal refundableBalance;
/**
* 异步支付通道发给网关的退款号, 用与将记录关联起来
*/
@Schema(description = "支付网关订单号")
private String gatewayOrderNo;
@Schema(description = "通道退款订单")
private List<RefundChannelOrderResult> channels;
@Schema(description = "退款终端ip")
private String clientIp;
@Schema(description = "退款时间")
private LocalDateTime refundTime;
@Schema(description = "通道退款订单")
private List<RefundChannelOrderResult> channels;
/**
* @see PayRefundStatusEnum
*/

View File

@@ -92,28 +92,37 @@ public interface WeChatPayCode {
// 交易状态
/** 支付成功 */
String TRADE_SUCCESS = "SUCCESS";
String PAY_SUCCESS = "SUCCESS";
/** 支付失败 */
String TRADE_FAIL = "FAIL";
String PAY_FAIL = "FAIL";
/** 退款 */
String TRADE_REFUND = "REFUND";
String PAY_REFUND = "REFUND";
/** 未支付 */
String TRADE_NOTPAY = "NOTPAY";
String PAY_NOTPAY = "NOTPAY";
/** 已关闭 */
String TRADE_CLOSED = "CLOSED";
String PAY_CLOSED = "CLOSED";
/** 已接收,等待扣款 */
String TRADE_ACCEPT = "ACCEPT";
String PAY_ACCEPT = "ACCEPT";
/** 已撤销(刷卡支付) */
String TRADE_REVOKED = "REVOKED";
String PAY_REVOKED = "REVOKED";
/** 用户支付中(刷卡支付) */
String TRADE_USERPAYING = "USERPAYING";
String PAY_USERPAYING = "USERPAYING";
/** 退款成功 */
String REFUND_USERPAYING = "SUCCESS";
/** 退款异常 */
String REFUND_CHANGE = "CHANGE";
/** 退款关闭 */
String REFUND_REFUNDCLOSE = "REFUNDCLOSE";
/** 支付失败(刷卡支付) */
String TRADE_PAYERROR = "PAYERROR";

View File

@@ -44,7 +44,7 @@ public class CallbackLocal {
private LocalDateTime finishTime;
/** 支付修复ID */
private Long payRepairOrderId;
private Long payRepairId;
/**
* 回调处理状态

View File

@@ -57,14 +57,15 @@ public class AliPayCallbackService extends AbsCallbackStrategy {
public boolean verifyNotify() {
Map<String, String> params =PaymentContextLocal.get().getCallbackInfo().getCallbackParam();
String callReq = JSONUtil.toJsonStr(params);
log.info("支付宝发起回调 报文: {}", callReq);
String appId = params.get(APP_ID);
if (StrUtil.isBlank(appId)) {
log.error("支付宝回调报文 appId 为空 {}", callReq);
log.error("支付宝回调报文appId为空");
return false;
}
AliPayConfig alipayConfig = aliasConfigService.getConfig();
if (Objects.isNull(alipayConfig)) {
log.error("支付宝支付配置不存在: {}", callReq);
log.error("支付宝支付配置不存在");
return false;
}
try {

View File

@@ -43,7 +43,7 @@ public class WeChatPayCallbackService extends AbsCallbackStrategy {
*/
@Override
public PayChannelEnum getChannel() {
return PayChannelEnum.ALI;
return PayChannelEnum.WECHAT;
}
/**
@@ -57,7 +57,7 @@ public class WeChatPayCallbackService extends AbsCallbackStrategy {
String appId = params.get(APPID);
if (StrUtil.isBlank(appId)) {
log.warn("微信回调报文 appId 为空 {}", callReq);
log.warn("微信回调报文 appId 为空");
return false;
}
@@ -69,7 +69,7 @@ public class WeChatPayCallbackService extends AbsCallbackStrategy {
// 支付回调信息校验
WeChatPayConfig weChatPayConfig = weChatPayConfigService.getConfig();
if (Objects.isNull(weChatPayConfig)) {
log.warn("微信支付配置不存在: {}", callReq);
log.warn("微信支付配置不存在");
return false;
}
return WxPayKit.verifyNotify(params, weChatPayConfig.getApiKeyV2(), SignType.HMACSHA256, null);
@@ -121,11 +121,13 @@ public class WeChatPayCallbackService extends AbsCallbackStrategy {
callbackInfo.setGatewayOrderNo(callbackParam.get(REFUND_ID));
// 退款订单Id
callbackInfo.setOrderId(Long.valueOf(callbackParam.get(OUT_REFUND_NO)));
// 交易状态
callbackInfo.setGatewayStatus(callbackParam.get(REFUND_STATUS));
// 退款金额
callbackInfo.setAmount(callbackParam.get(REFUND_FEE));
// 交易状态
PayStatusEnum payStatus = Objects.equals(callbackParam.get(REFUND_STATUS), REFUND_USERPAYING) ? PayStatusEnum.SUCCESS : PayStatusEnum.FAIL;
callbackInfo.setGatewayStatus(payStatus.getCode());
// 退款时间
String timeEnd = callbackParam.get(SUCCESS_TIME);
if (StrUtil.isNotBlank(timeEnd)) {

View File

@@ -202,25 +202,25 @@ public class WeChatPayService {
String resultCode = result.get(WeChatPayCode.RESULT_CODE);
String errCode = result.get(WeChatPayCode.ERR_CODE);
// 支付成功处理,
if (Objects.equals(resultCode, WeChatPayCode.TRADE_SUCCESS)) {
if (Objects.equals(resultCode, WeChatPayCode.PAY_SUCCESS)) {
asyncPayInfo.setGatewayOrderNo(result.get(WeChatPayCode.TRANSACTION_ID))
.setPayComplete(true);
return;
}
// 支付中, 发起轮训同步
if (Objects.equals(resultCode, WeChatPayCode.TRADE_FAIL)
&& Objects.equals(errCode, WeChatPayCode.TRADE_USERPAYING)) {
if (Objects.equals(resultCode, WeChatPayCode.PAY_FAIL)
&& Objects.equals(errCode, WeChatPayCode.PAY_USERPAYING)) {
SpringUtil.getBean(this.getClass()).rotationSync(payment);
asyncPayInfo.setGatewayOrderNo(result.get(WeChatPayCode.TRANSACTION_ID));
return;
}
// 支付撤销
if (Objects.equals(resultCode, WeChatPayCode.TRADE_REVOKED)) {
if (Objects.equals(resultCode, WeChatPayCode.PAY_REVOKED)) {
throw new PayFailureException("用户已撤销支付");
}
// 支付失败
if (Objects.equals(resultCode, WeChatPayCode.TRADE_PAYERROR)
|| Objects.equals(resultCode, WeChatPayCode.TRADE_FAIL)) {
|| Objects.equals(resultCode, WeChatPayCode.PAY_FAIL)) {
String errorMsg = result.get(WeChatPayCode.ERR_CODE_DES);
throw new PayFailureException(errorMsg);
}

View File

@@ -68,25 +68,25 @@ public class WeChatPaySyncService {
// 查询到订单的状态
String tradeStatus = result.get(WeChatPayCode.TRADE_STATE);
// 支付完成
if (Objects.equals(tradeStatus, WeChatPayCode.TRADE_SUCCESS) || Objects.equals(tradeStatus, WeChatPayCode.TRADE_ACCEPT)) {
if (Objects.equals(tradeStatus, WeChatPayCode.PAY_SUCCESS) || Objects.equals(tradeStatus, WeChatPayCode.PAY_ACCEPT)) {
String timeEnd = result.get(WeChatPayCode.TIME_END);
LocalDateTime time = LocalDateTimeUtil.parse(timeEnd, DatePattern.PURE_DATETIME_PATTERN);
PaymentContextLocal.get().getPaySyncInfo().setPayTime(time);
return syncResult.setSyncStatus(PaySyncStatusEnum.PAY_SUCCESS);
}
// 待支付
if (Objects.equals(tradeStatus, WeChatPayCode.TRADE_NOTPAY)
|| Objects.equals(tradeStatus, WeChatPayCode.TRADE_USERPAYING)) {
if (Objects.equals(tradeStatus, WeChatPayCode.PAY_NOTPAY)
|| Objects.equals(tradeStatus, WeChatPayCode.PAY_USERPAYING)) {
return syncResult.setSyncStatus(PaySyncStatusEnum.PAY_WAIT);
}
// 已退款/退款中 触发一下退款记录的查询
if (Objects.equals(tradeStatus, WeChatPayCode.TRADE_REFUND)) {
if (Objects.equals(tradeStatus, WeChatPayCode.PAY_REFUND)) {
this.syncRefundStatus(order, weChatPayConfig);
}
// 已关闭
if (Objects.equals(tradeStatus, WeChatPayCode.TRADE_CLOSED)
|| Objects.equals(tradeStatus, WeChatPayCode.TRADE_REVOKED)
if (Objects.equals(tradeStatus, WeChatPayCode.PAY_CLOSED)
|| Objects.equals(tradeStatus, WeChatPayCode.PAY_REVOKED)
|| Objects.equals(tradeStatus, WeChatPayCode.TRADE_PAYERROR)) {
return syncResult.setSyncStatus(PaySyncStatusEnum.CLOSED);
}

View File

@@ -54,9 +54,9 @@ public class PayOrder extends MpBaseEntity implements EntityBaseFunction<PayOrde
private String asyncChannel;
/**
* 如果有异步支付的情况下, 保存关联网关支付
* 如果有异步支付的情况下, 保存关联网关订单
*/
@DbColumn(comment = "关联网关支付")
@DbColumn(comment = "网关订单")
private String gatewayOrderNo;
/** 金额 */

View File

@@ -30,12 +30,6 @@ public class PayRefundChannelOrder extends MpBaseEntity implements EntityBaseFun
@DbColumn(comment = "通道支付单id")
private Long payChannelId;
/**
* 异步支付通道发给网关的退款号, 用与将记录关联起来
*/
@DbColumn(comment = "关联网关退款号")
private String gatewayOrderNo;
@DbColumn(comment = "通道")
private String channel;

View File

@@ -41,6 +41,10 @@ public class PayRefundOrder extends MpBaseEntity implements EntityBaseFunction<P
@DbColumn(comment = "退款号")
private String refundNo;
/** 如果有异步通道, 保存关联的网关订单号 */
@DbColumn(comment = "网关订单号")
private String gatewayOrderNo;
/** 标题 */
@DbColumn(comment = "标题")
private String title;

View File

@@ -8,6 +8,7 @@ import cn.bootx.platform.daxpay.service.common.context.CallbackLocal;
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.payment.repair.result.PayRepairResult;
import cn.bootx.platform.daxpay.service.core.payment.repair.service.PayRepairService;
import com.baomidou.lock.LockInfo;
import com.baomidou.lock.LockTemplate;
@@ -50,21 +51,16 @@ public class PayCallbackService {
try {
// 获取支付单
PayOrder payOrder = payOrderQueryService.findById(callbackInfo.getOrderId()).orElse(null);
// 支付单不存在,记录回调记录, TODO 取消支付网关的订单支付情况
// 本地支付单不存在,记录回调记录, TODO 需要补单或进行退款
if (Objects.isNull(payOrder)) {
callbackInfo.setCallbackStatus(PayCallbackStatusEnum.NOT_FOUND)
.setMsg("支付单不存在,记录回调记录");
callbackInfo.setCallbackStatus(PayCallbackStatusEnum.NOT_FOUND).setMsg("支付单不存在,记录回调记录");
return;
}
// 设置订单关联网关订单号
payOrder.setGatewayOrderNo(callbackInfo.getGatewayOrderNo());
// 成功状态
if (Objects.equals(PayCallbackStatusEnum.SUCCESS.getCode(), callbackInfo.getGatewayStatus())) {
// 回调时间超出了支付单超时时间, 记录一下, 不做处理 TODO 这块应该把订单给当成正常结束给处理了,
if (Objects.nonNull(payOrder.getExpiredTime())
&& LocalDateTimeUtil.ge(LocalDateTime.now(), payOrder.getExpiredTime())) {
callbackInfo.setCallbackStatus(PayCallbackStatusEnum.EXCEPTION).setMsg("回调时间超出了支付单支付有效时间");
return;
}
// 支付成功处理
this.success(payOrder);
} else {
@@ -81,20 +77,25 @@ public class PayCallbackService {
*/
private void success(PayOrder payOrder) {
CallbackLocal callbackInfo = PaymentContextLocal.get().getCallbackInfo();
// 回调时间超出了支付单超时时间, 记录一下, 不做处理 TODO 这块应该把订单给当成正常结束给处理了,
if (Objects.nonNull(payOrder.getExpiredTime())
&& LocalDateTimeUtil.ge(LocalDateTime.now(), payOrder.getExpiredTime())) {
callbackInfo.setCallbackStatus(PayCallbackStatusEnum.EXCEPTION).setMsg("回调时间超出了支付单支付有效时间");
return;
}
// 支付单已经被支付,不需要重复处理
if (Objects.equals(payOrder.getStatus(), PayStatusEnum.SUCCESS.getCode())) {
callbackInfo.setCallbackStatus(PayCallbackStatusEnum.IGNORE).setMsg("支付单已经是支付成功状态,不进行处理");
return;
}
// 支付单已被取消,记录回调记录 TODO 考虑不全, 需要做退款or人工处理
if (!Objects.equals(payOrder.getStatus(), PayStatusEnum.PROGRESS.getCode())) {
callbackInfo.setCallbackStatus(PayCallbackStatusEnum.EXCEPTION).setMsg("支付单不是待支付状态,记录回调记录");
return;
}
// 执行支付完成修复逻辑
payRepairService.repair(payOrder, PayRepairTypeEnum.SUCCESS);
PayRepairResult repair = payRepairService.repair(payOrder, PayRepairTypeEnum.SUCCESS);
callbackInfo.setPayRepairId(repair.getRepairId());
}
/**
@@ -102,21 +103,19 @@ public class PayCallbackService {
*/
private void fail(PayOrder payOrder) {
CallbackLocal callbackInfo = PaymentContextLocal.get().getCallbackInfo();
// payment已被取消,记录回调记录,后期处理 TODO 考虑不完善, 后续优化
if (!Objects.equals(payOrder.getStatus(), PayStatusEnum.PROGRESS.getCode())) {
callbackInfo.setCallbackStatus(PayCallbackStatusEnum.IGNORE).setMsg("支付单已经取消,记录回调记录");
return;
}
// payment支付成功, 状态非法 TODO 考虑不完善, 后续优化
if (!Objects.equals(payOrder.getStatus(), PayStatusEnum.SUCCESS.getCode())) {
callbackInfo.setCallbackStatus(PayCallbackStatusEnum.EXCEPTION).setMsg("支付单状态非法,支付网关状态为失败,但支付单状态为已完成");
return;
}
// 执行支付关闭修复逻辑
payRepairService.repair(payOrder, PayRepairTypeEnum.CLOSE_LOCAL);
PayRepairResult repair = payRepairService.repair(payOrder, PayRepairTypeEnum.CLOSE_LOCAL);
callbackInfo.setPayRepairId(repair.getRepairId());
}
}

View File

@@ -7,6 +7,7 @@ import cn.bootx.platform.daxpay.service.common.context.CallbackLocal;
import cn.bootx.platform.daxpay.service.common.local.PaymentContextLocal;
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.payment.repair.result.RefundRepairResult;
import cn.bootx.platform.daxpay.service.core.payment.repair.service.RefundRepairService;
import com.baomidou.lock.LockInfo;
import com.baomidou.lock.LockTemplate;
@@ -46,18 +47,24 @@ public class PayRefundCallbackService {
}
try {
// 获取退款单
PayRefundOrder refundOrder = refundOrderManager.findById(callbackInfo.getPayRepairOrderId()).orElse(null);
PayRefundOrder refundOrder = refundOrderManager.findById(callbackInfo.getOrderId()).orElse(null);
// 退款单不存在,记录回调记录
if (Objects.isNull(refundOrder)) {
callbackInfo.setCallbackStatus(PayCallbackStatusEnum.NOT_FOUND).setMsg("退款单不存在,记录回调记录");
return;
}
// 退款单已经被处理, 记录回调记录
if (!Objects.equals(PayRefundStatusEnum.PROGRESS.getCode(), refundOrder.getStatus())) {
callbackInfo.setCallbackStatus(PayCallbackStatusEnum.IGNORE).setMsg("退款单状态已处理,记录回调记录");
}
// 退款成功还是失败
if (Objects.equals(PayRefundStatusEnum.SUCCESS.getCode(), callbackInfo.getGatewayStatus())) {
reflectionService.repair(refundOrder, RefundRepairTypeEnum.SUCCESS);
RefundRepairResult repair = reflectionService.repair(refundOrder, RefundRepairTypeEnum.SUCCESS);
callbackInfo.setPayRepairId(repair.getRepairId());
} else {
reflectionService.repair(refundOrder, RefundRepairTypeEnum.FAIL);
RefundRepairResult repair = reflectionService.repair(refundOrder, RefundRepairTypeEnum.FAIL);
callbackInfo.setPayRepairId(repair.getRepairId());
}
} finally {
@@ -65,22 +72,4 @@ public class PayRefundCallbackService {
}
}
/**
* 成功处理
*/
private void success(PayRefundOrder refundOrder) {
// 支付退款订单修复
// 支付退款订单成功修复
}
/**
* 失败处理
*/
private void fail(PayRefundOrder refundOrder) {
// 退款订单失败修复
// 支付订单退款失败修复
}
}

View File

@@ -120,7 +120,7 @@ public class PayRefundAssistService {
}
/**
* 保存退款记录 成不成功都记录
* 保存退款订单 成不成功都记录
*/
public PayRefundOrder generateRefundOrder(RefundParam refundParam, PayOrder payOrder){
RefundLocal asyncRefundInfo = PaymentContextLocal.get().getRefundInfo();
@@ -139,6 +139,7 @@ public class PayRefundAssistService {
.setRefundableBalance(payOrder.getRefundableBalance())
.setRefundTime(LocalDateTime.now())
.setTitle(payOrder.getTitle())
.setGatewayOrderNo(asyncRefundInfo.getGatewayOrderNo())
.setErrorCode(asyncRefundInfo.getErrorCode())
.setErrorMsg(asyncRefundInfo.getErrorMsg())
.setStatus(asyncRefundInfo.getStatus().getCode())

View File

@@ -2,7 +2,7 @@ package cn.bootx.platform.daxpay.service.core.payment.repair.factory;
import cn.bootx.platform.daxpay.code.PayChannelEnum;
import cn.bootx.platform.daxpay.exception.pay.PayUnsupportedMethodException;
import cn.bootx.platform.daxpay.service.core.payment.repair.strategy.pay.*;
import cn.bootx.platform.daxpay.service.core.payment.repair.strategy.*;
import cn.bootx.platform.daxpay.service.func.AbsPayRepairStrategy;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.extra.spring.SpringUtil;

View File

@@ -1,114 +0,0 @@
package cn.bootx.platform.daxpay.service.core.payment.repair.factory;
import cn.bootx.platform.daxpay.code.PayChannelEnum;
import cn.bootx.platform.daxpay.exception.pay.PayUnsupportedMethodException;
import cn.bootx.platform.daxpay.service.core.payment.repair.strategy.refund.*;
import cn.bootx.platform.daxpay.service.func.AbsRefundRepairStrategy;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.extra.spring.SpringUtil;
import lombok.experimental.UtilityClass;
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.PayChannelEnum.ASYNC_TYPE_CODE;
/**
* 支付退款修复策略工厂
* @author xxm
* @since 2024/1/26
*/
@UtilityClass
public class RefundRepairStrategyFactory {
/**
* 根据传入的支付通道创建策略
* @return 支付策略
*/
public static AbsRefundRepairStrategy create(PayChannelEnum channelEnum) {
AbsRefundRepairStrategy strategy;
switch (channelEnum) {
case ALI:
strategy = SpringUtil.getBean(AliRefundRepairStrategy.class);
break;
case WECHAT:
strategy = SpringUtil.getBean(WeChatRefundRepairStrategy.class);
break;
case UNION_PAY:
strategy = SpringUtil.getBean(UnionRefundRepairStrategy.class);
break;
case CASH:
strategy = SpringUtil.getBean(CashRefundRepairStrategy.class);
break;
case WALLET:
strategy = SpringUtil.getBean(WalletRefundRepairStrategy.class);
break;
case VOUCHER:
strategy = SpringUtil.getBean(VoucherRefundRepairStrategy.class);
break;
default:
throw new PayUnsupportedMethodException();
}
return strategy;
}
/**
* 根据传入的支付类型批量创建策略, 异步支付在后面
*/
public static List<AbsRefundRepairStrategy> createAsyncLast(List<String> channelCodes) {
return create(channelCodes, true);
}
/**
* 根据传入的支付类型批量创建策略, 异步支付在前面
*/
public static List<AbsRefundRepairStrategy> createAsyncFront(List<String> channelCodes) {
return create(channelCodes, false);
}
/**
* 根据传入的支付类型批量创建策略
* @param asyncSort 是否异步支付在后面
* @return 支付策略
*/
private static List<AbsRefundRepairStrategy> create(List<String> channelCodes, boolean asyncSort) {
if (CollectionUtil.isEmpty(channelCodes)) {
return Collections.emptyList();
}
// 同步支付
val syncChannels = channelCodes.stream()
.filter(code -> !ASYNC_TYPE_CODE.contains(code))
.map(PayChannelEnum::findByCode)
.collect(Collectors.toList());
// 异步支付
val asyncChannels = channelCodes.stream()
.filter(ASYNC_TYPE_CODE::contains)
.map(PayChannelEnum::findByCode)
.collect(Collectors.toList());
List<PayChannelEnum> sortList = new ArrayList<>(channelCodes.size());
// 异步在后面
if (asyncSort) {
sortList.addAll(syncChannels);
sortList.addAll(asyncChannels);
}
else {
sortList.addAll(asyncChannels);
sortList.addAll(syncChannels);
}
// 此处有一个根据Type的反转排序
return sortList.stream()
.filter(Objects::nonNull)
.map(RefundRepairStrategyFactory::create)
.collect(Collectors.toList());
}
}

View File

@@ -59,7 +59,7 @@ public class RefundRepairService {
PayChannelOrder payChannelOrder = payChannelOrderManager.findByPaymentIdAndChannel(payOrder.getId(), payOrder.getAsyncChannel())
.orElseThrow(DataNotExistException::new);
// 异步通道退款单
PayRefundChannelOrder refundChannelOrder = refundChannelOrderManager.findByPaymentIdAndChannel(refundOrder.getPaymentId(), payOrder.getAsyncChannel())
PayRefundChannelOrder refundChannelOrder = refundChannelOrderManager.findByPaymentIdAndChannel(refundOrder.getId(), payOrder.getAsyncChannel())
.orElseThrow(DataNotExistException::new);
// 根据不同的类型执行对应的修复逻辑
@@ -106,7 +106,7 @@ public class RefundRepairService {
// 设置退款为完成状态
refundOrder.setStatus(PayRefundStatusEnum.SUCCESS.getCode());
refundChannelOrder.setStatus(PayRefundStatusEnum.SUCCESS.getCode());
payOrder.setStatus(afterPayRefundStatus.getCode());
// 更新订单和退款相关订单
payChannelOrderManager.updateById(payChannelOrder);
payOrderManager.updateById(payOrder);
@@ -124,42 +124,52 @@ public class RefundRepairService {
* 退款失败, 将失败的退款金额归还回订单
*/
private RefundRepairResult closeLocal(PayRefundOrder refundOrder, PayOrder payOrder, PayRefundChannelOrder refundChannelOrder, PayChannelOrder payChannelOrder) {
// 要返回的状态
RefundRepairResult repairResult = new RefundRepairResult();
// 更新通道支付单全部退款还是部分退款
if (Objects.equals(payChannelOrder.getRefundableBalance(),0)){
payChannelOrder.setStatus(PayStatusEnum.REFUNDED.getCode());
} else {
payChannelOrder.setStatus(PayStatusEnum.PARTIAL_REFUND.getCode());
}
// 订单相关状态
// 订单修复前状态
PayStatusEnum beforePayStatus = PayStatusEnum.findByCode(refundOrder.getStatus());
PayStatusEnum afterPayRefundStatus;
PayRefundStatusEnum beforeRefundStatus = PayRefundStatusEnum.findByCode(refundOrder.getStatus());
repairResult.setBeforePayStatus(beforePayStatus)
.setBeforeRefundStatus(beforeRefundStatus);
// 判断订单是支付成功还是部分退款
if (Objects.equals(payOrder.getRefundableBalance(),0)){
afterPayRefundStatus = PayStatusEnum.REFUNDED;
// 退款失败返还后的余额
int payOrderAmount = refundChannelOrder.getAmount() + payOrder.getRefundableBalance();
int payChannelOrderAmount = refundChannelOrder.getAmount() + payChannelOrder.getRefundableBalance();
// 退款失败返还后的余额+可退余额 == 订单金额 支付订单回退为为支付成功状态
if (payOrderAmount == payOrder.getAmount()){
payOrder.setStatus(PayStatusEnum.SUCCESS.getCode());
// 说明这个退款只有异步这一个支付, 所以也可以直接回退
payChannelOrder.setStatus(PayStatusEnum.SUCCESS.getCode());
repairResult.setAfterPayStatus(PayStatusEnum.SUCCESS);
} else {
afterPayRefundStatus = PayStatusEnum.PARTIAL_REFUND;
// 回归部分退款状态
payOrder.setStatus(PayStatusEnum.PARTIAL_REFUND.getCode());
repairResult.setAfterPayStatus(PayStatusEnum.PARTIAL_REFUND);
}
// 更新支付订单相关的可退款金额
payOrder.setRefundableBalance(payOrderAmount);
payChannelOrder.setRefundableBalance(payChannelOrderAmount);
// 设置退款为失败状态
refundOrder.setStatus(PayRefundStatusEnum.FAIL.getCode());
refundChannelOrder.setStatus(PayRefundStatusEnum.FAIL.getCode());
// 判断退款订单是否只有异步通道一个关联的通道退款单, 退款值一致说明是一个
if (Objects.equals(refundOrder.getAmount(), refundChannelOrder.getAmount())){
// 退款单和通道退款单统一设置为失败
refundOrder.setStatus(PayRefundStatusEnum.FAIL.getCode());
refundChannelOrder.setStatus(PayRefundStatusEnum.FAIL.getCode());
repairResult.setAfterRefundStatus(PayRefundStatusEnum.FAIL);
} else {
// 退款单设置为部分成功状态, 通道退款单设置为失败状态
refundOrder.setStatus(PayRefundStatusEnum.FAIL.getCode());
refundChannelOrder.setStatus(PayRefundStatusEnum.FAIL.getCode());
repairResult.setAfterRefundStatus(PayRefundStatusEnum.PART_SUCCESS);
}
// 更新订单和退款相关订单
payChannelOrderManager.updateById(payChannelOrder);
payOrderManager.updateById(payOrder);
refundOrderManager.updateById(refundOrder);
refundChannelOrderManager.updateById(refundChannelOrder);
return new RefundRepairResult()
.setBeforePayStatus(beforePayStatus)
.setAfterPayStatus(afterPayRefundStatus)
.setBeforeRefundStatus(beforeRefundStatus)
.setAfterRefundStatus(PayRefundStatusEnum.FAIL);
return repairResult;
}
/**

View File

@@ -1,4 +1,4 @@
package cn.bootx.platform.daxpay.service.core.payment.repair.strategy.pay;
package cn.bootx.platform.daxpay.service.core.payment.repair.strategy;
import cn.bootx.platform.daxpay.code.PayChannelEnum;
import cn.bootx.platform.daxpay.code.PayStatusEnum;

View File

@@ -1,4 +1,4 @@
package cn.bootx.platform.daxpay.service.core.payment.repair.strategy.pay;
package cn.bootx.platform.daxpay.service.core.payment.repair.strategy;
import cn.bootx.platform.daxpay.code.PayChannelEnum;
import cn.bootx.platform.daxpay.code.PayStatusEnum;

View File

@@ -1,4 +1,4 @@
package cn.bootx.platform.daxpay.service.core.payment.repair.strategy.pay;
package cn.bootx.platform.daxpay.service.core.payment.repair.strategy;
import cn.bootx.platform.daxpay.code.PayChannelEnum;
import cn.bootx.platform.daxpay.service.func.AbsPayRepairStrategy;

View File

@@ -1,4 +1,4 @@
package cn.bootx.platform.daxpay.service.core.payment.repair.strategy.pay;
package cn.bootx.platform.daxpay.service.core.payment.repair.strategy;
import cn.bootx.platform.daxpay.code.PayChannelEnum;
import cn.bootx.platform.daxpay.code.PayStatusEnum;

View File

@@ -1,4 +1,4 @@
package cn.bootx.platform.daxpay.service.core.payment.repair.strategy.pay;
package cn.bootx.platform.daxpay.service.core.payment.repair.strategy;
import cn.bootx.platform.daxpay.code.PayChannelEnum;
import cn.bootx.platform.daxpay.code.PayStatusEnum;

View File

@@ -1,4 +1,4 @@
package cn.bootx.platform.daxpay.service.core.payment.repair.strategy.pay;
package cn.bootx.platform.daxpay.service.core.payment.repair.strategy;
import cn.bootx.platform.daxpay.code.PayChannelEnum;
import cn.bootx.platform.daxpay.code.PayStatusEnum;

View File

@@ -1,29 +0,0 @@
package cn.bootx.platform.daxpay.service.core.payment.repair.strategy.refund;
import cn.bootx.platform.daxpay.code.PayChannelEnum;
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 static org.springframework.beans.factory.config.BeanDefinition.SCOPE_PROTOTYPE;
/**
*
* @author xxm
* @since 2024/1/26
*/
@Slf4j
@Scope(SCOPE_PROTOTYPE)
@Service
@RequiredArgsConstructor
public class AliRefundRepairStrategy extends AbsRefundRepairStrategy {
/**
* 策略标识
*/
@Override
public PayChannelEnum getChannel() {
return PayChannelEnum.ALI;
}
}

View File

@@ -1,29 +0,0 @@
package cn.bootx.platform.daxpay.service.core.payment.repair.strategy.refund;
import cn.bootx.platform.daxpay.code.PayChannelEnum;
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 static org.springframework.beans.factory.config.BeanDefinition.SCOPE_PROTOTYPE;
/**
*
* @author xxm
* @since 2024/1/26
*/
@Slf4j
@Scope(SCOPE_PROTOTYPE)
@Service
@RequiredArgsConstructor
public class CashRefundRepairStrategy extends AbsRefundRepairStrategy {
/**
* 策略标识
*/
@Override
public PayChannelEnum getChannel() {
return PayChannelEnum.CASH;
}
}

View File

@@ -1,29 +0,0 @@
package cn.bootx.platform.daxpay.service.core.payment.repair.strategy.refund;
import cn.bootx.platform.daxpay.code.PayChannelEnum;
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 static org.springframework.beans.factory.config.BeanDefinition.SCOPE_PROTOTYPE;
/**
*
* @author xxm
* @since 2024/1/26
*/
@Slf4j
@Scope(SCOPE_PROTOTYPE)
@Service
@RequiredArgsConstructor
public class UnionRefundRepairStrategy extends AbsRefundRepairStrategy {
/**
* 策略标识
*/
@Override
public PayChannelEnum getChannel() {
return PayChannelEnum.UNION_PAY;
}
}

View File

@@ -1,29 +0,0 @@
package cn.bootx.platform.daxpay.service.core.payment.repair.strategy.refund;
import cn.bootx.platform.daxpay.code.PayChannelEnum;
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 static org.springframework.beans.factory.config.BeanDefinition.SCOPE_PROTOTYPE;
/**
*
* @author xxm
* @since 2024/1/26
*/
@Slf4j
@Scope(SCOPE_PROTOTYPE)
@Service
@RequiredArgsConstructor
public class VoucherRefundRepairStrategy extends AbsRefundRepairStrategy {
/**
* 策略标识
*/
@Override
public PayChannelEnum getChannel() {
return PayChannelEnum.VOUCHER;
}
}

View File

@@ -1,24 +0,0 @@
package cn.bootx.platform.daxpay.service.core.payment.repair.strategy.refund;
import cn.bootx.platform.daxpay.code.PayChannelEnum;
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 static org.springframework.beans.factory.config.BeanDefinition.SCOPE_PROTOTYPE;
@Slf4j
@Scope(SCOPE_PROTOTYPE)
@Service
@RequiredArgsConstructor
public class WalletRefundRepairStrategy extends AbsRefundRepairStrategy {
/**
* 策略标识
*/
@Override
public PayChannelEnum getChannel() {
return PayChannelEnum.WALLET;
}
}

View File

@@ -1,29 +0,0 @@
package cn.bootx.platform.daxpay.service.core.payment.repair.strategy.refund;
import cn.bootx.platform.daxpay.code.PayChannelEnum;
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 static org.springframework.beans.factory.config.BeanDefinition.SCOPE_PROTOTYPE;
/**
*
* @author xxm
* @since 2024/1/26
*/
@Slf4j
@Scope(SCOPE_PROTOTYPE)
@Service
@RequiredArgsConstructor
public class WeChatRefundRepairStrategy extends AbsRefundRepairStrategy {
/**
* 策略标识
*/
@Override
public PayChannelEnum getChannel() {
return PayChannelEnum.WECHAT;
}
}

View File

@@ -2,7 +2,6 @@ package cn.bootx.platform.daxpay.service.dto.order.refund;
import cn.bootx.platform.common.core.rest.dto.BaseDto;
import cn.bootx.platform.daxpay.code.PayRefundStatusEnum;
import cn.bootx.platform.daxpay.entity.RefundableInfo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
@@ -10,7 +9,6 @@ import lombok.experimental.Accessors;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.List;
/**
* 退款记录
@@ -33,6 +31,9 @@ public class PayRefundOrderDto extends BaseDto {
@Schema(description = "退款号")
private String refundNo;
@Schema(description = "支付网关订单号")
private String gatewayOrderNo;
@Schema(description = "标题")
private String title;

View File

@@ -28,9 +28,6 @@ public class RefundChannelOrderDto extends BaseDto {
@Schema(description = "通道支付单id")
private Long payChannelId;
@Schema(description = "支付网关订单号")
private String gatewayOrderNo;
@Schema(description = "异步支付方式")
private boolean async;

View File

@@ -30,6 +30,9 @@ public class PayRefundOrderQuery extends QueryOrder {
@QueryParam(type = QueryParam.CompareTypeEnum.LIKE)
private String businessNo;
@Schema(description = "支付网关订单号")
private String gatewayOrderNo;
@Schema(description = "标题")
@QueryParam(type = QueryParam.CompareTypeEnum.LIKE)
private String title;