using CMSMicroservice.Application.Common; 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.Logging; namespace CMSMicroservice.Application.WalletCQ.Commands.VerifyMagicWalletCharge; public class VerifyMagicWalletChargeCommandHandler : IRequestHandler { private readonly IApplicationDbContext _context; private readonly IPaymentGatewayService _paymentGateway; private readonly ILogger _logger; private readonly IUserPaymentLock _paymentLock; public VerifyMagicWalletChargeCommandHandler( IApplicationDbContext context, IPaymentGatewayService paymentGateway, ILogger logger, IUserPaymentLock paymentLock) { _context = context; _paymentGateway = paymentGateway; _logger = logger; _paymentLock = paymentLock; } public async Task Handle( VerifyMagicWalletChargeCommand request, CancellationToken cancellationToken) { var paymentTx = await _context.PaymentTransactions .AsNoTracking() .FirstOrDefaultAsync(pt => pt.Authority == request.Authority, cancellationToken); if (paymentTx == null) throw new NotFoundException("تراکنش پرداخت یافت نشد"); if (!paymentTx.UserId.HasValue) throw new BadRequestException("شناسه کاربر در تراکنش پرداخت یافت نشد"); return await _paymentLock.ExecuteAsync( PaymentLockScopes.Verify(paymentTx.UserId.Value, request.Authority), PaymentLockStrategy.WaitForRelease, ct => HandleCore(request, ct), cancellationToken); } private async Task HandleCore( VerifyMagicWalletChargeCommand request, CancellationToken cancellationToken) { try { _logger.LogInformation( "Verifying magic wallet charge. Authority: {Authority}, Status: {Status}", request.Authority, request.Status ); // 1. پیدا کردن PaymentTransaction از Authority var paymentTx = await _context.PaymentTransactions .FirstOrDefaultAsync(pt => pt.Authority == request.Authority, cancellationToken); if (paymentTx == null) { _logger.LogError("PaymentTransaction not found for Authority: {Authority}", request.Authority); throw new NotFoundException("تراکنش پرداخت یافت نشد"); } if (paymentTx.PaymentStatus) { _logger.LogWarning("PaymentTransaction already verified: {Authority}", request.Authority); return true; // قبلاً تأیید شده } var userId = paymentTx.UserId ?? throw new BadRequestException("شناسه کاربر در تراکنش پرداخت یافت نشد"); var depositAmount = paymentTx.Amount; // مبلغ واقعی واریزی (تومان) // 2. بررسی وضعیت برگشتی از درگاه if (!string.Equals(request.Status, "OK", StringComparison.OrdinalIgnoreCase)) { _logger.LogWarning( "Magic charge cancelled by user. UserId: {UserId}, Authority: {Authority}", userId, request.Authority ); paymentTx.PaymentStatus = false; paymentTx.VerificationStatusMessage = "پرداخت توسط کاربر لغو شد"; await _context.SaveChangesAsync(cancellationToken); throw new BadRequestException("پرداخت توسط کاربر لغو شد"); } // 3. Verify با درگاه (مبلغ به تومان — تبدیل به ریال در ZarinPalService) var verifyResult = await _paymentGateway.VerifyPaymentAsync( request.Authority, request.Status, depositAmount, cancellationToken ); // آپدیت PaymentTransaction paymentTx.PaymentStatus = verifyResult.IsSuccess; paymentTx.VerificationStatusCode = verifyResult.VerificationCode; paymentTx.VerificationStatusMessage = verifyResult.Message; paymentTx.CardPan = verifyResult.CardPan; paymentTx.CardHash = verifyResult.CardHash; paymentTx.RefId = verifyResult.TrackingCode; if (!verifyResult.IsSuccess) { _logger.LogWarning( "Magic wallet charge verification failed for UserId {UserId}: {Message}", userId, verifyResult.Message ); await _context.SaveChangesAsync(cancellationToken); throw new BadRequestException($"تراکنش ناموفق: {verifyResult.Message}"); } // 4. بررسی کیف‌پول و حالت جادویی var wallet = await _context.UserWallets .FirstOrDefaultAsync(w => w.UserId == userId, cancellationToken); if (wallet == null) { _logger.LogError("Wallet not found for UserId: {UserId}", userId); throw new NotFoundException("کیف پول کاربر یافت نشد"); } if (wallet.WalletMode != WalletMode.Magic) { _logger.LogError( "Wallet is not in Magic mode during verify. UserId: {UserId}, Mode: {Mode}", userId, wallet.WalletMode ); throw new BadRequestException("کیف‌پول در حالت جادویی نیست"); } // 4.5. بارگذاری پکیج کاربر برای ضریب جادویی var currentCycle = await _context.ClubMembershipCycles .FirstOrDefaultAsync(c => c.UserId == 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("پکیج یافت نشد"); // 5. محاسبه اعتبار var creditAmount = (long)(depositAmount * package.MagicWalletMultiplier); // مبلغ × 2.5 var bonusAmount = creditAmount - depositAmount; // بونوس = مبلغ × 1.5 // 6. ثبت تراکنش واریز واقعی (MagicWalletDeposit) var depositTransaction = new Transaction { Amount = depositAmount, Description = $"شارژ کیف‌پول جادویی - واریز واقعی - کاربر {userId}", PaymentStatus = PaymentStatus.Success, PaymentDate = DateTime.UtcNow, RefId = verifyResult.RefId, Type = TransactionType.MagicWalletDeposit }; _context.Transactions.Add(depositTransaction); await _context.SaveChangesAsync(cancellationToken); // 7. ثبت تراکنش بونوس (MagicWalletBonus) var bonusTransaction = new Transaction { Amount = bonusAmount, Description = $"شارژ کیف‌پول جادویی - بونوس ×{package.MagicWalletMultiplier - 1} - کاربر {userId}", PaymentStatus = PaymentStatus.Success, PaymentDate = DateTime.UtcNow, RefId = $"MAGIC_BONUS_{depositTransaction.Id}", Type = TransactionType.MagicWalletBonus }; _context.Transactions.Add(bonusTransaction); await _context.SaveChangesAsync(cancellationToken); // 8. شارژ Balance و آپدیت شمارنده‌ها wallet.Balance += creditAmount; wallet.MagicTotalDeposited += depositAmount; wallet.MagicTotalCredited += creditAmount; // 9. ثبت WalletHistory (اجباری) var walletLog = new UserWalletHistory { WalletId = wallet.Id, CurrentBalance = wallet.Balance, ChangeValue = creditAmount, CurrentNetworkBalance = wallet.NetworkBalance, ChangeNerworkValue = 0, CurrentDiscountBalance = wallet.DiscountBalance, ChangeDiscountValue = 0, IsIncrease = true, RefrenceId = depositTransaction.Id }; _context.UserWalletHistories.Add(walletLog); // 10. لینک PaymentTransaction به Transaction داخلی paymentTx.TransactionId = depositTransaction.Id; await _context.SaveChangesAsync(cancellationToken); _logger.LogInformation( "Magic wallet charged successfully. UserId: {UserId}, " + "Deposit: {Deposit}, Credit: {Credit} (×{Multiplier}), Bonus: {Bonus}, " + "TotalDeposited: {TotalDeposited}/{MaxDeposit}, NewBalance: {NewBalance}", userId, depositAmount, creditAmount, package.MagicWalletMultiplier, bonusAmount, wallet.MagicTotalDeposited, package.MagicWalletMaxDeposit, wallet.Balance ); return true; } catch (Exception ex) { _logger.LogError( ex, "Error in VerifyMagicWalletChargeCommand. Authority: {Authority}", request.Authority ); throw; } } }