feat 客户系统通知联调, 修复手动发起退款上下文未进行初始化的问题, 其他一些小问题修复

This commit is contained in:
bootx
2024-02-25 21:54:14 +08:00
parent f26806816b
commit 536d3c6191
34 changed files with 348 additions and 225 deletions

View File

@@ -13,8 +13,7 @@ import lombok.Getter;
public enum AliPayRecordTypeEnum {
/** 支付 */
PAY("pay", "支付")
,
PAY("pay", "支付"),
/** 退款 */
REFUND("refund", "退款");

View File

@@ -0,0 +1,22 @@
package cn.bootx.platform.daxpay.service.code;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
* 微信支付流水记录类型
* @author xxm
* @since 2024/2/20
*/
@Getter
@AllArgsConstructor
public enum WechatPayRecordTypeEnum {
/** 支付 */
PAY("pay", "支付"),
/** 退款 */
REFUND("refund", "退款");
private final String code;
private final String name;
}

View File

@@ -8,6 +8,7 @@ import lombok.Data;
import lombok.experimental.Accessors;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
/**
@@ -47,6 +48,6 @@ public class PayLocal {
private PayOrderExtra payOrderExtra;
/** 通道支付订单 */
private List<PayChannelOrder> payChannelOrders;
private List<PayChannelOrder> payChannelOrders = new ArrayList<>();
}

View File

@@ -1,9 +1,14 @@
package cn.bootx.platform.daxpay.service.common.context;
import cn.bootx.platform.daxpay.code.RefundStatusEnum;
import cn.bootx.platform.daxpay.service.core.order.refund.entity.RefundChannelOrder;
import cn.bootx.platform.daxpay.service.core.order.refund.entity.RefundOrder;
import cn.bootx.platform.daxpay.service.core.order.refund.entity.RefundOrderExtra;
import lombok.Data;
import lombok.experimental.Accessors;
import java.util.List;
/**
* 异步退款信息
* @author xxm
@@ -28,4 +33,13 @@ public class RefundLocal {
/** 错误内容 */
private String errorMsg;
/** 退款订单 */
private RefundOrder refundOrder;
/** 退款订单扩展 */
private RefundOrderExtra runOrderExtra;
/** 通道退款订单 */
private List<RefundChannelOrder> refundChannelOrders;
}

View File

