using CMSMicroservice.Application.Common.Interfaces; using CMSMicroservice.Application.Common.Services; using CMSMicroservice.Domain.Entities.DiscountShop; 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.DiscountShopCQ.Commands.PlaceOrder; public class PlaceOrderCommandHandler : IRequestHandler { private readonly IApplicationDbContext _context; private readonly IInventoryService _inventoryService; private readonly IPaymentGatewayService _paymentGateway; private readonly IConfiguration _configuration; private readonly ILogger _logger; public PlaceOrderCommandHandler( IApplicationDbContext context, IInventoryService inventoryService, IPaymentGatewayService paymentGateway, IConfiguration configuration, ILogger logger) { _context = context; _inventoryService = inventoryService; _paymentGateway = paymentGateway; _configuration = configuration; _logger = logger; } public async Task Handle(PlaceOrderCommand request, CancellationToken cancellationToken) { // Get user wallet var userWallet = await _context.UserWallets .FirstOrDefaultAsync(w => w.UserId == request.UserId, cancellationToken); if (userWallet == null) { return new PlaceOrderResponseDto { Success = false, Message = "کیف پول کاربر یافت نشد" }; } // Get cart items with products var cartItems = await _context.DiscountShoppingCarts .Where(c => c.UserId == request.UserId) .Include(c => c.Product) .ToListAsync(cancellationToken); if (!cartItems.Any()) { return new PlaceOrderResponseDto { Success = false, Message = "سبد خرید خالی است" }; } // Validate stock and calculate totals long totalAmount = 0; long totalDiscountAmount = 0; var orderDetails = new List(); foreach (var cartItem in cartItems) { var product = cartItem.Product; // Check stock if (product.RemainingCount < cartItem.Count) { return new PlaceOrderResponseDto { Success = false, Message = $"موجودی محصول '{product.Title}' کافی نیست" }; } // Check if product is active if (!product.IsActive) { return new PlaceOrderResponseDto { Success = false, Message = $"محصول '{product.Title}' غیرفعال است" }; } // Calculate discount for this product var itemTotal = product.Price * cartItem.Count; var maxDiscountForItem = (itemTotal * product.MaxDiscountPercent) / 100; totalAmount += itemTotal; totalDiscountAmount += maxDiscountForItem; orderDetails.Add(new DiscountOrderDetail { ProductId = product.Id, Count = cartItem.Count, UnitPrice = product.Price, DiscountPercentUsed = product.MaxDiscountPercent, DiscountAmount = maxDiscountForItem, FinalPrice = itemTotal - maxDiscountForItem }); } // Always apply maximum possible discount (100%) — user cannot choose less var maxDiscountBalanceUsable = totalDiscountAmount; // بررسی کافی بودن موجودی کیف پول اعتباری (تخفیفی) if (userWallet.DiscountBalance < maxDiscountBalanceUsable) { return new PlaceOrderResponseDto { Success = false, Message = $"موجودی کیف پول اعتباری کافی نیست. مبلغ مورد نیاز: {maxDiscountBalanceUsable:N0} ریال، موجودی شما: {userWallet.DiscountBalance:N0} ریال" }; } var actualDiscountBalanceUsed = maxDiscountBalanceUsable; _logger.LogInformation( "Discount auto-applied: max allowed={MaxAllowed}, wallet balance={WalletBalance}, used={Used}", maxDiscountBalanceUsable, userWallet.DiscountBalance, actualDiscountBalanceUsed); var gatewayAmountRequired = totalAmount - actualDiscountBalanceUsed; // Calculate VAT using centralized calculator var vatBreakdown = VatCalculator.CalculateBreakdown(gatewayAmountRequired); var vatAmount = vatBreakdown.VatAmount; var finalGatewayAmount = vatBreakdown.GrossAmount; // Create transaction for gateway payment var transaction = new Transaction { Amount = finalGatewayAmount, Description = $"خرید از فروشگاه تخفیف - مبلغ کل: {totalAmount:N0}، اعتبار تخفیف: {actualDiscountBalanceUsed:N0}", PaymentStatus = PaymentStatus.Pending, Type = TransactionType.DiscountShopPurchase }; _context.Transactions.Add(transaction); await _context.SaveChangesAsync(cancellationToken); // Create order var order = new DiscountOrder { UserId = request.UserId, TotalAmount = totalAmount, DiscountBalanceUsed = actualDiscountBalanceUsed, GatewayAmountPaid = finalGatewayAmount, VatAmount = vatAmount, PaymentStatus = PaymentStatus.Pending, TransactionId = transaction.Id, UserAddressId = request.UserAddressId, DeliveryStatus = DeliveryStatus.Pending }; _context.DiscountOrders.Add(order); await _context.SaveChangesAsync(cancellationToken); // Add order details foreach (var detail in orderDetails) { detail.DiscountOrderId = order.Id; } _context.DiscountOrderDetails.AddRange(orderDetails); // رزرو موجودی برای سفارش pending foreach (var cartItem in cartItems) { await _inventoryService.ReserveStockAsync( cartItem.ProductId, ProductType.DiscountProduct, cartItem.Count, order.Id, cancellationToken); } // Clear cart _context.DiscountShoppingCarts.RemoveRange(cartItems); await _context.SaveChangesAsync(cancellationToken); // اگر مبلغ درگاه > ۰ باشد، باید به درگاه پرداخت متصل شویم string? paymentUrl = null; if (finalGatewayAmount > 0) { try { // آدرس callback — زرین‌پال بعد از پرداخت کاربر را به اینجا هدایت می‌کند var cmsBaseUrl = _configuration["CmsBaseUrl"] ?? "https://localhost:32846"; var callbackUrl = $"{cmsBaseUrl}/api/payment/discount-order/callback?orderId={order.Id}"; // درخواست به درگاه var paymentResult = await _paymentGateway.InitiatePaymentAsync(new PaymentRequest { Amount = finalGatewayAmount, UserId = request.UserId, Description = $"فروشگاه تخفیفی - سفارش #{order.Id}", CallbackUrl = callbackUrl }, cancellationToken); if (paymentResult.IsSuccess && !string.IsNullOrEmpty(paymentResult.GatewayUrl)) { // ذخیره Authority/RefId در تراکنش برای verify بعدی transaction.RefId = paymentResult.RefId; // ثبت PaymentTransaction — جدول جدید با اطلاعات درگاه var paymentTx = new PaymentTransaction { GatewayProvider = _configuration["PaymentProvider"] ?? "zarinpal", MerchantId = _configuration["ZarinPal:MerchantId"] ?? "", Amount = finalGatewayAmount, CallbackUrl = callbackUrl, Description = $"فروشگاه تخفیفی - سفارش #{order.Id}", Mobile = null, UserId = request.UserId, RequestStatusCode = 100, RequestStatusMessage = "Success", Authority = paymentResult.RefId, PaymentStatus = false, // هنوز verify نشده TransactionId = transaction.Id, OrderId = order.Id.ToString() }; _context.PaymentTransactions.Add(paymentTx); await _context.SaveChangesAsync(cancellationToken); paymentUrl = paymentResult.GatewayUrl; _logger.LogInformation( "Payment gateway initiated for DiscountOrder #{OrderId}: RefId={RefId}, Url={Url}", order.Id, paymentResult.RefId, paymentResult.GatewayUrl); } else { _logger.LogError( "Payment gateway initiation failed for DiscountOrder #{OrderId}: {Error}", order.Id, paymentResult.ErrorMessage); return new PlaceOrderResponseDto { Success = false, Message = $"خطا در اتصال به درگاه پرداخت: {paymentResult.ErrorMessage}", OrderId = order.Id }; } } catch (Exception ex) { _logger.LogError(ex, "Payment gateway exception for DiscountOrder #{OrderId}", order.Id); return new PlaceOrderResponseDto { Success = false, Message = $"خطا در اتصال به درگاه پرداخت: {ex.Message}", OrderId = order.Id }; } } else { // اگر کل مبلغ از کیف تخفیفی پرداخت شد — مستقیماً تکمیل شود transaction.PaymentStatus = PaymentStatus.Success; transaction.PaymentDate = DateTime.Now; order.PaymentStatus = PaymentStatus.Success; order.PaymentDate = DateTime.Now; order.DeliveryStatus = DeliveryStatus.Pending; var walletForDeduct = await _context.UserWallets .FirstOrDefaultAsync(w => w.UserId == request.UserId, cancellationToken); if (walletForDeduct != null && actualDiscountBalanceUsed > 0) { walletForDeduct.DiscountBalance -= actualDiscountBalanceUsed; // ثبت لاگ تغییرات کیف پول _context.UserWalletChangeLogs.Add(new Domain.Entities.UserWalletChangeLog { WalletId = walletForDeduct.Id, CurrentBalance = walletForDeduct.Balance, ChangeValue = 0, CurrentNetworkBalance = walletForDeduct.NetworkBalance, ChangeNerworkValue = 0, CurrentDiscountBalance = walletForDeduct.DiscountBalance, ChangeDiscountValue = -actualDiscountBalanceUsed, IsIncrease = false, RefrenceId = transaction.Id }); } foreach (var cartItem in cartItems) { await _inventoryService.ConfirmSaleAsync( cartItem.ProductId, ProductType.DiscountProduct, cartItem.Count, order.Id, cancellationToken); cartItem.Product.SaleCount += cartItem.Count; } await _context.SaveChangesAsync(cancellationToken); _logger.LogInformation( "DiscountOrder #{OrderId} fully paid via discount balance ({Amount} T)", order.Id, actualDiscountBalanceUsed); } return new PlaceOrderResponseDto { Success = true, Message = finalGatewayAmount > 0 ? "سفارش ایجاد شد. در حال انتقال به درگاه پرداخت..." : "سفارش با موفقیت ثبت و پرداخت شد", OrderId = order.Id, TransactionId = transaction.Id, TotalAmount = totalAmount, DiscountBalanceUsed = actualDiscountBalanceUsed, GatewayAmountRequired = finalGatewayAmount, PaymentUrl = paymentUrl }; } }