feat 分账消息通知和回调, 分账事件创建

This commit is contained in:
DaxPay
2024-12-17 18:08:03 +08:00
parent c0ee1dd7f8
commit aa6505e5ae
18 changed files with 171 additions and 26 deletions

View File

@@ -20,7 +20,7 @@
- [ ] 自动分账
- [ ] 自动完成
- [ ] 自动同步状态
- [ ] 分账消息通知和回调
- [x] 分账消息通知和回调
- [x] 分账接收方和分账组优化, 后台管理时, 编号自动生成
- [x] 微信通道添加单独的认证跳转地址, 处理它的特殊情况
- [x] 支付订单新增待支付状态

View File

@@ -147,7 +147,7 @@ public class WechatPayConfigService {
payConfig.setAppId(wechatPayConfig.getWxAppId());
payConfig.setMchKey(wechatPayConfig.getApiKeyV2());
payConfig.setApiV3Key(wechatPayConfig.getApiKeyV3());
// 注意不要使用base64的方式进行配置, 因为wxjava 是直接读取文本并不会进行解码
// 注意不要使用base64的方式进行配置, 因为wxjava 是直接读取文本并不会进行解码, 会导致证书异常
payConfig.setPrivateKeyContent(Base64.decode(wechatPayConfig.getPrivateKey()));
payConfig.setPrivateCertContent(Base64.decode(wechatPayConfig.getPrivateCert()));
payConfig.setCertSerialNo(wechatPayConfig.getCertSerialNo());

View File

@@ -17,14 +17,22 @@ public interface DaxPayCode {
/** 商户回调通知 */
String MERCHANT_CALLBACK_SENDER = "CallbackSender";
/** 支付任务超时 */
String MERCHANT_PAY_TIMEOUT = "PayTimeout";
/** 支付订单超时关闭 */
String ORDER_PAY_TIMEOUT = "PayTimeout";
/** 退款任务同步 默认两分钟后查询 */
String MERCHANT_REFUND_SYNC = "RefundSync";
String ORDER_REFUND_SYNC = "RefundSync";
/** 转账任务同步 默认两分钟后查询 */
String MERCHANT_TRANSFER_SYNC = "TransferSync";
String ORDER_TRANSFER_SYNC = "TransferSync";
/** 自动分账 */
String ORDER_ALLOC_START = "AllocStart";
/** 分账同步 */
String ORDER_ALLOC_SYNC = "AllocSync";
/** 分账完结 */
String ORDER_ALLOC_FINISH = "AllocFinish";
}
}

View File

@@ -9,6 +9,8 @@ import org.dromara.daxpay.service.result.allocation.order.AllocOrderVo;
import org.mapstruct.Mapper;
import org.mapstruct.factory.Mappers;
import java.util.List;
/**
*
* @author xxm
@@ -22,6 +24,8 @@ public interface AllocOrderConvert {
AllocDetailResult toResult(AllocDetail in);
List<AllocDetailResult> toList(List<AllocDetail> in);
AllocOrderVo toVo(AllocOrder in);
AllocDetailVo toVo(AllocDetail in);

View File

@@ -21,6 +21,9 @@ public enum NotifyContentTypeEnum {
/** 支付订单变动通知 */
TRANSFER("transfer", "转账订单变动通知"),
/** 分账订单变动通知 */
ALLOCATION("allocation", "分账订单变动通知"),
;
private final String code;
private final String name;

View File

