diff --git a/src/Api/TakeoutSaaS.TenantApi/Consumers/PaymentSucceededConsumer.cs b/src/Api/TakeoutSaaS.TenantApi/Consumers/PaymentSucceededConsumer.cs
new file mode 100644
index 0000000..6b26867
--- /dev/null
+++ b/src/Api/TakeoutSaaS.TenantApi/Consumers/PaymentSucceededConsumer.cs
@@ -0,0 +1,34 @@
+using MassTransit;
+using Microsoft.AspNetCore.SignalR;
+using TakeoutSaaS.Application.Messaging.Events;
+using TakeoutSaaS.TenantApi.Hubs;
+
+namespace TakeoutSaaS.TenantApi.Consumers;
+
+///
+/// 支付成功事件消费者 — 推送新订单到看板。
+///
+public sealed class PaymentSucceededConsumer(IHubContext hubContext)
+ : IConsumer
+{
+ ///
+ public async Task Consume(ConsumeContext context)
+ {
+ var e = context.Message;
+ var group = $"store:{e.TenantId}:{e.StoreId}";
+
+ // 1. 支付成功 = 新订单出现在待接单列,推送 NewOrder
+ await hubContext.Clients.Group(group).SendAsync("NewOrder", new
+ {
+ e.OrderId,
+ e.OrderNo,
+ e.Amount,
+ e.StoreId,
+ e.Channel,
+ e.DeliveryType,
+ e.CustomerName,
+ e.ItemsSummary,
+ e.PaidAt
+ }, context.CancellationToken);
+ }
+}
diff --git a/src/Api/TakeoutSaaS.TenantApi/Controllers/PaymentCallbackController.cs b/src/Api/TakeoutSaaS.TenantApi/Controllers/PaymentCallbackController.cs
new file mode 100644
index 0000000..553021d
--- /dev/null
+++ b/src/Api/TakeoutSaaS.TenantApi/Controllers/PaymentCallbackController.cs
@@ -0,0 +1,99 @@
+using Asp.Versioning;
+using MediatR;
+using Microsoft.AspNetCore.Authorization;
+using Microsoft.AspNetCore.Mvc;
+using TakeoutSaaS.Application.App.Payments.Commands;
+using TakeoutSaaS.Application.App.Stores.Services;
+using TakeoutSaaS.Domain.Payments.Enums;
+using TakeoutSaaS.Shared.Abstractions.Results;
+using TakeoutSaaS.Shared.Web.Api;
+
+namespace TakeoutSaaS.TenantApi.Controllers;
+
+///
+/// 支付回调接口。
+///
+[ApiVersion("1.0")]
+[Route("api/tenant/v{version:apiVersion}/payment/callback")]
+public sealed class PaymentCallbackController(
+ IMediator mediator,
+ StoreContextService storeContextService) : BaseApiController
+{
+ ///
+ /// 微信支付回调(预留,签名验证后续接入 SDK)。
+ ///
+ [HttpPost("wechat")]
+ [AllowAnonymous]
+ public async Task WeChatCallback(CancellationToken cancellationToken)
+ {
+ // TODO: 接入微信支付 V3 SDK 后实现签名验证与报文解析
+ return Ok(new { code = "SUCCESS", message = "成功" });
+ }
+
+ ///
+ /// 支付宝回调(预留,签名验证后续接入 SDK)。
+ ///
+ [HttpPost("alipay")]
+ [AllowAnonymous]
+ public async Task AlipayCallback(CancellationToken cancellationToken)
+ {
+ // TODO: 接入支付宝 RSA2 SDK 后实现签名验证与报文解析
+ return Ok("success");
+ }
+
+ ///
+ /// 内部模拟支付回调(仅开发环境)。
+ ///
+ [HttpPost("internal")]
+ [Authorize]
+ public async Task> InternalCallback(
+ [FromBody] InternalPaymentCallbackRequest request,
+ CancellationToken cancellationToken)
+ {
+ var (_, tenantId, _) = storeContextService.GetRequiredContext();
+
+ var result = await mediator.Send(new ProcessPaymentCallbackCommand
+ {
+ OrderNo = request.OrderNo,
+ ChannelTransactionId = request.TransactionId ?? $"internal-{Guid.NewGuid():N}",
+ Method = request.Method ?? PaymentMethod.WeChatPay,
+ Amount = request.Amount,
+ PaidAt = request.PaidAt ?? DateTime.UtcNow,
+ TenantId = tenantId,
+ RawPayload = "internal-test"
+ }, cancellationToken);
+
+ return ApiResponse.Ok(result);
+ }
+}
+
+///
+/// 内部模拟支付回调请求体。
+///
+public sealed record InternalPaymentCallbackRequest
+{
+ ///
+ /// 订单号。
+ ///
+ public required string OrderNo { get; init; }
+
+ ///
+ /// 支付金额。
+ ///
+ public required decimal Amount { get; init; }
+
+ ///
+ /// 支付流水号(可选)。
+ ///
+ public string? TransactionId { get; init; }
+
+ ///
+ /// 支付方式(可选,默认微信)。
+ ///
+ public PaymentMethod? Method { get; init; }
+
+ ///
+ /// 支付时间(可选,默认当前时间)。
+ ///
+ public DateTime? PaidAt { get; init; }
+}
diff --git a/src/Application/TakeoutSaaS.Application/App/Payments/Commands/ProcessPaymentCallbackCommand.cs b/src/Application/TakeoutSaaS.Application/App/Payments/Commands/ProcessPaymentCallbackCommand.cs
new file mode 100644
index 0000000..b6bb1d3
--- /dev/null
+++ b/src/Application/TakeoutSaaS.Application/App/Payments/Commands/ProcessPaymentCallbackCommand.cs
@@ -0,0 +1,45 @@
+using MediatR;
+using TakeoutSaaS.Domain.Payments.Enums;
+
+namespace TakeoutSaaS.Application.App.Payments.Commands;
+
+///
+/// 支付回调处理命令。
+///
+public sealed record ProcessPaymentCallbackCommand : IRequest
+{
+ ///
+ /// 商户订单号。
+ ///
+ public required string OrderNo { get; init; }
+
+ ///
+ /// 第三方支付流水号。
+ ///
+ public required string ChannelTransactionId { get; init; }
+
+ ///
+ /// 支付方式。
+ ///
+ public required PaymentMethod Method { get; init; }
+
+ ///
+ /// 实付金额。
+ ///
+ public required decimal Amount { get; init; }
+
+ ///
+ /// 支付时间。
+ ///
+ public required DateTime PaidAt { get; init; }
+
+ ///
+ /// 租户标识。
+ ///
+ public required long TenantId { get; init; }
+
+ ///
+ /// 原始回调报文(留存)。
+ ///
+ public string? RawPayload { get; init; }
+}
diff --git a/src/Application/TakeoutSaaS.Application/App/Payments/Handlers/ProcessPaymentCallbackCommandHandler.cs b/src/Application/TakeoutSaaS.Application/App/Payments/Handlers/ProcessPaymentCallbackCommandHandler.cs
new file mode 100644
index 0000000..81ae6a6
--- /dev/null
+++ b/src/Application/TakeoutSaaS.Application/App/Payments/Handlers/ProcessPaymentCallbackCommandHandler.cs
@@ -0,0 +1,132 @@
+using MediatR;
+using Microsoft.Extensions.Logging;
+using TakeoutSaaS.Application.App.Payments.Commands;
+using TakeoutSaaS.Application.Messaging;
+using TakeoutSaaS.Application.Messaging.Abstractions;
+using TakeoutSaaS.Application.Messaging.Events;
+using TakeoutSaaS.Domain.Orders.Entities;
+using TakeoutSaaS.Domain.Orders.Enums;
+using TakeoutSaaS.Domain.Orders.Repositories;
+using TakeoutSaaS.Domain.Payments.Entities;
+using TakeoutSaaS.Domain.Payments.Enums;
+using TakeoutSaaS.Domain.Payments.Repositories;
+using TakeoutSaaS.Shared.Abstractions.Constants;
+using TakeoutSaaS.Shared.Abstractions.Exceptions;
+using TakeoutSaaS.Shared.Abstractions.Ids;
+
+namespace TakeoutSaaS.Application.App.Payments.Handlers;
+
+///
+/// 支付回调处理器。
+///
+public sealed class ProcessPaymentCallbackCommandHandler(
+ IOrderRepository orderRepository,
+ IPaymentRepository paymentRepository,
+ IEventPublisher eventPublisher,
+ IIdGenerator idGenerator,
+ ILogger logger)
+ : IRequestHandler
+{
+ ///
+ public async Task Handle(ProcessPaymentCallbackCommand request, CancellationToken cancellationToken)
+ {
+ // 1. 通过 OrderNo 查找订单
+ var order = await orderRepository.FindByOrderNoAsync(request.OrderNo, request.TenantId, cancellationToken)
+ ?? throw new BusinessException(ErrorCodes.NotFound, "订单不存在");
+
+ // 2. 幂等检查:已支付则直接返回
+ if (order.PaymentStatus == PaymentStatus.Paid)
+ {
+ logger.LogWarning("订单 {OrderNo} 重复支付回调,已忽略", request.OrderNo);
+ return true;
+ }
+
+ // 3. 校验订单状态
+ if (order.Status != OrderStatus.PendingPayment)
+ {
+ throw new BusinessException(ErrorCodes.BadRequest, $"订单状态 {order.Status} 不允许支付");
+ }
+
+ // 4. 创建支付记录
+ var paymentRecord = new PaymentRecord
+ {
+ Id = idGenerator.NextId(),
+ TenantId = order.TenantId,
+ OrderId = order.Id,
+ Method = request.Method,
+ Status = PaymentStatus.Paid,
+ Amount = request.Amount,
+ ChannelTransactionId = request.ChannelTransactionId,
+ PaidAt = request.PaidAt,
+ Payload = request.RawPayload
+ };
+ await paymentRepository.AddPaymentAsync(paymentRecord, cancellationToken);
+
+ // 5. 更新订单支付状态与订单状态
+ order.PaymentStatus = PaymentStatus.Paid;
+ order.PaidAmount = request.Amount;
+ order.PaidAt = request.PaidAt;
+ order.Status = OrderStatus.AwaitingPreparation;
+ await orderRepository.UpdateOrderAsync(order, cancellationToken);
+
+ // 6. 写入状态流转记录
+ var history = new OrderStatusHistory
+ {
+ Id = idGenerator.NextId(),
+ OrderId = order.Id,
+ TenantId = order.TenantId,
+ Status = OrderStatus.AwaitingPreparation,
+ Notes = "支付成功,进入待接单",
+ OccurredAt = request.PaidAt
+ };
+ await orderRepository.AddStatusHistoryAsync(history, cancellationToken);
+
+ // 7. 持久化
+ await orderRepository.SaveChangesAsync(cancellationToken);
+
+ // 8. 构建商品摘要
+ var items = await orderRepository.GetItemsAsync(order.Id, order.TenantId, cancellationToken);
+ var itemsSummary = items.Count > 0
+ ? string.Join("、", items.Take(3).Select(x => x.ProductName))
+ + (items.Count > 3 ? $" 等{items.Count}件" : string.Empty)
+ : string.Empty;
+
+ // 9. 发布支付成功事件
+ await eventPublisher.PublishAsync(EventRoutingKeys.PaymentSucceeded, new PaymentSucceededEvent
+ {
+ OrderId = order.Id,
+ PaymentNo = paymentRecord.TradeNo ?? string.Empty,
+ Amount = request.Amount,
+ TenantId = order.TenantId,
+ StoreId = order.StoreId,
+ OrderNo = order.OrderNo,
+ Channel = (int)order.Channel,
+ DeliveryType = (int)order.DeliveryType,
+ CustomerName = order.CustomerName,
+ ItemsSummary = itemsSummary,
+ PaidAt = request.PaidAt
+ }, cancellationToken);
+
+ // 10. 发布订单状态变更事件(触发 SignalR 推送)
+ await eventPublisher.PublishAsync(EventRoutingKeys.OrderStatusChanged, new OrderStatusChangedEvent
+ {
+ OrderId = order.Id,
+ OrderNo = order.OrderNo,
+ TenantId = order.TenantId,
+ StoreId = order.StoreId,
+ OldStatus = (int)OrderStatus.PendingPayment,
+ NewStatus = (int)OrderStatus.AwaitingPreparation,
+ Channel = (int)order.Channel,
+ DeliveryType = (int)order.DeliveryType,
+ CustomerName = order.CustomerName,
+ ItemsSummary = itemsSummary,
+ PaidAmount = request.Amount,
+ OccurredAt = request.PaidAt
+ }, cancellationToken);
+
+ // 11. 记录日志
+ logger.LogInformation("支付回调成功 {OrderNo},金额 {Amount}", request.OrderNo, request.Amount);
+
+ return true;
+ }
+}
diff --git a/src/Application/TakeoutSaaS.Application/Messaging/Events/PaymentSucceededEvent.cs b/src/Application/TakeoutSaaS.Application/Messaging/Events/PaymentSucceededEvent.cs
index b0094f7..7a2dcfa 100644
--- a/src/Application/TakeoutSaaS.Application/Messaging/Events/PaymentSucceededEvent.cs
+++ b/src/Application/TakeoutSaaS.Application/Messaging/Events/PaymentSucceededEvent.cs
@@ -25,6 +25,36 @@ public sealed class PaymentSucceededEvent
///
public long TenantId { get; init; }
+ ///
+ /// 门店标识。
+ ///
+ public long StoreId { get; init; }
+
+ ///
+ /// 订单编号。
+ ///
+ public string OrderNo { get; init; } = string.Empty;
+
+ ///
+ /// 下单渠道。
+ ///
+ public int Channel { get; init; }
+
+ ///
+ /// 履约类型。
+ ///
+ public int DeliveryType { get; init; }
+
+ ///
+ /// 顾客姓名。
+ ///
+ public string? CustomerName { get; init; }
+
+ ///
+ /// 商品摘要。
+ ///
+ public string? ItemsSummary { get; init; }
+
///
/// 支付时间(UTC)。
///