0002a5a6f2
- CreateNewPackageCommand: Add 12 new Package fields (SortOrder, IsActive, IsBasePackage,
SupportsDayaPurchase, SupportsDirectPurchase, ActivationFee, DiscountMultiplier,
MagicWalletMultiplier, MaxBalancesPerLeg, MaxNetworkLevel, MagicWalletMaxDeposit,
MagicWalletMaxCredit) with sensible defaults
- GetPackageResponseDto: Add 12 new fields (Mapster auto-maps via ProjectToType)
- GetAllPackageByFilterResponseModel: Add 12 new fields (Mapster auto-maps)
- PurchaseGoldenPackage: Replace fragile Title string match ('طلایی'/'golden')
with proper IsDeleted/IsActive/SupportsDirectPurchase validation
- VerifyPackagePurchase: Replace hardcoded order.Amount*2 with
package.DiscountMultiplier from DB (fallback 2.0 for null Package)
167 lines
6.7 KiB
C#
167 lines
6.7 KiB
C#
using CMSMicroservice.Application.Common.Exceptions;
|
|
using CMSMicroservice.Application.Common.Interfaces;
|
|
using CMSMicroservice.Domain.Entities;
|
|
using CMSMicroservice.Domain.Enums;
|
|
using MediatR;
|
|
using Microsoft.EntityFrameworkCore;
|
|
using Microsoft.Extensions.Logging;
|
|
using ValidationException = FluentValidation.ValidationException;
|
|
|
|
namespace CMSMicroservice.Application.PackageCQ.Commands.PurchaseGoldenPackage;
|
|
|
|
public class PurchaseGoldenPackageCommandHandler : IRequestHandler<PurchaseGoldenPackageCommand, PurchaseGoldenPackageResponseDto>
|
|
{
|
|
private readonly IApplicationDbContext _context;
|
|
private readonly IPaymentGatewayService _paymentGateway;
|
|
private readonly ILogger<PurchaseGoldenPackageCommandHandler> _logger;
|
|
|
|
public PurchaseGoldenPackageCommandHandler(
|
|
IApplicationDbContext context,
|
|
IPaymentGatewayService paymentGateway,
|
|
ILogger<PurchaseGoldenPackageCommandHandler> logger)
|
|
{
|
|
_context = context;
|
|
_paymentGateway = paymentGateway;
|
|
_logger = logger;
|
|
}
|
|
|
|
public async Task<PurchaseGoldenPackageResponseDto> Handle(PurchaseGoldenPackageCommand request, CancellationToken cancellationToken)
|
|
{
|
|
try
|
|
{
|
|
_logger.LogInformation(
|
|
"Starting golden package purchase for UserId: {UserId}, PackageId: {PackageId}",
|
|
request.UserId,
|
|
request.PackageId);
|
|
|
|
// 1. پیدا کردن کاربر
|
|
var user = await _context.Users
|
|
.FirstOrDefaultAsync(u => u.Id == request.UserId, cancellationToken);
|
|
|
|
if (user == null)
|
|
{
|
|
_logger.LogWarning("User not found for golden package purchase. UserId: {UserId}", request.UserId);
|
|
throw new NotFoundException(nameof(User), request.UserId);
|
|
}
|
|
|
|
// 2. جلوگیری از خرید مجدد پکیج طلایی
|
|
if (user.PackagePurchaseMethod != PackagePurchaseMethod.None)
|
|
{
|
|
_logger.LogWarning(
|
|
"User {UserId} has already purchased golden package via {Method}",
|
|
request.UserId,
|
|
user.PackagePurchaseMethod);
|
|
|
|
throw new ValidationException("شما قبلاً پکیج طلایی را خریداری کردهاید.");
|
|
}
|
|
|
|
// 3. پیدا کردن پکیج
|
|
var package = await _context.Packages
|
|
.FirstOrDefaultAsync(p => p.Id == request.PackageId, cancellationToken);
|
|
|
|
if (package == null)
|
|
{
|
|
_logger.LogWarning("Golden package not found. PackageId: {PackageId}", request.PackageId);
|
|
throw new NotFoundException(nameof(Package), request.PackageId);
|
|
}
|
|
|
|
// اطمینان از فعال بودن و قابل خرید بودن پکیج
|
|
if (package.IsDeleted || !package.IsActive)
|
|
{
|
|
_logger.LogWarning(
|
|
"PackageId {PackageId} is not available. IsDeleted: {IsDeleted}, IsActive: {IsActive}",
|
|
request.PackageId,
|
|
package.IsDeleted,
|
|
package.IsActive);
|
|
|
|
throw new ValidationException("این پکیج در حال حاضر قابل خرید نیست.");
|
|
}
|
|
|
|
if (!package.SupportsDirectPurchase)
|
|
{
|
|
throw new ValidationException("این پکیج فقط از طریق وام دایا قابل خرید است.");
|
|
}
|
|
|
|
// 4. پیدا کردن آدرس پیشفرض کاربر (الزامی برای UserOrder)
|
|
var defaultAddress = await _context.UserAddresses
|
|
.Where(a => a.UserId == request.UserId)
|
|
.OrderByDescending(a => a.Created)
|
|
.FirstOrDefaultAsync(cancellationToken);
|
|
|
|
if (defaultAddress == null)
|
|
{
|
|
_logger.LogWarning("No address found for user {UserId} in golden package purchase", request.UserId);
|
|
throw new ValidationException("لطفاً ابتدا یک آدرس برای خود ثبت کنید.");
|
|
}
|
|
|
|
// 5. ایجاد سفارش
|
|
var order = new UserOrder
|
|
{
|
|
UserId = user.Id,
|
|
PackageId = package.Id,
|
|
Amount = package.Price,
|
|
PaymentStatus = PaymentStatus.Pending,
|
|
DeliveryStatus = DeliveryStatus.None,
|
|
UserAddressId = defaultAddress.Id,
|
|
PaymentMethod = PaymentMethod.IPG
|
|
};
|
|
|
|
_context.UserOrders.Add(order);
|
|
await _context.SaveChangesAsync(cancellationToken);
|
|
|
|
_logger.LogInformation(
|
|
"Created golden package UserOrder {OrderId} for UserId {UserId}, Amount: {Amount}",
|
|
order.Id,
|
|
request.UserId,
|
|
order.Amount);
|
|
|
|
// 6. شروع پرداخت با درگاه
|
|
var paymentRequest = new PaymentRequest
|
|
{
|
|
Amount = order.Amount,
|
|
UserId = user.Id,
|
|
Mobile = user.Mobile ?? string.Empty,
|
|
CallbackUrl = request.ReturnUrl,
|
|
Description = $"خرید پکیج طلایی - سفارش #{order.Id}"
|
|
};
|
|
|
|
var paymentResult = await _paymentGateway.InitiatePaymentAsync(paymentRequest, cancellationToken);
|
|
|
|
if (!paymentResult.IsSuccess)
|
|
{
|
|
_logger.LogError(
|
|
"Payment gateway initiation failed for golden package. OrderId {OrderId}: {ErrorMessage}",
|
|
order.Id,
|
|
paymentResult.ErrorMessage);
|
|
|
|
order.PaymentStatus = PaymentStatus.Reject;
|
|
await _context.SaveChangesAsync(cancellationToken);
|
|
|
|
throw new Exception($"خطا در ارتباط با درگاه پرداخت: {paymentResult.ErrorMessage}");
|
|
}
|
|
|
|
_logger.LogInformation(
|
|
"Golden package payment initiated successfully. OrderId: {OrderId}, RefId: {RefId}",
|
|
order.Id,
|
|
paymentResult.RefId);
|
|
|
|
return new PurchaseGoldenPackageResponseDto
|
|
{
|
|
Success = true,
|
|
Message = "لطفاً به درگاه پرداخت منتقل شوید.",
|
|
OrderId = order.Id,
|
|
PaymentGatewayUrl = paymentResult.GatewayUrl ?? string.Empty,
|
|
TrackingCode = paymentResult.RefId ?? string.Empty
|
|
};
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
_logger.LogError(
|
|
ex,
|
|
"Error in PurchaseGoldenPackageCommand for UserId: {UserId}",
|
|
request.UserId);
|
|
throw;
|
|
}
|
|
}
|
|
}
|