ref 统一交易调整模式更改为分开处理

This commit is contained in:
bootx
2024-07-21 14:06:35 +08:00
parent 864a3655a0
commit 1cec4f7138
41 changed files with 421 additions and 1242 deletions

View File

@@ -58,10 +58,6 @@ public class CallbackLocal {
*/
private PayCallbackStatusEnum callbackStatus = PayCallbackStatusEnum.SUCCESS;
/** 调整号 */
private String adjustNo;
/** 提示信息 */
private String errorMsg;
}

View File

@@ -1,17 +1,17 @@
package cn.daxpay.single.service.core.channel.alipay.service;
import cn.bootx.platform.common.core.util.LocalDateTimeUtil;
import cn.bootx.platform.common.core.function.CollectorsFunction;
import cn.bootx.platform.common.mybatisplus.base.MpIdEntity;
import cn.daxpay.single.core.code.AllocDetailResultEnum;
import cn.daxpay.single.core.exception.TradeFailException;
import cn.daxpay.single.core.util.PayUtil;
import cn.daxpay.single.service.code.AliPayCode;
import cn.daxpay.single.service.common.local.PaymentContextLocal;
import cn.daxpay.single.service.core.channel.alipay.entity.AliPayConfig;
import cn.daxpay.single.service.core.order.allocation.entity.AllocOrder;
import cn.daxpay.single.service.core.order.allocation.entity.AllocOrderDetail;
import cn.daxpay.single.service.core.payment.adjust.dto.AllocResultItem;
import cn.daxpay.single.service.core.payment.sync.result.AllocRemoteSyncResult;
import cn.daxpay.single.core.util.PayUtil;
import cn.hutool.core.date.LocalDateTimeUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.alipay.api.AlipayClient;
@@ -26,11 +26,12 @@ import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.function.Function;
import java.util.stream.Collectors;
/**
@@ -112,7 +113,7 @@ public class AliPayAllocService {
* 分账状态同步
*/
@SneakyThrows
public AllocRemoteSyncResult sync(AllocOrder allocOrder, AliPayConfig config){
public AllocRemoteSyncResult sync(AllocOrder allocOrder, List<AllocOrderDetail> allocOrderDetails, AliPayConfig config){
AlipayClient alipayClient = aliPayConfigService.getAlipayClient(config);
AlipayTradeOrderSettleQueryModel model = new AlipayTradeOrderSettleQueryModel();
model.setTradeNo(allocOrder.getOutOrderNo());
@@ -122,32 +123,25 @@ public class AliPayAllocService {
AlipayTradeOrderSettleQueryResponse response = alipayClient.execute(request);
// 验证
this.verifyErrorMsg(response);
// 接收到的结果
Map<String, AllocOrderDetail> detailMap = allocOrderDetails.stream()
.collect(Collectors.toMap(AllocOrderDetail::getReceiverAccount, Function.identity(), CollectorsFunction::retainLatest));
List<RoyaltyDetail> royaltyDetailList = response.getRoyaltyDetailList();
// 转换成通用的明细详情
List<AllocResultItem> resultItems = royaltyDetailList.stream()
.map(receiver -> {
// 金额
int amount = PayUtil.convertCentAmount(new BigDecimal(receiver.getAmount()));
AllocResultItem detail = new AllocResultItem()
.setResult(this.getDetailResultEnum(receiver.getState()).getCode())
.setAccount(receiver.getTransIn())
.setAmount(amount)
.setErrorCode(receiver.getErrorCode())
.setErrorMsg(receiver.getErrorDesc());
// 如果是完成, 更新时间
if (AllocDetailResultEnum.SUCCESS.getCode()
.equals(detail.getResult())) {
LocalDateTime finishTime = LocalDateTimeUtil.of(receiver.getExecuteDt());
detail.setFinishTime(finishTime)
.setErrorMsg(null)
.setErrorCode(null);
}
return detail;
})
.collect(Collectors.toList());
return new AllocRemoteSyncResult().setSyncInfo(JSONUtil.toJsonStr(response)).setResultItems(resultItems);
for (RoyaltyDetail receiver : royaltyDetailList) {
AllocOrderDetail detail = detailMap.get(receiver.getTransIn());
if (Objects.nonNull(detail)) {
detail.setResult(this.getDetailResultEnum(receiver.getState()).getCode());
detail.setErrorCode(receiver.getErrorCode());
detail.setErrorMsg(receiver.getErrorDesc());
// 如果是完成, 更新时间
if (AllocDetailResultEnum.SUCCESS.getCode().equals(detail.getResult())){
LocalDateTime finishTime = LocalDateTimeUtil.of(receiver.getExecuteDt());
detail.setFinishTime(finishTime)
.setErrorMsg(null)
.setErrorCode(null);
}
}
}
return new AllocRemoteSyncResult().setSyncInfo(JSONUtil.toJsonStr(response));
}
/**

View File

@@ -1,6 +1,6 @@
package cn.daxpay.single.service.core.channel.wechat.service;
import cn.bootx.platform.common.core.util.LocalDateTimeUtil;
import cn.bootx.platform.common.core.function.CollectorsFunction;
import cn.bootx.platform.common.mybatisplus.base.MpIdEntity;
import cn.daxpay.single.core.code.AllocDetailResultEnum;
import cn.daxpay.single.core.code.AllocReceiverTypeEnum;
@@ -9,11 +9,11 @@ import cn.daxpay.single.service.code.WeChatPayCode;
import cn.daxpay.single.service.core.channel.wechat.entity.WeChatPayConfig;
import cn.daxpay.single.service.core.order.allocation.entity.AllocOrder;
import cn.daxpay.single.service.core.order.allocation.entity.AllocOrderDetail;
import cn.daxpay.single.service.core.payment.adjust.dto.AllocResultItem;
import cn.daxpay.single.service.core.payment.sync.result.AllocRemoteSyncResult;
import cn.daxpay.single.service.dto.channel.wechat.WeChatPayAllocReceiver;
import cn.hutool.core.codec.Base64;
import cn.hutool.core.date.DatePattern;
import cn.hutool.core.date.LocalDateTimeUtil;
import cn.hutool.core.lang.TypeReference;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
@@ -34,6 +34,7 @@ import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.function.Function;
import java.util.stream.Collectors;
/**
@@ -111,7 +112,7 @@ public class WeChatPayAllocService {
/**
* 同步分账状态
*/
public AllocRemoteSyncResult sync(AllocOrder allocOrder, WeChatPayConfig config){
public AllocRemoteSyncResult sync(AllocOrder allocOrder, List<AllocOrderDetail> allocOrderDetails, WeChatPayConfig config){
// 不要传输AppId参数, 否则会失败
Map<String, String> params = ProfitSharingModel.builder()
.mch_id(config.getWxMchId())
@@ -124,26 +125,23 @@ public class WeChatPayAllocService {
Map<String, String> result = WxPayKit.xmlToMap(xmlResult);
this.verifyErrorMsg(result);
String json = result.get(WeChatPayCode.ALLOC_RECEIVERS);
// 接收到的分账内容
List<WeChatPayAllocReceiver> receivers = JSONUtil.toBean(json, new TypeReference<List<WeChatPayAllocReceiver>>() {}, false);
// 转换成通用的明细详情
List<AllocResultItem> resultItems = receivers.stream()
.map(receiver -> {
AllocResultItem detail = new AllocResultItem()
.setResult(this.getDetailResultEnum(receiver.getResult()).getCode())
.setAccount(receiver.getAccount())
.setErrorMsg(receiver.getFailReason());
// 如果是完成, 更新时间
if (AllocDetailResultEnum.SUCCESS.getCode().equals(detail.getResult())){
LocalDateTime finishTime = LocalDateTimeUtil.parse(receiver.getFinishTime(), DatePattern.PURE_DATETIME_PATTERN);
detail.setFinishTime(finishTime);
}
return detail;
})
.collect(Collectors.toList());
return new AllocRemoteSyncResult().setSyncInfo(json).setResultItems(resultItems);
Map<String, AllocOrderDetail> detailMap = allocOrderDetails.stream()
.collect(Collectors.toMap(AllocOrderDetail::getReceiverAccount, Function.identity(), CollectorsFunction::retainLatest));
// 根据明细更新订单明细内容
for (WeChatPayAllocReceiver receiver : receivers) {
AllocOrderDetail detail = detailMap.get(receiver.getAccount());
if (Objects.nonNull(detail)){
detail.setResult(this.getDetailResultEnum(receiver.getResult()).getCode());
detail.setErrorMsg(receiver.getFailReason());
// 如果是完成, 更新时间
if (AllocDetailResultEnum.SUCCESS.getCode().equals(detail.getResult())){
LocalDateTime finishTime = LocalDateTimeUtil.parse(receiver.getFinishTime(), DatePattern.PURE_DATETIME_PATTERN);
detail.setFinishTime(finishTime);
}
}
}
return new AllocRemoteSyncResult().setSyncInfo(JSONUtil.toJsonStr(receivers));
}

View File

@@ -4,8 +4,8 @@ import cn.bootx.platform.common.core.rest.param.PageParam;
import cn.bootx.platform.common.mybatisplus.impl.BaseManager;
import cn.bootx.platform.common.mybatisplus.util.MpUtil;
import cn.bootx.platform.common.query.generator.QueryGenerator;
import cn.daxpay.single.core.code.PayStatusEnum;
import cn.daxpay.single.service.core.order.refund.entity.RefundOrder;
import cn.daxpay.single.core.code.TransferStatusEnum;
import cn.daxpay.single.core.result.order.TransferOrderResult;
import cn.daxpay.single.service.core.order.transfer.entity.TransferOrder;
import cn.daxpay.single.service.param.order.TransferOrderQuery;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
@@ -55,7 +55,7 @@ public class TransferOrderManager extends BaseManager<TransferOrderMapper, Trans
*/
public Integer getTalAmount(TransferOrderQuery query){
QueryWrapper<TransferOrderQuery> generator = QueryGenerator.generator(query);
generator.eq(MpUtil.getColumnName(RefundOrder::getStatus), PayStatusEnum.SUCCESS.getCode());
generator.eq(MpUtil.getColumnName(TransferOrderResult::getStatus), TransferStatusEnum.SUCCESS.getCode());
return baseMapper.getTalAmount(generator);
}
}

View File

@@ -1,35 +0,0 @@
package cn.daxpay.single.service.core.payment.adjust.dto;
import lombok.Data;
import lombok.experimental.Accessors;
import java.time.LocalDateTime;
/**
* 分账结果明细
* @author xxm
* @since 2024/7/16
*/
@Data
@Accessors(chain = true)
public class AllocResultItem {
/** 账号 */
private String account;
/** 金额 */
private Integer amount;
/** 状态 */
private String result;
/** 完成时间 */
private LocalDateTime finishTime;
/** 错误码 */
private String errorCode;
/** 错误信息 */
private String errorMsg;
}

View File

@@ -1,36 +0,0 @@
package cn.daxpay.single.service.core.payment.adjust.param;
import cn.daxpay.single.service.code.TradeAdjustSourceEnum;
import cn.daxpay.single.service.core.order.allocation.entity.AllocOrder;
import cn.daxpay.single.service.core.order.allocation.entity.AllocOrderDetail;
import cn.daxpay.single.service.core.payment.adjust.dto.AllocResultItem;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.experimental.Accessors;
import java.util.List;
/**
* 分账调整参数
* @author xxm
* @since 2024/7/16
*/
@Data
@Accessors(chain = true)
@Schema(title = "分账调整参数")
public class AllocAdjustParam {
/** 触发来源 */
@Schema(description = "触发来源")
private TradeAdjustSourceEnum source;
/** 分账订单 */
private AllocOrder order;
/** 分账明细订单 */
private List<AllocOrderDetail> details;
/** 分账结果明细列表 */
private List<AllocResultItem> resultItems;
}

View File

@@ -1,35 +0,0 @@
package cn.daxpay.single.service.core.payment.adjust.param;
import cn.daxpay.single.service.code.PayAdjustWayEnum;
import cn.daxpay.single.service.code.TradeAdjustSourceEnum;
import cn.daxpay.single.service.core.order.pay.entity.PayOrder;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.experimental.Accessors;
import java.time.LocalDateTime;
/**
* 支付订单调整参数
* @author xxm
* @since 2023/12/27
*/
@Data
@Accessors(chain = true)
public class PayAdjustParam {
@Schema(description = "支付订单")
private PayOrder order;
@Schema(description = "通道交易号")
private String outTradeNo;
@Schema(description = "完成时间")
private LocalDateTime finishTime;
@Schema(description = "触发来源")
private TradeAdjustSourceEnum source;
@Schema(description = "调整方式")
private PayAdjustWayEnum adjustWay;
}

View File

@@ -1,35 +0,0 @@
package cn.daxpay.single.service.core.payment.adjust.param;
import cn.daxpay.single.service.code.RefundAdjustWayEnum;
import cn.daxpay.single.service.code.TradeAdjustSourceEnum;
import cn.daxpay.single.service.core.order.refund.entity.RefundOrder;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.experimental.Accessors;
import java.time.LocalDateTime;
/**
* 退款订单调整参数
* @author xxm
* @since 2023/12/27
*/
@Data
@Accessors(chain = true)
public class RefundAdjustParam {
@Schema(description = "退款订单")
private RefundOrder order;
@Schema(description = "通道交易号")
private String outTradeNo;
@Schema(description = "完成时间")
private LocalDateTime finishTime;
@Schema(description = "触发来源")
private TradeAdjustSourceEnum source;
@Schema(description = "调整方式")
private RefundAdjustWayEnum adjustWay;
}

View File

@@ -1,132 +0,0 @@
package cn.daxpay.single.service.core.payment.adjust.service;
import cn.bootx.platform.common.core.exception.RepetitiveOperationException;
import cn.daxpay.single.core.code.AllocDetailResultEnum;
import cn.daxpay.single.core.code.AllocOrderResultEnum;
import cn.daxpay.single.core.code.AllocOrderStatusEnum;
import cn.daxpay.single.core.util.TradeNoGenerateUtil;
import cn.daxpay.single.service.code.TradeTypeEnum;
import cn.daxpay.single.service.core.order.allocation.dao.AllocOrderDetailManager;
import cn.daxpay.single.service.core.order.allocation.dao.AllocOrderManager;
import cn.daxpay.single.service.core.order.allocation.entity.AllocOrder;
import cn.daxpay.single.service.core.order.allocation.entity.AllocOrderDetail;
import cn.daxpay.single.service.core.payment.adjust.dto.AllocResultItem;
import cn.daxpay.single.service.core.payment.adjust.param.AllocAdjustParam;
import cn.daxpay.single.service.core.payment.notice.service.ClientNoticeService;
import cn.daxpay.single.service.core.record.adjust.entity.TradeAdjustRecord;
import cn.daxpay.single.service.core.record.adjust.service.TradeAdjustRecordService;
import com.baomidou.lock.LockInfo;
import com.baomidou.lock.LockTemplate;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.function.Function;
import java.util.stream.Collectors;
/**
* 分账调整
* @author xxm
* @since 2024/7/16
*/
@Slf4j
@Service
@RequiredArgsConstructor
public class AllocAdjustService {
private final LockTemplate lockTemplate;
private final AllocOrderDetailManager allocOrderDetailManager;
private final AllocOrderManager allocOrderManager;
private final ClientNoticeService clientNoticeService;
private final TradeAdjustRecordService tradeAdjustRecordService;
/**
* 分账订单处理
*/
@Transactional(rollbackFor = Exception.class)
public String adjust(AllocAdjustParam allocAdjustParam){
AllocOrder allocOrder = allocAdjustParam.getOrder();
List<AllocResultItem> resultItems = allocAdjustParam.getResultItems();
List<AllocOrderDetail> details = allocAdjustParam.getDetails();
LockInfo lock = lockTemplate.lock("adjust:alloc:" + allocOrder.getOrderId(),10000,200);
if (Objects.isNull(lock)){
throw new RepetitiveOperationException("分账调整中,请勿重复操作");
}
// 如果是分账结束或失败, 不更新状态
String status = allocOrder.getStatus();
// 如果是分账结束或失败, 不进行对订单进行处理
List<String> list = Arrays.asList(AllocOrderStatusEnum.FINISH.getCode(), AllocOrderStatusEnum.FINISH_FAILED.getCode());
if (!list.contains(status)){
Map<Long, AllocOrderDetail> detailMap = allocAdjustParam.getDetails()
.stream()
.collect(Collectors.toMap(AllocOrderDetail::getId, Function.identity()));
// 更新状态
for (AllocResultItem resultItem : resultItems) {
}
// 判断明细状态. 获取成功和失败的
long successCount = details.stream()
.map(AllocOrderDetail::getResult)
.filter(AllocDetailResultEnum.SUCCESS.getCode()::equals)
.count();
long failCount = details.stream()
.map(AllocOrderDetail::getResult)
.filter(AllocDetailResultEnum.FAIL.getCode()::equals)
.count();
// 成功和失败都为0 表示进行中
if (successCount == 0 && failCount == 0){
allocOrder.setStatus(AllocOrderStatusEnum.ALLOCATION_PROCESSING.getCode())
.setResult(AllocOrderResultEnum.ALL_PENDING.getCode());
} else {
if (failCount == details.size()){
// 全部失败
allocOrder.setStatus(AllocOrderStatusEnum.ALLOCATION_END.getCode())
.setResult(AllocOrderResultEnum.ALL_FAILED.getCode());
} else if (successCount == details.size()){
// 全部成功
allocOrder.setStatus(AllocOrderStatusEnum.ALLOCATION_END.getCode())
.setResult(AllocOrderResultEnum.ALL_SUCCESS.getCode());
} else {
// 部分成功
allocOrder.setStatus(AllocOrderStatusEnum.ALLOCATION_END.getCode())
.setResult(AllocOrderResultEnum.PART_SUCCESS.getCode());
}
}
// 更新
allocOrderDetailManager.updateAllById(details);
allocOrderManager.updateById(allocOrder);
// 如果状态为完成, 发送通知
if (Objects.equals(AllocOrderStatusEnum.ALLOCATION_END.getCode(), allocOrder.getStatus())){
// 发送通知
clientNoticeService.registerAllocNotice(allocOrder, details);
}
return this.saveRecord(allocAdjustParam).getAdjustNo();
}
return null;
}
/**
* 保存记录
*/
private TradeAdjustRecord saveRecord(AllocAdjustParam param){
AllocOrder order = param.getOrder();
TradeAdjustRecord record = new TradeAdjustRecord()
.setAdjustNo(TradeNoGenerateUtil.adjust())
.setTradeId(order.getId())
.setChannel(order.getChannel())
.setSource(param.getSource().getCode())
.setTradeNo(order.getOrderNo())
.setType(TradeTypeEnum.ALLOCATION.getCode());
tradeAdjustRecordService.saveRecord(record);
return record;
}
}

View File

@@ -1,146 +0,0 @@
package cn.daxpay.single.service.core.payment.adjust.service;
import cn.daxpay.single.core.code.PayStatusEnum;
import cn.daxpay.single.core.exception.OperationProcessingException;
import cn.daxpay.single.core.exception.SystemUnknownErrorException;
import cn.daxpay.single.core.exception.TradeStatusErrorException;
import cn.daxpay.single.core.util.TradeNoGenerateUtil;
import cn.daxpay.single.service.code.TradeTypeEnum;
import cn.daxpay.single.service.core.order.pay.entity.PayOrder;
import cn.daxpay.single.service.core.order.pay.service.PayOrderService;
import cn.daxpay.single.service.core.payment.notice.service.ClientNoticeService;
import cn.daxpay.single.service.core.payment.adjust.param.PayAdjustParam;
import cn.daxpay.single.service.core.record.flow.service.TradeFlowRecordService;
import cn.daxpay.single.service.core.record.adjust.entity.TradeAdjustRecord;
import cn.daxpay.single.service.core.record.adjust.service.TradeAdjustRecordService;
import cn.daxpay.single.service.func.AbsPayAdjustStrategy;
import cn.daxpay.single.service.util.PayStrategyFactory;
import com.baomidou.lock.LockInfo;
import com.baomidou.lock.LockTemplate;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.time.LocalDateTime;
import java.util.Objects;
/**
* 支付订单调整服务
* @author xxm
* @since 2024/7/15
*/
@Slf4j
@Service
@RequiredArgsConstructor
public class PayAdjustService {
private final ClientNoticeService clientNoticeService;
private final LockTemplate lockTemplate;
private final TradeAdjustRecordService tradeAdjustRecordService;
private final PayOrderService payOrderService;
private final TradeFlowRecordService tradeFlowRecordService;
/**
* 调整服务
*/
@Transactional(rollbackFor = Exception.class)
public String adjust(PayAdjustParam param){
PayOrder order = param.getOrder();
// 添加分布式锁
LockInfo lock = lockTemplate.lock("adjust:pay:" + order.getId(), 10000, 200);
if (Objects.isNull(lock)){
log.warn("当前支付订单正在调整中: {}", order.getId());
throw new OperationProcessingException("当前支付订单正在调整中");
}
// 如果到达终态不能向前回滚
if (Objects.equals(order.getStatus(), PayStatusEnum.SUCCESS.getCode())){
throw new TradeStatusErrorException("当前支付订单已支付成功");
}
// 初始化调整参数
AbsPayAdjustStrategy adjustStrategy = PayStrategyFactory.create(order.getChannel(), AbsPayAdjustStrategy.class);
adjustStrategy.setOrder(order);
// 执行前置处理
adjustStrategy.doBeforeHandler();
String beforeStatus = order.getStatus();
// 根据不同的调整方式执行对应的调整逻辑
switch (param.getAdjustWay()) {
case SUCCESS:
this.success(order, param);
break;
case CLOSE_LOCAL:
this.closeLocal(order);
break;
case CLOSE_GATEWAY:
this.closeRemote(order, adjustStrategy);
break;
default:
log.error("走到了理论上讲不会走到的分支");
throw new SystemUnknownErrorException("走到了理论上讲不会走到的分支");
}
// 发送通知
clientNoticeService.registerPayNotice(order);
TradeAdjustRecord record = this.saveRecord(order, param, beforeStatus);
return record.getAdjustNo();
}
/**
* 变更为已支付
* 同步: 将异步支付状态修改为成功
* 回调: 将异步支付状态修改为成功
*/
private void success(PayOrder order, PayAdjustParam param) {
// 修改订单支付状态为成功
order.setStatus(PayStatusEnum.SUCCESS.getCode())
.setPayTime(param.getFinishTime())
.setOutOrderNo(param.getOutTradeNo())
.setCloseTime(null);
tradeFlowRecordService.savePay(order);
payOrderService.updateById(order);
}
/**
* 关闭支付
* 同步/对账: 执行支付单所有的支付通道关闭支付逻辑, 不需要调用网关关闭,
*/
private void closeLocal(PayOrder order) {
// 执行策略的关闭方法
order.setStatus(PayStatusEnum.CLOSE.getCode())
.setCloseTime(LocalDateTime.now());
payOrderService.updateById(order);
}
/**
* 关闭网关交易, 同时也会关闭本地支付
* 回调: 执行所有的支付通道关闭支付逻辑
*/
private void closeRemote(PayOrder payOrder, AbsPayAdjustStrategy strategy) {
// 执行策略的关闭方法
strategy.doCloseRemoteHandler();
payOrder.setStatus(PayStatusEnum.CLOSE.getCode())
.setCloseTime(LocalDateTime.now());
payOrderService.updateById(payOrder);
}
/**
* 保存记录
*/
private TradeAdjustRecord saveRecord(PayOrder order, PayAdjustParam param, String beforeStatus){
TradeAdjustRecord record = new TradeAdjustRecord()
.setAdjustNo(TradeNoGenerateUtil.adjust())
.setTradeId(order.getId())
.setChannel(order.getChannel())
.setSource(param.getSource().getCode())
.setTradeNo(order.getOrderNo())
.setBeforeStatus(beforeStatus)
.setAfterStatus(order.getStatus())
.setType(TradeTypeEnum.PAY.getCode())
.setWay(param.getAdjustWay().getCode());
tradeAdjustRecordService.saveRecord(record);
return record;
}
}

View File

@@ -1,179 +0,0 @@
package cn.daxpay.single.service.core.payment.adjust.service;
import cn.daxpay.single.core.code.PayOrderRefundStatusEnum;
import cn.daxpay.single.core.code.PayStatusEnum;
import cn.daxpay.single.core.code.RefundStatusEnum;
import cn.daxpay.single.core.exception.OperationProcessingException;
import cn.daxpay.single.core.exception.TradeStatusErrorException;
import cn.daxpay.single.core.util.TradeNoGenerateUtil;
import cn.daxpay.single.service.code.RefundAdjustWayEnum;
import cn.daxpay.single.service.code.TradeTypeEnum;
import cn.daxpay.single.service.core.order.pay.entity.PayOrder;
import cn.daxpay.single.service.core.order.pay.service.PayOrderQueryService;
import cn.daxpay.single.service.core.order.pay.service.PayOrderService;
import cn.daxpay.single.service.core.order.refund.dao.RefundOrderManager;
import cn.daxpay.single.service.core.order.refund.entity.RefundOrder;
import cn.daxpay.single.service.core.payment.adjust.param.RefundAdjustParam;
import cn.daxpay.single.service.core.payment.notice.service.ClientNoticeService;
import cn.daxpay.single.service.core.record.flow.service.TradeFlowRecordService;
import cn.daxpay.single.service.core.record.adjust.entity.TradeAdjustRecord;
import cn.daxpay.single.service.core.record.adjust.service.TradeAdjustRecordService;
import com.baomidou.lock.LockInfo;
import com.baomidou.lock.LockTemplate;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
/**
* 退款订单调整
* @author xxm
* @since 2024/1/26
*/
@Slf4j
@Service
@RequiredArgsConstructor
public class RefundAdjustService {
private final PayOrderService payOrderService;
private final PayOrderQueryService payOrderQueryService;
private final ClientNoticeService clientNoticeService;
private final RefundOrderManager refundOrderManager;
private final TradeFlowRecordService tradeFlowRecordService;
private final TradeAdjustRecordService tradeAdjustRecordService;
private final LockTemplate lockTemplate;
/**
* 退款订单调整
*/
@Transactional(rollbackFor = Exception.class)
public String adjust(RefundAdjustParam param){
RefundOrder refundOrder = param.getOrder();
RefundAdjustWayEnum adjustType = param.getAdjustWay();
// 添加分布式锁
LockInfo lock = lockTemplate.lock("adjust:refund:" + refundOrder.getId(), 10000, 200);
if (Objects.isNull(lock)){
log.warn("当前退款订单正在调整中: {}", refundOrder.getId());
throw new OperationProcessingException("当前退款订单正在调整中");
}
// 如果到达终态不能向前回滚
if (Arrays.asList(RefundStatusEnum.SUCCESS.getCode(), RefundStatusEnum.CLOSE.getCode()).contains(refundOrder.getStatus())){
throw new TradeStatusErrorException("当前退款订单已处理完成");
}
try {
// 获取关联支付单
PayOrder payOrder = payOrderQueryService.findById(refundOrder.getOrderId()).orElseThrow(() -> new RuntimeException("支付单不存在"));
String adjustNo = null;
// 根据不同的类型执行对应的调整逻辑
if (adjustType == RefundAdjustWayEnum.SUCCESS) {
adjustNo = this.success(param, payOrder);
} else if (adjustType == RefundAdjustWayEnum.FAIL) {
adjustNo = this.close(param, payOrder);
} else {
log.error("走到了理论上讲不会走到的分支");
}
// 发送通知
clientNoticeService.registerRefundNotice(refundOrder);
return adjustNo;
} finally {
lockTemplate.releaseLock(lock);
}
}
/**
* 退款成功, 更新退款单和支付单
*/
private String success(RefundAdjustParam param, PayOrder payOrder) {
RefundOrder refundOrder = param.getOrder();
String beforeRefundStatus = refundOrder.getStatus();
// 订单相关状态
PayOrderRefundStatusEnum afterPayRefundStatus;
// 判断订单全部退款还是部分退款
if (Objects.equals(payOrder.getRefundableBalance(),0)){
afterPayRefundStatus = PayOrderRefundStatusEnum.REFUNDED;
} else {
afterPayRefundStatus = PayOrderRefundStatusEnum.PARTIAL_REFUND;
}
// 设置退款为完成状态和完成时间
refundOrder.setStatus(RefundStatusEnum.SUCCESS.getCode())
.setFinishTime(param.getFinishTime());
payOrder.setRefundStatus(afterPayRefundStatus.getCode());
// 更新订单和退款相关订单
payOrderService.updateById(payOrder);
refundOrderManager.updateById(refundOrder);
// 记录流水
tradeFlowRecordService.saveRefund(refundOrder);
// 发送通知
List<String> list = Arrays.asList(RefundStatusEnum.SUCCESS.getCode(), RefundStatusEnum.CLOSE.getCode(), RefundStatusEnum.FAIL.getCode());
if (list.contains(refundOrder.getStatus())){
clientNoticeService.registerRefundNotice(refundOrder);
}
// 保存调整记录
TradeAdjustRecord adjustRecord = this.saveRecord(param, beforeRefundStatus);
return adjustRecord.getAdjustNo();
}
/**
* 退款失败, 关闭退款单并将失败的退款金额归还回订单
*/
private String close(RefundAdjustParam param, PayOrder payOrder) {
RefundOrder refundOrder = param.getOrder();
String beforeRefundStatus = refundOrder.getStatus();
// 退款失败返还后的余额
int payOrderAmount = refundOrder.getAmount() + payOrder.getRefundableBalance();
// 退款失败返还后的余额+可退余额 == 订单金额 支付订单回退为为支付成功状态
if (payOrderAmount == payOrder.getAmount()){
payOrder.setStatus(PayStatusEnum.SUCCESS.getCode());
} else {
// 回归部分退款状态
payOrder.setRefundStatus(PayOrderRefundStatusEnum.PARTIAL_REFUND.getCode());
}
// 更新支付订单相关的可退款金额
payOrder.setRefundableBalance(payOrderAmount);
refundOrder.setStatus(RefundStatusEnum.CLOSE.getCode());
// 更新订单和退款相关订单
payOrderService.updateById(payOrder);
refundOrderManager.updateById(refundOrder);
TradeAdjustRecord adjustRecord = this.saveRecord(param, beforeRefundStatus);
return adjustRecord.getAdjustNo();
}
/**
* 保存退款订单的调整记录
*/
private TradeAdjustRecord saveRecord(RefundAdjustParam param, String beforeRefundStatus){
RefundOrder refundOrder = param.getOrder();
TradeAdjustRecord record = new TradeAdjustRecord()
.setTradeId(refundOrder.getId())
.setAdjustNo(TradeNoGenerateUtil.adjust())
.setTradeNo(refundOrder.getRefundNo())
.setChannel(refundOrder.getChannel())
.setType(TradeTypeEnum.REFUND.getCode())
.setBeforeStatus(beforeRefundStatus)
.setAfterStatus(refundOrder.getStatus())
.setSource(param.getSource().getCode())
.setWay(param.getAdjustWay().getCode());
tradeAdjustRecordService.saveRecord(record);
return record;
}
}

View File

@@ -1,16 +0,0 @@
package cn.daxpay.single.service.core.payment.adjust.service;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
/**
* 转账订单调整服务
* @author xxm
* @since 2024/7/16
*/
@Slf4j
@Service
@RequiredArgsConstructor
public class TransferAdjustService {
}

View File

@@ -1,55 +0,0 @@
package cn.daxpay.single.service.core.payment.adjust.strategy.pay;
import cn.daxpay.single.core.code.PayChannelEnum;
import cn.daxpay.single.service.core.channel.alipay.entity.AliPayConfig;
import cn.daxpay.single.service.core.channel.alipay.service.AliPayCloseService;
import cn.daxpay.single.service.core.channel.alipay.service.AliPayConfigService;
import cn.daxpay.single.service.func.AbsPayAdjustStrategy;
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 2023/12/27
*/
@Slf4j
@Scope(SCOPE_PROTOTYPE)
@Service
@RequiredArgsConstructor
public class AliPayAdjustStrategy extends AbsPayAdjustStrategy {
private final AliPayCloseService closeService;
private final AliPayConfigService aliPayConfigService;
private AliPayConfig config;
/**
* 策略标识
*/
@Override
public String getChannel() {
return PayChannelEnum.ALI.getCode();
}
/**
* 调整前处理
*/
@Override
public void doBeforeHandler() {
this.config = aliPayConfigService.getConfig();
}
/**
* 关闭本地支付和网关支付
*/
@Override
public void doCloseRemoteHandler() {
closeService.close(this.getOrder(), this.config);
}
}

View File

@@ -1,38 +0,0 @@
package cn.daxpay.single.service.core.payment.adjust.strategy.pay;
import cn.daxpay.single.core.code.PayChannelEnum;
import cn.daxpay.single.service.func.AbsPayAdjustStrategy;
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 2023/12/29
*/
@Slf4j
@Scope(SCOPE_PROTOTYPE)
@Service
@RequiredArgsConstructor
public class UnionPayAdjustStrategy extends AbsPayAdjustStrategy {
/**
* 策略标识
*/
@Override
public String getChannel() {
return PayChannelEnum.UNION_PAY.getCode();
}
/**
* 关闭三方系统的支付
*/
@Override
public void doCloseRemoteHandler() {
log.warn("云闪付未提供支付订单关闭接口");
}
}

View File

@@ -1,56 +0,0 @@
package cn.daxpay.single.service.core.payment.adjust.strategy.pay;
import cn.daxpay.single.core.code.PayChannelEnum;
import cn.daxpay.single.service.core.channel.wechat.entity.WeChatPayConfig;
import cn.daxpay.single.service.core.channel.wechat.service.WeChatPayCloseService;
import cn.daxpay.single.service.core.channel.wechat.service.WeChatPayConfigService;
import cn.daxpay.single.service.func.AbsPayAdjustStrategy;
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 2023/12/29
*/
@Slf4j
@Scope(SCOPE_PROTOTYPE)
@Service
@RequiredArgsConstructor
public class WeChatPayAdjustStrategy extends AbsPayAdjustStrategy {
private final WeChatPayCloseService closeService;
private final WeChatPayConfigService weChatPayConfigService;
private WeChatPayConfig weChatPayConfig;
/**
* 策略标识
*/
@Override
public String getChannel() {
return PayChannelEnum.WECHAT.getCode();
}
/**
* 调整前处理
*/
@Override
public void doBeforeHandler() {
this.weChatPayConfig = weChatPayConfigService.getConfig();
}
/**
* 关闭本地支付和网关支付
*/
@Override
public void doCloseRemoteHandler() {
closeService.close(this.getOrder(),this.weChatPayConfig);
}
}

View File

@@ -5,6 +5,7 @@ import cn.daxpay.single.core.exception.ConfigNotEnableException;
import cn.daxpay.single.service.core.channel.alipay.entity.AliPayConfig;
import cn.daxpay.single.service.core.channel.alipay.service.AliPayAllocService;
import cn.daxpay.single.service.core.channel.alipay.service.AliPayConfigService;
import cn.daxpay.single.service.core.payment.sync.result.AllocRemoteSyncResult;
import cn.daxpay.single.service.func.AbsAllocStrategy;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
@@ -30,7 +31,7 @@ public class AliPayAllocStrategy extends AbsAllocStrategy {
private final AliPayConfigService aliPayConfigService;
private AliPayConfig aliPayConfig;
private AliPayConfig aliPayConfig;;
/**
@@ -69,4 +70,12 @@ public class AliPayAllocStrategy extends AbsAllocStrategy {
aliPayAllocService.finish(this.getAllocOrder(), this.getAllocOrderDetails(), this.aliPayConfig);
}
/**
* 同步状态
*/
@Override
public AllocRemoteSyncResult doSync() {
return aliPayAllocService.sync(this.getAllocOrder(), this.getAllocOrderDetails(), this.aliPayConfig);
}
}

View File

@@ -6,6 +6,7 @@ import cn.daxpay.single.core.exception.OperationFailException;
import cn.daxpay.single.service.core.channel.wechat.entity.WeChatPayConfig;
import cn.daxpay.single.service.core.channel.wechat.service.WeChatPayAllocService;
import cn.daxpay.single.service.core.channel.wechat.service.WeChatPayConfigService;
import cn.daxpay.single.service.core.payment.sync.result.AllocRemoteSyncResult;
import cn.daxpay.single.service.func.AbsAllocStrategy;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
@@ -73,5 +74,12 @@ public class WeChatPayAllocStrategy extends AbsAllocStrategy {
weChatPayAllocService.finish(getAllocOrder(), weChatPayConfig);
}
/**
* 同步状态
*/
@Override
public AllocRemoteSyncResult doSync() {
return weChatPayAllocService.sync(this.getAllocOrder(),this.getAllocOrderDetails(),weChatPayConfig);
}
}

View File

@@ -2,15 +2,14 @@ package cn.daxpay.single.service.core.payment.callback.service;
import cn.bootx.platform.common.core.util.LocalDateTimeUtil;
import cn.daxpay.single.core.code.PayStatusEnum;
import cn.daxpay.single.service.code.PayAdjustWayEnum;
import cn.daxpay.single.service.code.PayCallbackStatusEnum;
import cn.daxpay.single.service.code.TradeAdjustSourceEnum;
import cn.daxpay.single.service.common.context.CallbackLocal;
import cn.daxpay.single.service.common.local.PaymentContextLocal;
import cn.daxpay.single.service.core.order.pay.entity.PayOrder;
import cn.daxpay.single.service.core.order.pay.service.PayOrderQueryService;
import cn.daxpay.single.service.core.payment.adjust.param.PayAdjustParam;
import cn.daxpay.single.service.core.payment.adjust.service.PayAdjustService;
import cn.daxpay.single.service.core.order.pay.service.PayOrderService;
import cn.daxpay.single.service.core.payment.notice.service.ClientNoticeService;
import cn.daxpay.single.service.core.record.flow.service.TradeFlowRecordService;
import com.baomidou.lock.LockInfo;
import com.baomidou.lock.LockTemplate;
import lombok.RequiredArgsConstructor;
@@ -33,10 +32,12 @@ public class PayCallbackService {
private final PayOrderQueryService payOrderQueryService;
private final PayAdjustService payAdjustService;
private final LockTemplate lockTemplate;
private final PayOrderService payOrderService;
private final TradeFlowRecordService tradeFlowRecordService;
private final ClientNoticeService clientNoticeService;
/**
* 支付统一回调处理
*/
@@ -74,7 +75,7 @@ public class PayCallbackService {
}
/**
* 成功处理 使用调整策略将支付订单调整为支付成功状态
* 成功处理 将支付订单调整为支付成功状态
*/
private void success(PayOrder payOrder) {
CallbackLocal callbackInfo = PaymentContextLocal.get().getCallbackInfo();
@@ -94,15 +95,14 @@ public class PayCallbackService {
callbackInfo.setCallbackStatus(PayCallbackStatusEnum.EXCEPTION).setErrorMsg("支付单不是待支付状态,记录回调记录");
return;
}
// 执行支付成功的调整逻辑
PayAdjustParam param = new PayAdjustParam()
.setOrder(payOrder)
.setOutTradeNo(callbackInfo.getOutTradeNo())
.setAdjustWay(PayAdjustWayEnum.SUCCESS)
.setSource(TradeAdjustSourceEnum.CALLBACK)
.setFinishTime(callbackInfo.getFinishTime());
String adjustNo = payAdjustService.adjust(param);
callbackInfo.setAdjustNo(adjustNo);
// 修改订单支付状态为成功
payOrder.setStatus(PayStatusEnum.SUCCESS.getCode())
.setPayTime(callbackInfo.getFinishTime())
.setOutOrderNo(callbackInfo.getOutTradeNo())
.setCloseTime(null);
payOrderService.updateById(payOrder);
tradeFlowRecordService.savePay(payOrder);
clientNoticeService.registerPayNotice(payOrder);
}
/**
@@ -121,14 +121,11 @@ public class PayCallbackService {
return;
}
// 执行支付关闭的调整逻辑
PayAdjustParam param = new PayAdjustParam()
.setOrder(payOrder)
.setOutTradeNo(callbackInfo.getOutTradeNo())
.setAdjustWay(PayAdjustWayEnum.CLOSE_LOCAL)
.setSource(TradeAdjustSourceEnum.CALLBACK)
.setFinishTime(callbackInfo.getFinishTime());
String adjustNo = payAdjustService.adjust(param);
callbackInfo.setAdjustNo(adjustNo);
// 执行策略的关闭方法
payOrder.setStatus(PayStatusEnum.CLOSE.getCode())
.setCloseTime(LocalDateTime.now());
payOrderService.updateById(payOrder);
clientNoticeService.registerPayNotice(payOrder);
}
}

View File

@@ -1,15 +1,18 @@
package cn.daxpay.single.service.core.payment.callback.service;
import cn.bootx.platform.common.core.exception.DataNotExistException;
import cn.daxpay.single.core.code.PayOrderRefundStatusEnum;
import cn.daxpay.single.core.code.PayStatusEnum;
import cn.daxpay.single.core.code.RefundStatusEnum;
import cn.daxpay.single.service.code.PayCallbackStatusEnum;
import cn.daxpay.single.service.code.RefundAdjustWayEnum;
import cn.daxpay.single.service.code.TradeAdjustSourceEnum;
import cn.daxpay.single.service.common.context.CallbackLocal;
import cn.daxpay.single.service.common.local.PaymentContextLocal;
import cn.daxpay.single.service.core.order.pay.entity.PayOrder;
import cn.daxpay.single.service.core.order.pay.service.PayOrderService;
import cn.daxpay.single.service.core.order.refund.dao.RefundOrderManager;
import cn.daxpay.single.service.core.order.refund.entity.RefundOrder;
import cn.daxpay.single.service.core.payment.adjust.param.RefundAdjustParam;
import cn.daxpay.single.service.core.payment.adjust.service.RefundAdjustService;
import cn.daxpay.single.service.core.payment.notice.service.ClientNoticeService;
import cn.daxpay.single.service.core.record.flow.service.TradeFlowRecordService;
import com.baomidou.lock.LockInfo;
import com.baomidou.lock.LockTemplate;
import lombok.RequiredArgsConstructor;
@@ -29,9 +32,10 @@ import java.util.Objects;
public class RefundCallbackService {
private final RefundOrderManager refundOrderManager;
private final RefundAdjustService reflectionService;
private final LockTemplate lockTemplate;
private final TradeFlowRecordService tradeFlowRecordService;
private final PayOrderService payOrderService;
private final ClientNoticeService clientNoticeService;
/**
* 退款回调统一处理
@@ -61,23 +65,74 @@ public class RefundCallbackService {
}
// 退款成功还是失败
RefundAdjustParam param = new RefundAdjustParam()
.setOrder(refundOrder)
.setOutTradeNo(callbackInfo.getOutTradeNo())
.setSource(TradeAdjustSourceEnum.CALLBACK)
.setFinishTime(callbackInfo.getFinishTime());
if (Objects.equals(RefundStatusEnum.SUCCESS.getCode(), callbackInfo.getOutStatus())) {
param.setAdjustWay(RefundAdjustWayEnum.SUCCESS);
String adjustNo = reflectionService.adjust(param);
callbackInfo.setAdjustNo(adjustNo);
this.success(refundOrder);
} else {
param.setAdjustWay(RefundAdjustWayEnum.FAIL);
String adjustNo = reflectionService.adjust(param);
callbackInfo.setAdjustNo(adjustNo);
this.close(refundOrder);
}
} finally {
lockTemplate.releaseLock(lock);
}
}
/**
* 退款成功, 更新退款单和支付单
*/
private void success(RefundOrder refundOrder) {
CallbackLocal callbackInfo = PaymentContextLocal.get().getCallbackInfo();
PayOrder payOrder = payOrderService.findById(refundOrder.getOrderId())
.orElseThrow(() -> new DataNotExistException("退款订单关联支付订单不存在"));
// 订单相关状态
PayOrderRefundStatusEnum afterPayRefundStatus;
// 判断订单全部退款还是部分退款
if (Objects.equals(payOrder.getRefundableBalance(), 0)) {
afterPayRefundStatus = PayOrderRefundStatusEnum.REFUNDED;
} else {
afterPayRefundStatus = PayOrderRefundStatusEnum.PARTIAL_REFUND;
}
// 设置退款为完成状态和完成时间
refundOrder.setStatus(RefundStatusEnum.SUCCESS.getCode())
.setFinishTime(callbackInfo.getFinishTime());
payOrder.setRefundStatus(afterPayRefundStatus.getCode());
// 更新订单和退款相关订单
payOrderService.updateById(payOrder);
refundOrderManager.updateById(refundOrder);
// 记录流水
tradeFlowRecordService.saveRefund(refundOrder);
// 发送通知
clientNoticeService.registerRefundNotice(refundOrder);
}
/**
* 退款失败, 关闭退款单并将失败的退款金额归还回订单
*/
private void close(RefundOrder refundOrder) {
PayOrder payOrder = payOrderService.findById(refundOrder.getOrderId())
.orElseThrow(() -> new DataNotExistException("退款订单关联支付订单不存在"));
// 退款失败返还后的余额
int payOrderAmount = refundOrder.getAmount() + payOrder.getRefundableBalance();
// 退款失败返还后的余额+可退余额 == 订单金额 支付订单回退为为支付成功状态
if (payOrderAmount == payOrder.getAmount()) {
payOrder.setStatus(PayStatusEnum.SUCCESS.getCode());
} else {
// 回归部分退款状态
payOrder.setRefundStatus(PayOrderRefundStatusEnum.PARTIAL_REFUND.getCode());
}
// 更新支付订单相关的可退款金额
payOrder.setRefundableBalance(payOrderAmount);
refundOrder.setStatus(RefundStatusEnum.CLOSE.getCode());
// 更新订单和退款相关订单
payOrderService.updateById(payOrder);
refundOrderManager.updateById(refundOrder);
// 发送通知
clientNoticeService.registerRefundNotice(refundOrder);
}
}

View File

@@ -1,11 +1,8 @@
package cn.daxpay.single.service.core.payment.sync.result;
import cn.daxpay.single.service.core.payment.adjust.dto.AllocResultItem;
import lombok.Data;
import lombok.experimental.Accessors;
import java.util.List;
/**
* 分账同步结果
* @author xxm
@@ -16,14 +13,4 @@ import java.util.List;
public class AllocRemoteSyncResult {
/** 同步时网关返回的对象, 序列化为json字符串 */
private String syncInfo;
/** 通用分账结果明细 */
private List<AllocResultItem> resultItems;
/** 错误提示码 */
private String errorCode;
/** 错误提示 */
private String errorMsg;
}

View File

@@ -2,25 +2,23 @@ package cn.daxpay.single.service.core.payment.sync.service;
import cn.bootx.platform.common.core.exception.DataNotExistException;
import cn.bootx.platform.common.core.exception.RepetitiveOperationException;
import cn.daxpay.single.core.code.AllocDetailResultEnum;
import cn.daxpay.single.core.code.AllocOrderResultEnum;
import cn.daxpay.single.core.code.AllocOrderStatusEnum;
import cn.daxpay.single.core.param.payment.allocation.AllocSyncParam;
import cn.daxpay.single.core.result.sync.AllocSyncResult;
import cn.daxpay.single.service.code.TradeAdjustSourceEnum;
import cn.daxpay.single.service.code.TradeTypeEnum;
import cn.daxpay.single.service.common.local.PaymentContextLocal;
import cn.daxpay.single.service.core.order.allocation.dao.AllocOrderDetailManager;
import cn.daxpay.single.service.core.order.allocation.dao.AllocOrderManager;
import cn.daxpay.single.service.core.order.allocation.entity.AllocOrder;
import cn.daxpay.single.service.core.order.allocation.entity.AllocOrderDetail;
import cn.daxpay.single.service.core.payment.adjust.param.AllocAdjustParam;
import cn.daxpay.single.service.core.payment.adjust.service.AllocAdjustService;
import cn.daxpay.single.service.core.payment.notice.service.ClientNoticeService;
import cn.daxpay.single.service.core.payment.sync.result.AllocRemoteSyncResult;
import cn.daxpay.single.service.core.record.sync.entity.TradeSyncRecord;
import cn.daxpay.single.service.core.record.sync.service.TradeSyncRecordService;
import cn.daxpay.single.service.func.AbsAllocSyncStrategy;
import cn.daxpay.single.service.func.AbsAllocStrategy;
import cn.daxpay.single.service.util.PayStrategyFactory;
import cn.hutool.core.util.StrUtil;
import com.baomidou.lock.LockInfo;
import com.baomidou.lock.LockTemplate;
import lombok.RequiredArgsConstructor;
@@ -29,6 +27,7 @@ import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
@@ -50,8 +49,6 @@ public class AllocSyncService {
private final TradeSyncRecordService tradeSyncRecordService;
private final AllocAdjustService allocAdjustService;
private final LockTemplate lockTemplate;
/**
@@ -90,40 +87,86 @@ public class AllocSyncService {
try {
List<AllocOrderDetail> detailList = allocOrderDetailManager.findAllByOrderId(allocOrder.getId());
// 获取分账策略
AbsAllocSyncStrategy allocSyncStrategy = PayStrategyFactory.create(allocOrder.getChannel(), AbsAllocSyncStrategy.class);
allocSyncStrategy.initParam(allocOrder);
// 执行同步操作, 返账通用对账比对结果列表
AllocRemoteSyncResult allocRemoteSyncResult = allocSyncStrategy.doSync();
// 分账调整处理
AllocAdjustParam allocAdjustParam = new AllocAdjustParam()
.setSource(TradeAdjustSourceEnum.SYNC)
.setOrder(allocOrder)
.setDetails(detailList)
.setResultItems(allocRemoteSyncResult.getResultItems());
String adjustNo = allocAdjustService.adjust(allocAdjustParam);
AbsAllocStrategy allocStrategy = PayStrategyFactory.create(allocOrder.getChannel(),AbsAllocStrategy.class);
allocStrategy.initParam(allocOrder, detailList);
// 分账完结预处理
allocStrategy.doBeforeHandler();
// 执行同步操作, 分账明细的状态变更会在这个里面
AllocRemoteSyncResult allocRemoteSyncResult = allocStrategy.doSync();
// 保存分账同步记录
this.saveRecord(allocOrder, allocRemoteSyncResult, adjustNo);
this.saveRecord(allocOrder, allocRemoteSyncResult,null,null);
// 根据订单明细更新订单的状态和处理结果
this.updateOrderStatus(allocOrder, detailList);
} finally {
lockTemplate.releaseLock(lock);
}
}
/**
* 根据订单明细更新订单的状态和处理结果, 如果订单是分账结束或失败, 不更新状态
* TODO 是否多次同步会产生多次变动, 注意处理多次推送通知的问题, 目前是
*/
private void updateOrderStatus(AllocOrder allocOrder, List<AllocOrderDetail> details){
// 如果是分账结束或失败, 不更新状态
String status = allocOrder.getStatus();
// 如果是分账结束或失败, 不进行对订单进行处理
List<String> list = Arrays.asList(AllocOrderStatusEnum.FINISH.getCode(), AllocOrderStatusEnum.FINISH_FAILED.getCode());
if (!list.contains(status)){
// 判断明细状态. 获取成功和失败的
long successCount = details.stream()
.map(AllocOrderDetail::getResult)
.filter(AllocDetailResultEnum.SUCCESS.getCode()::equals)
.count();
long failCount = details.stream()
.map(AllocOrderDetail::getResult)
.filter(AllocDetailResultEnum.FAIL.getCode()::equals)
.count();
// 成功和失败都为0 表示进行中
if (successCount == 0 && failCount == 0){
allocOrder.setStatus(AllocOrderStatusEnum.ALLOCATION_PROCESSING.getCode())
.setResult(AllocOrderResultEnum.ALL_PENDING.getCode());
} else {
if (failCount == details.size()){
// 全部失败
allocOrder.setStatus(AllocOrderStatusEnum.ALLOCATION_END.getCode())
.setResult(AllocOrderResultEnum.ALL_FAILED.getCode());
} else if (successCount == details.size()){
// 全部成功
allocOrder.setStatus(AllocOrderStatusEnum.ALLOCATION_END.getCode())
.setResult(AllocOrderResultEnum.ALL_SUCCESS.getCode());
} else {
// 部分成功
allocOrder.setStatus(AllocOrderStatusEnum.ALLOCATION_END.getCode())
.setResult(AllocOrderResultEnum.PART_SUCCESS.getCode());
}
}
}
allocOrderDetailManager.updateAllById(details);
allocOrderManager.updateById(allocOrder);
// 如果状态为完成, 发送通知
if (Objects.equals(AllocOrderStatusEnum.ALLOCATION_END.getCode(), allocOrder.getStatus())){
// 发送通知
clientNoticeService.registerAllocNotice(allocOrder, details);
}
}
/**
* 保存同步记录
*/
private void saveRecord(AllocOrder order, AllocRemoteSyncResult syncResult, String adjustNo){
private void saveRecord(AllocOrder order, AllocRemoteSyncResult syncResult, String errorCode, String errorMsg){
TradeSyncRecord tradeSyncRecord = new TradeSyncRecord()
.setBizTradeNo(order.getBizAllocNo())
.setTradeNo(order.getAllocNo())
.setOutTradeNo(order.getOutAllocNo())
.setType(TradeTypeEnum.ALLOCATION.getCode())
.setAdjust(StrUtil.isNotBlank(adjustNo))
.setAdjustNo(adjustNo)
.setChannel(order.getChannel())
.setSyncInfo(syncResult.getSyncInfo())
.setErrorCode(syncResult.getErrorCode())
.setErrorMsg(syncResult.getErrorMsg())
.setErrorCode(errorCode)
.setErrorMsg(errorMsg)
.setClientIp(PaymentContextLocal.get().getClientInfo().getClientIp());
tradeSyncRecordService.saveRecord(tradeSyncRecord);
}

View File

@@ -9,21 +9,17 @@ import cn.daxpay.single.core.code.PaySyncStatusEnum;
import cn.daxpay.single.core.exception.*;
import cn.daxpay.single.core.param.payment.pay.PaySyncParam;
import cn.daxpay.single.core.result.sync.PaySyncResult;
import cn.daxpay.single.service.code.PayAdjustWayEnum;
import cn.daxpay.single.service.code.TradeAdjustSourceEnum;
import cn.daxpay.single.service.code.TradeTypeEnum;
import cn.daxpay.single.service.common.local.PaymentContextLocal;
import cn.daxpay.single.service.core.order.pay.entity.PayOrder;
import cn.daxpay.single.service.core.order.pay.service.PayOrderQueryService;
import cn.daxpay.single.service.core.order.pay.service.PayOrderService;
import cn.daxpay.single.service.core.payment.adjust.param.PayAdjustParam;
import cn.daxpay.single.service.core.payment.adjust.service.PayAdjustService;
import cn.daxpay.single.service.core.payment.sync.result.PayRemoteSyncResult;
import cn.daxpay.single.service.core.record.sync.entity.TradeSyncRecord;
import cn.daxpay.single.service.core.record.sync.service.TradeSyncRecordService;
import cn.daxpay.single.service.func.AbsPayCloseStrategy;
import cn.daxpay.single.service.func.AbsPaySyncStrategy;
import cn.daxpay.single.service.util.PayStrategyFactory;
import cn.hutool.core.util.StrUtil;
import com.baomidou.lock.LockInfo;
import com.baomidou.lock.LockTemplate;
import lombok.RequiredArgsConstructor;
@@ -54,8 +50,6 @@ public class PaySyncService {
private final TradeSyncRecordService tradeSyncRecordService;
private final PayAdjustService payAdjustService;
private final LockTemplate lockTemplate;
/**
@@ -95,7 +89,7 @@ public class PaySyncService {
// 判断是否同步成功
if (Objects.equals(payRemoteSyncResult.getSyncStatus(), FAIL)){
// 同步失败, 返回失败响应, 同时记录失败的日志
this.saveRecord(payOrder, payRemoteSyncResult, null);
this.saveRecord(payOrder, payRemoteSyncResult, false);
throw new OperationFailException(payRemoteSyncResult.getErrorMsg());
}
// 支付订单的网关订单号是否一致, 不一致进行更新
@@ -105,20 +99,19 @@ public class PaySyncService {
}
// 判断网关状态是否和支付单一致, 同时特定情况下更新网关同步状态
boolean statusSync = this.checkAndAdjustSyncStatus(payRemoteSyncResult,payOrder);
String adjustNo = null;
try {
// 状态不一致,执行支付单调整逻辑
if (!statusSync){
adjustNo = this.adjustHandler(payRemoteSyncResult, payOrder);
this.adjustHandler(payRemoteSyncResult, payOrder);
}
} catch (PayFailureException e) {
// 同步失败, 返回失败响应, 同时记录失败的日志
payRemoteSyncResult.setSyncStatus(FAIL);
this.saveRecord(payOrder, payRemoteSyncResult, null);
this.saveRecord(payOrder, payRemoteSyncResult, false);
throw e;
}
// 同步成功记录日志
this.saveRecord( payOrder, payRemoteSyncResult, adjustNo);
this.saveRecord( payOrder, payRemoteSyncResult, statusSync);
return new PaySyncResult().setStatus(payRemoteSyncResult.getSyncStatus().getCode());
} finally {
lockTemplate.releaseLock(lock);
@@ -171,34 +164,26 @@ public class PaySyncService {
/**
* 根据同步的结果对支付单进行调整处理
* @return 调整单号, 如果为空, 说明订单未做调整
*/
private String adjustHandler(PayRemoteSyncResult payRemoteSyncResult, PayOrder payOrder){
private void adjustHandler(PayRemoteSyncResult payRemoteSyncResult, PayOrder payOrder){
PaySyncStatusEnum syncStatusEnum = payRemoteSyncResult.getSyncStatus();
PayAdjustParam param = new PayAdjustParam()
.setOrder(payOrder)
.setSource(TradeAdjustSourceEnum.SYNC)
.setFinishTime(payRemoteSyncResult.getFinishTime());
// 对支付网关同步的结果进行处理
switch (syncStatusEnum) {
// 支付成功 支付宝退款时也是支付成功状态, 除非支付完成
case SUCCESS: {
param.setAdjustWay(PayAdjustWayEnum.SUCCESS);
return payAdjustService.adjust(param);
this.success(payOrder, payRemoteSyncResult);
}
case REFUND:
throw new TradeStatusErrorException("支付订单为退款状态,请通过执行对应的退款订单进行同步,来更新具体为什么类型退款状态");
// 交易关闭和未找到, 都对本地支付订单进行关闭, 不需要再调用网关进行关闭
case CLOSED:
case NOT_FOUND: {
param.setAdjustWay(PayAdjustWayEnum.CLOSE_LOCAL);
return payAdjustService.adjust(param);
this.closeLocal(payOrder);
}
// 超时关闭和交易不存在(特殊) 关闭本地支付订单, 同时调用网关进行关闭, 确保后续这个订单不能被支付
case TIMEOUT:
this.closeRemote(payOrder);
case NOT_FOUND_UNKNOWN:{
param.setAdjustWay(PayAdjustWayEnum.CLOSE_GATEWAY);
return payAdjustService.adjust(param);
}
// 调用出错
case FAIL: {
@@ -210,17 +195,55 @@ public class PaySyncService {
throw new SystemUnknownErrorException("代码有问题");
}
}
return null;
}
/**
* 变更为已支付
* 同步: 将异步支付状态修改为成功
* 回调: 将异步支付状态修改为成功
*/
private void success(PayOrder order, PayRemoteSyncResult param) {
// 修改订单支付状态为成功
order.setStatus(PayStatusEnum.SUCCESS.getCode())
.setPayTime(param.getFinishTime())
.setOutOrderNo(param.getOutOrderNo())
.setCloseTime(null);
payOrderService.updateById(order);
}
/**
* 关闭支付
* 同步/对账: 执行支付单所有的支付通道关闭支付逻辑, 不需要调用网关关闭,
*/
private void closeLocal(PayOrder order) {
// 执行策略的关闭方法
order.setStatus(PayStatusEnum.CLOSE.getCode())
.setCloseTime(LocalDateTime.now());
payOrderService.updateById(order);
}
/**
* 关闭网关交易, 同时也会关闭本地支付
* 回调: 执行所有的支付通道关闭支付逻辑
*/
private void closeRemote(PayOrder payOrder) {
// 初始化调整参数
AbsPayCloseStrategy strategy = PayStrategyFactory.create(payOrder.getChannel(), AbsPayCloseStrategy.class);
strategy.setOrder(payOrder);
// 执行策略的关闭方法
strategy.doCloseHandler();
payOrder.setStatus(PayStatusEnum.CLOSE.getCode())
.setCloseTime(LocalDateTime.now());
payOrderService.updateById(payOrder);
}
/**
* 保存同步记录
* @param payOrder 支付单
* @param payRemoteSyncResult 同步结果
* @param adjustNo 调整号
* @param adjust 调整号
*/
private void saveRecord(PayOrder payOrder, PayRemoteSyncResult payRemoteSyncResult, String adjustNo){
private void saveRecord(PayOrder payOrder, PayRemoteSyncResult payRemoteSyncResult, boolean adjust){
TradeSyncRecord tradeSyncRecord = new TradeSyncRecord()
.setBizTradeNo(payOrder.getBizOrderNo())
.setTradeNo(payOrder.getOrderNo())
@@ -229,8 +252,7 @@ public class PaySyncService {
.setType(TradeTypeEnum.PAY.getCode())
.setChannel(payOrder.getChannel())
.setSyncInfo(payRemoteSyncResult.getSyncInfo())
.setAdjust(StrUtil.isNotBlank(adjustNo))
.setAdjustNo(adjustNo)
.setAdjust(adjust)
.setErrorCode(payRemoteSyncResult.getErrorCode())
.setErrorMsg(payRemoteSyncResult.getErrorMsg())
.setClientIp(PaymentContextLocal.get().getClientInfo().getClientIp());

View File

@@ -1,7 +1,10 @@
package cn.daxpay.single.service.core.payment.sync.service;
import cn.bootx.platform.common.core.exception.BizException;
import cn.bootx.platform.common.core.exception.DataNotExistException;
import cn.bootx.platform.common.core.exception.RepetitiveOperationException;
import cn.daxpay.single.core.code.PayOrderRefundStatusEnum;
import cn.daxpay.single.core.code.PayStatusEnum;
import cn.daxpay.single.core.code.RefundStatusEnum;
import cn.daxpay.single.core.code.RefundSyncStatusEnum;
import cn.daxpay.single.core.exception.OperationFailException;
@@ -9,21 +12,20 @@ import cn.daxpay.single.core.exception.PayFailureException;
import cn.daxpay.single.core.exception.TradeNotExistException;
import cn.daxpay.single.core.param.payment.refund.RefundSyncParam;
import cn.daxpay.single.core.result.sync.RefundSyncResult;
import cn.daxpay.single.service.code.RefundAdjustWayEnum;
import cn.daxpay.single.service.code.TradeAdjustSourceEnum;
import cn.daxpay.single.service.code.TradeTypeEnum;
import cn.daxpay.single.service.common.local.PaymentContextLocal;
import cn.daxpay.single.service.core.order.pay.entity.PayOrder;
import cn.daxpay.single.service.core.order.pay.service.PayOrderService;
import cn.daxpay.single.service.core.order.refund.dao.RefundOrderManager;
import cn.daxpay.single.service.core.order.refund.entity.RefundOrder;
import cn.daxpay.single.service.core.order.refund.service.RefundOrderQueryService;
import cn.daxpay.single.service.core.payment.adjust.param.RefundAdjustParam;
import cn.daxpay.single.service.core.payment.adjust.service.RefundAdjustService;
import cn.daxpay.single.service.core.payment.notice.service.ClientNoticeService;
import cn.daxpay.single.service.core.payment.sync.result.RefundRemoteSyncResult;
import cn.daxpay.single.service.core.record.flow.service.TradeFlowRecordService;
import cn.daxpay.single.service.core.record.sync.entity.TradeSyncRecord;
import cn.daxpay.single.service.core.record.sync.service.TradeSyncRecordService;
import cn.daxpay.single.service.func.AbsRefundSyncStrategy;
import cn.daxpay.single.service.util.PayStrategyFactory;
import cn.hutool.core.util.StrUtil;
import com.baomidou.lock.LockInfo;
import com.baomidou.lock.LockTemplate;
import lombok.RequiredArgsConstructor;
@@ -49,9 +51,10 @@ public class RefundSyncService {
private final TradeSyncRecordService tradeSyncRecordService;
private final RefundAdjustService adjustService;
private final LockTemplate lockTemplate;
private final PayOrderService payOrderService;
private final TradeFlowRecordService tradeFlowRecordService;
private final ClientNoticeService clientNoticeService;
/**
* 退款同步, 开启一个新的事务, 不受外部抛出异常的影响
@@ -90,7 +93,7 @@ public class RefundSyncService {
// 判断是否同步成功
if (Objects.equals(refundRemoteSyncResult.getSyncStatus(), RefundSyncStatusEnum.FAIL)) {
// 同步失败, 返回失败响应, 同时记录失败的日志
this.saveRecord(refundOrder, refundRemoteSyncResult, null);
this.saveRecord(refundOrder, refundRemoteSyncResult, false);
throw new OperationFailException(refundRemoteSyncResult.getErrorMsg());
}
// 订单的通道交易号是否一致, 不一致进行更新
@@ -100,21 +103,20 @@ public class RefundSyncService {
}
// 判断网关状态是否和支付单一致
boolean statusSync = this.checkSyncStatus(refundRemoteSyncResult, refundOrder);
String adjustNo = null;
try {
// 状态不一致,执行退款单调整逻辑
if (!statusSync) {
// 如果没有支付来源, 设置支付来源为同步
adjustNo = this.adjustHandler(refundRemoteSyncResult, refundOrder);
this.adjustHandler(refundRemoteSyncResult, refundOrder);
}
} catch (PayFailureException e) {
// 同步失败, 返回失败响应, 同时记录失败的日志
refundRemoteSyncResult.setSyncStatus(RefundSyncStatusEnum.FAIL);
this.saveRecord(refundOrder, refundRemoteSyncResult, null);
this.saveRecord(refundOrder, refundRemoteSyncResult, statusSync);
throw e;
}
// 同步成功记录日志
this.saveRecord(refundOrder, refundRemoteSyncResult, adjustNo);
this.saveRecord(refundOrder, refundRemoteSyncResult, statusSync);
return new RefundSyncResult().setStatus(refundRemoteSyncResult.getSyncStatus().getCode());
} finally {
lockTemplate.releaseLock(lock);
@@ -135,7 +137,6 @@ public class RefundSyncService {
Objects.equals(orderStatus, RefundStatusEnum.SUCCESS.getCode())) {
return true;
}
// 退款失败
if (Objects.equals(syncStatus, RefundSyncStatusEnum.FAIL)&&
Objects.equals(orderStatus, RefundStatusEnum.FAIL.getCode())) {
@@ -152,43 +153,95 @@ public class RefundSyncService {
/**
* 进行退款订单和支付订单的调整
*/
private String adjustHandler(RefundRemoteSyncResult syncResult, RefundOrder order){
private void adjustHandler(RefundRemoteSyncResult syncResult, RefundOrder order){
RefundSyncStatusEnum syncStatusEnum = syncResult.getSyncStatus();
RefundAdjustParam param = new RefundAdjustParam()
.setOrder(order)
.setOutTradeNo(syncResult.getOutRefundNo())
.setSource(TradeAdjustSourceEnum.SYNC)
.setFinishTime(syncResult.getFinishTime());
// 对支付网关同步的结果进行处理
switch (syncStatusEnum) {
case SUCCESS:
param.setAdjustWay(RefundAdjustWayEnum.SUCCESS);
return adjustService.adjust(param);
this.success(order, syncResult);
break;
case PROGRESS:
// 不进行处理
break;
case FAIL: {
param.setAdjustWay(RefundAdjustWayEnum.FAIL);
return adjustService.adjust(param);
this.close(order);
break;
}
case NOT_FOUND:
param.setAdjustWay(RefundAdjustWayEnum.FAIL);
return adjustService.adjust(param);
this.close(order);
break;
default: {
throw new BizException("代码有问题");
}
}
return null;
}
/**
* 退款成功, 更新退款单和支付单
*/
private void success(RefundOrder refundOrder,RefundRemoteSyncResult syncResult) {
PayOrder payOrder = payOrderService.findById(refundOrder.getOrderId())
.orElseThrow(() -> new DataNotExistException("退款订单关联支付订单不存在"));
// 订单相关状态
PayOrderRefundStatusEnum afterPayRefundStatus;
// 判断订单全部退款还是部分退款
if (Objects.equals(payOrder.getRefundableBalance(), 0)) {
afterPayRefundStatus = PayOrderRefundStatusEnum.REFUNDED;
} else {
afterPayRefundStatus = PayOrderRefundStatusEnum.PARTIAL_REFUND;
}
// 设置退款为完成状态和完成时间
refundOrder.setStatus(RefundStatusEnum.SUCCESS.getCode())
.setFinishTime(syncResult.getFinishTime());
payOrder.setRefundStatus(afterPayRefundStatus.getCode());
// 更新订单和退款相关订单
payOrderService.updateById(payOrder);
refundOrderManager.updateById(refundOrder);
// 记录流水
tradeFlowRecordService.saveRefund(refundOrder);
// 发送通知
clientNoticeService.registerRefundNotice(refundOrder);
}
/**
* 退款失败, 关闭退款单并将失败的退款金额归还回订单
*/
private void close(RefundOrder refundOrder) {
PayOrder payOrder = payOrderService.findById(refundOrder.getOrderId())
.orElseThrow(() -> new DataNotExistException("退款订单关联支付订单不存在"));
// 退款失败返还后的余额
int payOrderAmount = refundOrder.getAmount() + payOrder.getRefundableBalance();
// 退款失败返还后的余额+可退余额 == 订单金额 支付订单回退为为支付成功状态
if (payOrderAmount == payOrder.getAmount()) {
payOrder.setStatus(PayStatusEnum.SUCCESS.getCode());
} else {
// 回归部分退款状态
payOrder.setRefundStatus(PayOrderRefundStatusEnum.PARTIAL_REFUND.getCode());
}
// 更新支付订单相关的可退款金额
payOrder.setRefundableBalance(payOrderAmount);
refundOrder.setStatus(RefundStatusEnum.CLOSE.getCode());
// 更新订单和退款相关订单
payOrderService.updateById(payOrder);
refundOrderManager.updateById(refundOrder);
// 发送通知
clientNoticeService.registerRefundNotice(refundOrder);
}
/**
* 保存同步记录, 使用新事务进行保存
* @param refundOrder 支付单
* @param syncResult 同步结果
* @param adjustNo 调整号
*/
private void saveRecord(RefundOrder refundOrder, RefundRemoteSyncResult syncResult, String adjustNo){
private void saveRecord(RefundOrder refundOrder, RefundRemoteSyncResult syncResult, boolean adjust){
TradeSyncRecord tradeSyncRecord = new TradeSyncRecord()
.setTradeNo(refundOrder.getRefundNo())
.setBizTradeNo(refundOrder.getBizRefundNo())
@@ -197,8 +250,7 @@ public class RefundSyncService {
.setType(TradeTypeEnum.REFUND.getCode())
.setChannel(refundOrder.getChannel())
.setSyncInfo(syncResult.getSyncInfo())
.setAdjust(StrUtil.isNotBlank(adjustNo))
.setAdjustNo(adjustNo)
.setAdjust(adjust)
.setErrorCode(syncResult.getErrorCode())
.setErrorMsg(syncResult.getErrorMsg())
.setClientIp(PaymentContextLocal.get().getClientInfo().getClientIp());

View File

@@ -33,7 +33,7 @@ public class AliAllocStrategy extends AbsAllocSyncStrategy {
@Override
public AllocRemoteSyncResult doSync() {
AliPayConfig config = alipayConfigService.getConfig();
return aliPayAllocService.sync(this.getAllocOrder(), config);
return aliPayAllocService.sync(this.getAllocOrder(),this.getAllocOrderDetails(), config);
}
}

View File

@@ -36,6 +36,6 @@ public class WeChatAllocSyncStrategy extends AbsAllocSyncStrategy {
@Override
public AllocRemoteSyncResult doSync() {
WeChatPayConfig config = weChatPayConfigService.getConfig();
return weChatPayAllocService.sync(this.getAllocOrder(),config);
return weChatPayAllocService.sync(this.getAllocOrder(),this.getAllocOrderDetails(), config);
}
}

View File

@@ -1,18 +0,0 @@
package cn.daxpay.single.service.core.record.adjust.convert;
import cn.daxpay.single.service.core.record.adjust.entity.TradeAdjustRecord;
import cn.daxpay.single.service.dto.record.adjust.TradeAdjustRecordDto;
import org.mapstruct.Mapper;
import org.mapstruct.factory.Mappers;
/**
*
* @author xxm
* @since 2024/7/15
*/
@Mapper
public interface TradeAdjustRecordConvert {
TradeAdjustRecordConvert CONVERT = Mappers.getMapper(TradeAdjustRecordConvert.class);
TradeAdjustRecordDto convert(TradeAdjustRecord in);
}

View File

@@ -1,32 +0,0 @@
package cn.daxpay.single.service.core.record.adjust.dao;
import cn.bootx.platform.common.core.rest.param.PageParam;
import cn.bootx.platform.common.mybatisplus.impl.BaseManager;
import cn.bootx.platform.common.mybatisplus.util.MpUtil;
import cn.bootx.platform.common.query.generator.QueryGenerator;
import cn.daxpay.single.service.core.record.adjust.entity.TradeAdjustRecord;
import cn.daxpay.single.service.param.report.TradeAdjustRecordQuery;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Repository;
/**
*
* @author xxm
* @since 2024/7/15
*/
@Slf4j
@Repository
@RequiredArgsConstructor
public class TradeAdjustRecordManager extends BaseManager<TradeAdjustRecordMapper, TradeAdjustRecord> {
public Page<TradeAdjustRecord> page(PageParam pageParam, TradeAdjustRecordQuery param){
QueryWrapper<TradeAdjustRecord> generator = QueryGenerator.generator(param);
Page<TradeAdjustRecord> mpPage = MpUtil.getMpPage(pageParam, TradeAdjustRecord.class);
return this.page(mpPage, generator);
}
}

View File

@@ -1,14 +0,0 @@
package cn.daxpay.single.service.core.record.adjust.dao;
import cn.daxpay.single.service.core.record.adjust.entity.TradeAdjustRecord;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Mapper;
/**
* 交易调整记录
* @author xxm
* @since 2024/7/15
*/
@Mapper
public interface TradeAdjustRecordMapper extends BaseMapper<TradeAdjustRecord> {
}

View File

@@ -1,100 +0,0 @@
package cn.daxpay.single.service.core.record.adjust.entity;
import cn.bootx.platform.common.core.function.EntityBaseFunction;
import cn.bootx.platform.common.mybatisplus.base.MpCreateEntity;
import cn.bootx.table.modify.annotation.DbColumn;
import cn.bootx.table.modify.mysql.annotation.DbMySqlFieldType;
import cn.bootx.table.modify.mysql.annotation.DbMySqlIndex;
import cn.bootx.table.modify.mysql.constants.MySqlFieldTypeEnum;
import cn.daxpay.single.core.code.PayStatusEnum;
import cn.daxpay.single.service.code.TradeAdjustSourceEnum;
import cn.daxpay.single.service.core.record.adjust.convert.TradeAdjustRecordConvert;
import cn.daxpay.single.service.dto.record.adjust.TradeAdjustRecordDto;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
/**
* 订单调整记录
* @author xxm
* @since 2024/7/15
*/
@EqualsAndHashCode(callSuper = true)
@Data
@Accessors(chain = true)
@TableName("pay_trade_adjust_record")
public class TradeAdjustRecord extends MpCreateEntity implements EntityBaseFunction<TradeAdjustRecordDto> {
/**
* 调整号
*/
@DbColumn(comment = "调整号", length = 32, isNull = false)
private String adjustNo;
/** 交易ID */
@DbColumn(comment = "本地订单ID", isNull = false)
private Long tradeId;
/**
* 本地交易号, 支付号/退款号
*/
@DbMySqlIndex(comment = "本地交易号索引")
@DbColumn(comment = "本地交易号", length = 32, isNull = false)
private String tradeNo;
/** 通道 */
@DbColumn(comment = "通道", length = 20, isNull = false)
private String channel;
/**
* 来源
* @see TradeAdjustSourceEnum
*/
@DbColumn(comment = "调整来源", length = 20, isNull = false)
private String source;
/**
* 调整类型
* @see cn.daxpay.single.service.code.TradeNotifyTypeEnum
*/
@DbColumn(comment = "调整类型", length = 20, isNull = false)
private String type;
/**
* 调整方式
*/
@DbColumn(comment = "调整方式", length = 20, isNull = false)
private String way;
/**
* 调整前状态
* @see PayStatusEnum
*/
@DbColumn(comment = "调整前状态", length = 20, isNull = false)
private String beforeStatus;
/**
* 调整后状态
*/
@DbColumn(comment = "调整后状态", length = 20, isNull = false)
private String afterStatus;
/**
* 扩展信息, json格式
*/
@DbColumn(comment = "扩展信息")
@DbMySqlFieldType(MySqlFieldTypeEnum.LONGTEXT)
private String ext;
/**
* 备注
*/
@DbColumn(comment = "备注", length = 600)
private String remark;
@Override
public TradeAdjustRecordDto toDto() {
return TradeAdjustRecordConvert.CONVERT.convert(this);
}
}

View File

@@ -1,58 +0,0 @@
package cn.daxpay.single.service.core.record.adjust.service;
import cn.bootx.platform.common.core.exception.DataNotExistException;
import cn.bootx.platform.common.core.rest.PageResult;
import cn.bootx.platform.common.core.rest.param.PageParam;
import cn.bootx.platform.common.mybatisplus.util.MpUtil;
import cn.daxpay.single.service.core.record.adjust.dao.TradeAdjustRecordManager;
import cn.daxpay.single.service.core.record.adjust.entity.TradeAdjustRecord;
import cn.daxpay.single.service.dto.record.adjust.TradeAdjustRecordDto;
import cn.daxpay.single.service.param.report.TradeAdjustRecordQuery;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import java.util.List;
/**
* 交易调整记录
* @author xxm
* @since 2024/7/15
*/
@Slf4j
@Service
@RequiredArgsConstructor
public class TradeAdjustRecordService {
private final TradeAdjustRecordManager tradeAdjustRecordManager;
/**
* 根据id查询
*/
public TradeAdjustRecordDto findById(Long id) {
return tradeAdjustRecordManager.findById(id).map(TradeAdjustRecord::toDto).orElseThrow(DataNotExistException::new);
}
/**
* 分页查询
*/
public PageResult<TradeAdjustRecordDto> page(PageParam pageParam, TradeAdjustRecordQuery param){
return MpUtil.convert2DtoPageResult(tradeAdjustRecordManager.page(pageParam,param));
}
/**
* 保存记录
*/
@Transactional(propagation = Propagation.REQUIRES_NEW, rollbackFor = Exception.class)
public void saveRecord(TradeAdjustRecord record){
tradeAdjustRecordManager.save(record);
}
/**
* 保存记录
*/
@Transactional(propagation = Propagation.REQUIRES_NEW, rollbackFor = Exception.class)
public void saveAllRecord(List<TradeAdjustRecord> records){
tradeAdjustRecordManager.saveAll(records);
}
}

View File

@@ -59,15 +59,12 @@ public class PayCallbackRecord extends MpCreateEntity implements EntityBaseFunct
private String notifyInfo;
/**
* 回调处理状态
* @see PayCallbackStatusEnum
*/
@DbColumn(comment = "回调处理状态", length = 20, isNull = false)
private String status;
/** 调整号 */
@DbColumn(comment = "调整号", length = 32, isNull = false)
private String adjustNo;
/** 错误码 */
@DbColumn(comment = "错误码", length = 10)
private String errorCode;

View File

@@ -54,7 +54,6 @@ public class PayCallbackRecordService {
.setChannel(callbackInfo.getChannel())
.setNotifyInfo(JSONUtil.toJsonStr(callbackInfo.getCallbackParam()))
.setCallbackType(callbackInfo.getCallbackType().getCode())
.setAdjustNo(callbackInfo.getAdjustNo())
.setStatus(callbackInfo.getCallbackStatus().getCode())
.setErrorMsg(callbackInfo.getErrorMsg());
callbackRecordManager.save(payNotifyRecord);

View File

@@ -78,10 +78,6 @@ public class TradeSyncRecord extends MpCreateEntity implements EntityBaseFunctio
@DbColumn(comment = "是否进行调整", isNull = false)
private boolean adjust;
/** 调整记录号 */
@DbColumn(comment = "调整记录号", length = 32)
private String adjustNo;
/** 错误码 */
@DbColumn(comment = "错误码", length = 10)
private String errorCode;

View File

@@ -15,6 +15,6 @@ import lombok.experimental.Accessors;
@Data
@Accessors(chain = true)
@Schema(title = "交易调整记录")
public class TradeAdjustRecordDto extends BaseDto {
public class PayAdjustRecordDto extends BaseDto {
}

View File

@@ -0,0 +1,20 @@
package cn.daxpay.single.service.dto.record.adjust;
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/7/15
*/
@EqualsAndHashCode(callSuper = true)
@Data
@Accessors(chain = true)
@Schema(title = "交易调整记录")
public class RefundAdjustRecordDto extends BaseDto {
}

View File

@@ -0,0 +1,20 @@
package cn.daxpay.single.service.dto.record.adjust;
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/7/15
*/
@EqualsAndHashCode(callSuper = true)
@Data
@Accessors(chain = true)
@Schema(title = "交易调整记录")
public class TransferAdjustRecordDto extends BaseDto {
}

View File

@@ -2,6 +2,7 @@ package cn.daxpay.single.service.func;
import cn.daxpay.single.service.core.order.allocation.entity.AllocOrder;
import cn.daxpay.single.service.core.order.allocation.entity.AllocOrderDetail;
import cn.daxpay.single.service.core.payment.sync.result.AllocRemoteSyncResult;
import lombok.Getter;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
@@ -44,4 +45,9 @@ public abstract class AbsAllocStrategy implements PayStrategy{
* 分账完结
*/
public abstract void finish();
/**
* 同步状态
*/
public abstract AllocRemoteSyncResult doSync();
}

View File

@@ -1,11 +1,14 @@
package cn.daxpay.single.service.func;
import cn.daxpay.single.service.core.order.allocation.entity.AllocOrder;
import cn.daxpay.single.service.core.order.allocation.entity.AllocOrderDetail;
import cn.daxpay.single.service.core.payment.sync.result.AllocRemoteSyncResult;
import lombok.Getter;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import java.util.List;
/**
* 分账同步策略
* @author xxm
@@ -18,11 +21,14 @@ public abstract class AbsAllocSyncStrategy implements PayStrategy{
private AllocOrder allocOrder;
private List<AllocOrderDetail> allocOrderDetails;
/**
* 初始化参数
*/
public void initParam(AllocOrder allocOrder) {
public void initParam(AllocOrder allocOrder, List<AllocOrderDetail> allocOrderDetails) {
this.allocOrder = allocOrder;
this.allocOrderDetails = allocOrderDetails;
}
/**