Files
pos-system/services/wallet-service-net/src/WalletService.API/Application/Queries/Payments/GetPaymentQueryHandler.cs
Ho Ngoc Hai 8af86e9e89 feat: implement Phase 1 payment gateway, real-time SignalR, kitchen-inventory deduction, and order payment flow
- wallet-service: IPaymentGateway abstraction + VN Pay implementation (HMAC-SHA512, sandbox), Payment aggregate root, PaymentsController with create/callback/query endpoints
- order-service: PosHub SignalR hub with Redis backplane + MessagePack, strongly-typed clients, 3 group types (shop/kds/pos), integrated into Create/Pay/Complete/Cancel order handlers
- fnb-engine + inventory-service: Kitchen→Inventory auto-deduction via domain events, HTTP with Polly retry + circuit breaker, idempotency check, graceful degradation on insufficient stock
- order-service: Enhanced PayOrderCommand with 3 flows (cash/card/online), PaymentPending status, WalletServiceClient, CompleteOrderPaymentCommand for gateway callbacks
- POS frontend: Cash/Card/QR payment components wired to real backend, BFF proxy updated
- infra: Traefik routes for fnb-engine, inventory-service, and SignalR WebSocket hub
- ROADMAP.md: Updated with Phase 1 progress tracking

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-06 13:28:46 +07:00

100 lines
3.1 KiB
C#

namespace WalletService.API.Application.Queries.Payments;
using MediatR;
using WalletService.Domain.AggregatesModel.PaymentAggregate;
/// <summary>
/// EN: Handler for GetPaymentByOrderIdQuery.
/// VI: Handler cho GetPaymentByOrderIdQuery.
/// </summary>
public class GetPaymentByOrderIdQueryHandler : IRequestHandler<GetPaymentByOrderIdQuery, PaymentDto?>
{
private readonly IPaymentRepository _paymentRepository;
private readonly ILogger<GetPaymentByOrderIdQueryHandler> _logger;
public GetPaymentByOrderIdQueryHandler(
IPaymentRepository paymentRepository,
ILogger<GetPaymentByOrderIdQueryHandler> logger)
{
_paymentRepository = paymentRepository;
_logger = logger;
}
public async Task<PaymentDto?> Handle(
GetPaymentByOrderIdQuery request,
CancellationToken cancellationToken)
{
_logger.LogInformation(
"EN: Getting payment for order {OrderId} / " +
"VI: Lay thanh toan cho don hang {OrderId}",
request.OrderId);
var payment = await _paymentRepository.GetByOrderIdAsync(request.OrderId);
return payment == null ? null : MapToDto(payment);
}
private static PaymentDto MapToDto(Payment payment)
{
return new PaymentDto(
payment.Id,
payment.OrderId,
payment.Amount,
payment.Currency,
payment.GatewayName,
payment.TransactionId,
payment.PaymentUrl,
payment.Status.Name,
payment.ErrorCode,
payment.ErrorMessage,
payment.CreatedAt,
payment.CompletedAt);
}
}
/// <summary>
/// EN: Handler for GetPaymentByTransactionIdQuery.
/// VI: Handler cho GetPaymentByTransactionIdQuery.
/// </summary>
public class GetPaymentByTransactionIdQueryHandler : IRequestHandler<GetPaymentByTransactionIdQuery, PaymentDto?>
{
private readonly IPaymentRepository _paymentRepository;
private readonly ILogger<GetPaymentByTransactionIdQueryHandler> _logger;
public GetPaymentByTransactionIdQueryHandler(
IPaymentRepository paymentRepository,
ILogger<GetPaymentByTransactionIdQueryHandler> logger)
{
_paymentRepository = paymentRepository;
_logger = logger;
}
public async Task<PaymentDto?> Handle(
GetPaymentByTransactionIdQuery request,
CancellationToken cancellationToken)
{
_logger.LogInformation(
"EN: Getting payment by transaction ID {TransactionId} / " +
"VI: Lay thanh toan theo ma giao dich {TransactionId}",
request.TransactionId);
var payment = await _paymentRepository.GetByTransactionIdAsync(request.TransactionId);
if (payment == null) return null;
return new PaymentDto(
payment.Id,
payment.OrderId,
payment.Amount,
payment.Currency,
payment.GatewayName,
payment.TransactionId,
payment.PaymentUrl,
payment.Status.Name,
payment.ErrorCode,
payment.ErrorMessage,
payment.CreatedAt,
payment.CompletedAt);
}
}