feat(payment): add per-user in-memory lock for gateway operations
Build and Deploy to Kubernetes / build-and-deploy (push) Failing after 9m58s

Introduce IUserPaymentLock to serialize payment initiate and verify flows
per user, preventing concurrent duplicate gateway requests across services.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
masoodafar-web
2026-06-08 01:55:00 +03:30
parent f08d3ac78f
commit d22eb1617f
14 changed files with 543 additions and 66 deletions
@@ -1,3 +1,5 @@
using CMSMicroservice.Application.Common;
using CMSMicroservice.Application.Common.Exceptions;
using CMSMicroservice.Application.Common.Interfaces;
using CMSMicroservice.Application.Common.Services;
using CMSMicroservice.Domain.Entities.DiscountShop;
@@ -17,22 +19,32 @@ public class PlaceOrderCommandHandler : IRequestHandler<PlaceOrderCommand, Place
private readonly IPaymentGatewayService _paymentGateway;
private readonly IConfiguration _configuration;
private readonly ILogger<PlaceOrderCommandHandler> _logger;
private readonly IUserPaymentLock _paymentLock;
public PlaceOrderCommandHandler(
IApplicationDbContext context,
IInventoryService inventoryService,
IPaymentGatewayService paymentGateway,
IConfiguration configuration,
ILogger<PlaceOrderCommandHandler> logger)
ILogger<PlaceOrderCommandHandler> logger,
IUserPaymentLock paymentLock)
{
_context = context;
_inventoryService = inventoryService;
_paymentGateway = paymentGateway;
_configuration = configuration;
_logger = logger;
_paymentLock = paymentLock;
}
public async Task<PlaceOrderResponseDto> Handle(PlaceOrderCommand request, CancellationToken cancellationToken)
public Task<PlaceOrderResponseDto> Handle(PlaceOrderCommand request, CancellationToken cancellationToken) =>
_paymentLock.ExecuteAsync(
PaymentLockScopes.Initiate(request.UserId),
PaymentLockStrategy.FailFast,
ct => HandleCore(request, ct),
cancellationToken);
private async Task<PlaceOrderResponseDto> HandleCore(PlaceOrderCommand request, CancellationToken cancellationToken)
{
// Get user wallet
var userWallet = await _context.UserWallets