Feature: Complete order management for pending orders

- Added delete order functionality with identity verification
- OrderDetailsMenu now shows conditional buttons based on order/payment state
- Retry payment if no payments exist
- View payment details if payment pending
- Delete order option for all pending orders
- Full client SDK implementation for CancelOrderAsync

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

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
SysAdmin 2025-10-06 04:19:47 +01:00
parent 3cc7092967
commit f440042204
5 changed files with 113 additions and 4 deletions

View File

@ -11,4 +11,5 @@ public interface IOrderService
Task<ApiResponse<Order>> GetOrderByCustomerIdAsync(Guid customerId, Guid orderId);
Task<ApiResponse<CryptoPayment>> CreatePaymentAsync(Guid orderId, int currency);
Task<ApiResponse<List<CryptoPayment>>> GetOrderPaymentsAsync(Guid orderId);
Task<ApiResponse<bool>> CancelOrderAsync(Guid orderId, string identityReference);
}

View File

@ -185,8 +185,31 @@ public class OrderService : IOrderService
{
_logger.LogError(ex, "Failed to get payments for order {OrderId}", orderId);
return ApiResponse<List<CryptoPayment>>.Failure(
ex.Message,
ex.Message,
System.Net.HttpStatusCode.InternalServerError);
}
}
public async Task<ApiResponse<bool>> CancelOrderAsync(Guid orderId, string identityReference)
{
try
{
var response = await _httpClient.PostAsJsonAsync(
$"api/orders/{orderId}/cancel",
new { IdentityReference = identityReference }
);
if (response.IsSuccessStatusCode)
{
return ApiResponse<bool>.Success(true, response.StatusCode);
}
return ApiResponse<bool>.Failure("Failed to cancel order", response.StatusCode);
}
catch (Exception ex)
{
_logger.LogError(ex, "Error cancelling order {OrderId}", orderId);
return ApiResponse<bool>.Failure($"Error: {ex.Message}", System.Net.HttpStatusCode.InternalServerError);
}
}
}

View File

@ -180,6 +180,10 @@ namespace TeleBot.Handlers
await HandleRetryPayment(bot, callbackQuery.Message, session, Guid.Parse(data[1]));
break;
case "delete_order":
await HandleDeleteOrder(bot, callbackQuery, session, Guid.Parse(data[1]), callbackQuery.From);
break;
case "reviews":
await HandleViewReviews(bot, callbackQuery.Message, session);
break;
@ -1215,6 +1219,43 @@ namespace TeleBot.Handlers
);
}
private async Task HandleDeleteOrder(ITelegramBotClient bot, CallbackQuery callbackQuery, UserSession session, Guid orderId, User telegramUser)
{
// Build identity reference from telegram user
var identityReference = $"telegram:{telegramUser.Id}:{telegramUser.Username ?? ""}";
var success = await _shopService.CancelOrderAsync(orderId, identityReference);
if (success)
{
await bot.EditMessageTextAsync(
callbackQuery.Message!.Chat.Id,
callbackQuery.Message.MessageId,
"✅ *Order Deleted*\n\n" +
$"Order #{orderId.ToString().Substring(orderId.ToString().Length - 8).ToUpper()} has been cancelled and removed.",
parseMode: Telegram.Bot.Types.Enums.ParseMode.Markdown,
replyMarkup: new InlineKeyboardMarkup(new[]
{
new[]
{
InlineKeyboardButton.WithCallbackData("⬅️ Back to Orders", "orders"),
InlineKeyboardButton.WithCallbackData("🏠 Main Menu", "menu")
}
})
);
await bot.AnswerCallbackQueryAsync(callbackQuery.Id, "Order deleted successfully");
}
else
{
await bot.AnswerCallbackQueryAsync(
callbackQuery.Id,
"Failed to delete order. It may have already been processed.",
showAlert: true
);
}
}
private async Task HandleViewPayment(ITelegramBotClient bot, Message message, UserSession session, Guid orderId, User telegramUser)
{
var order = await _shopService.GetCustomerOrderAsync(

View File

@ -24,6 +24,7 @@ namespace TeleBot.Services
Task<Order?> GetOrderAsync(Guid orderId);
Task<Order?> GetCustomerOrderAsync(Guid orderId, long telegramUserId, string telegramUsername, string displayName, string firstName, string lastName);
Task<CryptoPayment?> CreatePaymentAsync(Guid orderId, string currency);
Task<bool> CancelOrderAsync(Guid orderId, string identityReference);
Task<List<CustomerMessage>?> GetPendingMessagesAsync();
Task<bool> MarkMessageAsSentAsync(Guid messageId, string? platformMessageId = null);
Task<bool> MarkMessageAsFailedAsync(Guid messageId, string reason);
@ -358,6 +359,31 @@ namespace TeleBot.Services
}
}
public async Task<bool> CancelOrderAsync(Guid orderId, string identityReference)
{
try
{
if (!await AuthenticateAsync())
return false;
var result = await _client.Orders.CancelOrderAsync(orderId, identityReference);
if (result.IsSuccess)
{
_logger.LogInformation("Order {OrderId} cancelled for customer {Identity}", orderId, identityReference);
return true;
}
_logger.LogWarning("Failed to cancel order {OrderId}: {Error}", orderId, result.ErrorMessage);
return false;
}
catch (Exception ex)
{
_logger.LogError(ex, "Error cancelling order {OrderId}", orderId);
return false;
}
}
public async Task<List<CustomerMessage>?> GetPendingMessagesAsync()
{
try

View File

@ -209,12 +209,30 @@ namespace TeleBot.UI
{
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
// Order management based on status and payment state
if (order.Status == 0) // Pending Payment
{
if (!order.Payments.Any())
{
// No payment created - offer retry
buttons.Add(new[]
{
InlineKeyboardButton.WithCallbackData("🔄 Retry Payment", $"retry_payment:{order.Id}")
});
}
else if (order.Payments.Any(p => p.Status == 0)) // Has pending payment
{
// Show payment details
buttons.Add(new[]
{
InlineKeyboardButton.WithCallbackData("💳 View Payment Details", $"payment:{order.Id}")
});
}
// Allow deleting pending orders
buttons.Add(new[]
{
InlineKeyboardButton.WithCallbackData("💳 View Payment Details", $"payment:{order.Id}")
InlineKeyboardButton.WithCallbackData("🗑️ Delete Order", $"delete_order:{order.Id}")
});
}