diff --git a/src/CMSMicroservice.Application/PackageCQ/Commands/InitiateBasePackagePayment/InitiateBasePackagePaymentCommand.cs b/src/CMSMicroservice.Application/PackageCQ/Commands/InitiateBasePackagePayment/InitiateBasePackagePaymentCommand.cs deleted file mode 100644 index 29449f7..0000000 --- a/src/CMSMicroservice.Application/PackageCQ/Commands/InitiateBasePackagePayment/InitiateBasePackagePaymentCommand.cs +++ /dev/null @@ -1,43 +0,0 @@ -using MediatR; - -namespace CMSMicroservice.Application.PackageCQ.Commands.InitiateBasePackagePayment; - -/// -/// ثبت اولیه تراکنش و سفارش پکیج پایه (56 میلیون تومان) -/// این Command توسط BFF فراخوانی می‌شود قبل از ارسال کاربر به درگاه -/// -public record InitiateBasePackagePaymentCommand : IRequest -{ - /// - /// شناسه کاربر - /// - public long UserId { get; init; } -} - -public class InitiateBasePackagePaymentResponseDto -{ - /// - /// موفقیت عملیات - /// - public bool Success { get; set; } - - /// - /// پیام - /// - public string Message { get; set; } = string.Empty; - - /// - /// شناسه سفارش - /// - public long OrderId { get; set; } - - /// - /// شناسه تراکنش CMS - /// - public long TransactionId { get; set; } - - /// - /// مبلغ پکیج - /// - public long Amount { get; set; } -} diff --git a/src/CMSMicroservice.Application/PackageCQ/Commands/InitiateBasePackagePayment/InitiateBasePackagePaymentCommandHandler.cs b/src/CMSMicroservice.Application/PackageCQ/Commands/InitiateBasePackagePayment/InitiateBasePackagePaymentCommandHandler.cs deleted file mode 100644 index 0fde9b1..0000000 --- a/src/CMSMicroservice.Application/PackageCQ/Commands/InitiateBasePackagePayment/InitiateBasePackagePaymentCommandHandler.cs +++ /dev/null @@ -1,171 +0,0 @@ -using CMSMicroservice.Application.Common.Exceptions; -using CMSMicroservice.Application.Common.Interfaces; -using CMSMicroservice.Domain.Entities; -using CMSMicroservice.Domain.Enums; -using CMSMicroservice.Domain.Common; -using MediatR; -using Microsoft.EntityFrameworkCore; -using Microsoft.Extensions.Logging; -using ValidationException = FluentValidation.ValidationException; - -namespace CMSMicroservice.Application.PackageCQ.Commands.InitiateBasePackagePayment; - -/// -/// Handler برای ثبت اولیه تراکنش و سفارش پکیج پایه -/// این Handler فقط Transaction و Order را با وضعیت Pending ایجاد می‌کند -/// BFF سپس کاربر را به درگاه PYMS هدایت می‌کند -/// -public class InitiateBasePackagePaymentCommandHandler : IRequestHandler -{ - private readonly IApplicationDbContext _context; - private readonly ILogger _logger; - - // شناسه پکیج پایه از دیتابیس خوانده می‌شود (Package.IsBasePackage) - - public InitiateBasePackagePaymentCommandHandler( - IApplicationDbContext context, - ILogger logger) - { - _context = context; - _logger = logger; - } - - public async Task Handle( - InitiateBasePackagePaymentCommand request, - CancellationToken cancellationToken) - { - try - { - _logger.LogInformation( - "Initiating base package payment for UserId: {UserId}", - request.UserId); - - // 1. پیدا کردن کاربر - var user = await _context.Users - .FirstOrDefaultAsync(u => u.Id == request.UserId, cancellationToken); - - if (user == null) - { - _logger.LogWarning("User not found. UserId: {UserId}", request.UserId); - throw new NotFoundException(nameof(User), request.UserId); - } - - // 2. بررسی عدم خرید قبلی پکیج پایه - if (user.PackagePurchaseMethod != PackagePurchaseMethod.None) - { - _logger.LogWarning( - "User {UserId} has already purchased base package via {Method}", - request.UserId, - user.PackagePurchaseMethod); - - return new InitiateBasePackagePaymentResponseDto - { - Success = false, - Message = "شما قبلاً پکیج پایه را خریداری کرده‌اید." - }; - } - - // 2.5. بارگذاری پکیج پایه - var package = await _context.Packages - .FirstOrDefaultAsync(p => p.IsBasePackage && !p.IsDeleted, cancellationToken) - ?? throw new NotFoundException("پکیج پایه یافت نشد"); - - // 3. بررسی عدم وجود سفارش Pending - var pendingOrder = await _context.UserOrders - .FirstOrDefaultAsync(o => - o.UserId == request.UserId && - o.PaymentStatus == PaymentStatus.Pending && - o.PackageId == package.Id, - cancellationToken); - - if (pendingOrder != null) - { - _logger.LogWarning( - "User {UserId} has pending order {OrderId}", - request.UserId, - pendingOrder.Id); - - // برگرداندن سفارش موجود برای ادامه پرداخت - var existingTransaction = await _context.Transactions - .FirstOrDefaultAsync(t => t.Id == pendingOrder.TransactionId, cancellationToken); - - return new InitiateBasePackagePaymentResponseDto - { - Success = true, - Message = "سفارش قبلی در انتظار پرداخت یافت شد.", - OrderId = pendingOrder.Id, - TransactionId = existingTransaction?.Id ?? 0, - Amount = package.Price - }; - } - - // 4. پیدا کردن آدرس پیش‌فرض کاربر - 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}", request.UserId); - throw new ValidationException("لطفاً ابتدا یک آدرس برای خود ثبت کنید."); - } - - // 5. ایجاد Transaction با وضعیت Pending - var transaction = new Transaction - { - Amount = package.Price, - Description = $"خرید پکیج {package.Title} - کاربر #{user.Id}", - PaymentStatus = PaymentStatus.Pending, - Type = TransactionType.DepositIpg - }; - - _context.Transactions.Add(transaction); - await _context.SaveChangesAsync(cancellationToken); - - _logger.LogInformation( - "Created Transaction {TransactionId} with Pending status for UserId {UserId}", - transaction.Id, - request.UserId); - - // 6. ایجاد UserOrder با وضعیت Pending - 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, - TransactionId = transaction.Id - }; - - _context.UserOrders.Add(order); - await _context.SaveChangesAsync(cancellationToken); - - _logger.LogInformation( - "Created UserOrder {OrderId} for UserId {UserId}, TransactionId: {TransactionId}", - order.Id, - request.UserId, - transaction.Id); - - return new InitiateBasePackagePaymentResponseDto - { - Success = true, - Message = "تراکنش و سفارش با موفقیت ثبت شد.", - OrderId = order.Id, - TransactionId = transaction.Id, - Amount = package.Price - }; - } - catch (Exception ex) - { - _logger.LogError( - ex, - "Error in InitiateBasePackagePaymentCommand for UserId: {UserId}", - request.UserId); - throw; - } - } -} diff --git a/src/CMSMicroservice.Application/PackageCQ/Commands/InitiateBasePackagePayment/InitiateBasePackagePaymentCommandValidator.cs b/src/CMSMicroservice.Application/PackageCQ/Commands/InitiateBasePackagePayment/InitiateBasePackagePaymentCommandValidator.cs deleted file mode 100644 index 6639b26..0000000 --- a/src/CMSMicroservice.Application/PackageCQ/Commands/InitiateBasePackagePayment/InitiateBasePackagePaymentCommandValidator.cs +++ /dev/null @@ -1,13 +0,0 @@ -using FluentValidation; - -namespace CMSMicroservice.Application.PackageCQ.Commands.InitiateBasePackagePayment; - -public class InitiateBasePackagePaymentCommandValidator : AbstractValidator -{ - public InitiateBasePackagePaymentCommandValidator() - { - RuleFor(x => x.UserId) - .GreaterThan(0) - .WithMessage("شناسه کاربر معتبر نیست."); - } -} diff --git a/src/CMSMicroservice.Application/PackageCQ/Commands/PurchaseGoldenPackage/PurchaseGoldenPackageCommand.cs b/src/CMSMicroservice.Application/PackageCQ/Commands/PurchaseGoldenPackage/PurchaseGoldenPackageCommand.cs deleted file mode 100644 index e05e252..0000000 --- a/src/CMSMicroservice.Application/PackageCQ/Commands/PurchaseGoldenPackage/PurchaseGoldenPackageCommand.cs +++ /dev/null @@ -1,22 +0,0 @@ -using MediatR; - -namespace CMSMicroservice.Application.PackageCQ.Commands.PurchaseGoldenPackage; - -/// -/// خرید پکیج طلایی (شروع فرآیند پرداخت) -/// -public record PurchaseGoldenPackageCommand : IRequest -{ - public long UserId { get; init; } - public long PackageId { get; init; } - public string ReturnUrl { get; init; } = string.Empty; -} - -public class PurchaseGoldenPackageResponseDto -{ - public bool Success { get; set; } - public string Message { get; set; } = string.Empty; - public long OrderId { get; set; } - public string PaymentGatewayUrl { get; set; } = string.Empty; - public string TrackingCode { get; set; } = string.Empty; -} diff --git a/src/CMSMicroservice.Application/PackageCQ/Commands/PurchaseGoldenPackage/PurchaseGoldenPackageCommandHandler.cs b/src/CMSMicroservice.Application/PackageCQ/Commands/PurchaseGoldenPackage/PurchaseGoldenPackageCommandHandler.cs deleted file mode 100644 index 667daf2..0000000 --- a/src/CMSMicroservice.Application/PackageCQ/Commands/PurchaseGoldenPackage/PurchaseGoldenPackageCommandHandler.cs +++ /dev/null @@ -1,166 +0,0 @@ -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 -{ - private readonly IApplicationDbContext _context; - private readonly IPaymentGatewayService _paymentGateway; - private readonly ILogger _logger; - - public PurchaseGoldenPackageCommandHandler( - IApplicationDbContext context, - IPaymentGatewayService paymentGateway, - ILogger logger) - { - _context = context; - _paymentGateway = paymentGateway; - _logger = logger; - } - - public async Task 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; - } - } -} diff --git a/src/CMSMicroservice.Application/PackageCQ/Commands/PurchaseGoldenPackage/PurchaseGoldenPackageCommandValidator.cs b/src/CMSMicroservice.Application/PackageCQ/Commands/PurchaseGoldenPackage/PurchaseGoldenPackageCommandValidator.cs deleted file mode 100644 index b9f4411..0000000 --- a/src/CMSMicroservice.Application/PackageCQ/Commands/PurchaseGoldenPackage/PurchaseGoldenPackageCommandValidator.cs +++ /dev/null @@ -1,23 +0,0 @@ -using FluentValidation; - -namespace CMSMicroservice.Application.PackageCQ.Commands.PurchaseGoldenPackage; - -public class PurchaseGoldenPackageCommandValidator : AbstractValidator -{ - public PurchaseGoldenPackageCommandValidator() - { - RuleFor(x => x.UserId) - .GreaterThan(0) - .WithMessage("شناسه کاربر باید بزرگتر از 0 باشد"); - - RuleFor(x => x.PackageId) - .GreaterThan(0) - .WithMessage("شناسه پکیج باید بزرگتر از 0 باشد"); - - RuleFor(x => x.ReturnUrl) - .NotEmpty() - .WithMessage("آدرس بازگشت الزامی است") - .Must(url => Uri.TryCreate(url, UriKind.Absolute, out _)) - .WithMessage("آدرس بازگشت معتبر نیست"); - } -} diff --git a/src/CMSMicroservice.Application/PackageCQ/Commands/VerifyBasePackagePayment/VerifyBasePackagePaymentCommand.cs b/src/CMSMicroservice.Application/PackageCQ/Commands/VerifyBasePackagePayment/VerifyBasePackagePaymentCommand.cs deleted file mode 100644 index 9de3357..0000000 --- a/src/CMSMicroservice.Application/PackageCQ/Commands/VerifyBasePackagePayment/VerifyBasePackagePaymentCommand.cs +++ /dev/null @@ -1,72 +0,0 @@ -using MediatR; - -namespace CMSMicroservice.Application.PackageCQ.Commands.VerifyBasePackagePayment; - -/// -/// تکمیل یا رد پرداخت پکیج پایه (فراخوانی توسط BFF بعد از Callback) -/// -public record VerifyBasePackagePaymentCommand : IRequest -{ - /// - /// شناسه سفارش CMS - /// - public long OrderId { get; init; } - - /// - /// شناسه تراکنش CMS - /// - public long TransactionId { get; init; } - - /// - /// آیا پرداخت موفق بوده؟ (نتیجه Verify از BFF) - /// - public bool PaymentSuccess { get; init; } - - /// - /// کد پیگیری بانکی (در صورت موفقیت) - /// - public string? RefId { get; init; } - - /// - /// پیام از درگاه - /// - public string? Message { get; init; } -} - -public class VerifyBasePackagePaymentResponseDto -{ - /// - /// موفقیت عملیات - /// - public bool Success { get; set; } - - /// - /// پیام - /// - public string Message { get; set; } = string.Empty; - - /// - /// شناسه سفارش - /// - public long OrderId { get; set; } - - /// - /// شناسه تراکنش - /// - public long TransactionId { get; set; } - - /// - /// کد پیگیری بانکی - /// - public string? ReferenceCode { get; set; } - - /// - /// موجودی کیف پول بعد از شارژ - /// - public long WalletBalance { get; set; } - - /// - /// موجودی تخفیف بعد از شارژ - /// - public long DiscountBalance { get; set; } -} diff --git a/src/CMSMicroservice.Application/PackageCQ/Commands/VerifyBasePackagePayment/VerifyBasePackagePaymentCommandHandler.cs b/src/CMSMicroservice.Application/PackageCQ/Commands/VerifyBasePackagePayment/VerifyBasePackagePaymentCommandHandler.cs deleted file mode 100644 index 7708c9f..0000000 --- a/src/CMSMicroservice.Application/PackageCQ/Commands/VerifyBasePackagePayment/VerifyBasePackagePaymentCommandHandler.cs +++ /dev/null @@ -1,233 +0,0 @@ -using CMSMicroservice.Application.Common.Exceptions; -using CMSMicroservice.Application.Common.Interfaces; -using CMSMicroservice.Domain.Entities; -using CMSMicroservice.Domain.Enums; -using CMSMicroservice.Domain.Common; -using MediatR; -using Microsoft.EntityFrameworkCore; -using Microsoft.Extensions.Logging; - -namespace CMSMicroservice.Application.PackageCQ.Commands.VerifyBasePackagePayment; - -/// -/// Handler برای تکمیل یا رد پرداخت پکیج پایه -/// فراخوانی توسط BFF بعد از اینکه نتیجه Verify از PYMS مشخص شد -/// -/// اگر PaymentSuccess = true: -/// - شارژ کیف پول (Balance + DiscountBalance) -/// - ثبت لاگ تغییر کیف پول -/// - به‌روزرسانی سفارش و کاربر -/// -/// اگر PaymentSuccess = false: -/// - فقط آپدیت وضعیت تراکنش و سفارش به Reject -/// -public class VerifyBasePackagePaymentCommandHandler : IRequestHandler -{ - private readonly IApplicationDbContext _context; - private readonly ILogger _logger; - - public VerifyBasePackagePaymentCommandHandler( - IApplicationDbContext context, - ILogger logger) - { - _context = context; - _logger = logger; - } - - public async Task Handle( - VerifyBasePackagePaymentCommand request, - CancellationToken cancellationToken) - { - try - { - _logger.LogInformation( - "Processing base package payment result. OrderId: {OrderId}, TransactionId: {TransactionId}, PaymentSuccess: {PaymentSuccess}", - request.OrderId, - request.TransactionId, - request.PaymentSuccess); - - // 1. پیدا کردن سفارش با کاربر - var order = await _context.UserOrders - .Include(o => o.User) - .FirstOrDefaultAsync(o => o.Id == request.OrderId, cancellationToken); - - if (order == null) - { - _logger.LogWarning("Order not found. OrderId: {OrderId}", request.OrderId); - throw new NotFoundException(nameof(UserOrder), request.OrderId); - } - - // 2. پیدا کردن تراکنش - var transaction = await _context.Transactions - .FirstOrDefaultAsync(t => t.Id == request.TransactionId, cancellationToken); - - if (transaction == null) - { - _logger.LogWarning("Transaction not found. TransactionId: {TransactionId}", request.TransactionId); - throw new NotFoundException(nameof(Transaction), request.TransactionId); - } - - // 3. بررسی Idempotency - اگر قبلاً تایید شده باشد - if (order.PaymentStatus == PaymentStatus.Success) - { - _logger.LogInformation( - "Order {OrderId} already verified successfully", - request.OrderId); - - var existingWallet = await _context.UserWallets - .FirstOrDefaultAsync(w => w.UserId == order.UserId, cancellationToken); - - return new VerifyBasePackagePaymentResponseDto - { - Success = true, - Message = "پرداخت قبلاً با موفقیت تایید شده است.", - OrderId = order.Id, - TransactionId = transaction.Id, - ReferenceCode = transaction.RefId, - WalletBalance = existingWallet?.Balance ?? 0, - DiscountBalance = existingWallet?.DiscountBalance ?? 0 - }; - } - - // 4. اگر پرداخت ناموفق بود - فقط وضعیت را Reject می‌کنیم - if (!request.PaymentSuccess) - { - _logger.LogWarning( - "Payment failed. OrderId: {OrderId}, Message: {Message}", - request.OrderId, - request.Message); - - transaction.PaymentStatus = PaymentStatus.Reject; - order.PaymentStatus = PaymentStatus.Reject; - await _context.SaveChangesAsync(cancellationToken); - - return new VerifyBasePackagePaymentResponseDto - { - Success = false, - Message = request.Message ?? "پرداخت ناموفق بود.", - OrderId = order.Id, - TransactionId = transaction.Id - }; - } - - // 5. پرداخت موفق - پیدا کردن یا ایجاد کیف پول - var userWallet = await _context.UserWallets - .FirstOrDefaultAsync(w => w.UserId == order.UserId, cancellationToken); - - if (userWallet == null) - { - _logger.LogInformation("Creating new wallet for UserId: {UserId}", order.UserId); - userWallet = new UserWallet - { - UserId = order.UserId, - Balance = 0, - DiscountBalance = 0, - NetworkBalance = 0 - }; - _context.UserWallets.Add(userWallet); - await _context.SaveChangesAsync(cancellationToken); - } - - // 5.5. بارگذاری پکیج از سفارش - var package = order.PackageId.HasValue - ? await _context.Packages.FirstOrDefaultAsync(p => p.Id == order.PackageId.Value, cancellationToken) - : await _context.Packages.FirstOrDefaultAsync(p => p.IsBasePackage && !p.IsDeleted, cancellationToken); - - if (package == null) - throw new NotFoundException("پکیج یافت نشد"); - - // 6. شارژ کیف پول (هم Balance و هم DiscountBalance) - var oldBalance = userWallet.Balance; - var oldDiscountBalance = userWallet.DiscountBalance; - - userWallet.Balance += package.Price; - userWallet.DiscountBalance += (long)(package.Price * package.DiscountMultiplier); - - _logger.LogInformation( - "Charging wallet for user {UserId}. Balance: {OldBalance} -> {NewBalance}, DiscountBalance: {OldDiscount} -> {NewDiscount}", - order.UserId, - oldBalance, - userWallet.Balance, - oldDiscountBalance, - userWallet.DiscountBalance); - - // 7. به‌روزرسانی Transaction - transaction.PaymentStatus = PaymentStatus.Success; - transaction.PaymentDate = DateTime.Now; - transaction.RefId = request.RefId; - - // 8. ثبت لاگ تغییر کیف پول - var changeLog = new UserWalletChangeLog - { - WalletId = userWallet.Id, - CurrentBalance = userWallet.Balance, - ChangeValue = package.Price, - CurrentNetworkBalance = userWallet.NetworkBalance, - ChangeNerworkValue = 0, - CurrentDiscountBalance = userWallet.DiscountBalance, - ChangeDiscountValue = (long)(package.Price * package.DiscountMultiplier), - IsIncrease = true, - RefrenceId = transaction.Id - }; - - await _context.UserWalletChangeLogs.AddAsync(changeLog, cancellationToken); - - // 9. ثبت UserPackagePurchase - if (order.PackageId.HasValue) - { - var packagePurchase = new UserPackagePurchase - { - UserId = order.UserId, - PackageId = order.PackageId.Value, - PurchaseMethod = PackagePurchaseMethod.DirectPurchase, - PurchasedAt = DateTime.Now, - Amount = package.Price, - OrderId = order.Id, - TransactionId = transaction.Id - }; - await _context.UserPackagePurchases.AddAsync(packagePurchase, cancellationToken); - - _logger.LogInformation( - "Created UserPackagePurchase for UserId {UserId}, PackageId {PackageId}", - order.UserId, order.PackageId.Value); - } - - // 10. به‌روزرسانی Order - order.TransactionId = transaction.Id; - order.PaymentStatus = PaymentStatus.Success; - order.PaymentDate = DateTime.Now; - order.PaymentMethod = PaymentMethod.IPG; - - // 11. به‌روزرسانی User - order.User.PackagePurchaseMethod = PackagePurchaseMethod.DirectPurchase; - - await _context.SaveChangesAsync(cancellationToken); - - _logger.LogInformation( - "Base package payment completed successfully. OrderId: {OrderId}, UserId: {UserId}, TransactionId: {TransactionId}, RefId: {RefId}", - order.Id, - order.UserId, - transaction.Id, - request.RefId); - - return new VerifyBasePackagePaymentResponseDto - { - Success = true, - Message = "پرداخت با موفقیت تایید شد. کیف پول شما شارژ گردید.", - OrderId = order.Id, - TransactionId = transaction.Id, - ReferenceCode = request.RefId, - WalletBalance = userWallet.Balance, - DiscountBalance = userWallet.DiscountBalance - }; - } - catch (Exception ex) - { - _logger.LogError( - ex, - "Error in VerifyBasePackagePaymentCommand. OrderId: {OrderId}", - request.OrderId); - throw; - } - } -} diff --git a/src/CMSMicroservice.Application/PackageCQ/Commands/VerifyBasePackagePayment/VerifyBasePackagePaymentCommandValidator.cs b/src/CMSMicroservice.Application/PackageCQ/Commands/VerifyBasePackagePayment/VerifyBasePackagePaymentCommandValidator.cs deleted file mode 100644 index 52e03a3..0000000 --- a/src/CMSMicroservice.Application/PackageCQ/Commands/VerifyBasePackagePayment/VerifyBasePackagePaymentCommandValidator.cs +++ /dev/null @@ -1,17 +0,0 @@ -using FluentValidation; - -namespace CMSMicroservice.Application.PackageCQ.Commands.VerifyBasePackagePayment; - -public class VerifyBasePackagePaymentCommandValidator : AbstractValidator -{ - public VerifyBasePackagePaymentCommandValidator() - { - RuleFor(x => x.OrderId) - .GreaterThan(0) - .WithMessage("شناسه سفارش معتبر نیست."); - - RuleFor(x => x.TransactionId) - .GreaterThan(0) - .WithMessage("شناسه تراکنش معتبر نیست."); - } -} diff --git a/src/CMSMicroservice.Application/PackageCQ/Commands/VerifyGoldenPackagePurchase/VerifyGoldenPackagePurchaseCommand.cs b/src/CMSMicroservice.Application/PackageCQ/Commands/VerifyGoldenPackagePurchase/VerifyGoldenPackagePurchaseCommand.cs deleted file mode 100644 index d2ec876..0000000 --- a/src/CMSMicroservice.Application/PackageCQ/Commands/VerifyGoldenPackagePurchase/VerifyGoldenPackagePurchaseCommand.cs +++ /dev/null @@ -1,23 +0,0 @@ -using MediatR; - -namespace CMSMicroservice.Application.PackageCQ.Commands.VerifyGoldenPackagePurchase; - -/// -/// تایید پرداخت پکیج طلایی (پس از بازگشت از درگاه) -/// -public record VerifyGoldenPackagePurchaseCommand : IRequest -{ - public long OrderId { get; init; } - public string Authority { get; init; } = string.Empty; - public string Status { get; init; } = string.Empty; // OK یا NOK -} - -public class VerifyGoldenPackagePurchaseResponseDto -{ - public bool Success { get; set; } - public string Message { get; set; } = string.Empty; - public long OrderId { get; set; } - public long TransactionId { get; set; } - public string ReferenceCode { get; set; } = string.Empty; - public long WalletBalance { get; set; } -} diff --git a/src/CMSMicroservice.Application/PackageCQ/Commands/VerifyGoldenPackagePurchase/VerifyGoldenPackagePurchaseCommandHandler.cs b/src/CMSMicroservice.Application/PackageCQ/Commands/VerifyGoldenPackagePurchase/VerifyGoldenPackagePurchaseCommandHandler.cs deleted file mode 100644 index 08aa7ba..0000000 --- a/src/CMSMicroservice.Application/PackageCQ/Commands/VerifyGoldenPackagePurchase/VerifyGoldenPackagePurchaseCommandHandler.cs +++ /dev/null @@ -1,235 +0,0 @@ -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.VerifyGoldenPackagePurchase; - -public class VerifyGoldenPackagePurchaseCommandHandler : IRequestHandler -{ - private readonly IApplicationDbContext _context; - private readonly IPaymentGatewayService _paymentGateway; - private readonly ILogger _logger; - - public VerifyGoldenPackagePurchaseCommandHandler( - IApplicationDbContext context, - IPaymentGatewayService paymentGateway, - ILogger logger) - { - _context = context; - _paymentGateway = paymentGateway; - _logger = logger; - } - - public async Task Handle(VerifyGoldenPackagePurchaseCommand request, CancellationToken cancellationToken) - { - try - { - _logger.LogInformation( - "Verifying golden package purchase. OrderId: {OrderId}, Authority: {Authority}, Status: {Status}", - request.OrderId, - request.Authority, - request.Status); - - // 1. اگر پرداخت از سمت درگاه موفق گزارش نشده باشد - if (!string.Equals(request.Status, "OK", StringComparison.OrdinalIgnoreCase)) - { - var pendingOrder = await _context.UserOrders - .FirstOrDefaultAsync(o => o.Id == request.OrderId, cancellationToken); - - if (pendingOrder != null && pendingOrder.PaymentStatus == PaymentStatus.Pending) - { - pendingOrder.PaymentStatus = PaymentStatus.Reject; - await _context.SaveChangesAsync(cancellationToken); - } - - throw new ValidationException("پرداخت توسط کاربر لغو شد."); - } - - // 2. پیدا کردن سفارش به همراه کاربر - var order = await _context.UserOrders - .Include(o => o.User) - .FirstOrDefaultAsync(o => o.Id == request.OrderId, cancellationToken); - - if (order == null) - { - _logger.LogWarning("Golden package order not found. OrderId: {OrderId}", request.OrderId); - throw new NotFoundException(nameof(UserOrder), request.OrderId); - } - - // اگر قبلاً با موفقیت پرداخت شده، پاسخ idempotent برگردانیم - if (order.PaymentStatus == PaymentStatus.Success && order.TransactionId.HasValue) - { - var existingWallet = await _context.UserWallets - .FirstOrDefaultAsync(w => w.UserId == order.UserId, cancellationToken); - - var existingTransaction = await _context.Transactions - .FirstOrDefaultAsync(t => t.Id == order.TransactionId.Value, cancellationToken); - - return new VerifyGoldenPackagePurchaseResponseDto - { - Success = true, - Message = "پرداخت قبلاً با موفقیت تایید شده است.", - OrderId = order.Id, - TransactionId = existingTransaction?.Id ?? order.TransactionId.Value, - ReferenceCode = existingTransaction?.RefId ?? string.Empty, - WalletBalance = existingWallet?.Balance ?? 0 - }; - } - - // 3. Verify با درگاه پرداخت - var verifyResult = await _paymentGateway.VerifyPaymentAsync( - request.Authority, - request.Authority, - cancellationToken); - - if (!verifyResult.IsSuccess) - { - _logger.LogWarning( - "Golden package payment verification failed. OrderId: {OrderId}, Message: {Message}", - request.OrderId, - verifyResult.Message); - - order.PaymentStatus = PaymentStatus.Reject; - await _context.SaveChangesAsync(cancellationToken); - - throw new ValidationException($"تراکنش ناموفق: {verifyResult.Message}"); - } - - // 4. بارگذاری پکیج برای ضریب تخفیف - var package = order.PackageId.HasValue - ? await _context.Packages.FirstOrDefaultAsync(p => p.Id == order.PackageId.Value, cancellationToken) - : await _context.Packages.FirstOrDefaultAsync(p => p.IsBasePackage && !p.IsDeleted, cancellationToken); - - var discountMultiplier = package?.DiscountMultiplier ?? 2.0m; - - // 5. شارژ کیف پول (Balance فقط طبق سناریوی پکیج) - var wallet = await _context.UserWallets - .FirstOrDefaultAsync(w => w.UserId == order.UserId, cancellationToken); - - if (wallet == null) - { - _logger.LogError("Wallet not found for UserId: {UserId}", order.UserId); - throw new NotFoundException($"کیف پول کاربر با شناسه {order.UserId} یافت نشد"); - } - - var oldBalance = wallet.Balance; - wallet.Balance += order.Amount; - - // شارژ DiscountBalance (موجودی اعتباری) — ضریب تخفیف از پکیج - var oldDiscountBalance = wallet.DiscountBalance; - var discountAmount = (long)(order.Amount * discountMultiplier); - wallet.DiscountBalance += discountAmount; - - _logger.LogInformation( - "Charging wallet for user {UserId}. Balance: {OldBalance} -> {NewBalance}, DiscountBalance: {OldDiscount} -> {NewDiscount}", - order.UserId, - oldBalance, - wallet.Balance, - oldDiscountBalance, - wallet.DiscountBalance); - - // 5. ثبت Transaction - var transaction = new Transaction - { - Amount = order.Amount, - Description = $"خرید پکیج از درگاه - سفارش #{order.Id}", - PaymentStatus = PaymentStatus.Success, - PaymentDate = DateTime.Now, - RefId = verifyResult.RefId, - Type = TransactionType.DepositIpg - }; - - _context.Transactions.Add(transaction); - await _context.SaveChangesAsync(cancellationToken); - - // 6. ثبت لاگ تغییر Balance - var balanceLog = new UserWalletChangeLog - { - WalletId = wallet.Id, - CurrentBalance = wallet.Balance, - ChangeValue = order.Amount, - CurrentNetworkBalance = wallet.NetworkBalance, - ChangeNerworkValue = 0, - CurrentDiscountBalance = oldDiscountBalance, - ChangeDiscountValue = 0, - IsIncrease = true, - RefrenceId = transaction.Id - }; - await _context.UserWalletChangeLogs.AddAsync(balanceLog, cancellationToken); - - // 7. ثبت لاگ تغییر DiscountBalance - var discountLog = new UserWalletChangeLog - { - WalletId = wallet.Id, - CurrentBalance = wallet.Balance, - ChangeValue = 0, - CurrentNetworkBalance = wallet.NetworkBalance, - ChangeNerworkValue = 0, - CurrentDiscountBalance = wallet.DiscountBalance, - ChangeDiscountValue = discountAmount, - IsIncrease = true, - RefrenceId = transaction.Id - }; - await _context.UserWalletChangeLogs.AddAsync(discountLog, cancellationToken); - - // 8. ثبت UserPackagePurchase - if (order.PackageId.HasValue) - { - var packagePurchase = new UserPackagePurchase - { - UserId = order.UserId, - PackageId = order.PackageId.Value, - PurchaseMethod = PackagePurchaseMethod.DirectPurchase, - PurchasedAt = DateTime.Now, - Amount = order.Amount, - OrderId = order.Id, - TransactionId = transaction.Id - }; - await _context.UserPackagePurchases.AddAsync(packagePurchase, cancellationToken); - - _logger.LogInformation( - "Created UserPackagePurchase for UserId {UserId}, PackageId {PackageId}", - order.UserId, order.PackageId.Value); - } - - // 9. به‌روزرسانی سفارش و کاربر - order.TransactionId = transaction.Id; - order.PaymentStatus = PaymentStatus.Success; - order.PaymentDate = DateTime.Now; - order.PaymentMethod = PaymentMethod.IPG; - order.User.PackagePurchaseMethod = PackagePurchaseMethod.DirectPurchase; - - await _context.SaveChangesAsync(cancellationToken); - - _logger.LogInformation( - "Golden package purchase verified successfully. OrderId: {OrderId}, UserId: {UserId}, TransactionId: {TransactionId}, RefId: {RefId}", - order.Id, - order.UserId, - transaction.Id, - verifyResult.RefId); - - return new VerifyGoldenPackagePurchaseResponseDto - { - Success = true, - Message = "پرداخت با موفقیت تایید شد. کیف پول شما شارژ گردید.", - OrderId = order.Id, - TransactionId = transaction.Id, - ReferenceCode = verifyResult.RefId, - WalletBalance = wallet.Balance - }; - } - catch (Exception ex) - { - _logger.LogError( - ex, - "Error in VerifyGoldenPackagePurchaseCommand. OrderId: {OrderId}", - request.OrderId); - throw; - } - } -} diff --git a/src/CMSMicroservice.Application/PackageCQ/Commands/VerifyGoldenPackagePurchase/VerifyGoldenPackagePurchaseCommandValidator.cs b/src/CMSMicroservice.Application/PackageCQ/Commands/VerifyGoldenPackagePurchase/VerifyGoldenPackagePurchaseCommandValidator.cs deleted file mode 100644 index 8442702..0000000 --- a/src/CMSMicroservice.Application/PackageCQ/Commands/VerifyGoldenPackagePurchase/VerifyGoldenPackagePurchaseCommandValidator.cs +++ /dev/null @@ -1,23 +0,0 @@ -using FluentValidation; - -namespace CMSMicroservice.Application.PackageCQ.Commands.VerifyGoldenPackagePurchase; - -public class VerifyGoldenPackagePurchaseCommandValidator : AbstractValidator -{ - public VerifyGoldenPackagePurchaseCommandValidator() - { - RuleFor(x => x.OrderId) - .GreaterThan(0) - .WithMessage("شناسه سفارش باید بزرگتر از 0 باشد"); - - RuleFor(x => x.Authority) - .NotEmpty() - .WithMessage("کد Authority الزامی است"); - - RuleFor(x => x.Status) - .NotEmpty() - .WithMessage("وضعیت پرداخت الزامی است") - .Must(s => s == "OK" || s == "NOK") - .WithMessage("وضعیت باید OK یا NOK باشد"); - } -} diff --git a/src/CMSMicroservice.WebApi/Common/Mappings/PackageProfile.cs b/src/CMSMicroservice.WebApi/Common/Mappings/PackageProfile.cs index 915d1c5..d8702dc 100644 --- a/src/CMSMicroservice.WebApi/Common/Mappings/PackageProfile.cs +++ b/src/CMSMicroservice.WebApi/Common/Mappings/PackageProfile.cs @@ -1,7 +1,3 @@ -using CMSMicroservice.Application.PackageCQ.Commands.PurchaseGoldenPackage; -using CMSMicroservice.Application.PackageCQ.Commands.VerifyGoldenPackagePurchase; -using CMSMicroservice.Application.PackageCQ.Commands.InitiateBasePackagePayment; -using CMSMicroservice.Application.PackageCQ.Commands.VerifyBasePackagePayment; using CMSMicroservice.Application.PackageCQ.Queries.GetUserPackageStatus; using CMSMicroservice.Protobuf.Protos.Package; using Google.Protobuf.WellKnownTypes; @@ -12,33 +8,6 @@ public class PackageProfile : IRegister { void IRegister.Register(TypeAdapterConfig config) { - // PurchaseGoldenPackage - config.NewConfig() - .Map(dest => dest.UserId, src => src.UserId) - .Map(dest => dest.PackageId, src => src.PackageId) - .Map(dest => dest.ReturnUrl, src => src.ReturnUrl); - - config.NewConfig() - .Map(dest => dest.Success, src => src.Success) - .Map(dest => dest.Message, src => src.Message) - .Map(dest => dest.OrderId, src => src.OrderId) - .Map(dest => dest.PaymentGatewayUrl, src => src.PaymentGatewayUrl) - .Map(dest => dest.TrackingCode, src => src.TrackingCode); - - // VerifyGoldenPackagePurchase - config.NewConfig() - .Map(dest => dest.OrderId, src => src.OrderId) - .Map(dest => dest.Authority, src => src.Authority) - .Map(dest => dest.Status, src => src.Status); - - config.NewConfig() - .Map(dest => dest.Success, src => src.Success) - .Map(dest => dest.Message, src => src.Message) - .Map(dest => dest.OrderId, src => src.OrderId) - .Map(dest => dest.TransactionId, src => src.TransactionId) - .Map(dest => dest.ReferenceCode, src => src.ReferenceCode) - .Map(dest => dest.WalletBalance, src => src.WalletBalance); - // GetUserPackageStatus config.NewConfig() .Map(dest => dest.UserId, src => src.UserId); @@ -55,34 +24,6 @@ public class PackageProfile : IRegister .Map(dest => dest.LastPurchaseDate, src => src.LastPurchaseDate.HasValue ? Timestamp.FromDateTime(src.LastPurchaseDate.Value.ToUniversalTime()) : null); - - // InitiateBasePackagePayment (56M پکیج پایه) - config.NewConfig() - .Map(dest => dest.UserId, src => src.UserId); - - config.NewConfig() - .Map(dest => dest.Success, src => src.Success) - .Map(dest => dest.Message, src => src.Message) - .Map(dest => dest.OrderId, src => src.OrderId) - .Map(dest => dest.TransactionId, src => src.TransactionId) - .Map(dest => dest.Amount, src => src.Amount); - - // VerifyBasePackagePayment (تأیید پرداخت پکیج پایه) - config.NewConfig() - .Map(dest => dest.OrderId, src => src.OrderId) - .Map(dest => dest.TransactionId, src => src.TransactionId) - .Map(dest => dest.PaymentSuccess, src => src.PaymentSuccess) - .Map(dest => dest.RefId, src => src.RefId) - .Map(dest => dest.Message, src => src.Message); - - config.NewConfig() - .Map(dest => dest.Success, src => src.Success) - .Map(dest => dest.Message, src => src.Message) - .Map(dest => dest.OrderId, src => src.OrderId) - .Map(dest => dest.TransactionId, src => src.TransactionId) - .Map(dest => dest.ReferenceCode, src => src.ReferenceCode) - .Map(dest => dest.WalletBalance, src => src.WalletBalance) - .Map(dest => dest.DiscountBalance, src => src.DiscountBalance); } } diff --git a/src/CMSMicroservice.WebApi/Services/PackageService.cs b/src/CMSMicroservice.WebApi/Services/PackageService.cs index d8e961a..b8dcf21 100644 --- a/src/CMSMicroservice.WebApi/Services/PackageService.cs +++ b/src/CMSMicroservice.WebApi/Services/PackageService.cs @@ -3,10 +3,6 @@ using CMSMicroservice.WebApi.Common.Services; using CMSMicroservice.Application.PackageCQ.Commands.CreateNewPackage; using CMSMicroservice.Application.PackageCQ.Commands.UpdatePackage; using CMSMicroservice.Application.PackageCQ.Commands.DeletePackage; -using CMSMicroservice.Application.PackageCQ.Commands.PurchaseGoldenPackage; -using CMSMicroservice.Application.PackageCQ.Commands.VerifyGoldenPackagePurchase; -using CMSMicroservice.Application.PackageCQ.Commands.InitiateBasePackagePayment; -using CMSMicroservice.Application.PackageCQ.Commands.VerifyBasePackagePayment; using CMSMicroservice.Application.PackageCQ.Queries.GetPackage; using CMSMicroservice.Application.PackageCQ.Queries.GetAllPackageByFilter; using CMSMicroservice.Application.PackageCQ.Queries.GetUserPackageStatus; @@ -72,32 +68,11 @@ public class PackageService : PackageContract.PackageContractBase return await _dispatchRequestToCQRS.Handle(request, context); } - public override async Task PurchaseGoldenPackage(PurchaseGoldenPackageRequest request, ServerCallContext context) - { - return await _dispatchRequestToCQRS.Handle(request, context); - } - - public override async Task VerifyGoldenPackagePurchase(VerifyGoldenPackagePurchaseRequest request, ServerCallContext context) - { - return await _dispatchRequestToCQRS.Handle(request, context); - } - public override async Task GetUserPackageStatus(GetUserPackageStatusRequest request, ServerCallContext context) { return await _dispatchRequestToCQRS.Handle(request, context); } - // Base Package Payment (56M) - public override async Task InitiateBasePackagePayment(InitiateBasePackagePaymentRequest request, ServerCallContext context) - { - return await _dispatchRequestToCQRS.Handle(request, context); - } - - public override async Task VerifyBasePackagePayment(VerifyBasePackagePaymentRequest request, ServerCallContext context) - { - return await _dispatchRequestToCQRS.Handle(request, context); - } - // ============= Customer-specific Method Implementations ============= public override async Task GetCustomerPackages(GetCustomerPackagesRequest request, ServerCallContext context)