ref 签名注解和上下文初始化注解改造, 初步实现分账通知功能

This commit is contained in:
DaxPay
2024-05-22 22:07:34 +08:00
parent 3ac539f277
commit 5280793f31
44 changed files with 530 additions and 315 deletions

View File

@@ -18,6 +18,7 @@
- [ ] DEMO增加获取微信OpenID和支付宝OpenId功能
- [ ] 分账回调处理
- [ ] 分账通知发送功能
- [ ] 保存分账同步记录
- [x] 分账支持手动和自动分账两种
- [ ] 金额过小不进行分账, 增加新状态, 金额小于0.01元直接忽略
- [ ] 增加收单收银台功能

View File

@@ -5,9 +5,11 @@ import cn.bootx.platform.common.core.rest.Res;
import cn.bootx.platform.common.core.rest.ResResult;
import cn.bootx.platform.common.core.rest.dto.LabelValue;
import cn.bootx.platform.common.core.rest.param.PageParam;
import cn.daxpay.single.param.payment.allocation.AllocStartParam;
import cn.daxpay.single.code.PaymentApiCode;
import cn.daxpay.single.param.payment.allocation.AllocationParam;
import cn.daxpay.single.param.payment.allocation.AllocSyncParam;
import cn.daxpay.single.param.payment.allocation.AllocFinishParam;
import cn.daxpay.single.service.annotation.PlatformInitContext;
import cn.daxpay.single.service.core.order.allocation.service.AllocationOrderService;
import cn.daxpay.single.service.core.payment.allocation.service.AllocationService;
import cn.daxpay.single.service.dto.order.allocation.AllocationOrderDetailDto;
@@ -69,6 +71,7 @@ public class AllocationOrderController {
return Res.ok(allocationOrderService.findChannels());
}
@PlatformInitContext(PaymentApiCode.SYNC_ALLOCATION)
@Operation(summary = "同步分账结果")
@PostMapping("/sync")
public ResResult<Void> sync(String allocationNo){
@@ -78,6 +81,7 @@ public class AllocationOrderController {
return Res.ok();
}
@PlatformInitContext(PaymentApiCode.SYNC_REFUND)
@Operation(summary = "分账完结")
@PostMapping("/finish")
public ResResult<Void> finish(String allocationNo){
@@ -87,10 +91,11 @@ public class AllocationOrderController {
return Res.ok();
}
@PlatformInitContext(PaymentApiCode.ALLOCATION)
@Operation(summary = "重新发起分账")
@PostMapping("/retry")
public ResResult<Void> retryAllocation(String bizAllocationNo){
AllocStartParam param = new AllocStartParam();
AllocationParam param = new AllocationParam();
param.setBizAllocationNo(bizAllocationNo);
allocationService.allocation(param);
return Res.ok();

View File

@@ -5,7 +5,7 @@ import cn.bootx.platform.common.core.rest.PageResult;
import cn.bootx.platform.common.core.rest.Res;
import cn.bootx.platform.common.core.rest.ResResult;
import cn.bootx.platform.common.core.rest.param.PageParam;
import cn.daxpay.single.param.payment.allocation.AllocStartParam;
import cn.daxpay.single.param.payment.allocation.AllocationParam;
import cn.daxpay.single.param.payment.pay.PayCloseParam;
import cn.daxpay.single.param.payment.pay.PaySyncParam;
import cn.daxpay.single.result.pay.SyncResult;
@@ -99,7 +99,7 @@ public class PayOrderController {
@Operation(summary = "发起分账")
@PostMapping("/allocation")
public ResResult<Void> allocation(String orderNo){
AllocStartParam param = new AllocStartParam();
AllocationParam param = new AllocationParam();
param.setOrderNo(orderNo);
param.setBizAllocationNo(OrderNoGenerateUtil.allocation());
allocationService.allocation(param);

View File

@@ -4,8 +4,10 @@ import cn.bootx.platform.common.core.rest.PageResult;
import cn.bootx.platform.common.core.rest.Res;
import cn.bootx.platform.common.core.rest.ResResult;
import cn.bootx.platform.common.core.rest.param.PageParam;
import cn.daxpay.single.code.PaymentApiCode;
import cn.daxpay.single.param.payment.refund.RefundSyncParam;
import cn.daxpay.single.result.pay.SyncResult;
import cn.daxpay.single.service.annotation.PlatformInitContext;
import cn.daxpay.single.service.core.order.refund.service.RefundOrderQueryService;
import cn.daxpay.single.service.core.order.refund.service.RefundOrderService;
import cn.daxpay.single.service.core.payment.sync.service.RefundSyncService;
@@ -63,6 +65,7 @@ public class RefundOrderController {
return Res.ok(queryService.findExtraById(id));
}
@PlatformInitContext(PaymentApiCode.REFUND)
@Operation(summary = "手动发起退款")
@PostMapping("/refund")
public ResResult<Void> refund(@RequestBody PayOrderRefundParam param){
@@ -70,6 +73,7 @@ public class RefundOrderController {
return Res.ok();
}
@PlatformInitContext(PaymentApiCode.REFUND)
@Operation(summary = "重新发起退款")
@PostMapping("/resetRefund")
public ResResult<Void> resetRefund(Long id){

View File

@@ -4,7 +4,7 @@ import cn.bootx.platform.common.core.rest.PageResult;
import cn.bootx.platform.common.core.rest.Res;
import cn.bootx.platform.common.core.rest.ResResult;
import cn.bootx.platform.common.core.rest.param.PageParam;
import cn.daxpay.single.service.core.task.notice.service.ClientNoticeTaskService;
import cn.daxpay.single.service.core.notice.service.ClientNoticeTaskService;
import cn.daxpay.single.service.dto.record.notice.ClientNoticeRecordDto;
import cn.daxpay.single.service.dto.record.notice.ClientNoticeTaskDto;
import cn.daxpay.single.service.param.record.ClientNoticeRecordQuery;

View File

@@ -15,8 +15,8 @@ import java.util.List;
*/
@EqualsAndHashCode(callSuper = true)
@Data
@Schema(title = "开始分账请求参数")
public class AllocStartParam extends PaymentCommonParam {
@Schema(title = "分账请求参数")
public class AllocationParam extends PaymentCommonParam {
/** 商户分账单号 */
@Schema(description = "商户分账单号")
@@ -45,4 +45,12 @@ public class AllocStartParam extends PaymentCommonParam {
@Schema(description = "分账接收方列表")
private List<AllocReceiverParam> receivers;
/** 回调通知地址 */
@Schema(description = "回调通知地址")
private String notifyUrl;
/** 商户扩展参数,回调时会原样返回 */
@Schema(description = "商户扩展参数,回调时会原样返回")
private String attach;
}

View File

@@ -26,31 +26,31 @@ public class AllocOrderResult extends PaymentCommonResult {
/**
* 分账单号
*/
@Schema(description = "分账单号")
@Schema(description = "分账单号")
private String allocationNo;
/**
* 商户分账单号
*/
@Schema(description = "商户分账单号")
@Schema(description = "商户分账单号")
private String bizAllocationNo;
/**
* 通道分账号
*/
@Schema(description = "通道分账号")
@Schema(description = "通道分账号")
private String outAllocationNo;
/**
* 支付订单号
*/
@Schema(description = "支付订单号")
@Schema(description = "支付订单号")
private String orderNo;
/**
* 商户支付订单号
*/
@Schema(description = "商户支付订单号")
@Schema(description = "商户支付订单号")
private String bizOrderNo;
@@ -64,53 +64,58 @@ public class AllocOrderResult extends PaymentCommonResult {
* 所属通道
* @see PayChannelEnum
*/
@Schema(description = "所属通道")
@Schema(description = "所属通道")
private String channel;
/**
* 总分账金额
*/
@Schema(description = "总分账金额")
@Schema(description = "总分账金额")
private Integer amount;
/**
* 分账描述
*/
@Schema(description = "分账描述")
@Schema(description = "分账描述")
private String description;
/**
* 状态
* @see AllocOrderStatusEnum
*/
@Schema(description = "状态")
@Schema(description = "状态")
private String status;
/**
* 处理结果
* @see AllocOrderResultEnum
*/
@Schema(description = "处理结果")
@Schema(description = "处理结果")
private String result;
/**
* 错误码
*/
@Schema(description = "错误码")
@Schema(description = "错误码")
private String errorCode;
/**
* 错误信息
*/
@Schema(description = "错误原因")
@Schema(description = "错误原因")
private String errorMsg;
/** 分账订单完成时间 */
@Schema(description = "分账订单完成时间")
@Schema(description = "分账订单完成时间")
private LocalDateTime finishTime;
/** 分账明细 */
@Schema(description = "分账明细")
private List<AllocOrderDetailResult> details;
/** 商户扩展参数,回调时会原样返回 */
@Schema(description = "商户扩展参数,回调时会原样返回")
private String attach;
/** 分账明细 */
@Schema(description = "分账明细")
private List<AllocOrderDetailResult> details;
}

View File

@@ -3,8 +3,12 @@ package cn.daxpay.single.gateway.controller;
import cn.daxpay.single.code.PaymentApiCode;
import cn.daxpay.single.param.payment.allocation.*;
import cn.daxpay.single.result.DaxResult;
import cn.daxpay.single.result.allocation.*;
import cn.daxpay.single.service.annotation.PaymentApi;
import cn.daxpay.single.result.allocation.AllocReceiverAddResult;
import cn.daxpay.single.result.allocation.AllocReceiverRemoveResult;
import cn.daxpay.single.result.allocation.AllocationResult;
import cn.daxpay.single.result.allocation.AllocationSyncResult;
import cn.daxpay.single.service.annotation.PaymentSign;
import cn.daxpay.single.service.annotation.PlatformInitContext;
import cn.daxpay.single.service.core.payment.allocation.service.AllocationReceiverService;
import cn.daxpay.single.service.core.payment.allocation.service.AllocationService;
import cn.daxpay.single.util.DaxRes;
@@ -31,35 +35,40 @@ public class UniAllocationController {
private final AllocationReceiverService receiverService;
@PaymentApi(PaymentApiCode.ALLOCATION)
@PaymentSign
@PlatformInitContext(PaymentApiCode.ALLOCATION)
@Operation(summary = "触发分账")
@PostMapping("/open")
public DaxResult<AllocationResult> open(@RequestBody AllocStartParam param){
public DaxResult<AllocationResult> open(@RequestBody AllocationParam param){
return DaxRes.ok(allocationService.allocation(param));
}
@PaymentApi(PaymentApiCode.ALLOCATION_FINISH)
@PaymentSign
@PlatformInitContext(PaymentApiCode.ALLOCATION_FINISH)
@Operation(summary = "分账完结接口")
@PostMapping("/finish")
public DaxResult<AllocationResult> finish(@RequestBody AllocFinishParam param){
return DaxRes.ok(allocationService.finish(param));
}
@PaymentApi(PaymentApiCode.SYNC_ALLOCATION)
@PaymentSign
@PlatformInitContext(PaymentApiCode.SYNC_ALLOCATION)
@Operation(summary = "分账同步接口")
@PostMapping("/sync")
public DaxResult<AllocationSyncResult> sync(@RequestBody AllocSyncParam param){
return DaxRes.ok(allocationService.sync(param));
}
@PaymentApi(PaymentApiCode.ALLOCATION_RECEIVER_ADD)
@PaymentSign
@PlatformInitContext(PaymentApiCode.ALLOCATION_RECEIVER_ADD)
@Operation(summary = "添加分账接收方接口")
@PostMapping("/receiver/add")
public DaxResult<AllocReceiverAddResult> receiverAdd(@RequestBody AllocReceiverAddParam param){
return DaxRes.ok(receiverService.addAndSync(param));
}
@PaymentApi(PaymentApiCode.ALLOCATION_RECEIVER_REMOVE)
@PaymentSign
@PlatformInitContext(PaymentApiCode.ALLOCATION_RECEIVER_REMOVE)
@Operation(summary = "删除分账接收方接口")
@PostMapping("/receiver/remove")
public DaxResult<AllocReceiverRemoveResult> receiverRemove(@RequestBody AllocReceiverRemoveParam param){

View File

@@ -9,7 +9,8 @@ import cn.daxpay.single.param.assist.WxAuthUrlParam;
import cn.daxpay.single.result.DaxResult;
import cn.daxpay.single.result.assist.WxAccessTokenResult;
import cn.daxpay.single.result.assist.WxAuthUrlResult;
import cn.daxpay.single.service.annotation.PaymentApi;
import cn.daxpay.single.service.annotation.PlatformInitContext;
import cn.daxpay.single.service.annotation.PaymentSign;
import cn.daxpay.single.service.core.payment.assist.service.UniPayAssistService;
import cn.daxpay.single.util.DaxRes;
import io.swagger.v3.oas.annotations.Operation;
@@ -33,14 +34,16 @@ import org.springframework.web.bind.annotation.RestController;
public class UniPayAssistController {
private final UniPayAssistService uniPayAssistService;
@PaymentApi(PaymentApiCode.GET_WX_AUTH_URL)
@PaymentSign
@PlatformInitContext(PaymentApiCode.GET_WX_AUTH_URL)
@Operation(summary = "获取微信OAuth2授权链接")
@PostMapping("/getWxAuthUrl")
public DaxResult<WxAuthUrlResult> getWxAuthUrl(@RequestBody WxAuthUrlParam param){
return DaxRes.ok(uniPayAssistService.getWxAuthUrl(param));
}
@PaymentApi(PaymentApiCode.GET_WX_ACCESS_TOKEN)
@PaymentSign
@PlatformInitContext(PaymentApiCode.GET_WX_ACCESS_TOKEN)
@Operation(summary = "获取微信AccessToken")
@PostMapping("/getWxAccessToken")
public ResResult<WxAccessTokenResult> getWxAccessToken(@RequestBody WxAccessTokenParam param){

View File

@@ -12,7 +12,8 @@ import cn.daxpay.single.result.pay.PayCloseResult;
import cn.daxpay.single.result.pay.PayResult;
import cn.daxpay.single.result.pay.RefundResult;
import cn.daxpay.single.result.pay.SyncResult;
import cn.daxpay.single.service.annotation.PaymentApi;
import cn.daxpay.single.service.annotation.PlatformInitContext;
import cn.daxpay.single.service.annotation.PaymentSign;
import cn.daxpay.single.service.core.payment.close.service.PayCloseService;
import cn.daxpay.single.service.core.payment.pay.service.PayService;
import cn.daxpay.single.service.core.payment.refund.service.RefundService;
@@ -44,36 +45,40 @@ public class UniPayController {
private final PayCloseService payCloseService;
private final RefundSyncService refundSyncService;
@PaymentApi(PaymentApiCode.PAY)
@PaymentSign
@PlatformInitContext(PaymentApiCode.PAY)
@Operation(summary = "统一支付接口")
@PostMapping("/pay")
public DaxResult<PayResult> pay(@RequestBody PayParam payParam){
return DaxRes.ok(payService.pay(payParam));
}
@PaymentApi(PaymentApiCode.CLOSE)
@PaymentSign
@PlatformInitContext(PaymentApiCode.CLOSE)
@Operation(summary = "支付关闭接口")
@PostMapping("/close")
public DaxResult<PayCloseResult> close(@RequestBody PayCloseParam param){
return DaxRes.ok(payCloseService.close(param));
}
@PaymentApi(PaymentApiCode.REFUND)
@PaymentSign
@PlatformInitContext(PaymentApiCode.REFUND)
@Operation(summary = "统一退款接口")
@PostMapping("/refund")
public DaxResult<RefundResult> refund(@RequestBody RefundParam param){
return DaxRes.ok(refundService.refund(param));
}
@PaymentApi(PaymentApiCode.SYNC_PAY)
@PaymentSign
@PlatformInitContext(PaymentApiCode.SYNC_PAY)
@Operation(summary = "支付同步接口")
@PostMapping("/syncPay")
public DaxResult<SyncResult> syncPay(@RequestBody PaySyncParam param){
return DaxRes.ok(paySyncService.sync(param));
}
@PaymentApi(PaymentApiCode.SYNC_REFUND)
@PaymentSign
@PlatformInitContext(PaymentApiCode.SYNC_REFUND)
@Operation(summary = "退款同步接口")
@PostMapping("/syncRefund")
public DaxResult<SyncResult> syncRefund(@RequestBody RefundSyncParam param){

View File

@@ -11,7 +11,8 @@ import cn.daxpay.single.result.allocation.AllocOrderResult;
import cn.daxpay.single.result.allocation.AllocReceiversResult;
import cn.daxpay.single.result.order.PayOrderResult;
import cn.daxpay.single.result.order.RefundOrderResult;
import cn.daxpay.single.service.annotation.PaymentApi;
import cn.daxpay.single.service.annotation.PlatformInitContext;
import cn.daxpay.single.service.annotation.PaymentSign;
import cn.daxpay.single.service.core.order.pay.service.PayOrderQueryService;
import cn.daxpay.single.service.core.order.refund.service.RefundOrderQueryService;
import cn.daxpay.single.service.core.payment.allocation.service.AllocationReceiverService;
@@ -42,28 +43,32 @@ public class UniQueryController {
private final AllocationReceiverService allocationReceiverService;
private final AllocationService allocationService;
@PaymentApi(PaymentApiCode.QUERY_PAY_ORDER)
@PaymentSign
@PlatformInitContext(PaymentApiCode.QUERY_PAY_ORDER)
@Operation(summary = "支付订单查询接口")
@PostMapping("/payOrder")
public DaxResult<PayOrderResult> queryPayOrder(@RequestBody QueryPayParam param){
return DaxRes.ok(payOrderQueryService.queryPayOrder(param));
}
@PaymentApi(PaymentApiCode.QUERY_REFUND_ORDER)
@PaymentSign
@PlatformInitContext(PaymentApiCode.QUERY_REFUND_ORDER)
@Operation(summary = "退款订单查询接口")
@PostMapping("/refundOrder")
public DaxResult<RefundOrderResult> queryRefundOrder(@RequestBody QueryRefundParam param){
return DaxRes.ok(refundOrderQueryService.queryRefundOrder(param));
}
@PaymentApi(PaymentApiCode.QUERY_ALLOCATION_ORDER)
@PaymentSign
@PlatformInitContext(PaymentApiCode.QUERY_ALLOCATION_ORDER)
@Operation(summary = "分账订单查询接口")
@PostMapping("/allocationOrder")
public DaxResult<AllocOrderResult> queryAllocationOrder(@RequestBody QueryAllocOrderParam param){
return DaxRes.ok(allocationService.queryAllocationOrder(param));
}
@PaymentApi(PaymentApiCode.QUERY_ALLOCATION_RECEIVER)
@PaymentSign
@PlatformInitContext(PaymentApiCode.QUERY_ALLOCATION_RECEIVER)
@Operation(summary = "分账接收方查询接口")
@PostMapping("/allocReceiver")
public DaxResult<AllocReceiversResult> queryAllocReceive(@RequestBody QueryAllocReceiverParam param){

View File

@@ -3,7 +3,8 @@ package cn.daxpay.single.gateway.controller;
import cn.daxpay.single.code.PaymentApiCode;
import cn.daxpay.single.param.payment.pay.PayParam;
import cn.daxpay.single.result.DaxResult;
import cn.daxpay.single.service.annotation.PaymentApi;
import cn.daxpay.single.service.annotation.PlatformInitContext;
import cn.daxpay.single.service.annotation.PaymentSign;
import cn.daxpay.single.util.DaxRes;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
@@ -24,7 +25,8 @@ import org.springframework.web.bind.annotation.RestController;
@RequiredArgsConstructor
public class UniReconcileController {
@PaymentApi(PaymentApiCode.PAY)
@PaymentSign
@PlatformInitContext(PaymentApiCode.PAY)
@Operation(summary = "下载指定日期的资金流水")
@PostMapping("/pay")
public DaxResult<?> down(@RequestBody PayParam payParam){

View File

@@ -3,7 +3,7 @@ package cn.daxpay.single.service.annotation;
import java.lang.annotation.*;
/**
* 支付接口标识
* 支付签名标识
* 支付方法至少有一个参数并且需要签名支付参数需要放在第一位
* 返回对象必须为 ResResult<T extends PaymentCommonResult> 格式
* @author xxm
@@ -13,10 +13,5 @@ import java.lang.annotation.*;
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
public @interface PaymentApi {
/**
* 支付接口编码
*/
String value();
public @interface PaymentSign {
}

View File

@@ -0,0 +1,23 @@
package cn.daxpay.single.service.annotation;
import cn.daxpay.single.code.PaymentApiCode;
import java.lang.annotation.*;
/**
* 初始化平台信息和接口API相关的上下文信息
* @author xxm
* @since 2024/5/22
*/
@Target({ ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
public @interface PlatformInitContext {
/**
* 接口标识
* @see PaymentApiCode
*/
String value();
}

View File

@@ -1,7 +1,7 @@
package cn.daxpay.single.service.core.task.notice.convert;
package cn.daxpay.single.service.core.notice.convert;
import cn.daxpay.single.service.core.task.notice.entity.ClientNoticeRecord;
import cn.daxpay.single.service.core.task.notice.entity.ClientNoticeTask;
import cn.daxpay.single.service.core.notice.entity.ClientNoticeRecord;
import cn.daxpay.single.service.core.notice.entity.ClientNoticeTask;
import cn.daxpay.single.service.dto.record.notice.ClientNoticeRecordDto;
import cn.daxpay.single.service.dto.record.notice.ClientNoticeTaskDto;
import org.mapstruct.Mapper;

View File

@@ -1,10 +1,10 @@
package cn.daxpay.single.service.core.task.notice.dao;
package cn.daxpay.single.service.core.notice.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.task.notice.entity.ClientNoticeRecord;
import cn.daxpay.single.service.core.notice.entity.ClientNoticeRecord;
import cn.daxpay.single.service.param.record.ClientNoticeRecordQuery;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;

View File

@@ -1,6 +1,6 @@
package cn.daxpay.single.service.core.task.notice.dao;
package cn.daxpay.single.service.core.notice.dao;
import cn.daxpay.single.service.core.task.notice.entity.ClientNoticeRecord;
import cn.daxpay.single.service.core.notice.entity.ClientNoticeRecord;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Mapper;

View File

@@ -1,10 +1,10 @@
package cn.daxpay.single.service.core.task.notice.dao;
package cn.daxpay.single.service.core.notice.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.task.notice.entity.ClientNoticeTask;
import cn.daxpay.single.service.core.notice.entity.ClientNoticeTask;
import cn.daxpay.single.service.param.record.ClientNoticeTaskQuery;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;

View File

@@ -1,6 +1,6 @@
package cn.daxpay.single.service.core.task.notice.dao;
package cn.daxpay.single.service.core.notice.dao;
import cn.daxpay.single.service.core.task.notice.entity.ClientNoticeTask;
import cn.daxpay.single.service.core.notice.entity.ClientNoticeTask;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Mapper;

View File

@@ -1,9 +1,9 @@
package cn.daxpay.single.service.core.task.notice.entity;
package cn.daxpay.single.service.core.notice.entity;
import cn.bootx.platform.common.core.function.EntityBaseFunction;
import cn.bootx.platform.common.mybatisplus.base.MpCreateEntity;
import cn.daxpay.single.service.code.ClientNoticeSendTypeEnum;
import cn.daxpay.single.service.core.task.notice.convert.ClientNoticeConvert;
import cn.daxpay.single.service.core.notice.convert.ClientNoticeConvert;
import cn.daxpay.single.service.dto.record.notice.ClientNoticeRecordDto;
import cn.bootx.table.modify.annotation.DbColumn;
import cn.bootx.table.modify.annotation.DbTable;

View File

@@ -1,4 +1,4 @@
package cn.daxpay.single.service.core.task.notice.entity;
package cn.daxpay.single.service.core.notice.entity;
import cn.bootx.platform.common.core.function.EntityBaseFunction;
import cn.bootx.platform.common.mybatisplus.base.MpBaseEntity;
@@ -8,10 +8,11 @@ import cn.bootx.table.modify.mysql.annotation.DbMySqlFieldType;
import cn.bootx.table.modify.mysql.constants.MySqlFieldTypeEnum;
import cn.daxpay.single.code.PayStatusEnum;
import cn.daxpay.single.code.RefundStatusEnum;
import cn.daxpay.single.result.allocation.AllocOrderResult;
import cn.daxpay.single.service.code.ClientNoticeTypeEnum;
import cn.daxpay.single.service.core.payment.notice.result.PayNoticeResult;
import cn.daxpay.single.service.core.payment.notice.result.RefundNoticeResult;
import cn.daxpay.single.service.core.task.notice.convert.ClientNoticeConvert;
import cn.daxpay.single.service.core.notice.convert.ClientNoticeConvert;
import cn.daxpay.single.service.dto.record.notice.ClientNoticeTaskDto;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
@@ -59,6 +60,7 @@ public class ClientNoticeTask extends MpBaseEntity implements EntityBaseFunction
* 消息内容
* @see PayNoticeResult
* @see RefundNoticeResult
* @see AllocOrderResult
*/
@DbColumn(comment = "消息内容")
@DbMySqlFieldType(MySqlFieldTypeEnum.LONGTEXT)

View File

@@ -1,7 +1,7 @@
package cn.daxpay.single.service.core.task.notice.service;
package cn.daxpay.single.service.core.notice.service;
import cn.daxpay.single.service.core.task.notice.dao.ClientNoticeRecordManager;
import cn.daxpay.single.service.core.task.notice.entity.ClientNoticeRecord;
import cn.daxpay.single.service.core.notice.dao.ClientNoticeRecordManager;
import cn.daxpay.single.service.core.notice.entity.ClientNoticeRecord;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

View File

@@ -1,14 +1,14 @@
package cn.daxpay.single.service.core.task.notice.service;
package cn.daxpay.single.service.core.notice.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.task.notice.entity.ClientNoticeRecord;
import cn.daxpay.single.service.core.task.notice.entity.ClientNoticeTask;
import cn.daxpay.single.service.core.notice.entity.ClientNoticeRecord;
import cn.daxpay.single.service.core.notice.entity.ClientNoticeTask;
import cn.daxpay.single.service.core.payment.notice.service.ClientNoticeService;
import cn.daxpay.single.service.core.task.notice.dao.ClientNoticeRecordManager;
import cn.daxpay.single.service.core.task.notice.dao.ClientNoticeTaskManager;
import cn.daxpay.single.service.core.notice.dao.ClientNoticeRecordManager;
import cn.daxpay.single.service.core.notice.dao.ClientNoticeTaskManager;
import cn.daxpay.single.service.dto.record.notice.ClientNoticeRecordDto;
import cn.daxpay.single.service.dto.record.notice.ClientNoticeTaskDto;
import cn.daxpay.single.service.param.record.ClientNoticeRecordQuery;

View File

@@ -0,0 +1,18 @@
package cn.daxpay.single.service.core.order.allocation.dao;
import cn.bootx.platform.common.mybatisplus.impl.BaseManager;
import cn.daxpay.single.service.core.order.allocation.entity.AllocationOrderExtra;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Repository;
/**
* 分账订单扩展
* @author xxm
* @since 2024/5/22
*/
@Slf4j
@Repository
@RequiredArgsConstructor
public class AllocationOrderExtraManager extends BaseManager<AllocationOrderExtraMapper, AllocationOrderExtra> {
}

View File

@@ -0,0 +1,14 @@
package cn.daxpay.single.service.core.order.allocation.dao;
import cn.daxpay.single.service.core.order.allocation.entity.AllocationOrderExtra;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Mapper;
/**
* 分账订单扩展
* @author xxm
* @since 2024/5/22
*/
@Mapper
public interface AllocationOrderExtraMapper extends BaseMapper<AllocationOrderExtra> {
}

View File

@@ -0,0 +1,56 @@
package cn.daxpay.single.service.core.order.allocation.entity;
import cn.bootx.platform.common.mybatisplus.base.MpBaseEntity;
import cn.bootx.table.modify.annotation.DbColumn;
import cn.bootx.table.modify.annotation.DbTable;
import cn.daxpay.single.param.channel.AliPayParam;
import cn.daxpay.single.param.channel.WalletPayParam;
import cn.daxpay.single.param.channel.WeChatPayParam;
import com.baomidou.mybatisplus.annotation.FieldStrategy;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
import java.time.LocalDateTime;
/**
* 分账订单扩展
* @author xxm
* @since 2024/5/22
*/
@EqualsAndHashCode(callSuper = true)
@Data
@Accessors(chain = true)
@DbTable(comment = "分账订单扩展")
@TableName("pay_allocation_order_extra")
public class AllocationOrderExtra extends MpBaseEntity {
/** 异步通知地址 */
@DbColumn(comment = "异步通知地址")
@TableField(updateStrategy = FieldStrategy.ALWAYS)
private String notifyUrl;
/** 商户扩展参数,回调时会原样返回, 以最后一次为准 */
@DbColumn(comment = "商户扩展参数")
private String attach;
/**
* 附加参数 以最后一次为准
* @see AliPayParam
* @see WeChatPayParam
* @see WalletPayParam
*/
@DbColumn(comment = "附加参数")
@TableField(updateStrategy = FieldStrategy.ALWAYS)
private String extraParam;
/** 请求时间,时间戳转时间 */
@DbColumn(comment = "请求时间,传输时间戳")
private LocalDateTime reqTime;
/** 终端ip */
@DbColumn(comment = "支付终端ip")
private String clientIp;
}

View File

@@ -12,7 +12,7 @@ import cn.daxpay.single.code.PayChannelEnum;
import cn.daxpay.single.code.PayOrderAllocStatusEnum;
import cn.daxpay.single.exception.pay.PayFailureException;
import cn.daxpay.single.param.payment.allocation.AllocReceiverParam;
import cn.daxpay.single.param.payment.allocation.AllocStartParam;
import cn.daxpay.single.param.payment.allocation.AllocationParam;
import cn.daxpay.single.service.core.order.allocation.dao.AllocationOrderDetailManager;
import cn.daxpay.single.service.core.order.allocation.dao.AllocationOrderManager;
import cn.daxpay.single.service.core.order.allocation.entity.AllocationOrder;
@@ -102,7 +102,7 @@ public class AllocationOrderService {
* 生成分账订单, 根据分账组创建
*/
@Transactional(rollbackFor = Exception.class)
public OrderAndDetail createAndUpdate(AllocStartParam param, PayOrder payOrder, List<AllocationGroupReceiverResult> receiversByGroups){
public OrderAndDetail createAndUpdate(AllocationParam param, PayOrder payOrder, List<AllocationGroupReceiverResult> receiversByGroups){
long orderId = IdUtil.getSnowflakeNextId();
// 订单明细
@@ -153,7 +153,7 @@ public class AllocationOrderService {
/**
* 生成分账订单, 通过传入的分账方创建
*/
public OrderAndDetail createAndUpdate(AllocStartParam param, PayOrder payOrder) {
public OrderAndDetail createAndUpdate(AllocationParam param, PayOrder payOrder) {
List<String> receiverNos = param.getReceivers()
.stream()
.map(AllocReceiverParam::getReceiverNo)

View File

@@ -2,7 +2,6 @@ package cn.daxpay.single.service.core.order.refund.service;
import cn.bootx.platform.common.core.exception.DataNotExistException;
import cn.bootx.platform.common.spring.util.WebServletUtil;
import cn.daxpay.single.code.PaymentApiCode;
import cn.daxpay.single.param.payment.refund.RefundParam;
import cn.daxpay.single.service.core.order.refund.dao.RefundOrderExtraManager;
import cn.daxpay.single.service.core.order.refund.dao.RefundOrderManager;
@@ -10,9 +9,6 @@ import cn.daxpay.single.service.core.order.refund.entity.RefundOrder;
import cn.daxpay.single.service.core.order.refund.entity.RefundOrderExtra;
import cn.daxpay.single.service.core.payment.common.service.PaymentAssistService;
import cn.daxpay.single.service.core.payment.refund.service.RefundService;
import cn.daxpay.single.service.core.system.config.dao.PayApiConfigManager;
import cn.daxpay.single.service.core.system.config.entity.PayApiConfig;
import cn.daxpay.single.service.core.system.config.service.PayApiConfigService;
import cn.daxpay.single.service.param.order.PayOrderRefundParam;
import cn.daxpay.single.util.OrderNoGenerateUtil;
import cn.hutool.extra.servlet.ServletUtil;
@@ -36,16 +32,12 @@ public class RefundOrderService {
private final RefundService refundService;
private final PayApiConfigService apiConfigService;
private final PaymentAssistService paymentAssistService;
private final RefundOrderExtraManager refundOrderExtraManager;
private final RefundOrderManager refundOrderManager;
private final PayApiConfigManager apiConfigManager;
/**
* 手动发起退款
* 退款涉及到回调通知, 索所以需要手动初始化一下上下文
@@ -64,11 +56,7 @@ public class RefundOrderService {
refundParam.setReqTime(LocalDateTime.now());
refundParam.setClientIp(ip);
// 手动初始化上下文
paymentAssistService.initContext(refundParam);
// 初始化接口信息为统一退款
PayApiConfig api = apiConfigManager.findByCode(PaymentApiCode.REFUND).orElseThrow(() -> new DataNotExistException("未找到统一退款接口信息"));
// 设置接口信息
apiConfigService.initApiInfo(api);
paymentAssistService.initRequest(refundParam);
// 调用统一退款接口
refundService.refund(refundParam);
}
@@ -98,13 +86,7 @@ public class RefundOrderService {
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);
// 手动初始化请求上下文
paymentAssistService.initRequest(refundParam);
}
}

View File

@@ -4,7 +4,6 @@ import cn.daxpay.single.param.payment.allocation.AllocReceiverAddParam;
import cn.daxpay.single.result.allocation.AllocReceiverResult;
import cn.daxpay.single.service.core.payment.allocation.entity.AllocationReceiver;
import cn.daxpay.single.service.dto.allocation.AllocationReceiverDto;
import cn.daxpay.single.service.param.allocation.receiver.AllocationReceiverParam;
import org.mapstruct.Mapper;
import org.mapstruct.factory.Mappers;
@@ -21,7 +20,5 @@ public interface AllocationReceiverConvert {
AllocReceiverResult toResult(AllocationReceiver in);
AllocationReceiver convert(AllocationReceiverParam in);
AllocationReceiver convert(AllocReceiverAddParam in);
}

View File

@@ -0,0 +1,73 @@
package cn.daxpay.single.service.core.payment.allocation.service;
import cn.daxpay.single.param.payment.allocation.AllocationParam;
import cn.daxpay.single.service.common.context.ApiInfoLocal;
import cn.daxpay.single.service.common.context.NoticeLocal;
import cn.daxpay.single.service.common.context.PlatformLocal;
import cn.daxpay.single.service.common.local.PaymentContextLocal;
import cn.daxpay.single.service.core.order.allocation.dao.AllocationOrderExtraManager;
import cn.daxpay.single.service.core.order.allocation.entity.AllocationOrderExtra;
import cn.hutool.core.util.StrUtil;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
/**
* 分账支撑方法
* @author xxm
* @since 2024/5/22
*/
@Slf4j
@Service
@RequiredArgsConstructor
public class AllocationAssistService {
private final AllocationOrderExtraManager allocationOrderExtraManager;
/**
* 初始化通知相关上下文
* 1. 异步通知参数: 读取参数配置 -> 读取接口配置 -> 读取平台参数
* 2. 同步跳转参数: 读取参数配置 -> 读取平台参数
* 3. 中途退出地址: 读取参数配置
*/
public void initNotice(AllocationParam allocationParam) {
NoticeLocal noticeInfo = PaymentContextLocal.get().getNoticeInfo();
ApiInfoLocal apiInfo = PaymentContextLocal.get().getApiInfo();
PlatformLocal platform = PaymentContextLocal.get()
.getPlatformInfo();
// 异步回调为开启状态
if (apiInfo.isNotice()) {
// 首先读取请求参数
noticeInfo.setNotifyUrl(allocationParam.getNotifyUrl());
// 读取接口配置
if (StrUtil.isBlank(noticeInfo.getNotifyUrl())) {
noticeInfo.setNotifyUrl(apiInfo.getNoticeUrl());
}
// 读取平台配置
if (StrUtil.isBlank(noticeInfo.getNotifyUrl())) {
noticeInfo.setNotifyUrl(platform.getNotifyUrl());
}
}
}
/**
* 根据新传入的分账订单更新订单和扩展信息
*/
@Transactional(rollbackFor = Exception.class)
public void updateOrder(AllocationParam allocationParam, AllocationOrderExtra orderExtra) {
// 扩展信息
NoticeLocal noticeInfo = PaymentContextLocal.get().getNoticeInfo();
orderExtra.setClientIp(allocationParam.getClientIp())
.setNotifyUrl(noticeInfo.getNotifyUrl())
.setAttach(allocationParam.getAttach())
.setClientIp(allocationParam.getClientIp())
.setReqTime(allocationParam.getReqTime());
allocationOrderExtraManager.updateById(orderExtra);
}
}

View File

@@ -9,8 +9,8 @@ import cn.daxpay.single.code.AllocOrderStatusEnum;
import cn.daxpay.single.code.PayOrderAllocStatusEnum;
import cn.daxpay.single.exception.pay.PayFailureException;
import cn.daxpay.single.param.payment.allocation.AllocFinishParam;
import cn.daxpay.single.param.payment.allocation.AllocStartParam;
import cn.daxpay.single.param.payment.allocation.AllocSyncParam;
import cn.daxpay.single.param.payment.allocation.AllocationParam;
import cn.daxpay.single.param.payment.allocation.QueryAllocOrderParam;
import cn.daxpay.single.result.allocation.AllocOrderDetailResult;
import cn.daxpay.single.result.allocation.AllocOrderResult;
@@ -19,9 +19,11 @@ import cn.daxpay.single.result.allocation.AllocationSyncResult;
import cn.daxpay.single.service.common.local.PaymentContextLocal;
import cn.daxpay.single.service.core.order.allocation.convert.AllocationConvert;
import cn.daxpay.single.service.core.order.allocation.dao.AllocationOrderDetailManager;
import cn.daxpay.single.service.core.order.allocation.dao.AllocationOrderExtraManager;
import cn.daxpay.single.service.core.order.allocation.dao.AllocationOrderManager;
import cn.daxpay.single.service.core.order.allocation.entity.AllocationOrder;
import cn.daxpay.single.service.core.order.allocation.entity.AllocationOrderDetail;
import cn.daxpay.single.service.core.order.allocation.entity.AllocationOrderExtra;
import cn.daxpay.single.service.core.order.allocation.entity.OrderAndDetail;
import cn.daxpay.single.service.core.order.allocation.service.AllocationOrderService;
import cn.daxpay.single.service.core.order.pay.entity.PayOrder;
@@ -29,6 +31,7 @@ import cn.daxpay.single.service.core.order.pay.service.PayOrderQueryService;
import cn.daxpay.single.service.core.payment.allocation.dao.AllocationGroupManager;
import cn.daxpay.single.service.core.payment.allocation.entity.AllocationGroup;
import cn.daxpay.single.service.core.payment.allocation.factory.AllocationFactory;
import cn.daxpay.single.service.core.payment.notice.service.ClientNoticeService;
import cn.daxpay.single.service.dto.allocation.AllocationGroupReceiverResult;
import cn.daxpay.single.service.func.AbsAllocationStrategy;
import com.baomidou.lock.LockInfo;
@@ -64,22 +67,26 @@ public class AllocationService {
private final AllocationOrderDetailManager allocationOrderDetailManager;
private final AllocationAssistService allocationAssistService;
private final PayOrderQueryService payOrderQueryService;
private final LockTemplate lockTemplate;
private final AllocationOrderExtraManager allocationOrderExtraManager;
private final ClientNoticeService clientNoticeService;
/**
* 开启分账 多次请求只会分账一次
* 优先级 分账接收方列表 > 分账组编号 > 默认分账组
*/
public AllocationResult allocation(AllocStartParam param) {
public AllocationResult allocation(AllocationParam param) {
// 判断是否已经有分账订单
AllocationOrder allocationOrder = allocationOrderManager.findByBizAllocationNo(param.getBizAllocationNo())
.orElse(null);
if (Objects.nonNull(allocationOrder)){
// 重复分账
return this.retryAllocation(allocationOrder);
return this.retryAllocation(param, allocationOrder);
} else {
// 首次分账
PayOrder payOrder = this.getAndCheckPayOrder(param);
@@ -90,7 +97,7 @@ public class AllocationService {
/**
* 开启分账 优先级 分账接收方列表 > 分账组编号 > 默认分账组
*/
public AllocationResult allocation(AllocStartParam param, PayOrder payOrder) {
public AllocationResult allocation(AllocationParam param, PayOrder payOrder) {
LockInfo lock = lockTemplate.lock("payment:allocation:" + payOrder.getId(),10000,200);
if (Objects.isNull(lock)){
throw new RepetitiveOperationException("分账发起处理中,请勿重复操作");
@@ -130,6 +137,7 @@ public class AllocationService {
// 失败
order.setStatus(AllocOrderStatusEnum.ALLOCATION_FAILED.getCode())
.setErrorMsg(e.getMessage());
// TODO 返回异常处理
}
// 网关分账号
String gatewayNo = PaymentContextLocal.get()
@@ -148,7 +156,7 @@ public class AllocationService {
/**
* 重新分账
*/
private AllocationResult retryAllocation(AllocationOrder order){
private AllocationResult retryAllocation(AllocationParam param, AllocationOrder order){
LockInfo lock = lockTemplate.lock("payment:allocation:" + order.getOrderId(),10000,200);
if (Objects.isNull(lock)){
throw new RepetitiveOperationException("分账发起处理中,请勿重复操作");
@@ -165,9 +173,13 @@ public class AllocationService {
// 创建分账策略并初始化
AbsAllocationStrategy allocationStrategy = AllocationFactory.create(order.getChannel());
allocationStrategy.initParam(order, details);
// 分账预处理
allocationStrategy.doBeforeHandler();
// 查询扩展信息
AllocationOrderExtra orderExtra = allocationOrderExtraManager.findById(order.getId())
.orElseThrow(() -> new PayFailureException("未查询到分账单扩展信息"));
// 更新分账单扩展信息
allocationAssistService.updateOrder(param, orderExtra);
try {
// 重复分账处理
allocationStrategy.allocation();
@@ -176,7 +188,7 @@ public class AllocationService {
} catch (Exception e) {
log.error("重新分账出现错误:", e);
// 失败
// TODO 失败
order.setStatus(AllocOrderStatusEnum.ALLOCATION_FAILED.getCode())
.setErrorMsg(e.getMessage());
}
@@ -273,10 +285,9 @@ public class AllocationService {
allocationStrategy.doBeforeHandler();
allocationStrategy.doSync();
// TODO 保存分账同步记录
// 根据订单明细更新订单的状态和处理结果
this.updateOrderStatus(allocationOrder, detailList);
allocationOrderDetailManager.updateAllById(detailList);
allocationOrderManager.updateById(allocationOrder);
} finally {
lockTemplate.releaseLock(lock);
}
@@ -284,49 +295,59 @@ public class AllocationService {
/**
* 根据订单明细更新订单的状态和处理结果, 如果订单是分账结束或失败, 不更新状态
* TODO 是否多次同步会产生多次变动, 注意处理多次推送通知的问题, 目前是
*/
private void updateOrderStatus(AllocationOrder allocationOrder, List<AllocationOrderDetail> details){
// 如果是分账结束或失败, 不更新状态
String status = allocationOrder.getStatus();
// 判断明细状态. 获取成功和失败的
long successCount = details.stream()
.map(AllocationOrderDetail::getResult)
.filter(AllocDetailResultEnum.SUCCESS.getCode()::equals)
.count();
long failCount = details.stream()
.map(AllocationOrderDetail::getResult)
.filter(AllocDetailResultEnum.FAIL.getCode()::equals)
.count();
// 成功和失败都为0 进行中
if (successCount == 0 && failCount == 0){
allocationOrder.setStatus(AllocOrderStatusEnum.ALLOCATION_PROCESSING.getCode())
.setResult(AllocOrderResultEnum.ALL_PENDING.getCode());
} else if (failCount == details.size()){
// 全部失败
allocationOrder.setStatus(AllocOrderStatusEnum.ALLOCATION_END.getCode())
.setResult(AllocOrderResultEnum.ALL_FAILED.getCode());
} else if (successCount == details.size()){
// 全部成功
allocationOrder.setStatus(AllocOrderStatusEnum.ALLOCATION_END.getCode())
.setResult(AllocOrderResultEnum.ALL_SUCCESS.getCode());
} else {
// 部分成功
allocationOrder.setStatus(AllocOrderStatusEnum.ALLOCATION_END.getCode())
.setResult(AllocOrderResultEnum.PART_SUCCESS.getCode());
}
// 如果是分账结束或失败, 状态复原
// 如果是分账结束或失败, 不进行对订单进行处理
List<String> list = Arrays.asList(AllocOrderStatusEnum.FINISH.getCode(), AllocOrderStatusEnum.FINISH_FAILED.getCode());
if (list.contains(status)){
allocationOrder.setStatus(AllocOrderStatusEnum.FINISH.getCode())
.setResult(AllocOrderResultEnum.ALL_SUCCESS.getCode());
if (!list.contains(status)){
// 判断明细状态. 获取成功和失败的
long successCount = details.stream()
.map(AllocationOrderDetail::getResult)
.filter(AllocDetailResultEnum.SUCCESS.getCode()::equals)
.count();
long failCount = details.stream()
.map(AllocationOrderDetail::getResult)
.filter(AllocDetailResultEnum.FAIL.getCode()::equals)
.count();
// 成功和失败都为0 表示进行中
if (successCount == 0 && failCount == 0){
allocationOrder.setStatus(AllocOrderStatusEnum.ALLOCATION_PROCESSING.getCode())
.setResult(AllocOrderResultEnum.ALL_PENDING.getCode());
} else {
if (failCount == details.size()){
// 全部失败
allocationOrder.setStatus(AllocOrderStatusEnum.ALLOCATION_END.getCode())
.setResult(AllocOrderResultEnum.ALL_FAILED.getCode());
} else if (successCount == details.size()){
// 全部成功
allocationOrder.setStatus(AllocOrderStatusEnum.ALLOCATION_END.getCode())
.setResult(AllocOrderResultEnum.ALL_SUCCESS.getCode());
} else {
// 部分成功
allocationOrder.setStatus(AllocOrderStatusEnum.ALLOCATION_END.getCode())
.setResult(AllocOrderResultEnum.PART_SUCCESS.getCode());
}
}
}
allocationOrderDetailManager.updateAllById(details);
allocationOrderManager.updateById(allocationOrder);
// 如果状态为完成, 发送通知
if (Objects.equals(AllocOrderStatusEnum.ALLOCATION_END.getCode(), allocationOrder.getStatus())){
// 发送通知
clientNoticeService.registerAllocNotice(allocationOrder, null, details);
}
}
/**
* 获取并检查支付订单
*/
private PayOrder getAndCheckPayOrder(AllocStartParam param) {
private PayOrder getAndCheckPayOrder(AllocationParam param) {
// 查询支付单
PayOrder payOrder = payOrderQueryService.findByBizOrOrderNo(param.getOrderNo(), param.getBizOrderNo())
.orElseThrow(() -> new PayFailureException("支付单不存在"));

View File

@@ -0,0 +1,53 @@
package cn.daxpay.single.service.core.payment.common.aop;
import cn.bootx.platform.common.core.exception.DataNotExistException;
import cn.daxpay.single.exception.pay.PayFailureException;
import cn.daxpay.single.service.annotation.PlatformInitContext;
import cn.daxpay.single.service.core.system.config.dao.PayApiConfigManager;
import cn.daxpay.single.service.core.system.config.entity.PayApiConfig;
import cn.daxpay.single.service.core.system.config.service.PayApiConfigService;
import cn.daxpay.single.service.core.system.config.service.PlatformConfigService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.stereotype.Component;
/**
*
* 执行顺序: 过滤器 -> 拦截器 -> 切面 -> 方法
* @author xxm
* @since 2023/12/24
*/
@Aspect
@Slf4j
@Component
@RequiredArgsConstructor
public class InitPlatformInfoAop {
private final PayApiConfigService payApiConfigService;
private final PayApiConfigManager payApiConfigManager;
private final PlatformConfigService platformConfigService;
/**
* 拦截注解
*/
@Around("@annotation(platformContext)")
public Object beforeMethod(ProceedingJoinPoint pjp, PlatformInitContext platformContext) throws Throwable {
String code = platformContext.value();
// 接口信息
PayApiConfig api = payApiConfigManager.findByCode(code)
.orElseThrow(() -> new DataNotExistException("未找到接口信息"));
if (!api.isEnable()){
throw new PayFailureException("该接口权限未开放");
}
// 设置接口信息
payApiConfigService.initApiInfo(api);
// 初始化平台信息
platformConfigService.initPlatform();
return pjp.proceed();
}
}

View File

@@ -5,7 +5,7 @@ import cn.bootx.platform.common.core.util.ValidationUtil;
import cn.daxpay.single.exception.pay.PayFailureException;
import cn.daxpay.single.param.PaymentCommonParam;
import cn.daxpay.single.result.PaymentCommonResult;
import cn.daxpay.single.service.annotation.PaymentApi;
import cn.daxpay.single.service.annotation.PaymentSign;
import cn.daxpay.single.service.core.payment.common.service.PaymentSignService;
import cn.daxpay.single.util.DaxRes;
import lombok.RequiredArgsConstructor;
@@ -13,6 +13,7 @@ import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
/**
@@ -24,12 +25,13 @@ import org.springframework.stereotype.Component;
@Aspect
@Slf4j
@Component
@Order()
@RequiredArgsConstructor
public class PaymentVerifySignAop {
private final PaymentSignService paymentSignService;
@Around("@annotation(paymentApi)")
public Object beforeMethod(ProceedingJoinPoint pjp, PaymentApi paymentApi) throws Throwable {
@Around("@annotation(paymentSign)")
public Object beforeMethod(ProceedingJoinPoint pjp, @SuppressWarnings("unused") PaymentSign paymentSign) throws Throwable {
Object[] args = pjp.getArgs();
if (args.length == 0){
throw new PayFailureException("支付方法至少有一个参数,并且需要签名支付参数需要放在第一位");
@@ -49,7 +51,7 @@ public class PaymentVerifySignAop {
} catch (PayFailureException ex) {
// 如果抛出支付异常, 包裹异常信息, 进行返回
PaymentCommonResult commonResult = new PaymentCommonResult();
// todo 后期错误码统一管理后, 进行更改
// todo 后期错误码统一管理后进行更改
commonResult.setCode(1);
commonResult.setMsg(ex.getMessage());
paymentSignService.sign(commonResult);

View File

@@ -1,10 +1,8 @@
package cn.daxpay.single.service.core.payment.common.service;
import cn.daxpay.single.param.PaymentCommonParam;
import cn.daxpay.single.service.common.context.PlatformLocal;
import cn.daxpay.single.service.common.context.RequestLocal;
import cn.daxpay.single.service.common.local.PaymentContextLocal;
import cn.daxpay.single.service.core.system.config.entity.PlatformConfig;
import cn.daxpay.single.service.core.system.config.service.PlatformConfigService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
@@ -25,29 +23,13 @@ public class PaymentAssistService {
* 初始化上下文
*/
public void initContext(PaymentCommonParam paymentCommonParam){
this.initPlatform();
platformConfigService.initPlatform();
this.initRequest(paymentCommonParam);
}
/**
* 初始化平台配置上下文
*/
public void initPlatform(){
PlatformConfig config = platformConfigService.getConfig();
PlatformLocal platform = PaymentContextLocal.get().getPlatformInfo();
platform.setSignType(config.getSignType());
platform.setSignSecret(config.getSignSecret());
platform.setNotifyUrl(config.getNotifyUrl());
platform.setOrderTimeout(config.getOrderTimeout());
platform.setLimitAmount(config.getLimitAmount());
platform.setWebsiteUrl(config.getWebsiteUrl());
}
/**
* 初始化请求相关信息上下文
*/
private void initRequest(PaymentCommonParam paymentCommonParam){
public void initRequest(PaymentCommonParam paymentCommonParam){
RequestLocal request = PaymentContextLocal.get().getRequestInfo();
request.setClientIp(paymentCommonParam.getClientIp())
.setSign(paymentCommonParam.getSign())

View File

@@ -7,7 +7,9 @@ import cn.daxpay.single.result.PaymentCommonResult;
import cn.daxpay.single.service.common.context.ApiInfoLocal;
import cn.daxpay.single.service.common.context.PlatformLocal;
import cn.daxpay.single.service.common.local.PaymentContextLocal;
import cn.daxpay.single.service.core.system.config.service.PlatformConfigService;
import cn.daxpay.single.util.PaySignUtil;
import cn.hutool.core.bean.BeanUtil;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
@@ -24,7 +26,9 @@ import java.util.Objects;
@RequiredArgsConstructor
public class PaymentSignService {
private final PaymentAssistService paymentAssistService;;
private final PaymentAssistService paymentAssistService;
private final PlatformConfigService platformConfigService;
/**
* 入参签名校验
@@ -61,6 +65,10 @@ public class PaymentSignService {
*/
public void sign(PaymentCommonResult result) {
PlatformLocal platformInfo = PaymentContextLocal.get().getPlatformInfo();
// 如果平台配置所有属性为空, 进行初始化
if (BeanUtil.isEmpty(platformInfo)){
platformConfigService.initPlatform();
}
String signType = platformInfo.getSignType();
if (Objects.equals(PaySignTypeEnum.HMAC_SHA256.getCode(), signType)){
result.setSign(PaySignUtil.hmacSha256Sign(result, platformInfo.getSignSecret()));

View File

@@ -1,22 +1,30 @@
package cn.daxpay.single.service.core.payment.notice.service;
import cn.bootx.platform.common.jackson.util.JacksonUtil;
import cn.daxpay.single.result.allocation.AllocOrderDetailResult;
import cn.daxpay.single.result.allocation.AllocOrderResult;
import cn.daxpay.single.service.code.ClientNoticeTypeEnum;
import cn.daxpay.single.service.core.notice.entity.ClientNoticeTask;
import cn.daxpay.single.service.core.order.allocation.convert.AllocationConvert;
import cn.daxpay.single.service.core.order.allocation.entity.AllocationOrder;
import cn.daxpay.single.service.core.order.allocation.entity.AllocationOrderDetail;
import cn.daxpay.single.service.core.order.allocation.entity.AllocationOrderExtra;
import cn.daxpay.single.service.core.order.pay.convert.PayOrderConvert;
import cn.daxpay.single.service.core.order.pay.entity.PayOrder;
import cn.daxpay.single.service.core.order.pay.entity.PayOrderExtra;
import cn.daxpay.single.service.core.order.refund.convert.RefundOrderConvert;
import cn.daxpay.single.service.core.order.refund.entity.RefundOrder;
import cn.daxpay.single.service.core.order.refund.entity.RefundOrderExtra;
import cn.daxpay.single.service.core.payment.common.service.PaymentAssistService;
import cn.daxpay.single.service.core.payment.common.service.PaymentSignService;
import cn.daxpay.single.service.core.payment.notice.result.PayNoticeResult;
import cn.daxpay.single.service.core.payment.notice.result.RefundNoticeResult;
import cn.daxpay.single.service.core.task.notice.entity.ClientNoticeTask;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import java.util.List;
import java.util.stream.Collectors;
/**
* 客户系统消息通知任务支撑服务
* @author xxm
@@ -29,14 +37,10 @@ public class ClientNoticeAssistService {
private final PaymentSignService paymentSignService;
private final PaymentAssistService paymentAssistService;
/**
* 构建出支付通知任务对象
*/
public ClientNoticeTask buildPayTask(PayOrder order, PayOrderExtra orderExtra){
// 获取系统签名
paymentAssistService.initPlatform();
PayNoticeResult payNoticeResult = PayOrderConvert.CONVERT.convertNotice(order);
payNoticeResult.setAttach(orderExtra.getAttach());
paymentSignService.sign(payNoticeResult);
@@ -55,12 +59,9 @@ public class ClientNoticeAssistService {
* 构建出退款通知任务对象
*/
public ClientNoticeTask buildRefundTask(RefundOrder order, RefundOrderExtra orderExtra){
// 获取系统签名
paymentAssistService.initPlatform();
// 创建退款通知内容
RefundNoticeResult refundNoticeResult = RefundOrderConvert.CONVERT.convertNotice(order);
refundNoticeResult.setAttach(orderExtra.getAttach());
// 签名
paymentSignService.sign(refundNoticeResult);
return new ClientNoticeTask()
@@ -77,8 +78,26 @@ public class ClientNoticeAssistService {
/**
* 构建分账通知
*/
public ClientNoticeTask buildDivideTask(String tradeNo, String tradeStatus){
return null;
public ClientNoticeTask buildAllocTask(AllocationOrder order, AllocationOrderExtra orderExtra, List<AllocationOrderDetail> list){
// 分账
AllocOrderResult allocOrderResult = AllocationConvert.CONVERT.toResult(order);
// 分账详情
List<AllocOrderDetailResult> details = list.stream()
.map(AllocationConvert.CONVERT::toResult)
.collect(Collectors.toList());
// 分账扩展
allocOrderResult.setAttach(orderExtra.getAttach());
// 签名
paymentSignService.sign(allocOrderResult);
return new ClientNoticeTask()
.setUrl(orderExtra.getNotifyUrl())
// 时间序列化进行了重写
.setContent(JacksonUtil.toJson(allocOrderResult))
.setNoticeType(ClientNoticeTypeEnum.REFUND.getType())
.setSendCount(0)
.setTradeId(order.getId())
.setTradeNo(order.getAllocationNo())
.setTradeStatus(order.getStatus());
}
}

View File

@@ -4,16 +4,20 @@ import cn.bootx.platform.common.core.exception.RepetitiveOperationException;
import cn.bootx.platform.common.core.util.LocalDateTimeUtil;
import cn.bootx.platform.common.redis.RedisClient;
import cn.daxpay.single.service.code.ClientNoticeSendTypeEnum;
import cn.daxpay.single.service.core.notice.dao.ClientNoticeTaskManager;
import cn.daxpay.single.service.core.notice.entity.ClientNoticeRecord;
import cn.daxpay.single.service.core.notice.entity.ClientNoticeTask;
import cn.daxpay.single.service.core.notice.service.ClientNoticeRecordService;
import cn.daxpay.single.service.core.order.allocation.dao.AllocationOrderExtraManager;
import cn.daxpay.single.service.core.order.allocation.entity.AllocationOrder;
import cn.daxpay.single.service.core.order.allocation.entity.AllocationOrderDetail;
import cn.daxpay.single.service.core.order.allocation.entity.AllocationOrderExtra;
import cn.daxpay.single.service.core.order.pay.dao.PayOrderExtraManager;
import cn.daxpay.single.service.core.order.pay.entity.PayOrder;
import cn.daxpay.single.service.core.order.pay.entity.PayOrderExtra;
import cn.daxpay.single.service.core.order.refund.dao.RefundOrderExtraManager;
import cn.daxpay.single.service.core.order.refund.entity.RefundOrder;
import cn.daxpay.single.service.core.order.refund.entity.RefundOrderExtra;
import cn.daxpay.single.service.core.task.notice.dao.ClientNoticeTaskManager;
import cn.daxpay.single.service.core.task.notice.entity.ClientNoticeRecord;
import cn.daxpay.single.service.core.task.notice.entity.ClientNoticeTask;
import cn.daxpay.single.service.core.task.notice.service.ClientNoticeRecordService;
import cn.hutool.core.util.StrUtil;
import cn.hutool.http.ContentType;
import cn.hutool.http.HttpResponse;
@@ -49,6 +53,8 @@ public class ClientNoticeService {
private final ClientNoticeRecordService recordService;
private final AllocationOrderExtraManager allocationOrderExtraManager;
private final RedisClient redisClient;
private final LockTemplate lockTemplate;
@@ -126,7 +132,7 @@ public class ClientNoticeService {
if (Objects.isNull(orderExtra)){
Optional<RefundOrderExtra> extraOpt = refundOrderExtraManager.findById(order.getId());
if (!extraOpt.isPresent()){
log.error("未找到支付扩展信息数据错误订单ID{}",order.getId());
log.error("未找到退款扩展信息数据错误订单ID{}",order.getId());
return;
}
orderExtra = extraOpt.get();
@@ -154,8 +160,32 @@ public class ClientNoticeService {
* 注册分账消息通知任务
*/
@Async("bigExecutor")
public void registerAllocNotice() {
public void registerAllocNotice(AllocationOrder order, AllocationOrderExtra orderExtra, List<AllocationOrderDetail> list) {
// 创建通知任务并保存
if (Objects.isNull(orderExtra)){
Optional<AllocationOrderExtra> extraOpt = allocationOrderExtraManager.findById(order.getId());
if (!extraOpt.isPresent()){
log.error("未找到分账扩展信息数据错误订单ID{}",order.getId());
return;
}
orderExtra = extraOpt.get();
}
// 判断是否需要进行通知
if (StrUtil.isBlank(orderExtra.getNotifyUrl())){
log.info("分账订单无需通知订单ID{}",order.getId());
return;
}
// 创建通知任务并保存
ClientNoticeTask task = clientNoticeAssistService.buildAllocTask(order, orderExtra, list);
try {
taskManager.save(task);
} catch (Exception e) {
log.error("注册分账消息通知任务失败数据错误订单ID{}",order.getId());
log.error("错误内容",e);
throw new RuntimeException(e);
}
// 同时触发一次通知, 如果成功发送, 任务结束
this.sendData(task, LocalDateTime.now());
}
/**

View File

@@ -151,8 +151,6 @@ public class PayAssistService {
*/
@Transactional(rollbackFor = Exception.class)
public void updatePayOrder(PayParam payParam,PayOrder order, PayOrderExtra payOrderExtra) {
PayLocal payInfo = PaymentContextLocal.get()
.getPayInfo();
// 订单信息
order.setAllocation(payParam.getAllocation())
.setChannel(payParam.getChannel())

View File

@@ -54,7 +54,7 @@ public class PayApiConfigService {
* 初始化接口上下文信息
*/
public void initApiInfo(PayApiConfig api){
// 记录支付接口信息
// 接口信息
ApiInfoLocal apiInfoLocal = PaymentContextLocal.get().getApiInfo();
apiInfoLocal.setApiCode(api.getCode())
.setReqSign(api.isReqSign())

View File

@@ -1,8 +1,10 @@
package cn.daxpay.single.service.core.system.config.service;
import cn.bootx.platform.common.core.exception.DataNotExistException;
import cn.daxpay.single.service.core.system.config.entity.PlatformConfig;
import cn.daxpay.single.service.common.context.PlatformLocal;
import cn.daxpay.single.service.common.local.PaymentContextLocal;
import cn.daxpay.single.service.core.system.config.dao.PlatformConfigManager;
import cn.daxpay.single.service.core.system.config.entity.PlatformConfig;
import cn.daxpay.single.service.param.system.config.PlatformConfigParam;
import cn.hutool.core.bean.BeanUtil;
import lombok.RequiredArgsConstructor;
@@ -38,4 +40,18 @@ public class PlatformConfigService {
BeanUtil.copyProperties(param,config);
platformConfigManager.updateById(config);
}
/**
* 初始化平台配置上下文
*/
public void initPlatform(){
PlatformConfig config = this.getConfig();
PlatformLocal platform = PaymentContextLocal.get().getPlatformInfo();
platform.setSignType(config.getSignType());
platform.setSignSecret(config.getSignSecret());
platform.setNotifyUrl(config.getNotifyUrl());
platform.setOrderTimeout(config.getOrderTimeout());
platform.setLimitAmount(config.getLimitAmount());
platform.setWebsiteUrl(config.getWebsiteUrl());
}
}

View File

@@ -1,56 +0,0 @@
package cn.daxpay.single.service.handler;
import cn.bootx.platform.common.core.exception.DataNotExistException;
import cn.daxpay.single.exception.pay.PayFailureException;
import cn.daxpay.single.service.annotation.PaymentApi;
import cn.daxpay.single.service.core.system.config.dao.PayApiConfigManager;
import cn.daxpay.single.service.core.system.config.entity.PayApiConfig;
import cn.daxpay.single.service.core.system.config.service.PayApiConfigService;
import cn.bootx.platform.starter.auth.service.RouterCheck;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.springframework.web.method.HandlerMethod;
import java.util.Objects;
/**
* 支付接口请求检查器, 用于判断请求的支付接口是否允许被外部访问.
* 同时如果检查的结果是放行, 同时初始化支付上下文线程对象
* @author xxm
* @since 2023/12/22
*/
@Slf4j
@Component
@RequiredArgsConstructor
public class PayApiCheckHandler implements RouterCheck {
private final PayApiConfigManager apiConfigManager;
private final PayApiConfigService apiConfigService;
@Override
public int sortNo() {
return -1000;
}
@Override
public boolean check(Object handler) {
// 如果请求的接口未启用
if (handler instanceof HandlerMethod) {
HandlerMethod handlerMethod = (HandlerMethod) handler;
PaymentApi paymentApi = handlerMethod.getMethodAnnotation(PaymentApi.class);
if (Objects.isNull(paymentApi)){
return false;
}
String code = paymentApi.value();
PayApiConfig api = apiConfigManager.findByCode(code)
.orElseThrow(() -> new DataNotExistException("未找到接口信息"));
if (!api.isEnable()){
throw new PayFailureException("该接口权限未开放");
}
// 设置接口信息
apiConfigService.initApiInfo(api);
return true;
}
return false;
}
}

View File

@@ -1,65 +0,0 @@
package cn.daxpay.single.service.param.allocation.receiver;
import cn.daxpay.single.code.AllocReceiverTypeEnum;
import cn.daxpay.single.code.AllocRelationTypeEnum;
import cn.daxpay.single.code.PayChannelEnum;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.experimental.Accessors;
/**
* 分账接收方参数
* @author xxm
* @since 2024/3/28
*/
@Data
@Deprecated
@Accessors(chain = true)
@Schema(title = "分账接收方参数")
public class AllocationReceiverParam {
@Schema(description = "主键")
private Long id;
@Schema(description = "账号别名")
private String name;
/**
* @see PayChannelEnum
*/
@Schema(description = "所属通道")
private String channel;
/**
* 分账接收方类型 个人/商户
* @see AllocReceiverTypeEnum
*/
@Schema(description = "分账接收方类型")
private String receiverType;
@Schema(description = "接收方账号")
private String receiverAccount;
/** 接收方姓名 */
@Schema(description = "接收方姓名")
private String receiverName;
/**
* 分账关系类型
* @see AllocRelationTypeEnum
*/
@Schema(description = "分账关系类型")
private String relationType;
@Schema(description = "关系名称")
private String relationName;
@Schema(description = "是否已经同步到网关")
private Boolean sync;
@Schema(description = "备注")
private String remark;
}

View File

@@ -1,6 +1,6 @@
package cn.daxpay.single.service.task;
import cn.daxpay.single.param.payment.allocation.AllocStartParam;
import cn.daxpay.single.param.payment.allocation.AllocationParam;
import cn.daxpay.single.service.core.order.pay.dao.PayOrderManager;
import cn.daxpay.single.service.core.order.pay.entity.PayOrder;
import cn.daxpay.single.service.core.payment.allocation.service.AllocationService;
@@ -27,7 +27,7 @@ public class AllocationAutoStartTask implements Job {
@Override
public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {
for (PayOrder payOrder : payOrderManager.findAutoAllocation()) {
AllocStartParam param = new AllocStartParam();
AllocationParam param = new AllocationParam();
param.setBizAllocationNo(OrderNoGenerateUtil.allocation());
try {
allocationService.allocation(param, payOrder);

View File

@@ -27,7 +27,7 @@ public class RefundSyncTask implements Job {
private final RefundOrderManager refundOrderManager;
@Override
public void execute(JobExecutionContext context) throws JobExecutionException {
public void execute(JobExecutionContext context) {
// 查询退款中的退款订单
List<RefundOrder> list = refundOrderManager.findAllByProgress();
for (RefundOrder refundOrder : list) {