@@ -0,0 +1,41 @@
package org.dromara.daxpay.service.event;
import cn.bootx.platform.starter.redis.delay.annotation.DelayEventListener;
import cn.bootx.platform.starter.redis.delay.annotation.DelayJobEvent;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.dromara.daxpay.service.code.DaxPayCode;
import org.springframework.stereotype.Service;
/**
* 分账事件
* @author xxm
* @since 2024/12/17
*/
@Slf4j
@Service
@RequiredArgsConstructor
public class AllocationEventService {
/**
* 自动分账 参数为订单号
*/
@DelayEventListener(DaxPayCode.Event.ORDER_ALLOC_START)
public void start(DelayJobEvent<Long> event) {
log.info("分账开始,订单号:{}", event.getMessage());
}
/**
* 分账同步
*/
@DelayEventListener(DaxPayCode.Event.ORDER_ALLOC_SYNC)
public void sync(DelayJobEvent<Long> event) {
log.info("分账同步,订单号:{}", event.getMessage());
}
@DelayEventListener(DaxPayCode.Event.ORDER_ALLOC_FINISH)
public void finish(DelayJobEvent<Long> event) {
log.info("分账完成,订单号:{}", event.getMessage());
}
}

View File

@@ -44,7 +44,7 @@ public class TradeOrderEventService {
/**
* 接收订单超时事件, 发起同步
*/
@DelayEventListener(DaxPayCode.Event.MERCHANT_PAY_TIMEOUT)
@DelayEventListener(DaxPayCode.Event.ORDER_PAY_TIMEOUT)
public void payExpired(DelayJobEvent<Long> event) {
Optional<PayOrder> orderOpt = payOrderManager.findById(event.getMessage());
if (orderOpt.isPresent()) {
@@ -59,7 +59,7 @@ public class TradeOrderEventService {
/**
* 接收退款订单同步事件
*/
@DelayEventListener(DaxPayCode.Event.MERCHANT_REFUND_SYNC)
@DelayEventListener(DaxPayCode.Event.ORDER_REFUND_SYNC)
public void refundDelaySync(DelayJobEvent<Long> event) {
var orderOpt = refundOrderManager.findById(event.getMessage());
if (orderOpt.isPresent()) {
@@ -76,7 +76,7 @@ public class TradeOrderEventService {
/**
* 接收转账订单超时事件
*/
@DelayEventListener(DaxPayCode.Event.MERCHANT_TRANSFER_SYNC)
@DelayEventListener(DaxPayCode.Event.ORDER_TRANSFER_SYNC)
public void TransferDelaySync(DelayJobEvent<Long> event) {
var orderOpt = transferOrderManager.findById(event.getMessage());
if (orderOpt.isPresent()) {

View File

@@ -26,6 +26,7 @@ import org.dromara.daxpay.service.entity.order.pay.PayOrder;
import org.dromara.daxpay.service.service.allocation.order.AllocOrderQueryService;
import org.dromara.daxpay.service.service.allocation.order.AllocOrderService;
import org.dromara.daxpay.service.service.assist.PaymentAssistService;
import org.dromara.daxpay.service.service.notice.MerchantNoticeService;
import org.dromara.daxpay.service.strategy.AbsAllocationStrategy;
import org.dromara.daxpay.service.util.PaymentStrategyFactory;
import org.springframework.stereotype.Service;
@@ -61,6 +62,7 @@ public class AllocationService {
private final AllocOrderQueryService allocOrderQueryService;
private final AllocOrderService allocOrderService;
private final MerchantNoticeService merchantNoticeService;
/**
* 开启分账 多次请求只会分账一次
@@ -244,6 +246,7 @@ public class AllocationService {
.setErrorMsg(e.getMessage());
}
allocationOrderManager.updateById(allocOrder);
merchantNoticeService.registerAllocNotice(allocOrder, details);
return new AllocationResult()
.setAllocNo(allocOrder.getAllocNo())
.setBizAllocNo(allocOrder.getBizAllocNo())
@@ -266,6 +269,4 @@ public class AllocationService {
result.setDetails(details);
return result;
}
}

View File

@@ -20,6 +20,7 @@ import org.dromara.daxpay.service.entity.allocation.transaction.AllocDetail;
import org.dromara.daxpay.service.entity.allocation.transaction.AllocOrder;
import org.dromara.daxpay.service.entity.record.sync.TradeSyncRecord;
import org.dromara.daxpay.service.service.assist.PaymentAssistService;
import org.dromara.daxpay.service.service.notice.MerchantNoticeService;
import org.dromara.daxpay.service.service.record.sync.TradeSyncRecordService;
import org.dromara.daxpay.service.strategy.AbsAllocationStrategy;
import org.dromara.daxpay.service.util.PaymentStrategyFactory;
@@ -48,6 +49,7 @@ public class AllocationSyncService {
private final LockTemplate lockTemplate;
private final PaymentAssistService paymentAssistService;
private final MerchantNoticeService merchantNoticeService;
/**
* 分账同步
@@ -120,12 +122,10 @@ public class AllocationSyncService {
/**
* 根据订单明细更新订单的状态和处理结果, 如果订单是分账结束或失败, 不更新状态
* TODO 是否多次同步会产生多次变动, 注意处理多次推送通知的问题, 目前是
*/
private void updateOrderStatus(AllocOrder allocOrder, List<AllocDetail> details){
// 如果是分账结束或失败, 不更新状态
String status = allocOrder.getStatus();
// 如果是分账结束或失败, 不进行对订单进行处理
List<String> list = Arrays.asList(AllocationStatusEnum.FINISH.getCode(), AllocationStatusEnum.FINISH_FAILED.getCode());
if (!list.contains(status)){
@@ -162,10 +162,10 @@ public class AllocationSyncService {
allocOrderDetailManager.updateAllById(details);
allocationOrderManager.updateById(allocOrder);
// 如果状态为完成, 发送通知
if (Objects.equals(AllocationStatusEnum.ALLOC_END.getCode(), allocOrder.getStatus())){
// 发送通知
// clientNoticeService.registerAllocNotice(allocOrder, details);
// 如果状态为分账完成, 发送通知
if (Objects.equals(AllocationStatusEnum.FINISH.getCode(), allocOrder.getStatus())){
// 注册通知 多次同步会产生多次变动, 注意处理多次推送通知的问题
merchantNoticeService.registerAllocNotice(allocOrder, details);
}
}

View File

@@ -138,7 +138,7 @@ public class CheckoutAssistService {
}
payOrderManager.save(order);
// 注册支付超时任务
delayJobService.registerByTransaction(order.getId(), DaxPayCode.Event.MERCHANT_PAY_TIMEOUT, order.getExpiredTime());
delayJobService.registerByTransaction(order.getId(), DaxPayCode.Event.ORDER_PAY_TIMEOUT, order.getExpiredTime());
return order;
}

View File

@@ -1,15 +1,19 @@
package org.dromara.daxpay.service.service.notice;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.dromara.daxpay.service.entity.allocation.transaction.AllocDetail;
import org.dromara.daxpay.service.entity.allocation.transaction.AllocOrder;
import org.dromara.daxpay.service.entity.order.pay.PayOrder;
import org.dromara.daxpay.service.entity.order.refund.RefundOrder;
import org.dromara.daxpay.service.entity.order.transfer.TransferOrder;
import org.dromara.daxpay.service.service.notice.callback.MerchantCallbackTaskService;
import org.dromara.daxpay.service.service.notice.notify.MerchantNotifyTaskService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.List;
/**
* 客户通知服务
* @author xxm
@@ -44,8 +48,18 @@ public class MerchantNoticeService {
/**
* 注册转账通知
*/
@Transactional(rollbackFor = Exception.class)
public void registerTransferNotice(TransferOrder order) {
merchantNotifyService.registerTransferNotice(order);
merchantCallbackService.registerTransferNotice(order);
}
/**
* 注册分账通知
*/
@Transactional(rollbackFor = Exception.class)
public void registerAllocNotice(AllocOrder order, List<AllocDetail> details) {
merchantNotifyService.registerAllocNotice(order,details);
merchantCallbackService.registerAllocNotice(order,details);
}
}

View File

@@ -3,11 +3,15 @@ package org.dromara.daxpay.service.service.notice.callback;
import cn.bootx.platform.common.jackson.util.JacksonUtil;
import cn.bootx.platform.starter.redis.delay.service.DelayJobService;
import org.dromara.daxpay.core.enums.TradeTypeEnum;
import org.dromara.daxpay.core.result.allocation.order.AllocDetailResult;
import org.dromara.daxpay.service.code.DaxPayCode;
import org.dromara.daxpay.service.convert.allocation.AllocOrderConvert;
import org.dromara.daxpay.service.convert.order.pay.PayOrderConvert;
import org.dromara.daxpay.service.convert.order.refund.RefundOrderConvert;
import org.dromara.daxpay.service.convert.order.transfer.TransferOrderConvert;
import org.dromara.daxpay.service.dao.notice.callback.MerchantCallbackTaskManager;
import org.dromara.daxpay.service.entity.allocation.transaction.AllocDetail;
import org.dromara.daxpay.service.entity.allocation.transaction.AllocOrder;
import org.dromara.daxpay.service.entity.notice.callback.MerchantCallbackTask;
import org.dromara.daxpay.service.entity.order.pay.PayOrder;
import org.dromara.daxpay.service.entity.order.refund.RefundOrder;
@@ -17,6 +21,8 @@ import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import java.util.List;
/**
* 商户回调消息服务类
* @author xxm
@@ -100,4 +106,29 @@ public class MerchantCallbackTaskService {
delayJobService.registerByTransaction(task.getId(), DaxPayCode.Event.MERCHANT_CALLBACK_SENDER, 0);
log.info("注册转账通知");
}
/**
* 注册分账通知
*/
public void registerAllocNotice(AllocOrder order, List<AllocDetail> details) {
// 判断是否需要进行通知
if (StrUtil.isBlank(order.getNotifyUrl())){
log.info("分账订单无需通知,订单号:{}",order.getAllocNo());
return;
}
var noticeResult = AllocOrderConvert.CONVERT.toResult(order);
List<AllocDetailResult> detailResults = AllocOrderConvert.CONVERT.toList(details);
noticeResult.setDetails(detailResults);
var task = new MerchantCallbackTask()
// 时间序列化进行了重写, 所以使用Jackson的序列化工具类
.setContent(JacksonUtil.toJson(noticeResult))
.setTradeType(TradeTypeEnum.ALLOCATION.getCode())
.setUrl(order.getNotifyUrl())
.setSendCount(0)
.setTradeId(order.getId())
.setTradeNo(order.getAllocNo());
taskManager.save(task);
delayJobService.registerByTransaction(task.getId(), DaxPayCode.Event.MERCHANT_CALLBACK_SENDER, 0);
log.info("注册分账通知");
}
}

View File

@@ -3,13 +3,17 @@ package org.dromara.daxpay.service.service.notice.notify;
import cn.bootx.platform.common.jackson.util.JacksonUtil;
import cn.bootx.platform.starter.redis.delay.service.DelayJobService;
import org.dromara.daxpay.core.enums.MerchantNotifyTypeEnum;
import org.dromara.daxpay.core.result.allocation.order.AllocDetailResult;
import org.dromara.daxpay.service.code.DaxPayCode;
import org.dromara.daxpay.service.common.context.MchAppLocal;
import org.dromara.daxpay.service.common.local.PaymentContextLocal;
import org.dromara.daxpay.service.convert.allocation.AllocOrderConvert;
import org.dromara.daxpay.service.convert.order.pay.PayOrderConvert;
import org.dromara.daxpay.service.convert.order.refund.RefundOrderConvert;
import org.dromara.daxpay.service.convert.order.transfer.TransferOrderConvert;
import org.dromara.daxpay.service.dao.notice.notify.MerchantNotifyTaskManager;
import org.dromara.daxpay.service.entity.allocation.transaction.AllocDetail;
import org.dromara.daxpay.service.entity.allocation.transaction.AllocOrder;
import org.dromara.daxpay.service.entity.notice.notify.MerchantNotifyTask;
import org.dromara.daxpay.service.entity.order.pay.PayOrder;
import org.dromara.daxpay.service.entity.order.refund.RefundOrder;
@@ -21,6 +25,7 @@ import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import java.util.List;
import java.util.Objects;
/**
@@ -102,6 +107,29 @@ public class MerchantNotifyTaskService {
log.info("注册转账通知");
}
/**
* 注册分账通知
*/
public void registerAllocNotice(AllocOrder order, List<AllocDetail> details) {
if (this.nonRegister(NotifyContentTypeEnum.ALLOCATION)){
log.info("分账无需回调,订单号:{}",order.getAllocNo());
return;
}
var noticeResult = AllocOrderConvert.CONVERT.toResult(order);
List<AllocDetailResult> detailResults = AllocOrderConvert.CONVERT.toList(details);
noticeResult.setDetails(detailResults);
var task = new MerchantNotifyTask()
// 时间序列化进行了重写, 所以使用Jackson的序列化工具类
.setContent(JacksonUtil.toJson(noticeResult))
.setNotifyType(NotifyContentTypeEnum.ALLOCATION.getCode())
.setSendCount(0)
.setTradeId(order.getId())
.setTradeNo(order.getAllocNo());
taskManager.save(task);
delayJobService.registerByTransaction(task.getId(), DaxPayCode.Event.MERCHANT_NOTIFY_SENDER, 0);
log.info("注册分账通知");
}
/**
* 判断是否 不需要注册通知
* true 不需要

View File

@@ -67,7 +67,7 @@ public class PayAssistService {
}
payOrderManager.save(order);
// 注册支付超时任务
delayJobService.registerByTransaction(order.getId(), DaxPayCode.Event.MERCHANT_PAY_TIMEOUT, order.getExpiredTime());
delayJobService.registerByTransaction(order.getId(), DaxPayCode.Event.ORDER_PAY_TIMEOUT, order.getExpiredTime());
return order;
}

View File

@@ -159,7 +159,7 @@ public class RefundService {
// 执行退款策略
refundResultBo = refundStrategy.doRefundHandler();
// 注册一个两分钟后执行的同步任务, 作为接不到回调任务的兜底
delayJobService.registerByTransaction(refundOrder.getId(), DaxPayCode.Event.MERCHANT_PAY_TIMEOUT, 2*60*1000L);
delayJobService.registerByTransaction(refundOrder.getId(), DaxPayCode.Event.ORDER_PAY_TIMEOUT, 2*60*1000L);
} catch (Exception e) {
log.error("重新退款失败:", e);
// 记录退款失败的记录
@@ -217,7 +217,7 @@ public class RefundService {
merchantNoticeService.registerRefundNotice(refundOrder);
} else {
// 注册延时同步事件
delayJobService.registerByTransaction(refundOrder.getId(), DaxPayCode.Event.MERCHANT_REFUND_SYNC, 2*60*1000L);
delayJobService.registerByTransaction(refundOrder.getId(), DaxPayCode.Event.ORDER_REFUND_SYNC, 2*60*1000L);
}
refundOrderManager.updateById(refundOrder);
}

View File

@@ -142,7 +142,7 @@ public class TransferService {
tradeFlowRecordService.saveTransfer(order);
} else {
// 注册延时同步事件
delayJobService.registerByTransaction(order.getId(), DaxPayCode.Event.MERCHANT_TRANSFER_SYNC, 2*60*1000L);
delayJobService.registerByTransaction(order.getId(), DaxPayCode.Event.ORDER_TRANSFER_SYNC, 2*60*1000L);
}
transferOrderManager.updateById(order);
}

View File

@@ -0,0 +1,16 @@
package org.dromara.daxpay.service.task;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
/**
* 分账同步任务
* @author xxm
* @since 2024/12/17
*/
@Slf4j
@Service
@RequiredArgsConstructor
public class AllocationSyncTaskService {
}

View File

@@ -44,9 +44,8 @@ public class ReconcileTask {
private final PaymentAssistService paymentAssistService;
/**
* 任务实现, 上午10.30执行
* 对账任务实现, 上午10.30执行
*/
@Scheduled(cron = "0 30 10 * * ?")
public void reconcileTask() {