Fix multiple TeleBot and admin panel issues

- Fix admin panel to show all pending orders (PendingPayment + PaymentReceived)
- Fix currency display from USD ($) to GBP (£) throughout TeleBot
- Update payment methods to use dynamic SilverPay currency list
- Consolidate shipping address collection into single message
- Implement cart backup/restore on payment failure
- Remove unsupported XMR from TeleBot config

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
SysAdmin 2025-09-24 15:04:24 +01:00
parent e7d97f581c
commit 524f0639e1
6 changed files with 140 additions and 122 deletions

View File

@ -32,23 +32,27 @@ public class CurrencyController : ControllerBase
// Get SilverPay supported currencies // Get SilverPay supported currencies
var silverPayCurrencies = await _silverPayService.GetSupportedCurrenciesAsync(); var silverPayCurrencies = await _silverPayService.GetSupportedCurrenciesAsync();
// Production currencies (always enabled if supported by SilverPay) // Test currencies that should be excluded unless explicitly enabled
var productionCurrencies = new[] { "BTC", "ETH" }; var testCurrencies = new[] { "TBTC", "TETH", "TLTC", "TESTBTC", "TESTNET" };
foreach (var currency in productionCurrencies)
{
if (silverPayCurrencies.Contains(currency))
{
availableCurrencies.Add(currency);
}
}
// Test currencies (enabled via admin settings) // Add all SilverPay supported currencies except test ones
var testCurrencies = new[] { "TBTC", "TLTC" }; foreach (var currency in silverPayCurrencies)
foreach (var currency in testCurrencies)
{ {
if (silverPayCurrencies.Contains(currency) && var isTestCurrency = testCurrencies.Any(tc =>
await _systemSettingsService.IsTestCurrencyEnabledAsync(currency)) currency.Contains(tc, StringComparison.OrdinalIgnoreCase) ||
currency.StartsWith("T", StringComparison.OrdinalIgnoreCase));
if (isTestCurrency)
{ {
// Only add test currencies if explicitly enabled in settings
if (await _systemSettingsService.IsTestCurrencyEnabledAsync(currency))
{
availableCurrencies.Add(currency);
}
}
else
{
// Add all production currencies
availableCurrencies.Add(currency); availableCurrencies.Add(currency);
} }
} }

View File

@ -511,7 +511,18 @@ public class OrderService : IOrderService
public async Task<IEnumerable<OrderDto>> GetOrdersRequiringActionAsync() public async Task<IEnumerable<OrderDto>> GetOrdersRequiringActionAsync()
{ {
return await GetOrdersByStatusAsync(OrderStatus.PaymentReceived); var orders = await _context.Orders
.Include(o => o.Customer)
.Include(o => o.Items)
.ThenInclude(oi => oi.Product)
.Include(o => o.Items)
.ThenInclude(oi => oi.ProductMultiBuy)
.Include(o => o.Payments)
.Where(o => o.Status == OrderStatus.PendingPayment || o.Status == OrderStatus.PaymentReceived)
.OrderByDescending(o => o.CreatedAt)
.ToListAsync();
return orders.Select(MapToDto);
} }
public async Task<IEnumerable<OrderDto>> GetOrdersForPackingAsync() public async Task<IEnumerable<OrderDto>> GetOrdersForPackingAsync()

View File

