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
@@ -13,6 +13,7 @@ using CMSMicroservice.Application.UserWalletCQ.Queries.GetCustomerWalletHistory;
using CMSMicroservice.Application.UserWalletCQ.Queries.GetCustomerWithdrawals;
using CMSMicroservice.Application.UserWalletCQ.Queries.GetCustomerWithdrawalSettings;
using CMSMicroservice.Application.Common.Interfaces;
using CMSMicroservice.Application.Common.Exceptions;
using CMSMicroservice.Domain.Common;
using CMSMicroservice.Domain.Enums;
using Grpc.Core;
@@ -184,18 +185,29 @@ public class UserWalletService : UserWalletContract.UserWalletContractBase
{
var userId = GetCurrentUserId();
var result = await _sender.Send(new ChargeMagicWalletCommand
try
{
UserId = userId,
Amount = request.Amount
}, context.CancellationToken);
var result = await _sender.Send(new ChargeMagicWalletCommand
{
UserId = userId,
Amount = request.Amount
}, context.CancellationToken);
return new InitiateMagicChargeResponse
return new InitiateMagicChargeResponse
{
IsSuccess = result.IsSuccess,
GatewayUrl = result.GatewayUrl ?? "",
ErrorMessage = result.ErrorMessage ?? ""
};
}
catch (PaymentInProgressException ex)
{
IsSuccess = result.IsSuccess,
GatewayUrl = result.GatewayUrl ?? "",
ErrorMessage = result.ErrorMessage ?? ""
};
return new InitiateMagicChargeResponse
{
IsSuccess = false,
ErrorMessage = ex.Message
};
}
}
// ============= Discount Wallet Methods =============
@@ -205,18 +217,29 @@ public class UserWalletService : UserWalletContract.UserWalletContractBase
{
var userId = GetCurrentUserId();
var result = await _sender.Send(new ChargeDiscountWalletCommand
try
{
UserId = userId,
Amount = request.Amount
}, context.CancellationToken);
var result = await _sender.Send(new ChargeDiscountWalletCommand
{
UserId = userId,
Amount = request.Amount
}, context.CancellationToken);
return new InitiateDiscountChargeResponse
return new InitiateDiscountChargeResponse
{
IsSuccess = result.IsSuccess,
GatewayUrl = result.GatewayUrl ?? "",
ErrorMessage = result.ErrorMessage ?? ""
};
}
catch (PaymentInProgressException ex)
{
IsSuccess = result.IsSuccess,
GatewayUrl = result.GatewayUrl ?? "",
ErrorMessage = result.ErrorMessage ?? ""
};
return new InitiateDiscountChargeResponse
{
IsSuccess = false,
ErrorMessage = ex.Message
};
}
}
// ============= Wallet Verify Methods =============