Files
CMS/src/CMSMicroservice.Application/WalletCQ/Commands/ChargeMagicWallet/ChargeMagicWalletCommandHandler.cs
T
masoodafar-web 1e7c17f090
Build and Deploy to Kubernetes / build-and-deploy (push) Successful in 8m22s
feat: Update payment processing and callback mechanisms
- Refactor ActivateClubMembershipCommandHandler to use UserPackagePurchases instead of UserOrders for package activation.
- Modify PlaceOrderCommandHandler to redirect payment callbacks to the front office.
- Update ChargeDiscountWalletCommandHandler and ChargeMagicWalletCommandHandler to direct payment callbacks to the front office.
- Remove PaymentCallbackController and integrate payment verification directly into DiscountOrderService and UserWalletService.
- Add CustomerVerifyDiscountOrderPayment RPC to DiscountOrderService for verifying discount order payments.
- Implement VerifyMagicCharge and VerifyDiscountCharge methods in UserWalletService for wallet charge verifications.
- Update appsettings.json to use local URLs for development.
- Remove appsettings.Development.json as it is no longer needed.
- Comment out history tracking methods in ClubMembershipCycle and Package classes.
- Update PackageService to automatically activate club membership after successful payment verification.
- Adjust UserService to generate JWT tokens with user details.
2026-02-28 03:27:46 +03:30

184 lines
7.3 KiB
C#

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<ChargeMagicWalletCommand, PaymentInitiateResult>
{
private readonly IApplicationDbContext _context;
private readonly IPaymentGatewayService _paymentGateway;
private readonly IConfiguration _configuration;
private readonly ILogger<ChargeMagicWalletCommandHandler> _logger;
public ChargeMagicWalletCommandHandler(
IApplicationDbContext context,
IPaymentGatewayService paymentGateway,
IConfiguration configuration,
ILogger<ChargeMagicWalletCommandHandler> logger)
{
_context = context;
_paymentGateway = paymentGateway;
_configuration = configuration;
_logger = logger;
}
public async Task<PaymentInitiateResult> 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;
}
}
}