Feature: Enhanced order display and basket summary
1. Basket Summary Below Navigation
- Welcome message now shows basket items and total
- Format: "🛒 Basket: X item(s) - £X.XX"
- Only shown when basket has items
2. Order Display Improvements
- Order GUID replaced with friendly reference (last 8 chars: #XXXXXXXX)
- Added "View Payment Details" button for pending payment orders
- Button shows QR code, amount, address, and time until expiry
- Variants now properly displayed in order items (already working via commit 330116e)
3. Payment Details View (New Feature)
- HandleViewPayment: Shows payment info with QR code
- Displays: Currency, amount, address, expiry time
- Shows time remaining until payment expires
- QR code generation for easy mobile payment
- OrderDetailsMenu: Context-aware navigation buttons
4. Postal Address Auto-Load (Verified Working)
- Checkout automatically detects saved addresses
- Offers to use saved address with preview
- One-click selection or option to enter new address
- SavedAddress copied to OrderFlow when selected
Technical Changes:
- MessageFormatter.FormatWelcome: Added optional cart parameter
- MessageFormatter.FormatOrder: Uses friendly order reference
- MenuBuilder.OrderDetailsMenu: New menu with payment button
- CallbackHandler.HandleViewPayment: Payment details with QR
- CallbackHandler.HandleMainMenu: Pass cart to welcome formatter
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
parent
9432ae8ccb
commit
99bb083bd6
@ -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<bool>("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)
|
||||
|
||||
@ -206,6 +206,28 @@ namespace TeleBot.UI
|
||||
return new InlineKeyboardMarkup(buttons);
|
||||
}
|
||||
|
||||
public InlineKeyboardMarkup OrderDetailsMenu(Order order)
|
||||
{
|
||||
var buttons = new List<InlineKeyboardButton[]>();
|
||||
|
||||
// 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<InlineKeyboardButton[]>();
|
||||
|
||||
@ -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<Category> 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");
|
||||
|
||||
Loading…
Reference in New Issue
Block a user