using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using LittleShop.DTOs; using LittleShop.Services; using LittleShop.Enums; namespace LittleShop.Controllers; [ApiController] [Route("api/[controller]")] [Authorize(AuthenticationSchemes = "Bearer")] public class OrdersController : ControllerBase { private readonly IOrderService _orderService; private readonly ICryptoPaymentService _cryptoPaymentService; public OrdersController(IOrderService orderService, ICryptoPaymentService cryptoPaymentService) { _orderService = orderService; _cryptoPaymentService = cryptoPaymentService; } // Admin endpoints [HttpGet] [Authorize(Roles = "Admin")] public async Task>> GetAllOrders() { var orders = await _orderService.GetAllOrdersAsync(); return Ok(orders); } [HttpGet("{id}")] [Authorize(Roles = "Admin")] public async Task> GetOrder(Guid id) { var order = await _orderService.GetOrderByIdAsync(id); if (order == null) { return NotFound(); } return Ok(order); } [HttpPut("{id}/status")] [Authorize(Roles = "Admin")] public async Task UpdateOrderStatus(Guid id, [FromBody] UpdateOrderStatusDto updateOrderStatusDto) { var success = await _orderService.UpdateOrderStatusAsync(id, updateOrderStatusDto); if (!success) { return NotFound(); } return NoContent(); } // Public endpoints for client identity [HttpGet("by-identity/{identityReference}")] [AllowAnonymous] public async Task>> GetOrdersByIdentity(string identityReference) { var orders = await _orderService.GetOrdersByIdentityAsync(identityReference); return Ok(orders); } [HttpGet("by-identity/{identityReference}/{id}")] [AllowAnonymous] public async Task> GetOrderByIdentity(string identityReference, Guid id) { var order = await _orderService.GetOrderByIdAsync(id); if (order == null || order.IdentityReference != identityReference) { return NotFound(); } return Ok(order); } [HttpPost] [AllowAnonymous] public async Task> CreateOrder([FromBody] CreateOrderDto createOrderDto) { try { var order = await _orderService.CreateOrderAsync(createOrderDto); return CreatedAtAction(nameof(GetOrderByIdentity), new { identityReference = order.IdentityReference, id = order.Id }, order); } catch (ArgumentException ex) { return BadRequest(ex.Message); } } [HttpPost("{id}/payments")] [AllowAnonymous] public async Task> CreatePayment(Guid id, [FromBody] CreatePaymentDto createPaymentDto) { var order = await _orderService.GetOrderByIdAsync(id); if (order == null) { return NotFound("Order not found"); } try { var payment = await _cryptoPaymentService.CreatePaymentAsync(id, createPaymentDto.Currency); return Ok(payment); } catch (ArgumentException ex) { return BadRequest(ex.Message); } } [HttpGet("{id}/payments")] public async Task>> GetOrderPayments(Guid id) { var payments = await _cryptoPaymentService.GetPaymentsByOrderAsync(id); return Ok(payments); } [HttpGet("payments/{paymentId}/status")] public async Task> GetPaymentStatus(Guid paymentId) { try { var status = await _cryptoPaymentService.GetPaymentStatusAsync(paymentId); return Ok(status); } catch (ArgumentException) { return NotFound(); } } [HttpPost("{id}/cancel")] public async Task CancelOrder(Guid id, [FromBody] CancelOrderDto cancelOrderDto) { var success = await _orderService.CancelOrderAsync(id, cancelOrderDto.IdentityReference); if (!success) { return BadRequest("Cannot cancel order - order not found or already processed"); } return NoContent(); } // Webhook endpoint for BTCPay Server [HttpPost("payments/webhook")] public async Task PaymentWebhook([FromBody] PaymentWebhookDto webhookDto) { var success = await _cryptoPaymentService.ProcessPaymentWebhookAsync( webhookDto.InvoiceId, webhookDto.Status, webhookDto.Amount, webhookDto.TransactionHash); if (!success) { return BadRequest("Invalid webhook data"); } return Ok(); } } public class CreatePaymentDto { public CryptoCurrency Currency { get; set; } } public class CancelOrderDto { public string IdentityReference { get; set; } = string.Empty; } public class PaymentWebhookDto { public string InvoiceId { get; set; } = string.Empty; public PaymentStatus Status { get; set; } public decimal Amount { get; set; } public string? TransactionHash { get; set; } }