using CMSMicroservice.Application.Common.Exceptions; using CMSMicroservice.Application.Common.Interfaces; using CMSMicroservice.Domain.Common; using CMSMicroservice.Domain.Entities; using CMSMicroservice.Domain.Entities.Payment; using CMSMicroservice.Domain.Enums; using MediatR; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Logging; namespace CMSMicroservice.Application.WalletCQ.Commands.ChargeMagicWallet; public class ChargeMagicWalletCommandHandler : IRequestHandler { private readonly IApplicationDbContext _context; private readonly IPaymentGatewayService _paymentGateway; private readonly IConfiguration _configuration; private readonly ILogger _logger; public ChargeMagicWalletCommandHandler( IApplicationDbContext context, IPaymentGatewayService paymentGateway, IConfiguration configuration, ILogger logger) { _context = context; _paymentGateway = paymentGateway; _configuration = configuration; _logger = logger; } public async Task Handle( ChargeMagicWalletCommand request, CancellationToken cancellationToken) { try { _logger.LogInformation( "Initiating magic wallet charge for UserId: {UserId}, Amount: {Amount}", request.UserId, request.Amount ); // 1. بررسی وجود کاربر var user = await _context.Users .FirstOrDefaultAsync(u => u.Id == request.UserId, cancellationToken); if (user == null) { _logger.LogWarning("User not found: {UserId}", request.UserId); throw new NotFoundException(nameof(User), request.UserId); } // 2. بررسی وجود کیف‌پول var wallet = await _context.UserWallets .FirstOrDefaultAsync(w => w.UserId == request.UserId, cancellationToken); if (wallet == null) { _logger.LogError("Wallet not found for UserId: {UserId}", request.UserId); throw new NotFoundException("کیف پول کاربر یافت نشد"); } // 3. بررسی حالت جادویی if (wallet.WalletMode != WalletMode.Magic) { _logger.LogWarning( "User {UserId} wallet is not in Magic mode (current: {Mode})", request.UserId, wallet.WalletMode ); throw new BadRequestException( "شارژ جادویی فقط در حالت کیف‌پول جادویی امکان‌پذیر است" ); } // 3.5. بارگذاری پکیج کاربر برای سقف کیف‌پول جادویی var currentCycle = await _context.ClubMembershipCycles .FirstOrDefaultAsync(c => c.UserId == request.UserId && c.IsCurrentCycle, cancellationToken); var package = currentCycle != null ? await _context.Packages.FirstOrDefaultAsync(p => p.Id == currentCycle.PackageId, cancellationToken) : await _context.Packages.FirstOrDefaultAsync(p => p.IsBasePackage && !p.IsDeleted, cancellationToken); if (package == null) throw new NotFoundException("پکیج یافت نشد"); // 4. بررسی سقف واریزی (per-cycle) var remainingDeposit = package.MagicWalletMaxDeposit - wallet.MagicTotalDeposited; if (remainingDeposit <= 0) { _logger.LogWarning( "User {UserId} has reached magic deposit cap. TotalDeposited: {TotalDeposited}", request.UserId, wallet.MagicTotalDeposited ); throw new BadRequestException( "سقف شارژ جادویی در این دور پر شده است" ); } if (request.Amount > remainingDeposit) { _logger.LogWarning( "User {UserId} amount {Amount} exceeds remaining deposit cap {Remaining}", request.UserId, request.Amount, remainingDeposit ); throw new BadRequestException( $"مبلغ واریزی بیش از سقف باقیمانده است. حداکثر مبلغ قابل واریز: {remainingDeposit:N0} تومان" ); } // 5. ایجاد درخواست پرداخت — callback مستقیم به فرانت‌آفیس var frontOfficeBaseUrl = _configuration["FrontOfficeBaseUrl"] ?? "https://localhost:5268"; var callbackUrl = $"{frontOfficeBaseUrl}/profile/payment-callback?type=magic-wallet"; var paymentRequest = new PaymentRequest { Amount = request.Amount, UserId = user.Id, Mobile = user.Mobile ?? "", CallbackUrl = callbackUrl, Description = $"شارژ کیف‌پول جادویی - کاربر {user.Id}" }; var paymentResult = await _paymentGateway.InitiatePaymentAsync(paymentRequest); if (!paymentResult.IsSuccess) { _logger.LogError( "Payment gateway failed for magic charge UserId {UserId}: {ErrorMessage}", user.Id, paymentResult.ErrorMessage ); throw new Exception($"خطا در ارتباط با درگاه پرداخت: {paymentResult.ErrorMessage}"); } // 6. ثبت PaymentTransaction var paymentTx = new PaymentTransaction { GatewayProvider = _configuration["PaymentProvider"] ?? "zarinpal", MerchantId = _configuration["ZarinPal:MerchantId"] ?? "", Amount = request.Amount, CallbackUrl = callbackUrl, Description = $"شارژ کیف‌پول جادویی - کاربر {user.Id}", Mobile = user.Mobile, UserId = user.Id, RequestStatusCode = 100, RequestStatusMessage = "Success", Authority = paymentResult.RefId, PaymentStatus = false }; _context.PaymentTransactions.Add(paymentTx); await _context.SaveChangesAsync(cancellationToken); _logger.LogInformation( "Magic wallet charge initiated. UserId: {UserId}, Amount: {Amount}, " + "RemainingCap: {Remaining}, RefId: {RefId}, PaymentTxId: {PaymentTxId}", user.Id, request.Amount, remainingDeposit - request.Amount, paymentResult.RefId, paymentTx.Id ); return paymentResult; } catch (Exception ex) { _logger.LogError( ex, "Error in ChargeMagicWalletCommand for UserId: {UserId}", request.UserId ); throw; } } }