@ -649,7 +649,11 @@ namespace TeleBot.Handlers
return; return;
} }
// Clear cart after successful order // Backup cart in case payment fails - we'll need to restore it
var cartBackup = System.Text.Json.JsonSerializer.Serialize(session.Cart);
session.TempData["cart_backup"] = cartBackup;
// Clear cart after successful order (will restore if payment fails)
session.Cart.Clear(); session.Cart.Clear();
// Store order ID for payment // Store order ID for payment
@ -700,6 +704,20 @@ namespace TeleBot.Handlers
if (payment == null) if (payment == null)
{ {
// Restore cart from backup since payment failed
if (session.TempData.TryGetValue("cart_backup", out var cartBackupObj) && cartBackupObj is string cartBackupJson)
{
try
{
session.Cart = System.Text.Json.JsonSerializer.Deserialize<ShoppingCart>(cartBackupJson) ?? new ShoppingCart();
session.TempData.Remove("cart_backup");
}
catch
{
// If restoration fails, cart remains empty
}
}
await SafeEditMessageAsync( await SafeEditMessageAsync(
bot, bot,
message.Chat.Id, message.Chat.Id,
@ -709,14 +727,17 @@ namespace TeleBot.Handlers
$"This might be due to:\n" + $"This might be due to:\n" +
$"• Payment gateway temporarily unavailable\n" + $"• Payment gateway temporarily unavailable\n" +
$"• Network connectivity issues\n\n" + $"• Network connectivity issues\n\n" +
$"Please try again in a few minutes.", $"Your cart has been restored. Please try again.",
Telegram.Bot.Types.Enums.ParseMode.Markdown, Telegram.Bot.Types.Enums.ParseMode.Markdown,
MenuBuilder.MainMenu() MenuBuilder.CartMenu(session.Cart)
); );
return; return;
} }
// Payment created successfully, continue with display // Payment created successfully, remove cart backup
session.TempData.Remove("cart_backup");
// Continue with display
var paymentText = MessageFormatter.FormatPayment(payment); var paymentText = MessageFormatter.FormatPayment(payment);
await DisplayPaymentInfo(bot, message, payment, paymentText); await DisplayPaymentInfo(bot, message, payment, paymentText);
@ -725,6 +746,20 @@ namespace TeleBot.Handlers
{ {
_logger.LogError(ex, "Failed to create payment for order {OrderId} with currency {Currency}", orderId, currency); _logger.LogError(ex, "Failed to create payment for order {OrderId} with currency {Currency}", orderId, currency);
// Restore cart from backup since payment failed
if (session.TempData.TryGetValue("cart_backup", out var cartBackupObj) && cartBackupObj is string cartBackupJson)
{
try
{
session.Cart = System.Text.Json.JsonSerializer.Deserialize<ShoppingCart>(cartBackupJson) ?? new ShoppingCart();
session.TempData.Remove("cart_backup");
}
catch
{
// If restoration fails, cart remains empty
}
}
await SafeEditMessageAsync( await SafeEditMessageAsync(
bot, bot,
message.Chat.Id, message.Chat.Id,
@ -732,9 +767,9 @@ namespace TeleBot.Handlers
$"❌ *Payment System Error*\n\n" + $"❌ *Payment System Error*\n\n" +
$"Sorry, there was a technical issue creating your {currency} payment.\n\n" + $"Sorry, there was a technical issue creating your {currency} payment.\n\n" +
$"Our payment system may be undergoing maintenance.\n" + $"Our payment system may be undergoing maintenance.\n" +
$"Please try again later or contact support.", $"Your cart has been restored. Please try again later.",
Telegram.Bot.Types.Enums.ParseMode.Markdown, Telegram.Bot.Types.Enums.ParseMode.Markdown,
MenuBuilder.MainMenu() MenuBuilder.CartMenu(session.Cart)
); );
return; return;
} }

View File