@@ -16,25 +16,6 @@ public final class PaymentContextLocal {
private static final ThreadLocal<PaymentContext> THREAD_LOCAL = new TransmittableThreadLocal<>();
/**
* 设置
*/
public static void set(PaymentContext paymentContext){
THREAD_LOCAL.set(paymentContext);
}
/**
* 不存在则进行赋值
*/
public static boolean setIfAbsent(PaymentContext paymentContext){
if (THREAD_LOCAL.get() == null){
set(paymentContext);
return true;
}
return false;
}
/**
* 获取
*/
@@ -53,5 +34,4 @@ public final class PaymentContextLocal {
public static void clear() {
THREAD_LOCAL.remove();
}
}

View File

@@ -47,7 +47,7 @@ public class AliPayRecordService {
*/
public void refund(RefundOrder order, RefundChannelOrder channelOrder){
AliPayRecord aliPayRecord = new AliPayRecord()
.setType(AliPayRecordTypeEnum.PAY.getCode())
.setType(AliPayRecordTypeEnum.REFUND.getCode())
.setTitle(order.getTitle())
.setOrderId(order.getId())
.setGatewayOrderNo(order.getGatewayOrderNo())

View File

@@ -2,7 +2,7 @@ package cn.bootx.platform.daxpay.service.core.channel.wechat.entity;
import cn.bootx.platform.common.core.function.EntityBaseFunction;
import cn.bootx.platform.common.mybatisplus.base.MpCreateEntity;
import cn.bootx.platform.daxpay.service.code.AliPayRecordTypeEnum;
import cn.bootx.platform.daxpay.service.code.WechatPayRecordTypeEnum;
import cn.bootx.platform.daxpay.service.core.channel.wechat.convert.WeChatConvert;
import cn.bootx.platform.daxpay.service.dto.channel.wechat.WeChatPayRecordDto;
import cn.bootx.table.modify.annotation.DbColumn;
@@ -34,7 +34,7 @@ public class WeChatPayRecord extends MpCreateEntity implements EntityBaseFunctio
/**
* 业务类型
* @see AliPayRecordTypeEnum
* @see WechatPayRecordTypeEnum
*/
@DbColumn(comment = "业务类型")
private String type;

View File

@@ -4,7 +4,7 @@ import cn.bootx.platform.common.core.exception.DataNotExistException;
import cn.bootx.platform.common.core.rest.PageResult;
import cn.bootx.platform.common.core.rest.param.PageParam;
import cn.bootx.platform.common.mybatisplus.util.MpUtil;
import cn.bootx.platform.daxpay.service.code.AliPayRecordTypeEnum;
import cn.bootx.platform.daxpay.service.code.WechatPayRecordTypeEnum;
import cn.bootx.platform.daxpay.service.core.channel.wechat.dao.WeChatPayRecordManager;
import cn.bootx.platform.daxpay.service.core.channel.wechat.entity.WeChatPayRecord;
import cn.bootx.platform.daxpay.service.core.order.pay.entity.PayChannelOrder;
@@ -33,7 +33,7 @@ public class WeChatPayRecordService {
*/
public void pay(PayOrder order, PayChannelOrder channelOrder){
WeChatPayRecord weChatPayRecord = new WeChatPayRecord()
.setType(AliPayRecordTypeEnum.PAY.getCode())
.setType(WechatPayRecordTypeEnum.PAY.getCode())
.setTitle(order.getTitle())
.setOrderId(order.getId())
.setGatewayOrderNo(order.getGatewayOrderNo())
@@ -46,7 +46,7 @@ public class WeChatPayRecordService {
*/
public void refund(RefundOrder order, RefundChannelOrder channelOrder){
WeChatPayRecord weChatPayRecord = new WeChatPayRecord()
.setType(AliPayRecordTypeEnum.PAY.getCode())
.setType(WechatPayRecordTypeEnum.REFUND.getCode())
.setTitle(order.getTitle())
.setOrderId(order.getId())
.setGatewayOrderNo(order.getGatewayOrderNo())

View File

@@ -54,9 +54,9 @@ public class PayChannelOrderService {
*/
@Transactional(rollbackFor = Exception.class)
public void switchAsyncPayChannel(PayOrder payOrder, PayChannelParam payChannelParam){
PayLocal asyncPayInfo = PaymentContextLocal.get().getPayInfo();
PayLocal payInfo = PaymentContextLocal.get().getPayInfo();
// 是否支付完成
PayStatusEnum payStatus = asyncPayInfo.isPayComplete() ? PayStatusEnum.SUCCESS : PayStatusEnum.PROGRESS;
PayStatusEnum payStatus = payInfo.isPayComplete() ? PayStatusEnum.SUCCESS : PayStatusEnum.PROGRESS;
// 判断新发起的
Optional<PayChannelOrder> payOrderChannelOpt = channelOrderManager.findByPaymentIdAndChannel(payOrder.getId(), payChannelParam.getChannel());
// 扩展信息处理
@@ -80,6 +80,7 @@ public class PayChannelOrderService {
.setStatus(payStatus.getCode());
channelOrderManager.deleteByPaymentIdAndAsync(payOrder.getId());
channelOrderManager.save(payChannelOrder);
payInfo.getPayChannelOrders().add(payChannelOrder);
} else {
// 更新支付通道信息
payOrderChannelOpt.get()
@@ -88,6 +89,7 @@ public class PayChannelOrderService {
.setChannelExtra(channelParamStr)
.setStatus(payStatus.getCode());
channelOrderManager.updateById(payOrderChannelOpt.get());
payInfo.getPayChannelOrders().add(payOrderChannelOpt.get());
}
}

View File

@@ -31,14 +31,18 @@ import java.time.LocalDateTime;
@TableName(value = "pay_refund_order", autoResultMap = true)
public class RefundOrder extends MpBaseEntity implements EntityBaseFunction<RefundOrderDto> {
/** 关联支付id */
@DbColumn(comment = "关联支付id")
/** 支付id */
@DbColumn(comment = "支付id")
private Long paymentId;
/** 关联业务号 */
@DbColumn(comment = "关联业务号")
/** 原支付业务号 */
@DbColumn(comment = "原支付业务号")
private String businessNo;
/** 原支付标题 */
@DbColumn(comment = "原支付标题")
private String title;
/**
* 需要保证全局唯一
*/
@@ -60,9 +64,6 @@ public class RefundOrder extends MpBaseEntity implements EntityBaseFunction<Refu
@DbColumn(comment = "网关订单号")
private String gatewayOrderNo;
/** 标题 */
@DbColumn(comment = "标题")
private String title;
/** 订单金额 */
@DbColumn(comment = "订单金额")

View File

@@ -5,7 +5,10 @@ import cn.bootx.platform.common.core.exception.ValidationFailedException;
import cn.bootx.platform.common.core.rest.PageResult;
import cn.bootx.platform.common.core.rest.param.PageParam;
import cn.bootx.platform.common.mybatisplus.util.MpUtil;
import cn.bootx.platform.common.spring.util.WebServletUtil;
import cn.bootx.platform.daxpay.code.PaymentApiCode;
import cn.bootx.platform.daxpay.param.pay.QueryRefundParam;
import cn.bootx.platform.daxpay.param.pay.RefundParam;
import cn.bootx.platform.daxpay.result.order.RefundChannelOrderResult;
import cn.bootx.platform.daxpay.result.order.RefundOrderResult;
import cn.bootx.platform.daxpay.service.core.order.refund.convert.RefundOrderConvert;
@@ -14,17 +17,26 @@ import cn.bootx.platform.daxpay.service.core.order.refund.dao.RefundChannelOrder
import cn.bootx.platform.daxpay.service.core.order.refund.dao.RefundOrderManager;
import cn.bootx.platform.daxpay.service.core.order.refund.entity.RefundChannelOrder;
import cn.bootx.platform.daxpay.service.core.order.refund.entity.RefundOrder;
import cn.bootx.platform.daxpay.service.core.payment.common.service.PaymentAssistService;
import cn.bootx.platform.daxpay.service.core.payment.refund.service.RefundService;
import cn.bootx.platform.daxpay.service.core.system.config.dao.PayApiConfigManager;
import cn.bootx.platform.daxpay.service.core.system.config.entity.PayApiConfig;
import cn.bootx.platform.daxpay.service.core.system.config.service.PayApiConfigService;
import cn.bootx.platform.daxpay.service.dto.order.refund.RefundOrderDto;
import cn.bootx.platform.daxpay.service.dto.order.refund.RefundChannelOrderDto;
import cn.bootx.platform.daxpay.service.param.order.PayOrderRefundParam;
import cn.bootx.platform.daxpay.service.param.order.RefundOrderQuery;
import cn.hutool.core.util.StrUtil;
import cn.hutool.extra.servlet.ServletUtil;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.stream.Collectors;
/**
@@ -36,8 +48,13 @@ import java.util.stream.Collectors;
@Slf4j
@Service
@RequiredArgsConstructor
public class RefundOrderQueryService {
public class RefundOrderService {
private final RefundService refundService;
private final PayApiConfigService apiConfigService;
private final PaymentAssistService paymentAssistService;
private final PayApiConfigManager apiConfigManager;
private final RefundOrderManager refundOrderManager;
private final RefundChannelOrderManager refundOrderChannelManager;
@@ -106,4 +123,29 @@ public class RefundOrderQueryService {
refundOrderResult.setChannels(channels);
return refundOrderResult;
}
/**
* 手动发起退款
*/
public void refund(PayOrderRefundParam param) {
String ip = Optional.ofNullable(WebServletUtil.getRequest())
.map(ServletUtil::getClientIP)
.orElse("未知");
RefundParam refundParam = new RefundParam();
refundParam.setPaymentId(param.getPaymentId());
refundParam.setRefundChannels(param.getRefundChannels());
refundParam.setReason(param.getReason());
refundParam.setReqTime(LocalDateTime.now());
refundParam.setClientIp(ip);
// 手动初始化上下文
paymentAssistService.initContext(refundParam);
// 初始化接口信息为统一退款
PayApiConfig api = apiConfigManager.findByCode(PaymentApiCode.REFUND).orElseThrow(() -> new DataNotExistException("未找到统一退款接口信息"));
// 设置接口信息
apiConfigService.initApiInfo(api);
// 调用统一退款接口
refundService.refund(refundParam);
}
}

View File

@@ -23,7 +23,6 @@ import org.springframework.stereotype.Service;
public class PaymentAssistService {
private final PlatformConfigService platformConfigService;
/**
* 初始化上下文
*/

View File

@@ -2,6 +2,8 @@ package cn.bootx.platform.daxpay.service.core.payment.notice.result;
import cn.bootx.platform.daxpay.code.PayChannelEnum;
import cn.bootx.platform.daxpay.code.RefundStatusEnum;
import cn.bootx.platform.daxpay.serializer.LocalDateTimeToTimestampSerializer;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.experimental.Accessors;
@@ -47,9 +49,11 @@ public class RefundNoticeResult {
private String status;
@Schema(description = "退款成功时间")
@JsonSerialize(using = LocalDateTimeToTimestampSerializer.class)
private LocalDateTime refundTime;
@Schema(description = "退款创建时间")
@JsonSerialize(using = LocalDateTimeToTimestampSerializer.class)
private LocalDateTime createTime;
@Schema(description = "商户扩展参数,回调时会原样返回")

View File

@@ -0,0 +1,121 @@
package cn.bootx.platform.daxpay.service.core.payment.notice.service;
import cn.bootx.platform.common.jackson.util.JacksonUtil;
import cn.bootx.platform.daxpay.code.PaySignTypeEnum;
import cn.bootx.platform.daxpay.service.code.ClientNoticeTypeEnum;
import cn.bootx.platform.daxpay.service.core.order.pay.entity.PayChannelOrder;
import cn.bootx.platform.daxpay.service.core.order.pay.entity.PayOrder;
import cn.bootx.platform.daxpay.service.core.order.pay.entity.PayOrderExtra;
import cn.bootx.platform.daxpay.service.core.order.refund.entity.RefundChannelOrder;
import cn.bootx.platform.daxpay.service.core.order.refund.entity.RefundOrder;
import cn.bootx.platform.daxpay.service.core.order.refund.entity.RefundOrderExtra;
import cn.bootx.platform.daxpay.service.core.payment.notice.result.PayChannelResult;
import cn.bootx.platform.daxpay.service.core.payment.notice.result.PayNoticeResult;
import cn.bootx.platform.daxpay.service.core.payment.notice.result.RefundChannelResult;
import cn.bootx.platform.daxpay.service.core.payment.notice.result.RefundNoticeResult;
import cn.bootx.platform.daxpay.service.core.system.config.entity.PlatformConfig;
import cn.bootx.platform.daxpay.service.core.system.config.service.PlatformConfigService;
import cn.bootx.platform.daxpay.service.core.task.notice.entity.ClientNoticeTask;
import cn.bootx.platform.daxpay.util.PaySignUtil;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;
/**
* 客户系统消息通知任务支撑服务
* @author xxm
* @since 2024/2/25
*/
@Slf4j
@Service
@RequiredArgsConstructor
public class ClientNoticeAssistService {
private final PlatformConfigService configService;
/**
* 构建出支付通知任务对象
*/
public ClientNoticeTask buildPayTask(PayOrder order, PayOrderExtra orderExtra, List<PayChannelOrder> channelOrders){
// 组装内容
List<PayChannelResult> channels = channelOrders.stream()
.map(o->new PayChannelResult().setChannel(o.getChannel()).setWay(o.getPayWay()).setAmount(o.getAmount()))
.collect(Collectors.toList());
PayNoticeResult payNoticeResult = new PayNoticeResult()
.setPaymentId(order.getId())
.setAsyncPay(order.isAsyncPay())
.setBusinessNo(order.getBusinessNo())
.setAmount(order.getAmount())
.setPayTime(order.getPayTime())
.setCloseTime(order.getCloseTime())
.setCreateTime(order.getCreateTime())
.setStatus(order.getStatus())
.setAttach(orderExtra.getAttach())
.setPayChannels(channels);
PlatformConfig config = configService.getConfig();
// 是否需要签名
if (orderExtra.isNoticeSign()){
// 签名
if (Objects.equals(config.getSignType(), PaySignTypeEnum.MD5.getCode())){
payNoticeResult.setSign(PaySignUtil.md5Sign(payNoticeResult,config.getSignSecret()));
} else {
payNoticeResult.setSign(PaySignUtil.hmacSha256Sign(payNoticeResult,config.getSignSecret()));
}
}
return new ClientNoticeTask()
.setUrl(orderExtra.getNotifyUrl())
// 时间序列化进行了重写
.setContent(JacksonUtil.toJson(payNoticeResult))
.setType(ClientNoticeTypeEnum.PAY.getType())
.setSendCount(0)
.setOrderId(order.getId());
}
/**
* 构建出退款通知任务对象
*/
public ClientNoticeTask buildRefundTask(RefundOrder order, RefundOrderExtra orderExtra, List<RefundChannelOrder> channelOrders){
// 组装内容
List<RefundChannelResult> channels = channelOrders.stream()
.map(o->new RefundChannelResult().setChannel(o.getChannel()).setAmount(o.getAmount()))
.collect(Collectors.toList());
RefundNoticeResult payNoticeResult = new RefundNoticeResult()
.setRefundId(order.getId())
.setAsyncPay(order.isAsyncPay())
.setAsyncChannel(order.getAsyncChannel())
.setRefundNo(order.getRefundNo())
.setAmount(order.getAmount())
.setRefundTime(order.getRefundTime())
.setCreateTime(order.getCreateTime())
.setStatus(order.getStatus())
.setAttach(orderExtra.getAttach())
.setRefundChannels(channels);
PlatformConfig config = configService.getConfig();
// 是否需要签名
if (orderExtra.isNoticeSign()){
// 签名
if (Objects.equals(config.getSignType(), PaySignTypeEnum.MD5.getCode())){
payNoticeResult.setSign(PaySignUtil.md5Sign(payNoticeResult,config.getSignSecret()));
} else {
payNoticeResult.setSign(PaySignUtil.hmacSha256Sign(payNoticeResult,config.getSignSecret()));
}
}
return new ClientNoticeTask()
.setUrl(orderExtra.getNotifyUrl())
// 时间序列化进行了重写
.setContent(JacksonUtil.toJson(payNoticeResult))
.setType(ClientNoticeTypeEnum.REFUND.getType())
.setSendCount(0)
.setOrderId(order.getId());
}
}

View File

@@ -3,30 +3,22 @@ package cn.bootx.platform.daxpay.service.core.payment.notice.service;
import cn.bootx.platform.common.core.exception.RepetitiveOperationException;
import cn.bootx.platform.common.core.util.CollUtil;
import cn.bootx.platform.common.core.util.LocalDateTimeUtil;
import cn.bootx.platform.common.jackson.util.JacksonUtil;
import cn.bootx.platform.common.redis.RedisClient;
import cn.bootx.platform.daxpay.code.PaySignTypeEnum;
import cn.bootx.platform.daxpay.service.code.ClientNoticeSendTypeEnum;
import cn.bootx.platform.daxpay.service.code.ClientNoticeTypeEnum;
import cn.bootx.platform.daxpay.service.core.order.pay.dao.PayChannelOrderManager;
import cn.bootx.platform.daxpay.service.core.order.pay.dao.PayOrderExtraManager;
import cn.bootx.platform.daxpay.service.core.order.pay.entity.PayChannelOrder;
import cn.bootx.platform.daxpay.service.core.order.pay.entity.PayOrder;
import cn.bootx.platform.daxpay.service.core.order.pay.entity.PayOrderExtra;
import cn.bootx.platform.daxpay.service.core.order.refund.dao.RefundChannelOrderManager;
import cn.bootx.platform.daxpay.service.core.order.refund.dao.RefundOrderExtraManager;
import cn.bootx.platform.daxpay.service.core.order.refund.entity.RefundChannelOrder;
import cn.bootx.platform.daxpay.service.core.order.refund.entity.RefundOrder;
import cn.bootx.platform.daxpay.service.core.payment.notice.result.PayChannelResult;
import cn.bootx.platform.daxpay.service.core.payment.notice.result.PayNoticeResult;
import cn.bootx.platform.daxpay.service.core.payment.notice.result.RefundChannelResult;
import cn.bootx.platform.daxpay.service.core.payment.notice.result.RefundNoticeResult;
import cn.bootx.platform.daxpay.service.core.system.config.entity.PlatformConfig;
import cn.bootx.platform.daxpay.service.core.system.config.service.PlatformConfigService;
import cn.bootx.platform.daxpay.service.core.order.refund.entity.RefundOrderExtra;
import cn.bootx.platform.daxpay.service.core.task.notice.dao.ClientNoticeTaskManager;
import cn.bootx.platform.daxpay.service.core.task.notice.entity.ClientNoticeRecord;
import cn.bootx.platform.daxpay.service.core.task.notice.entity.ClientNoticeTask;
import cn.bootx.platform.daxpay.service.core.task.notice.service.ClientNoticeRecordService;
import cn.bootx.platform.daxpay.util.PaySignUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.http.ContentType;
import cn.hutool.http.HttpResponse;
@@ -40,10 +32,9 @@ import org.springframework.stereotype.Service;
import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;
/**
* 消息通知任务服务
* 客户系统消息通知任务服务
* 如果失败总共会重新发起15次通知通知频率为15s/15s/30s/3m/10m/20m/30m/30m/30m/60m/3h/3h/3h/6h/6h
* @author xxm
* @since 2024/2/20
@@ -59,7 +50,9 @@ public class ClientNoticeService {
private final RefundChannelOrderManager refundChannelOrderManager;
private final PlatformConfigService configService;
private final RefundOrderExtraManager refundOrderExtraManager;
private final ClientNoticeAssistService clientNoticeAssistService;
private final ClientNoticeTaskManager taskManager;
@@ -116,6 +109,7 @@ public class ClientNoticeService {
// 判断是否需要进行通知
if (StrUtil.isBlank(orderExtra.getNotifyUrl())){
log.info("支付订单无需通知订单ID{}",order.getId());
return;
}
// 通道支付订单为空则进行查询
@@ -123,7 +117,7 @@ public class ClientNoticeService {
channelOrders = payChannelOrderManager.findAllByPaymentId(order.getId());
}
// 创建通知任务并保存
ClientNoticeTask task = this.buildPayTask(order, orderExtra, channelOrders);
ClientNoticeTask task = clientNoticeAssistService.buildPayTask(order, orderExtra, channelOrders);
try {
taskManager.save(task);
} catch (Exception e) {
@@ -134,47 +128,6 @@ public class ClientNoticeService {
this.sendData(task, LocalDateTime.now());
}
/**
* 构建出支付通知任务对象
*/
private ClientNoticeTask buildPayTask(PayOrder order, PayOrderExtra orderExtra, List<PayChannelOrder> channelOrders){
// 组装内容
List<PayChannelResult> channels = channelOrders.stream()
.map(o->new PayChannelResult().setChannel(o.getChannel()).setWay(o.getPayWay()).setAmount(o.getAmount()))
.collect(Collectors.toList());
PayNoticeResult payNoticeResult = new PayNoticeResult()
.setPaymentId(order.getId())
.setAsyncPay(order.isAsyncPay())
.setBusinessNo(order.getBusinessNo())
.setAmount(order.getAmount())
.setPayTime(order.getPayTime())
.setCloseTime(order.getCloseTime())
.setCreateTime(order.getCreateTime())
.setStatus(order.getStatus())
.setAttach(orderExtra.getAttach())
.setPayChannels(channels);
PlatformConfig config = configService.getConfig();
// 是否需要签名
if (orderExtra.isNoticeSign()){
// 签名
if (Objects.equals(config.getSignType(), PaySignTypeEnum.MD5.getCode())){
payNoticeResult.setSign(PaySignUtil.md5Sign(payNoticeResult,config.getSignSecret()));
} else {
payNoticeResult.setSign(PaySignUtil.hmacSha256Sign(payNoticeResult,config.getSignSecret()));
}
}
return new ClientNoticeTask()
.setUrl(orderExtra.getNotifyUrl())
// 时间序列化进行了重写
.setContent(JacksonUtil.toJson(payNoticeResult))
.setType(ClientNoticeTypeEnum.PAY.getType())
.setSendCount(0)
.setOrderId(order.getId());
}
/**
* 注册退款消息通知任务
* @param order 退款订单
@@ -182,10 +135,10 @@ public class ClientNoticeService {
* @param channelOrders 退款通道订单
*/
@Async("bigExecutor")
public void registerRefundNotice(RefundOrder order, PayOrderExtra orderExtra, List<RefundChannelOrder> channelOrders) {
public void registerRefundNotice(RefundOrder order, RefundOrderExtra orderExtra, List<RefundChannelOrder> channelOrders) {
// 退款订单扩展信息为空则进行查询
if (Objects.isNull(orderExtra)){
Optional<PayOrderExtra> extraOpt = payOrderExtraManager.findById(order.getId());
Optional<RefundOrderExtra> extraOpt = refundOrderExtraManager.findById(order.getId());
if (!extraOpt.isPresent()){
log.error("未找到支付扩展信息数据错误订单ID{}",order.getId());
return;
@@ -195,6 +148,7 @@ public class ClientNoticeService {
// 判断是否需要进行通知
if (StrUtil.isBlank(orderExtra.getNotifyUrl())){
log.info("退款订单无需通知订单ID{}",order.getId());
return;
}
// 通道退款订单为空则进行查询
@@ -202,7 +156,7 @@ public class ClientNoticeService {
channelOrders = refundChannelOrderManager.findAllByRefundId(order.getId());
}
// 创建通知任务并保存
ClientNoticeTask task = this.buildRefundTask(order, orderExtra, channelOrders);
ClientNoticeTask task = clientNoticeAssistService.buildRefundTask(order, orderExtra, channelOrders);
try {
taskManager.save(task);
} catch (Exception e) {
@@ -214,47 +168,6 @@ public class ClientNoticeService {
this.sendData(task, LocalDateTime.now());
}
/**
* 构建出退款通知任务对象
*/
private ClientNoticeTask buildRefundTask(RefundOrder order, PayOrderExtra orderExtra, List<RefundChannelOrder> channelOrders){
// 组装内容
List<RefundChannelResult> channels = channelOrders.stream()
.map(o->new RefundChannelResult().setChannel(o.getChannel()).setAmount(o.getAmount()))
.collect(Collectors.toList());
RefundNoticeResult payNoticeResult = new RefundNoticeResult()
.setRefundId(order.getId())
.setAsyncPay(order.isAsyncPay())
.setAsyncChannel(order.getAsyncChannel())
.setRefundNo(order.getRefundNo())
.setAmount(order.getAmount())
.setRefundTime(order.getRefundTime())
.setCreateTime(order.getCreateTime())
.setStatus(order.getStatus())
.setAttach(orderExtra.getAttach())
.setRefundChannels(channels);
PlatformConfig config = configService.getConfig();
// 是否需要签名
if (orderExtra.isNoticeSign()){
// 签名
if (Objects.equals(config.getSignType(), PaySignTypeEnum.MD5.getCode())){
payNoticeResult.setSign(PaySignUtil.md5Sign(payNoticeResult,config.getSignSecret()));
} else {
payNoticeResult.setSign(PaySignUtil.hmacSha256Sign(payNoticeResult,config.getSignSecret()));
}
}
return new ClientNoticeTask()
.setUrl(orderExtra.getNotifyUrl())
// 时间序列化进行了重写
.setContent(JacksonUtil.toJson(payNoticeResult))
.setType(ClientNoticeTypeEnum.PAY.getType())
.setSendCount(0)
.setOrderId(order.getId());
}
/**
* 从redis中执行任务, 通过定时任务触发
*/
@@ -322,7 +235,7 @@ public class ClientNoticeService {
}
// 如果响应值等于SUCCESS, 说明发送成功, 进行成功处理
if (Objects.equals(body, "SUCCESS")){
this.successHandler(task);
this.successHandler(task,now);
record.setSuccess(true);
} else {
this.failHandler(task,now);
@@ -339,10 +252,9 @@ public class ClientNoticeService {
/**
* 成功处理
*/
private void successHandler(ClientNoticeTask task){
private void successHandler(ClientNoticeTask task, LocalDateTime now){
// 记录成功并保存
task.setSendCount(task.getSendCount() + 1);
task.setSuccess(true);
task.setSendCount(task.getSendCount() + 1).setLatestTime(now).setSuccess(true);;
taskManager.updateById(task);
}

View File

@@ -167,7 +167,7 @@ public class PayAssistService {
.filter(Objects::nonNull)
.collect(Collectors.toList());
payChannelOrderManager.saveAll(channelOrders);
payInfo.setPayChannelOrders(channelOrders);
payInfo.getPayChannelOrders().addAll(channelOrders);
}
/**

View File

@@ -88,6 +88,14 @@ public class AliPayStrategy extends AbsPayStrategy {
aliPayService.pay(this.getOrder(), this.getPayChannelParam(), this.aliPayParam, this.alipayConfig);
}
/**
* 不使用默认的生成通道支付单方法, 异步支付通道的支付订单自己管理
* channelOrderService.switchAsyncPayChannel 进行切换
*/
@Override
public void generateChannelOrder() {
}
/**
* 支付调起成功, 保存或更新通道支付订单
*/

View File

@@ -1,7 +1,6 @@
package cn.bootx.platform.daxpay.service.core.payment.reconcile.service;
import cn.bootx.platform.common.core.exception.DataNotExistException;
import cn.bootx.platform.daxpay.service.common.context.PaymentContext;
import cn.bootx.platform.daxpay.service.common.local.PaymentContextLocal;
import cn.bootx.platform.daxpay.service.core.order.reconcile.dao.PayReconcileDetailManager;
import cn.bootx.platform.daxpay.service.core.order.reconcile.entity.PayReconcileDetail;
@@ -40,8 +39,6 @@ public class PayReconcileService {
* 下载对账单并进行保存
*/
public void downAndSave(PayReconcileOrder recordOrder) {
// 初始化上下文
PaymentContextLocal.setIfAbsent(new PaymentContext());
// 构建对账策略
AbsReconcileStrategy absReconcileStrategy = PayReconcileStrategyFactory.create(recordOrder.getChannel());
absReconcileStrategy.initParam(recordOrder);

View File

@@ -74,11 +74,11 @@ public class RefundAssistService {
// 首先读取请求参数
noticeInfo.setNotifyUrl(param.getNotifyUrl());
// 读取接口配置
if (StrUtil.isNotBlank(noticeInfo.getNotifyUrl())){
if (StrUtil.isBlank(noticeInfo.getNotifyUrl())){
noticeInfo.setNotifyUrl(apiInfo.getNoticeUrl());
}
// 读取平台配置
if (StrUtil.isNotBlank(noticeInfo.getNotifyUrl())){
if (StrUtil.isBlank(noticeInfo.getNotifyUrl())){
noticeInfo.setNotifyUrl(platform.getNotifyUrl());
}
}
@@ -235,9 +235,9 @@ public class RefundAssistService {
*/
@Transactional(propagation = Propagation.REQUIRES_NEW, rollbackFor = Exception.class)
public void updateOrderByError(RefundOrder refundOrder){
RefundLocal asyncRefundInfo = PaymentContextLocal.get().getRefundInfo();
refundOrder.setErrorCode(asyncRefundInfo.getErrorCode());
refundOrder.setErrorMsg(asyncRefundInfo.getErrorMsg());
RefundLocal refundInfo = PaymentContextLocal.get().getRefundInfo();
refundOrder.setErrorCode(refundInfo.getErrorCode());
refundOrder.setErrorMsg(refundInfo.getErrorMsg());
// 退款失败不保存剩余可退余额, 否则数据看起开会产生困惑
refundOrder.setRefundableBalance(null);
}

View File

@@ -3,8 +3,8 @@ package cn.bootx.platform.daxpay.service.core.payment.refund.service;
import cn.bootx.platform.common.core.exception.RepetitiveOperationException;
import cn.bootx.platform.common.core.function.CollectorsFunction;
import cn.bootx.platform.common.core.util.ValidationUtil;
import cn.bootx.platform.daxpay.code.RefundStatusEnum;
import cn.bootx.platform.daxpay.code.PayStatusEnum;
import cn.bootx.platform.daxpay.code.RefundStatusEnum;
import cn.bootx.platform.daxpay.exception.pay.PayFailureException;
import cn.bootx.platform.daxpay.exception.pay.PayUnsupportedMethodException;
import cn.bootx.platform.daxpay.param.pay.RefundChannelParam;
@@ -218,11 +218,11 @@ public class RefundService {
* 成功处理, 更新退款订单, 退款通道订单, 支付订单, 支付通道订单
*/
private void successHandler(RefundOrder refundOrder, List<RefundChannelOrder> refundChannelOrders, PayOrder payOrder) {
RefundLocal asyncRefundInfo = PaymentContextLocal.get().getRefundInfo();
RefundLocal refundInfo = PaymentContextLocal.get().getRefundInfo();
// 剩余可退款余额
int refundableBalance = refundOrder.getRefundableBalance();
// 设置支付订单状态
if (asyncRefundInfo.getStatus() == RefundStatusEnum.PROGRESS) {
if (refundInfo.getStatus() == RefundStatusEnum.PROGRESS) {
// 设置为退款中
payOrder.setStatus(PayStatusEnum.REFUNDING.getCode());
} else if (refundableBalance == 0) {
@@ -238,9 +238,9 @@ public class RefundService {
refundAssistService.updateOrderAndChannel(refundOrder,refundChannelOrders);
// 发送通知
List<String> list = Arrays.asList(PayStatusEnum.REFUNDING.getCode(), PayStatusEnum.REFUNDED.getCode());
if (list.contains(payOrder.getStatus())){
clientNoticeService.registerRefundNotice(refundOrder, null, refundChannelOrders);
List<String> list = Arrays.asList(RefundStatusEnum.SUCCESS.getCode(), RefundStatusEnum.CLOSE.getCode(), RefundStatusEnum.FAIL.getCode());
if (list.contains(refundOrder.getStatus())){
clientNoticeService.registerRefundNotice(refundOrder, refundInfo.getRunOrderExtra(), refundChannelOrders);
}
}

View File

@@ -145,8 +145,8 @@ public class RefundRepairService {
refundChannelOrderManager.updateAllById(refundChannelOrders);
// 发送通知
List<String> list = Arrays.asList(PayStatusEnum.REFUNDING.getCode(), PayStatusEnum.REFUNDED.getCode());
if (list.contains(payOrder.getStatus())){
List<String> list = Arrays.asList(RefundStatusEnum.SUCCESS.getCode(), RefundStatusEnum.CLOSE.getCode(), RefundStatusEnum.FAIL.getCode());
if (list.contains(refundOrder.getStatus())){
clientNoticeService.registerRefundNotice(refundOrder, null, refundChannelOrders);
}

View File

@@ -7,6 +7,8 @@ import cn.bootx.platform.daxpay.service.core.task.notice.convert.ClientNoticeCon
import cn.bootx.platform.daxpay.service.dto.record.notice.ClientNoticeTaskDto;
import cn.bootx.table.modify.annotation.DbColumn;
import cn.bootx.table.modify.annotation.DbTable;
import cn.bootx.table.modify.mysql.annotation.DbMySqlFieldType;
import cn.bootx.table.modify.mysql.constants.MySqlFieldTypeEnum;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import lombok.EqualsAndHashCode;
@@ -39,6 +41,7 @@ public class ClientNoticeTask extends MpBaseEntity implements EntityBaseFunction
/** 消息内容 */
@DbColumn(comment = "消息内容")
@DbMySqlFieldType(MySqlFieldTypeEnum.LONGTEXT)
private String content;
/** 是否发送成功 */

View File

@@ -23,12 +23,15 @@ import java.time.LocalDateTime;
@Schema(title = "退款记录")
public class RefundOrderDto extends BaseDto {
@Schema(description = "支付号")
@Schema(description = "支付号")
private Long paymentId;
@Schema(description = "关联的业务id")
@Schema(description = "原支付业务号")
private String businessNo;
@Schema(description = "原支付标题")
private String title;
@Schema(description = "退款号")
private String refundNo;
@@ -45,9 +48,6 @@ public class RefundOrderDto extends BaseDto {
@Schema(description = "支付网关订单号")
private String gatewayOrderNo;
@Schema(description = "标题")
private String title;
@Schema(description = "退款金额")
private BigDecimal amount;
@@ -60,6 +60,10 @@ public class RefundOrderDto extends BaseDto {
@Schema(description = "退款时间")
private LocalDateTime refundTime;
/** 退款原因 */
@Schema(description = "退款原因")
private String reason;
/**
* @see RefundStatusEnum
*/

View File

@@ -1,13 +1,11 @@
package cn.bootx.platform.daxpay.service.handler;
import cn.bootx.platform.common.core.exception.DataNotExistException;
import cn.bootx.platform.daxpay.exception.pay.PayFailureException;
import cn.bootx.platform.daxpay.service.annotation.PaymentApi;
import cn.bootx.platform.daxpay.service.common.context.PaymentContext;
import cn.bootx.platform.daxpay.service.common.local.PaymentContextLocal;
import cn.bootx.platform.daxpay.service.core.system.config.dao.PayApiConfigManager;
import cn.bootx.platform.daxpay.service.core.system.config.entity.PayApiConfig;
import cn.bootx.platform.daxpay.service.core.system.config.service.PayApiConfigService;
import cn.bootx.platform.daxpay.exception.pay.PayFailureException;
import cn.bootx.platform.starter.auth.service.RouterCheck;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
@@ -26,8 +24,8 @@ import java.util.Objects;
@Component
@RequiredArgsConstructor
public class PayApiCheckHandler implements RouterCheck {
private final PayApiConfigManager openApiInfoManager;
private final PayApiConfigService openApiService;
private final PayApiConfigManager apiConfigManager;
private final PayApiConfigService apiConfigService;
@Override
public int sortNo() {
@@ -39,21 +37,18 @@ public class PayApiCheckHandler implements RouterCheck {
// 如果请求的接口未启用
if (handler instanceof HandlerMethod) {
HandlerMethod handlerMethod = (HandlerMethod) handler;
PaymentApi ignoreAuth = handlerMethod.getMethodAnnotation(PaymentApi.class);
if (Objects.isNull(ignoreAuth)){
PaymentApi paymentApi = handlerMethod.getMethodAnnotation(PaymentApi.class);
if (Objects.isNull(paymentApi)){
return false;
}
String code = ignoreAuth.value();
PayApiConfig api = openApiInfoManager.findByCode(code)
String code = paymentApi.value();
PayApiConfig api = apiConfigManager.findByCode(code)
.orElseThrow(() -> new DataNotExistException("未找到接口信息"));
if (!api.isEnable()){
throw new PayFailureException("该接口权限未开放");
}
// 初始化支付上下文
PaymentContext paymentContext = new PaymentContext();
PaymentContextLocal.set(paymentContext);
// 设置接口信息
openApiService.initApiInfo(api);
apiConfigService.initApiInfo(api);
return true;
}
return false;