using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using Microsoft.EntityFrameworkCore; using NodaTime; using System.ComponentModel.DataAnnotations; namespace DysonNetwork.Sphere.Wallet; [ApiController] [Route("/subscriptions")] public class SubscriptionController(SubscriptionService subscriptions, AppDatabase db) : ControllerBase { [HttpGet] [Authorize] public async Task>> ListSubscriptions( [FromQuery] int offset = 0, [FromQuery] int take = 20 ) { if (HttpContext.Items["CurrentUser"] is not Account.Account currentUser) return Unauthorized(); var query = db.WalletSubscriptions.AsQueryable() .Where(s => s.AccountId == currentUser.Id) .Include(s => s.Coupon) .OrderByDescending(s => s.BegunAt); var totalCount = await query.CountAsync(); var subscriptionsList = await query .Skip(offset) .Take(take) .ToListAsync(); Response.Headers["X-Total"] = totalCount.ToString(); return subscriptionsList; } [HttpGet("{identifier}")] [Authorize] public async Task> GetSubscription(string identifier) { if (HttpContext.Items["CurrentUser"] is not Account.Account currentUser) return Unauthorized(); var subscription = await subscriptions.GetSubscriptionAsync(currentUser.Id, identifier); if (subscription is null) return NotFound($"Subscription with identifier {identifier} was not found."); return subscription; } public class CreateSubscriptionRequest { [Required] public string Identifier { get; set; } = null!; [Required] public string PaymentMethod { get; set; } = null!; [Required] public PaymentDetails PaymentDetails { get; set; } = null!; public string? Coupon { get; set; } public int? CycleDurationDays { get; set; } public bool IsFreeTrial { get; set; } = false; public bool IsAutoRenewal { get; set; } = true; } [HttpPost] [Authorize] public async Task> CreateSubscription([FromBody] CreateSubscriptionRequest request) { if (HttpContext.Items["CurrentUser"] is not Account.Account currentUser) return Unauthorized(); Duration? cycleDuration = null; if (request.CycleDurationDays.HasValue) { cycleDuration = Duration.FromDays(request.CycleDurationDays.Value); } try { var subscription = await subscriptions.CreateSubscriptionAsync( currentUser, request.Identifier, request.PaymentMethod, request.PaymentDetails, cycleDuration, request.Coupon, request.IsFreeTrial, request.IsAutoRenewal ); return subscription; } catch (ArgumentOutOfRangeException ex) { return BadRequest(ex.Message); } catch (InvalidOperationException ex) { return BadRequest(ex.Message); } } [HttpPost("{identifier}/cancel")] [Authorize] public async Task> CancelSubscription(string identifier) { if (HttpContext.Items["CurrentUser"] is not Account.Account currentUser) return Unauthorized(); try { var subscription = await subscriptions.CancelSubscriptionAsync(currentUser.Id, identifier); return subscription; } catch (InvalidOperationException ex) { return BadRequest(ex.Message); } } [HttpPost("{identifier}/order")] [Authorize] public async Task> CreateSubscriptionOrder(string identifier) { if (HttpContext.Items["CurrentUser"] is not Account.Account currentUser) return Unauthorized(); try { var order = await subscriptions.CreateSubscriptionOrder(currentUser.Id, identifier); return order; } catch (InvalidOperationException ex) { return BadRequest(ex.Message); } } public class SubscriptionOrderRequest { [Required] public string OrderId { get; set; } = null!; } [HttpPost("order/handle")] [Authorize] public async Task> HandleSubscriptionOrder([FromBody] SubscriptionOrderRequest request) { var order = await db.PaymentOrders.FindAsync(request.OrderId); if (order is null) return NotFound($"Order with ID {request.OrderId} was not found."); try { var subscription = await subscriptions.HandleSubscriptionOrder(order); return subscription; } catch (InvalidOperationException ex) { return BadRequest(ex.Message); } } }