refactor(payment): 订单关闭入口统一, 网关/普通收归单一接口(type 分流), 权限收敛 trade:order:manage, dev 移除 /** 全量放行, /unipay/close 支持网关订单

This commit is contained in:
daxpay
2026-08-01 10:01:53 +08:00
parent 523289e6d0
commit 24bee166c5
21 changed files with 248 additions and 180 deletions

View File

@@ -50,14 +50,4 @@ public class GatewayPayOrderAdminController {
@NotNull(message = "{validation.field.id.notNull}") Long id) {
return Res.ok(gatewayPayOrderAdminService.sync(id));
}
@PermCode(code = PermCodes.Action.MANAGE)
@Operation(summary = "关闭订单")
@PostMapping("/close")
public Result<Void> close(
@NotNull(message = "{validation.field.id.notNull}") Long id,
@RequestParam(defaultValue = "false") boolean useCancel) {
gatewayPayOrderAdminService.close(id, useCancel);
return Res.ok();
}
}

View File

@@ -56,14 +56,4 @@ public class NormalPayOrderAdminController {
@NotNull(message = "{validation.field.id.notNull}") Long id) {
return Res.ok(normalPayOrderAdminService.sync(id));
}
@PermCode(code = PermCodes.Action.MANAGE)
@Operation(summary = "关闭/撤销订单")
@PostMapping("/close")
public Result<Void> close(
@NotNull(message = "{validation.field.id.notNull}") Long id,
@RequestParam(defaultValue = "false") boolean useCancel) {
normalPayOrderAdminService.close(id, useCancel);
return Res.ok();
}
}

View File

@@ -0,0 +1,38 @@
package cn.daxpay.open.payment.admin.controller.trade;
import cn.daxpay.open.payment.admin.service.trade.OrderCloseAdminService;
import cn.daxpay.open.platform.core.annotation.PermCode;
import cn.daxpay.open.platform.core.code.PermCodes;
import cn.daxpay.open.platform.core.rest.Res;
import cn.daxpay.open.platform.core.rest.result.Result;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import lombok.RequiredArgsConstructor;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
/// # 订单关闭(统一入口, 管理)
///
/// 收归网关支付/普通支付的关闭入口, 前端按 type 区分; 权限由路由层统一校验 trade:order:manage。
@Validated
@Tag(name = "订单关闭(统一入口)")
@RestController
@RequestMapping("/admin/order")
@RequiredArgsConstructor
public class OrderCloseAdminController {
private final OrderCloseAdminService orderCloseAdminService;
@PermCode(menuCode = PermCodes.Trade.Order.MENU, code = PermCodes.Action.MANAGE)
@Operation(summary = "关闭/撤销订单(网关/普通统一入口)")
@PostMapping("/close")
public Result<Void> close(
@NotNull(message = "{validation.field.id.notNull}") Long id,
@NotBlank String type,
@RequestParam(defaultValue = "false") boolean useCancel) {
orderCloseAdminService.close(id, type, useCancel);
return Res.ok();
}
}

View File

@@ -1,22 +1,15 @@
package cn.daxpay.open.payment.admin.service.trade;
import cn.daxpay.open.payment.trade.enums.GatewayOrderStatusEnum;
import cn.daxpay.open.payment.trade.enums.PayTradeTypeEnum;
import cn.daxpay.open.payment.trade.order.convert.GatewayPayOrderConvert;
import cn.daxpay.open.payment.trade.order.dao.GatewayPayOrderManager;
import cn.daxpay.open.payment.trade.order.dao.PayTradeManager;
import cn.daxpay.open.payment.trade.order.entity.GatewayPayOrder;
import cn.daxpay.open.payment.trade.order.entity.PayTrade;
import cn.daxpay.open.payment.trade.order.param.GatewayPayOrderQuery;
import cn.daxpay.open.payment.trade.order.result.GatewayPayOrderResult;
import cn.daxpay.open.payment.trade.order.service.TradeOrderDetailAssembler;
import cn.daxpay.open.payment.trade.runtime.service.close.PayCloseService;
import cn.daxpay.open.payment.trade.runtime.service.sync.PaySyncService;
import cn.daxpay.open.payment.trade.runtime.service.pay.common.PayUniHandleService;
import cn.daxpay.open.payment.unipay.result.trade.pay.NormalPaySyncResult;
import cn.daxpay.open.platform.common.translate.service.TransService;
import cn.daxpay.open.platform.core.code.CommonErrorCode;
import cn.daxpay.open.platform.core.exception.BizInfoException;
import cn.daxpay.open.platform.core.exception.DataNotExistException;
import cn.daxpay.open.platform.core.rest.param.PageParam;
import cn.daxpay.open.platform.core.rest.result.PageResult;
@@ -24,18 +17,13 @@ import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import java.util.List;
/// # 网关支付业务单管理服务
@Service
@RequiredArgsConstructor
public class GatewayPayOrderAdminService {
private final GatewayPayOrderManager gatewayPayOrderManager;
private final PayTradeManager payTradeManager;
private final PaySyncService paySyncService;
private final PayCloseService payCloseService;
private final PayUniHandleService payUniHandleService;
private final TransService transService;
private final TradeOrderDetailAssembler tradeOrderDetailAssembler;
@@ -64,25 +52,6 @@ public class GatewayPayOrderAdminService {
}
public NormalPaySyncResult sync(Long id) {
PayTrade trade = payTradeManager.findByContainerId(id, PayTradeTypeEnum.GATEWAY.getCode())
.orElseThrow(() -> new DataNotExistException("pay.error.payOrderNotExist"));
return paySyncService.syncPayOrder(trade);
}
public void close(Long id, boolean useCancel) {
GatewayPayOrder order = gatewayPayOrderManager.findById(id)
// 支付: 支付订单不存在
.orElseThrow(() -> new DataNotExistException("pay.error.payOrderNotExist"));
if (!List.of(GatewayOrderStatusEnum.WAIT_PAY.getCode(), GatewayOrderStatusEnum.PAYING.getCode())
.contains(order.getStatus())) {
// 支付: 订单不是支付中无法进行关闭订单
throw new BizInfoException(CommonErrorCode.VALIDATE_PARAMETERS_ERROR, "pay.error.pay.closeNotPaying");
}
PayTrade trade = payTradeManager.findByContainerId(id, PayTradeTypeEnum.GATEWAY.getCode()).orElse(null);
if (trade != null) {
payCloseService.closeOrder(trade, useCancel);
} else {
payUniHandleService.gatewayOrderClose(order);
}
return paySyncService.syncByContainer(id, PayTradeTypeEnum.GATEWAY.getCode());
}
}

View File

@@ -3,13 +3,10 @@ package cn.daxpay.open.payment.admin.service.trade;
import cn.daxpay.open.payment.trade.enums.PayTradeTypeEnum;
import cn.daxpay.open.payment.trade.order.convert.NormalPayOrderConvert;
import cn.daxpay.open.payment.trade.order.dao.NormalPayOrderManager;
import cn.daxpay.open.payment.trade.order.dao.PayTradeManager;
import cn.daxpay.open.payment.trade.order.entity.NormalPayOrder;
import cn.daxpay.open.payment.trade.order.entity.PayTrade;
import cn.daxpay.open.payment.trade.order.param.NormalPayOrderQuery;
import cn.daxpay.open.payment.trade.order.result.NormalPayOrderResult;
import cn.daxpay.open.payment.trade.order.service.TradeOrderDetailAssembler;
import cn.daxpay.open.payment.trade.runtime.service.close.PayCloseService;
import cn.daxpay.open.payment.trade.runtime.service.sync.PaySyncService;
import cn.daxpay.open.payment.unipay.result.trade.pay.NormalPaySyncResult;
import cn.daxpay.open.platform.common.translate.service.TransService;
@@ -28,9 +25,7 @@ import org.springframework.stereotype.Service;
public class NormalPayOrderAdminService {
private final NormalPayOrderManager normalPayOrderManager;
private final PayTradeManager payTradeManager;
private final PaySyncService paySyncService;
private final PayCloseService payCloseService;
private final TransService transService;
private final TradeOrderDetailAssembler tradeOrderDetailAssembler;
@@ -63,15 +58,6 @@ public class NormalPayOrderAdminService {
/// 同步支付状态(传入业务订单ID)
public NormalPaySyncResult sync(Long id) {
PayTrade trade = payTradeManager.findByContainerId(id, PayTradeTypeEnum.NORMAL.getCode())
.orElseThrow(() -> new DataNotExistException("pay.error.payOrderNotExist"));
return paySyncService.syncPayOrder(trade);
}
/// 关闭/撤销订单(传入业务订单ID)
public void close(Long id, boolean useCancel) {
PayTrade trade = payTradeManager.findByContainerId(id, PayTradeTypeEnum.NORMAL.getCode())
.orElseThrow(() -> new DataNotExistException("pay.error.payOrderNotExist"));
payCloseService.closeOrder(trade, useCancel);
return paySyncService.syncByContainer(id, PayTradeTypeEnum.NORMAL.getCode());
}
}

View File

@@ -0,0 +1,25 @@
package cn.daxpay.open.payment.admin.service.trade;
import cn.daxpay.open.payment.trade.runtime.service.close.PayCloseService;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
/// # 订单关闭统一管理服务
///
/// 收归网关支付与普通支付的关闭入口, 委托 [PayCloseService.closeByContainer]。
/// 网关预下单未生成 Trade 的兜底关单由 closeByContainer 内部处理, 本服务不重复实现。
@Service
@RequiredArgsConstructor
public class OrderCloseAdminService {
private final PayCloseService payCloseService;
/// 关闭/撤销订单(统一入口)
///
/// @param id 业务容器ID(pay_gateway_order / pay_normal_order 主键)
/// @param type 容器类型: gateway=网关支付, normal=普通支付
/// @param useCancel 是否以撤销方式关闭
public void close(Long id, String type, boolean useCancel) {
payCloseService.closeByContainer(id, type, useCancel);
}
}

View File

@@ -54,14 +54,4 @@ public class AppAdminGatewayPayOrderController {
@NotNull(message = "{validation.field.id.notNull}") Long id) {
return Res.ok(gatewayPayOrderService.sync(id));
}
@PermCode(code = PermCodes.Action.MANAGE)
@Operation(summary = "关闭订单")
@PostMapping("/close")
public Result<Void> close(
@NotNull(message = "{validation.field.id.notNull}") Long id,
@RequestParam(defaultValue = "false") boolean useCancel) {
gatewayPayOrderService.close(id, useCancel);
return Res.ok();
}
}

View File

@@ -56,14 +56,4 @@ public class AppAdminNormalPayOrderController {
@NotNull(message = "{validation.field.id.notNull}") Long id) {
return Res.ok(normalPayOrderService.sync(id));
}
@PermCode(code = PermCodes.Action.MANAGE)
@Operation(summary = "关闭/撤销订单")
@PostMapping("/close")
public Result<Void> close(
@NotNull(message = "{validation.field.id.notNull}") Long id,
@RequestParam(defaultValue = "false") boolean useCancel) {
normalPayOrderService.close(id, useCancel);
return Res.ok();
}
}

View File

@@ -0,0 +1,38 @@
package cn.daxpay.open.payment.app.admin.controller.trade;
import cn.daxpay.open.payment.app.admin.service.trade.AppAdminOrderCloseService;
import cn.daxpay.open.platform.core.annotation.PermCode;
import cn.daxpay.open.platform.core.code.PermCodes;
import cn.daxpay.open.platform.core.rest.Res;
import cn.daxpay.open.platform.core.rest.result.Result;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import lombok.RequiredArgsConstructor;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
/// # 订单关闭(统一入口, 运营移动端)
///
/// 收归网关支付/普通支付的关闭入口, 前端按 type 区分; 权限由路由层统一校验 trade:order:manage。
@Validated
@Tag(name = "订单关闭(统一入口)")
@RestController
@RequestMapping("/app-admin/order")
@RequiredArgsConstructor
public class AppAdminOrderCloseController {
private final AppAdminOrderCloseService appAdminOrderCloseService;
@PermCode(menuCode = PermCodes.Trade.Order.MENU, code = PermCodes.Action.MANAGE)
@Operation(summary = "关闭/撤销订单(网关/普通统一入口)")
@PostMapping("/close")
public Result<Void> close(
@NotNull(message = "{validation.field.id.notNull}") Long id,
@NotBlank String type,
@RequestParam(defaultValue = "false") boolean useCancel) {
appAdminOrderCloseService.close(id, type, useCancel);
return Res.ok();
}
}

View File

@@ -32,9 +32,4 @@ public class AppAdminGatewayPayOrderService {
public NormalPaySyncResult sync(Long id) {
return gatewayPayOrderAdminService.sync(id);
}
/// 关闭订单
public void close(Long id, boolean useCancel) {
gatewayPayOrderAdminService.close(id, useCancel);
}
}

View File

@@ -32,9 +32,4 @@ public class AppAdminNormalPayOrderService {
public NormalPaySyncResult sync(Long id) {
return normalPayOrderAdminService.sync(id);
}
/// 关闭/撤销订单
public void close(Long id, boolean useCancel) {
normalPayOrderAdminService.close(id, useCancel);
}
}

View File

@@ -0,0 +1,20 @@
package cn.daxpay.open.payment.app.admin.service.trade;
import cn.daxpay.open.payment.admin.service.trade.OrderCloseAdminService;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
/// # 运营移动端-订单关闭统一服务
///
/// 逻辑转发至 [OrderCloseAdminService]。
@Service
@RequiredArgsConstructor
public class AppAdminOrderCloseService {
private final OrderCloseAdminService orderCloseAdminService;
/// 关闭/撤销订单(统一入口)
public void close(Long id, String type, boolean useCancel) {
orderCloseAdminService.close(id, type, useCancel);
}
}

View File

@@ -1,6 +1,7 @@
package cn.daxpay.open.payment.trade.runtime.service.close;
import cn.daxpay.open.payment.common.context.PaymentContext;
import cn.daxpay.open.payment.trade.enums.GatewayOrderStatusEnum;
import cn.daxpay.open.payment.trade.enums.PayFundStatusEnum;
import cn.daxpay.open.payment.trade.enums.PayTradeTypeEnum;
import cn.daxpay.open.payment.strategy.PaymentStrategyFactory;
@@ -54,28 +55,71 @@ public class PayCloseService {
private final LockExecutor lockExecutor;
private final PaymentContext paymentContext;
/// 按容器ID关闭/撤销订单(含网关预下单未生成 Trade 的兜底)
///
/// 供容器视角的对外 Service(Admin/Merchant)调用, 消除两端重复的容器状态校验与预下单分支。
public void closeByContainer(Long containerId, String tradeType, boolean useCancel) {
// 网关: 预下单后可能尚未生成 Trade
if (Objects.equals(tradeType, PayTradeTypeEnum.GATEWAY.getCode())) {
GatewayPayOrder order = gatewayPayOrderManager.findById(containerId)
.orElseThrow(() -> new BizInfoException(CommonErrorCode.VALIDATE_PARAMETERS_ERROR, "pay.error.payOrderNotExist"));
if (!List.of(GatewayOrderStatusEnum.WAIT_PAY.getCode(), GatewayOrderStatusEnum.PAYING.getCode())
.contains(order.getStatus())) {
// 支付: 订单不是支付中, 无法进行关闭订单
throw new BizInfoException(DaxPayErrorCode.TRADE_STATUS_ERROR, "pay.error.pay.closeNotPaying");
}
PayTrade trade = payTradeManager.findByContainerId(containerId, tradeType).orElse(null);
if (Objects.nonNull(trade)) {
this.closeOrder(trade, useCancel);
} else {
payUniHandleService.gatewayOrderClose(order);
}
return;
}
// 普通支付等其他形态: 直接反查 Trade 关单
PayTrade trade = payTradeManager.findByContainerId(containerId, tradeType)
.orElseThrow(() -> new BizInfoException(CommonErrorCode.VALIDATE_PARAMETERS_ERROR, "pay.error.payOrderNotExist"));
this.closeOrder(trade, useCancel);
}
/// 关闭支付(商户 API)
///
/// 同时支持普通支付与网关支付两种容器:
/// - orderNo 优先按 tradeNo(平台支付订单号)查资金交易; 未命中再按网关容器 orderNo 反查(运营友好, 与退款服务对齐)
/// - bizOrderNo 回退: 先普通容器再网关容器, 委托 [closeByContainer] 复用网关预下单无 Trade 的兜底关单
public void close(NormalPayCloseParam param) {
if (StrUtil.isBlank(param.getOrderNo()) && Objects.isNull(param.getBizOrderNo())) {
throw new BizInfoException(CommonErrorCode.VALIDATE_PARAMETERS_ERROR, "pay.error.orderNoRequired");
}
PayTrade trade = null;
// orderNo 优先: 先按 tradeNo 查资金交易(实体自带 tradeType, 直接 closeOrder)
if (StrUtil.isNotBlank(param.getOrderNo())) {
trade = payTradeManager.findByTradeNo(param.getOrderNo()).orElse(null);
}
if (Objects.isNull(trade) && Objects.nonNull(param.getBizOrderNo())) {
NormalPayOrder normalOrder = payNormalOrderManager.findByBizOrderNo(param.getBizOrderNo())
.orElse(null);
if (Objects.nonNull(normalOrder)) {
trade = payTradeManager.findByContainerId(normalOrder.getId(), PayTradeTypeEnum.NORMAL.getCode())
.orElse(null);
PayTrade trade = payTradeManager.findByTradeNo(param.getOrderNo()).orElse(null);
if (Objects.nonNull(trade)) {
this.closeOrder(trade, param.isUseCancel());
return;
}
// tradeNo 未命中: 尝试用网关容器 orderNo 反查
GatewayPayOrder gateway = gatewayPayOrderManager.findByOrderNo(param.getOrderNo()).orElse(null);
if (Objects.nonNull(gateway)) {
this.closeByContainer(gateway.getId(), PayTradeTypeEnum.GATEWAY.getCode(), param.isUseCancel());
return;
}
}
if (Objects.isNull(trade)) {
// 支付: 支付订单不存在
throw new BizInfoException(CommonErrorCode.VALIDATE_PARAMETERS_ERROR, "pay.error.payOrderNotExist");
// bizOrderNo 回退: 先普通再网关, 委托 closeByContainer(含网关无 Trade 兜底)
if (Objects.nonNull(param.getBizOrderNo())) {
NormalPayOrder normalOrder = payNormalOrderManager.findByBizOrderNo(param.getBizOrderNo()).orElse(null);
if (Objects.nonNull(normalOrder)) {
this.closeByContainer(normalOrder.getId(), PayTradeTypeEnum.NORMAL.getCode(), param.isUseCancel());
return;
}
GatewayPayOrder gatewayOrder = gatewayPayOrderManager.findByBizOrderNo(param.getBizOrderNo()).orElse(null);
if (Objects.nonNull(gatewayOrder)) {
this.closeByContainer(gatewayOrder.getId(), PayTradeTypeEnum.GATEWAY.getCode(), param.isUseCancel());
return;
}
}
this.closeOrder(trade, param.isUseCancel());
// 支付: 支付订单不存在
throw new BizInfoException(CommonErrorCode.VALIDATE_PARAMETERS_ERROR, "pay.error.payOrderNotExist");
}
/// 关闭支付记录

View File

@@ -13,12 +13,12 @@ import lombok.EqualsAndHashCode;
@Schema(title = "支付关闭参数")
public class NormalPayCloseParam extends MerchantPaymentCommonParam {
/// 支付订单号/商户订单号/通道订单号至少要传输一个,支付订单号 > 商户订单号 > 通道订单号
/// 平台支付订单号(tradeNo)或网关订单号(orderNo), 优先按 tradeNo 查, 未命中按网关 orderNo 反查
@Schema(description = "订单号")
@Size(max = 100, message = "{validation.field.orderNo.size}")
private String orderNo;
/// 支付订单号/商户订单号/通道订单号至少要传输一个,支付订单号 > 商户订单号 > 通道订单号
/// 商户订单号, 支持普通支付与网关支付两种容器, 先普通容器再网关容器查询
@Schema(description = "商户订单号")
@Size(max = 100, message = "{validation.field.bizOrderNo.size}")
private String bizOrderNo;

View File

@@ -56,14 +56,4 @@ public class MchGatewayPayOrderController {
@NotNull(message = "{validation.field.id.notNull}") Long id) {
return Res.ok(mchGatewayPayOrderService.sync(id));
}
@PermCode(code = PermCodes.Action.MANAGE)
@Operation(summary = "关闭订单")
@PostMapping("/close")
public Result<Void> close(
@NotNull(message = "{validation.field.id.notNull}") Long id,
@RequestParam(defaultValue = "false") boolean useCancel) {
mchGatewayPayOrderService.close(id, useCancel);
return Res.ok();
}
}

View File

@@ -56,14 +56,4 @@ public class MchNormalPayOrderController {
@NotNull(message = "{validation.field.id.notNull}") Long id) {
return Res.ok(mchNormalPayOrderService.sync(id));
}
@PermCode(code = PermCodes.Action.MANAGE)
@Operation(summary = "关闭/撤销订单")
@PostMapping("/close")
public Result<Void> close(
@NotNull(message = "{validation.field.id.notNull}") Long id,
@RequestParam(defaultValue = "false") boolean useCancel) {
mchNormalPayOrderService.close(id, useCancel);
return Res.ok();
}
}

View File

@@ -0,0 +1,38 @@
package cn.daxpay.open.payment.merchant.controller.trade;
import cn.daxpay.open.payment.merchant.service.trade.MchOrderCloseService;
import cn.daxpay.open.platform.core.annotation.PermCode;
import cn.daxpay.open.platform.core.code.PermCodes;
import cn.daxpay.open.platform.core.rest.Res;
import cn.daxpay.open.platform.core.rest.result.Result;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import lombok.RequiredArgsConstructor;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
/// # 订单关闭(统一入口, 商户)
///
/// 收归网关支付/普通支付的关闭入口, 前端按 type 区分; 权限由路由层统一校验 trade:order:manage。
@Validated
@Tag(name = "订单关闭(统一入口)")
@RestController
@RequestMapping("/mch/order")
@RequiredArgsConstructor
public class MchOrderCloseController {
private final MchOrderCloseService mchOrderCloseService;
@PermCode(menuCode = PermCodes.Trade.Order.MENU, code = PermCodes.Action.MANAGE)
@Operation(summary = "关闭/撤销订单(网关/普通统一入口)")
@PostMapping("/close")
public Result<Void> close(
@NotNull(message = "{validation.field.id.notNull}") Long id,
@NotBlank String type,
@RequestParam(defaultValue = "false") boolean useCancel) {
mchOrderCloseService.close(id, type, useCancel);
return Res.ok();
}
}

View File

@@ -1,22 +1,16 @@
package cn.daxpay.open.payment.merchant.service.trade;
import cn.daxpay.open.payment.common.context.PaymentContext;
import cn.daxpay.open.payment.trade.enums.GatewayOrderStatusEnum;
import cn.daxpay.open.payment.trade.enums.PayTradeTypeEnum;
import cn.daxpay.open.payment.trade.order.convert.GatewayPayOrderConvert;
import cn.daxpay.open.payment.trade.order.dao.GatewayPayOrderManager;
import cn.daxpay.open.payment.trade.order.dao.PayTradeManager;
import cn.daxpay.open.payment.trade.order.entity.GatewayPayOrder;
import cn.daxpay.open.payment.trade.order.entity.PayTrade;
import cn.daxpay.open.payment.trade.order.param.GatewayPayOrderQuery;
import cn.daxpay.open.payment.trade.order.result.GatewayPayOrderResult;
import cn.daxpay.open.payment.trade.order.service.TradeOrderDetailAssembler;
import cn.daxpay.open.payment.trade.runtime.service.close.PayCloseService;
import cn.daxpay.open.payment.trade.runtime.service.pay.common.PayUniHandleService;
import cn.daxpay.open.payment.trade.runtime.service.sync.PaySyncService;
import cn.daxpay.open.payment.unipay.result.trade.pay.NormalPaySyncResult;
import cn.daxpay.open.platform.core.code.CommonCode;
import cn.daxpay.open.platform.core.code.CommonErrorCode;
import cn.daxpay.open.platform.core.exception.BizInfoException;
import cn.daxpay.open.platform.core.exception.DataNotExistException;
import cn.daxpay.open.platform.core.rest.param.PageParam;
@@ -25,8 +19,6 @@ import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import java.util.List;
/// # 网关支付业务单(商户端)
///
/// 强制按 [PaymentContext#getMchNo] 过滤;行级隔离另由 TenantLine 兜底。
@@ -36,10 +28,7 @@ public class MchGatewayPayOrderService {
private final PaymentContext paymentContext;
private final GatewayPayOrderManager gatewayPayOrderManager;
private final PayTradeManager payTradeManager;
private final PaySyncService paySyncService;
private final PayCloseService payCloseService;
private final PayUniHandleService payUniHandleService;
private final TradeOrderDetailAssembler tradeOrderDetailAssembler;
/// 分页查询(强制当前商户)
@@ -67,27 +56,7 @@ public class MchGatewayPayOrderService {
/// 同步支付状态
public NormalPaySyncResult sync(Long id) {
PayTrade trade = payTradeManager.findByContainerId(id, PayTradeTypeEnum.GATEWAY.getCode())
.orElseThrow(() -> new DataNotExistException("pay.error.payOrderNotExist"));
return paySyncService.syncPayOrder(trade);
}
/// 关闭/撤销订单
public void close(Long id, boolean useCancel) {
GatewayPayOrder order = gatewayPayOrderManager.findById(id)
// 支付: 支付订单不存在
.orElseThrow(() -> new DataNotExistException("pay.error.payOrderNotExist"));
if (!List.of(GatewayOrderStatusEnum.WAIT_PAY.getCode(), GatewayOrderStatusEnum.PAYING.getCode())
.contains(order.getStatus())) {
// 支付: 订单不是支付中无法进行关闭订单
throw new BizInfoException(CommonErrorCode.VALIDATE_PARAMETERS_ERROR, "pay.error.pay.closeNotPaying");
}
PayTrade trade = payTradeManager.findByContainerId(id, PayTradeTypeEnum.GATEWAY.getCode()).orElse(null);
if (trade != null) {
payCloseService.closeOrder(trade, useCancel);
} else {
payUniHandleService.gatewayOrderClose(order);
}
return paySyncService.syncByContainer(id, PayTradeTypeEnum.GATEWAY.getCode());
}
/// 解析并强制写入当前商户号

View File

@@ -4,13 +4,10 @@ import cn.daxpay.open.payment.common.context.PaymentContext;
import cn.daxpay.open.payment.trade.enums.PayTradeTypeEnum;
import cn.daxpay.open.payment.trade.order.convert.NormalPayOrderConvert;
import cn.daxpay.open.payment.trade.order.dao.NormalPayOrderManager;
import cn.daxpay.open.payment.trade.order.dao.PayTradeManager;
import cn.daxpay.open.payment.trade.order.entity.NormalPayOrder;
import cn.daxpay.open.payment.trade.order.entity.PayTrade;
import cn.daxpay.open.payment.trade.order.param.NormalPayOrderQuery;
import cn.daxpay.open.payment.trade.order.result.NormalPayOrderResult;
import cn.daxpay.open.payment.trade.order.service.TradeOrderDetailAssembler;
import cn.daxpay.open.payment.trade.runtime.service.close.PayCloseService;
import cn.daxpay.open.payment.trade.runtime.service.sync.PaySyncService;
import cn.daxpay.open.payment.unipay.result.trade.pay.NormalPaySyncResult;
import cn.daxpay.open.platform.core.code.CommonCode;
@@ -31,9 +28,7 @@ public class MchNormalPayOrderService {
private final PaymentContext paymentContext;
private final NormalPayOrderManager normalPayOrderManager;
private final PayTradeManager payTradeManager;
private final PaySyncService paySyncService;
private final PayCloseService payCloseService;
private final TradeOrderDetailAssembler tradeOrderDetailAssembler;
/// 分页查询(强制当前商户)
@@ -61,16 +56,7 @@ public class MchNormalPayOrderService {
/// 同步支付状态(传入业务订单ID)
public NormalPaySyncResult sync(Long id) {
PayTrade trade = payTradeManager.findByContainerId(id, PayTradeTypeEnum.NORMAL.getCode())
.orElseThrow(() -> new DataNotExistException("pay.error.payOrderNotExist"));
return paySyncService.syncPayOrder(trade);
}
/// 关闭/撤销订单(传入业务订单ID)
public void close(Long id, boolean useCancel) {
PayTrade trade = payTradeManager.findByContainerId(id, PayTradeTypeEnum.NORMAL.getCode())
.orElseThrow(() -> new DataNotExistException("pay.error.payOrderNotExist"));
payCloseService.closeOrder(trade, useCancel);
return paySyncService.syncByContainer(id, PayTradeTypeEnum.NORMAL.getCode());
}
/// 解析并强制写入当前商户号

View File

@@ -0,0 +1,24 @@
package cn.daxpay.open.payment.merchant.service.trade;
import cn.daxpay.open.payment.trade.runtime.service.close.PayCloseService;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
/// # 订单关闭统一管理服务(商户端)
///
/// 收归网关支付与普通支付的关闭入口, 委托 [PayCloseService#closeByContainer]。
@Service
@RequiredArgsConstructor
public class MchOrderCloseService {
private final PayCloseService payCloseService;
/// 关闭/撤销订单(统一入口)
///
/// @param id 业务容器ID(pay_gateway_order / pay_normal_order 主键)
/// @param type 容器类型: gateway=网关支付, normal=普通支付
/// @param useCancel 是否以撤销方式关闭
public void close(Long id, String type, boolean useCancel) {
payCloseService.closeByContainer(id, type, useCancel);
}
}

View File

@@ -100,7 +100,6 @@ daxpay:
- '/test/**'
- '/error'
- '/favicon.ico'
- '/**'
# 审计日志配置
audit-log:
ip2region:
@@ -109,6 +108,8 @@ daxpay:
# 查询模式: VECTOR_INDEX(推荐) 或 CACHE
search-type: VECTOR_INDEX
config:
# 交易定时同步开关(dev 环境开启)
trade-sync-enabled: true
# 平台公私钥(证书)配置, 用于接口请求/回调的 RSA 签名与验签
key-config:
# 私钥配置