@ -98,99 +98,41 @@ namespace TeleBot.Handlers
switch (session.OrderFlow.CurrentStep) switch (session.OrderFlow.CurrentStep)
{ {
case OrderFlowStep.CollectingName: case OrderFlowStep.CollectingName:
if (string.IsNullOrWhiteSpace(input)) // Parse multi-line address input
var lines = input.Split('\n', StringSplitOptions.TrimEntries);
if (lines.Length < 4)
{ {
await bot.SendTextMessageAsync( await bot.SendTextMessageAsync(
message.Chat.Id, message.Chat.Id,
"❌ Name cannot be empty. Please enter your shipping name:" "📦 *Checkout - Delivery Details*\n\n" +
"Please provide all delivery details in one message:\n\n" +
"• Shipping Name\n" +
"• Street Address\n" +
"• City Name\n" +
"• Post/Zip Code\n" +
"• Country (or leave blank for UK)\n\n" +
"_Example:_\n" +
"`John Smith\n" +
"123 Main Street\n" +
"London\n" +
"SW1A 1AA\n" +
"United Kingdom`",
parseMode: Telegram.Bot.Types.Enums.ParseMode.Markdown
); );
return; return;
} }
session.OrderFlow.ShippingName = input; // Parse the address fields
session.OrderFlow.CurrentStep = OrderFlowStep.CollectingAddress; session.OrderFlow.ShippingName = lines[0];
session.OrderFlow.ShippingAddress = lines[1];
await bot.SendTextMessageAsync( session.OrderFlow.ShippingCity = lines[2];
message.Chat.Id, session.OrderFlow.ShippingPostCode = lines[3];
"📦 *Checkout - Step 2/5*\n\n" + session.OrderFlow.ShippingCountry = lines.Length > 4 && !string.IsNullOrWhiteSpace(lines[4])
"Please enter your shipping address:\n\n" + ? lines[4]
"_Reply with your street address_", : "United Kingdom";
parseMode: Telegram.Bot.Types.Enums.ParseMode.Markdown
);
break;
case OrderFlowStep.CollectingAddress:
if (string.IsNullOrWhiteSpace(input))
{
await bot.SendTextMessageAsync(
message.Chat.Id,
"❌ Address cannot be empty. Please enter your shipping address:"
);
return;
}
session.OrderFlow.ShippingAddress = input;
session.OrderFlow.CurrentStep = OrderFlowStep.CollectingCity;
await bot.SendTextMessageAsync(
message.Chat.Id,
"📦 *Checkout - Step 3/5*\n\n" +
"Please enter your city:\n\n" +
"_Reply with your city name_",
parseMode: Telegram.Bot.Types.Enums.ParseMode.Markdown
);
break;
case OrderFlowStep.CollectingCity:
if (string.IsNullOrWhiteSpace(input))
{
await bot.SendTextMessageAsync(
message.Chat.Id,
"❌ City cannot be empty. Please enter your city:"
);
return;
}
session.OrderFlow.ShippingCity = input;
session.OrderFlow.CurrentStep = OrderFlowStep.CollectingPostCode;
await bot.SendTextMessageAsync(
message.Chat.Id,
"📦 *Checkout - Step 4/5*\n\n" +
"Please enter your postal/ZIP code:\n\n" +
"_Reply with your postal code_",
parseMode: Telegram.Bot.Types.Enums.ParseMode.Markdown
);
break;
case OrderFlowStep.CollectingPostCode:
if (string.IsNullOrWhiteSpace(input))
{
await bot.SendTextMessageAsync(
message.Chat.Id,
"❌ Postal code cannot be empty. Please enter your postal code:"
);
return;
}
session.OrderFlow.ShippingPostCode = input;
session.OrderFlow.CurrentStep = OrderFlowStep.CollectingCountry;
await bot.SendTextMessageAsync(
message.Chat.Id,
"📦 *Checkout - Step 5/5*\n\n" +
"Please enter your country (or press Enter for United Kingdom):\n\n" +
"_Reply with your country name_",
parseMode: Telegram.Bot.Types.Enums.ParseMode.Markdown
);
break;
case OrderFlowStep.CollectingCountry:
if (!string.IsNullOrWhiteSpace(input))
{
session.OrderFlow.ShippingCountry = input;
}
// Skip to review step
session.OrderFlow.CurrentStep = OrderFlowStep.ReviewingOrder; session.OrderFlow.CurrentStep = OrderFlowStep.ReviewingOrder;
// Show order summary // Show order summary
@ -204,6 +146,32 @@ namespace TeleBot.Handlers
); );
break; break;
// Legacy steps - redirect to new single-message collection
case OrderFlowStep.CollectingAddress:
case OrderFlowStep.CollectingCity:
case OrderFlowStep.CollectingPostCode:
case OrderFlowStep.CollectingCountry:
// Reset to name collection with new format
session.OrderFlow.CurrentStep = OrderFlowStep.CollectingName;
await bot.SendTextMessageAsync(
message.Chat.Id,
"📦 *Checkout - Delivery Details*\n\n" +
"Please provide all delivery details in one message:\n\n" +
"• Shipping Name\n" +
"• Street Address\n" +
"• City Name\n" +
"• Post/Zip Code\n" +
"• Country (or leave blank for UK)\n\n" +
"_Example:_\n" +
"`John Smith\n" +
"123 Main Street\n" +
"London\n" +
"SW1A 1AA\n" +
"United Kingdom`",
parseMode: Telegram.Bot.Types.Enums.ParseMode.Markdown
);
break;
case OrderFlowStep.CollectingNotes: case OrderFlowStep.CollectingNotes:
session.OrderFlow.Notes = string.IsNullOrWhiteSpace(input) ? null : input; session.OrderFlow.Notes = string.IsNullOrWhiteSpace(input) ? null : input;
session.OrderFlow.CurrentStep = OrderFlowStep.ReviewingOrder; session.OrderFlow.CurrentStep = OrderFlowStep.ReviewingOrder;

View File

