Files
CMS/src/CMSMicroservice.Application/WalletCQ/Commands/VerifyMagicWalletCharge/VerifyMagicWalletChargeCommandHandler.cs
T
masoodafar-web aee7b59fc1
Build and Deploy to Kubernetes / build-and-deploy (push) Successful in 10m16s
fix: Toman/Rial — system uses Toman everywhere, only ZarinPal gets Rial
Core change in ZarinPalPaymentService:
- InitiatePaymentAsync: Amount (Toman) × 10 → Rial for gateway
- VerifyPaymentWithAmountAsync: Amount (Toman) × 10 → Rial for verify
- Verify result: gateway Rial ÷ 10 → Toman back to system

Also fixed:
- Package.Price doc: ریال → تومان
- Error messages in 3 handlers: ریال → تومان
- ConfigurationService descriptions: ریال → تومان (7 entries)
- Notification SMS/email: ریال → تومان
- Doc comments in 5 command files: ریال → تومان
- PaymentCallback comments: ریال → تومان

14 files changed across Domain, Application, Infrastructure, WebApi
2026-02-27 21:03:27 +03:30

218 lines
9.1 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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<VerifyMagicWalletChargeCommand, bool>
{
private readonly IApplicationDbContext _context;
private readonly IPaymentGatewayService _paymentGateway;
private readonly ILogger<VerifyMagicWalletChargeCommandHandler> _logger;
public VerifyMagicWalletChargeCommandHandler(
IApplicationDbContext context,
IPaymentGatewayService paymentGateway,
ILogger<VerifyMagicWalletChargeCommandHandler> logger)
{
_context = context;
_paymentGateway = paymentGateway;
_logger = logger;
}
public async Task<bool> Handle(
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;
}
}
}