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:
parent
e7d97f581c
commit
524f0639e1
@ -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)
|
|
||||||
|
// Add all SilverPay supported currencies except test ones
|
||||||
|
foreach (var currency in silverPayCurrencies)
|
||||||
{
|
{
|
||||||
if (silverPayCurrencies.Contains(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);
|
availableCurrencies.Add(currency);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else
|
||||||
// Test currencies (enabled via admin settings)
|
|
||||||
var testCurrencies = new[] { "TBTC", "TLTC" };
|
|
||||||
foreach (var currency in testCurrencies)
|
|
||||||
{
|
|
||||||
if (silverPayCurrencies.Contains(currency) &&
|
|
||||||
await _systemSettingsService.IsTestCurrencyEnabledAsync(currency))
|
|
||||||
{
|
{
|
||||||
|
// Add all production currencies
|
||||||
availableCurrencies.Add(currency);
|
availableCurrencies.Add(currency);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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()
|
||||||
|
|||||||
@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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;
|
||||||
|
|||||||
@ -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}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -70,9 +70,9 @@
|
|||||||
},
|
},
|
||||||
"Cryptocurrencies": [
|
"Cryptocurrencies": [
|
||||||
"BTC",
|
"BTC",
|
||||||
"XMR",
|
"ETH",
|
||||||
"LTC",
|
"LTC",
|
||||||
"DASH"
|
"DOGE"
|
||||||
],
|
],
|
||||||
"Kestrel": {
|
"Kestrel": {
|
||||||
"Endpoints": {
|
"Endpoints": {
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user