@ -59,7 +59,7 @@ namespace TeleBot.UI
foreach (var product in products.Items) foreach (var product in products.Items)
{ {
sb.AppendLine($"*{product.Name}*"); sb.AppendLine($"*{product.Name}*");
sb.AppendLine($"💰 Price: ${product.Price:F2}"); sb.AppendLine($"💰 Price: £{product.Price:F2}");
if (!string.IsNullOrEmpty(product.Description)) if (!string.IsNullOrEmpty(product.Description))
{ {
@ -122,7 +122,7 @@ namespace TeleBot.UI
var sb = new StringBuilder(); var sb = new StringBuilder();
sb.AppendLine($"🛍️ *{product.Name}*\n"); sb.AppendLine($"🛍️ *{product.Name}*\n");
sb.AppendLine($"💰 *Price:* ${product.Price:F2}"); sb.AppendLine($"💰 *Price:* £{product.Price:F2}");
sb.AppendLine($"⚖️ *Weight:* {product.Weight} {product.WeightUnit}"); sb.AppendLine($"⚖️ *Weight:* {product.Weight} {product.WeightUnit}");
sb.AppendLine($"📁 *Category:* {product.CategoryName ?? "Uncategorized"}"); sb.AppendLine($"📁 *Category:* {product.CategoryName ?? "Uncategorized"}");
@ -203,12 +203,12 @@ namespace TeleBot.UI
foreach (var item in cart.Items) foreach (var item in cart.Items)
{ {
sb.AppendLine($"• *{item.ProductName}*"); sb.AppendLine($"• *{item.ProductName}*");
sb.AppendLine($" Qty: {item.Quantity} × ${item.UnitPrice:F2} = *${item.TotalPrice:F2}*"); sb.AppendLine($" Qty: {item.Quantity} × £{item.UnitPrice:F2} = *£{item.TotalPrice:F2}*");
} }
sb.AppendLine($"\n📊 *Summary:*"); sb.AppendLine($"\n📊 *Summary:*");
sb.AppendLine($"Items: {cart.GetTotalItems()}"); sb.AppendLine($"Items: {cart.GetTotalItems()}");
sb.AppendLine($"*Total: ${cart.GetTotalAmount():F2}*"); sb.AppendLine($"*Total: £{cart.GetTotalAmount():F2}*");
return sb.ToString(); return sb.ToString();
} }
@ -238,7 +238,7 @@ namespace TeleBot.UI
sb.AppendLine($"Notes: {orderFlow.Notes}"); sb.AppendLine($"Notes: {orderFlow.Notes}");
} }
sb.AppendLine($"\n*Order Total: ${cart.GetTotalAmount():F2}*"); sb.AppendLine($"\n*Order Total: £{cart.GetTotalAmount():F2}*");
sb.AppendLine($"Items: {cart.GetTotalItems()}"); sb.AppendLine($"Items: {cart.GetTotalItems()}");
sb.AppendLine("\nPlease confirm your order:"); sb.AppendLine("\nPlease confirm your order:");
@ -253,7 +253,7 @@ namespace TeleBot.UI
sb.AppendLine($"📦 *Order Details*\n"); sb.AppendLine($"📦 *Order Details*\n");
sb.AppendLine($"*Order ID:* `{order.Id}`"); sb.AppendLine($"*Order ID:* `{order.Id}`");
sb.AppendLine($"*Status:* {FormatOrderStatus(order.Status)}"); sb.AppendLine($"*Status:* {FormatOrderStatus(order.Status)}");
sb.AppendLine($"*Total:* ${order.TotalAmount:F2}"); sb.AppendLine($"*Total:* £{order.TotalAmount:F2}");
sb.AppendLine($"*Created:* {order.CreatedAt:yyyy-MM-dd HH:mm} UTC"); sb.AppendLine($"*Created:* {order.CreatedAt:yyyy-MM-dd HH:mm} UTC");
if (order.PaidAt.HasValue) if (order.PaidAt.HasValue)
@ -276,7 +276,7 @@ namespace TeleBot.UI
sb.AppendLine("\n*Items:*"); sb.AppendLine("\n*Items:*");
foreach (var item in order.Items) foreach (var item in order.Items)
{ {
sb.AppendLine($"• {item.ProductName} - Qty: {item.Quantity} - ${item.TotalPrice:F2}"); sb.AppendLine($"• {item.ProductName} - Qty: {item.Quantity} - £{item.TotalPrice:F2}");
} }
} }

View File

@ -70,9 +70,9 @@
}, },
"Cryptocurrencies": [ "Cryptocurrencies": [
"BTC", "BTC",
"XMR", "ETH",
"LTC", "LTC",
"DASH" "DOGE"
], ],
"Kestrel": { "Kestrel": {
"Endpoints": { "Endpoints": {