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
var silverPayCurrencies = await _silverPayService.GetSupportedCurrenciesAsync();
// Production currencies (always enabled if supported by SilverPay)
var productionCurrencies = new[] { "BTC", "ETH" };
foreach (var currency in productionCurrencies)
{
if (silverPayCurrencies.Contains(currency))
{
availableCurrencies.Add(currency);
}
}
// Test currencies that should be excluded unless explicitly enabled
var testCurrencies = new[] { "TBTC", "TETH", "TLTC", "TESTBTC", "TESTNET" };
// Test currencies (enabled via admin settings)
var testCurrencies = new[] { "TBTC", "TLTC" };
foreach (var currency in testCurrencies)
// Add all SilverPay supported currencies except test ones
foreach (var currency in silverPayCurrencies)
{
if (silverPayCurrencies.Contains(currency) &&
await _systemSettingsService.IsTestCurrencyEnabledAsync(currency))
var isTestCurrency = testCurrencies.Any(tc =>
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);
}
}

View File

@ -511,7 +511,18 @@ public class OrderService : IOrderService
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()

View File

@ -649,9 +649,13 @@ namespace TeleBot.Handlers
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();
// Store order ID for payment
session.TempData["current_order_id"] = order.Id;
@ -700,6 +704,20 @@ namespace TeleBot.Handlers
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(
bot,
message.Chat.Id,
@ -709,22 +727,39 @@ namespace TeleBot.Handlers
$"This might be due to:\n" +
$"• Payment gateway temporarily unavailable\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,
MenuBuilder.MainMenu()
MenuBuilder.CartMenu(session.Cart)
);
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);
await DisplayPaymentInfo(bot, message, payment, paymentText);
}
catch (Exception ex)
{
_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(
bot,
message.Chat.Id,
@ -732,9 +767,9 @@ namespace TeleBot.Handlers
$"❌ *Payment System Error*\n\n" +
$"Sorry, there was a technical issue creating your {currency} payment.\n\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,
MenuBuilder.MainMenu()
MenuBuilder.CartMenu(session.Cart)
);
return;
}

View File

@ -98,104 +98,46 @@ namespace TeleBot.Handlers
switch (session.OrderFlow.CurrentStep)
{
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(
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;
}
session.OrderFlow.ShippingName = input;
session.OrderFlow.CurrentStep = OrderFlowStep.CollectingAddress;
await bot.SendTextMessageAsync(
message.Chat.Id,
"📦 *Checkout - Step 2/5*\n\n" +
"Please enter your shipping address:\n\n" +
"_Reply with your street address_",
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;
}
// Parse the address fields
session.OrderFlow.ShippingName = lines[0];
session.OrderFlow.ShippingAddress = lines[1];
session.OrderFlow.ShippingCity = lines[2];
session.OrderFlow.ShippingPostCode = lines[3];
session.OrderFlow.ShippingCountry = lines.Length > 4 && !string.IsNullOrWhiteSpace(lines[4])
? lines[4]
: "United Kingdom";
// Skip to review step
session.OrderFlow.CurrentStep = OrderFlowStep.ReviewingOrder;
// Show order summary
var summary = MessageFormatter.FormatOrderSummary(session.OrderFlow, session.Cart);
await bot.SendTextMessageAsync(
message.Chat.Id,
summary,
@ -204,6 +146,32 @@ namespace TeleBot.Handlers
);
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:
session.OrderFlow.Notes = string.IsNullOrWhiteSpace(input) ? null : input;
session.OrderFlow.CurrentStep = OrderFlowStep.ReviewingOrder;

View File

@ -59,7 +59,7 @@ namespace TeleBot.UI
foreach (var product in products.Items)
{
sb.AppendLine($"*{product.Name}*");
sb.AppendLine($"💰 Price: ${product.Price:F2}");
sb.AppendLine($"💰 Price: £{product.Price:F2}");
if (!string.IsNullOrEmpty(product.Description))
{
@ -122,7 +122,7 @@ namespace TeleBot.UI
var sb = new StringBuilder();
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($"📁 *Category:* {product.CategoryName ?? "Uncategorized"}");
@ -203,12 +203,12 @@ namespace TeleBot.UI
foreach (var item in cart.Items)
{
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($"Items: {cart.GetTotalItems()}");
sb.AppendLine($"*Total: ${cart.GetTotalAmount():F2}*");
sb.AppendLine($"*Total: £{cart.GetTotalAmount():F2}*");
return sb.ToString();
}
@ -238,7 +238,7 @@ namespace TeleBot.UI
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("\nPlease confirm your order:");
@ -253,7 +253,7 @@ namespace TeleBot.UI
sb.AppendLine($"📦 *Order Details*\n");
sb.AppendLine($"*Order ID:* `{order.Id}`");
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");
if (order.PaidAt.HasValue)
@ -276,7 +276,7 @@ namespace TeleBot.UI
sb.AppendLine("\n*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": [
"BTC",
"XMR",
"ETH",
"LTC",
"DASH"
"DOGE"
],
"Kestrel": {
"Endpoints": {