diff --git a/TeleBot/TeleBot/Handlers/CallbackHandler.cs b/TeleBot/TeleBot/Handlers/CallbackHandler.cs index 94a9e7b..35db0c2 100644 --- a/TeleBot/TeleBot/Handlers/CallbackHandler.cs +++ b/TeleBot/TeleBot/Handlers/CallbackHandler.cs @@ -172,6 +172,10 @@ namespace TeleBot.Handlers await HandleViewOrder(bot, callbackQuery.Message, session, Guid.Parse(data[1]), callbackQuery.From); break; + case "payment": + await HandleViewPayment(bot, callbackQuery.Message, session, Guid.Parse(data[1]), callbackQuery.From); + break; + case "reviews": await HandleViewReviews(bot, callbackQuery.Message, session); break; @@ -256,7 +260,7 @@ namespace TeleBot.Handlers // Send new message at bottom instead of editing old one await bot.SendTextMessageAsync( message.Chat.Id, - MessageFormatter.FormatWelcome(true), + MessageFormatter.FormatWelcome(true, session.Cart), parseMode: Telegram.Bot.Types.Enums.ParseMode.Markdown, replyMarkup: MenuBuilder.MainMenu() ); @@ -1166,12 +1170,91 @@ namespace TeleBot.Handlers message.Chat.Id, MessageFormatter.FormatOrder(order), parseMode: Telegram.Bot.Types.Enums.ParseMode.Markdown, - replyMarkup: MenuBuilder.MainMenu() + replyMarkup: _menuBuilder.OrderDetailsMenu(order) ); session.State = SessionState.ViewingOrder; } - + + private async Task HandleViewPayment(ITelegramBotClient bot, Message message, UserSession session, Guid orderId, User telegramUser) + { + var order = await _shopService.GetCustomerOrderAsync( + orderId, + telegramUser.Id, + telegramUser.Username ?? "", + $"{telegramUser.FirstName} {telegramUser.LastName}".Trim(), + telegramUser.FirstName ?? "", + telegramUser.LastName ?? "" + ); + + if (order == null) + { + await bot.AnswerCallbackQueryAsync("", "Order not found", showAlert: true); + return; + } + + var payment = order.Payments.FirstOrDefault(p => p.Status == 0); // Find pending payment + if (payment == null) + { + await bot.SendTextMessageAsync( + message.Chat.Id, + "No pending payment found for this order.", + replyMarkup: _menuBuilder.OrderDetailsMenu(order) + ); + return; + } + + var paymentText = MessageFormatter.FormatPayment(payment); + + // Calculate time until expiry + var timeUntilExpiry = payment.ExpiresAt - DateTime.UtcNow; + if (timeUntilExpiry.TotalMinutes > 0) + { + paymentText += $"\nā° *Time remaining:* {timeUntilExpiry.Hours}h {timeUntilExpiry.Minutes}m"; + } + + // Generate QR code if enabled + if (_configuration.GetValue("Features:EnableQRCodes")) + { + try + { + using var qrGenerator = new QRCodeGenerator(); + var qrCodeData = qrGenerator.CreateQrCode(payment.WalletAddress, QRCodeGenerator.ECCLevel.Q); + using var qrCode = new PngByteQRCode(qrCodeData); + var qrCodeBytes = qrCode.GetGraphic(10); + + using var stream = new System.IO.MemoryStream(qrCodeBytes); + await bot.SendPhotoAsync( + message.Chat.Id, + InputFile.FromStream(stream, "payment_qr.png"), + caption: paymentText, + parseMode: Telegram.Bot.Types.Enums.ParseMode.Markdown, + replyMarkup: _menuBuilder.OrderDetailsMenu(order) + ); + } + catch (Exception ex) + { + _logger.LogError(ex, "Failed to generate QR code for payment view"); + // Fall back to text-only + await bot.SendTextMessageAsync( + message.Chat.Id, + paymentText, + parseMode: Telegram.Bot.Types.Enums.ParseMode.Markdown, + replyMarkup: _menuBuilder.OrderDetailsMenu(order) + ); + } + } + else + { + await bot.SendTextMessageAsync( + message.Chat.Id, + paymentText, + parseMode: Telegram.Bot.Types.Enums.ParseMode.Markdown, + replyMarkup: _menuBuilder.OrderDetailsMenu(order) + ); + } + } + private async Task HandlePrivacySettings(ITelegramBotClient bot, Message message, UserSession session, string? setting) { if (setting != null) diff --git a/TeleBot/TeleBot/UI/MenuBuilder.cs b/TeleBot/TeleBot/UI/MenuBuilder.cs index 6b2a768..075678d 100644 --- a/TeleBot/TeleBot/UI/MenuBuilder.cs +++ b/TeleBot/TeleBot/UI/MenuBuilder.cs @@ -206,6 +206,28 @@ namespace TeleBot.UI return new InlineKeyboardMarkup(buttons); } + public InlineKeyboardMarkup OrderDetailsMenu(Order order) + { + var buttons = new List(); + + // If order has pending payment, show payment details button + if (order.Status == 0 && order.Payments.Any(p => p.Status == 0)) // PendingPayment order with pending payment + { + buttons.Add(new[] + { + InlineKeyboardButton.WithCallbackData("šŸ’³ View Payment Details", $"payment:{order.Id}") + }); + } + + buttons.Add(new[] + { + InlineKeyboardButton.WithCallbackData("ā¬…ļø Back to Orders", "orders"), + InlineKeyboardButton.WithCallbackData("šŸ  Main Menu", "menu") + }); + + return new InlineKeyboardMarkup(buttons); + } + public InlineKeyboardMarkup CartMenu(ShoppingCart cart) { var buttons = new List(); diff --git a/TeleBot/TeleBot/UI/MessageFormatter.cs b/TeleBot/TeleBot/UI/MessageFormatter.cs index ed03896..2d0acbf 100644 --- a/TeleBot/TeleBot/UI/MessageFormatter.cs +++ b/TeleBot/TeleBot/UI/MessageFormatter.cs @@ -8,24 +8,36 @@ namespace TeleBot.UI { public static class MessageFormatter { - public static string FormatWelcome(bool isReturning) + public static string FormatWelcome(bool isReturning, ShoppingCart? cart = null) { + var message = ""; + if (isReturning) { - return $"šŸ”’ *Welcome back to {BotConfig.BrandName}*\n\n" + + message = $"šŸ”’ *Welcome back to {BotConfig.BrandName}*\n\n" + "Your privacy is our priority. All sessions are ephemeral by default.\n\n" + "šŸ–¼ļø *New Feature:* Browse products with beautiful image carousels!\n\n" + "How can I help you today?"; } - - return $"šŸ”’ *Welcome to {BotConfig.BrandName}*\n\n" + - "šŸ›”ļø *Your Privacy Matters:*\n" + - "• No account required\n" + - "• Ephemeral sessions by default\n" + - "• Optional PGP encryption for shipping\n" + - "• Cryptocurrency payments only\n\n" + - "šŸ–¼ļø *New Feature:* Browse products with beautiful image carousels!\n\n" + - "Use /help for available commands or choose from the menu below:"; + else + { + message = $"šŸ”’ *Welcome to {BotConfig.BrandName}*\n\n" + + "šŸ›”ļø *Your Privacy Matters:*\n" + + "• No account required\n" + + "• Ephemeral sessions by default\n" + + "• Optional PGP encryption for shipping\n" + + "• Cryptocurrency payments only\n\n" + + "šŸ–¼ļø *New Feature:* Browse products with beautiful image carousels!\n\n" + + "Use /help for available commands or choose from the menu below:"; + } + + // Add basket summary if items present + if (cart != null && !cart.IsEmpty()) + { + message += $"\n\nšŸ›’ *Basket:* {cart.GetTotalItems()} item(s) - Ā£{cart.GetTotalAmount():F2}"; + } + + return message; } public static string FormatCategories(List categories) @@ -271,9 +283,12 @@ namespace TeleBot.UI public static string FormatOrder(Order order) { var sb = new StringBuilder(); - + + // Generate friendly order reference from GUID (last 8 characters) + var orderRef = order.Id.ToString().Substring(order.Id.ToString().Length - 8).ToUpper(); + sb.AppendLine($"šŸ“¦ *Order Details*\n"); - sb.AppendLine($"*Order ID:* `{order.Id}`"); + sb.AppendLine($"*Order Reference:* `#{orderRef}`"); sb.AppendLine($"*Status:* {FormatOrderStatus(order.Status)}"); sb.AppendLine($"*Total:* Ā£{order.TotalAmount:F2}"); sb.AppendLine($"*Created:* {order.CreatedAt:yyyy-MM-dd HH:mm} UTC");