181 lines
		
	
	
		
			5.9 KiB
		
	
	
	
		
			C#
		
	
	
	
	
	
			
		
		
	
	
			181 lines
		
	
	
		
			5.9 KiB
		
	
	
	
		
			C#
		
	
	
	
	
	
using Microsoft.AspNetCore.Authorization;
 | 
						|
using Microsoft.AspNetCore.Mvc;
 | 
						|
using Microsoft.EntityFrameworkCore;
 | 
						|
using NodaTime;
 | 
						|
using System.ComponentModel.DataAnnotations;
 | 
						|
using DysonNetwork.Pass.Wallet.PaymentHandlers;
 | 
						|
using DysonNetwork.Shared.Models;
 | 
						|
 | 
						|
namespace DysonNetwork.Pass.Wallet;
 | 
						|
 | 
						|
[ApiController]
 | 
						|
[Route("/api/subscriptions")]
 | 
						|
public class SubscriptionController(SubscriptionService subscriptions, AfdianPaymentHandler afdian, AppDatabase db)
 | 
						|
    : ControllerBase
 | 
						|
{
 | 
						|
    [HttpGet]
 | 
						|
    [Authorize]
 | 
						|
    public async Task<ActionResult<List<SnWalletSubscription>>> ListSubscriptions(
 | 
						|
        [FromQuery] int offset = 0,
 | 
						|
        [FromQuery] int take = 20
 | 
						|
    )
 | 
						|
    {
 | 
						|
        if (HttpContext.Items["CurrentUser"] is not SnAccount 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("fuzzy/{prefix}")]
 | 
						|
    [Authorize]
 | 
						|
    public async Task<ActionResult<SnWalletSubscription>> GetSubscriptionFuzzy(string prefix)
 | 
						|
    {
 | 
						|
        if (HttpContext.Items["CurrentUser"] is not SnAccount currentUser) return Unauthorized();
 | 
						|
 | 
						|
        var subscription = await db.WalletSubscriptions
 | 
						|
            .Where(s => s.AccountId == currentUser.Id && s.IsActive)
 | 
						|
            .Where(s => EF.Functions.ILike(s.Identifier, prefix + "%"))
 | 
						|
            .OrderByDescending(s => s.BegunAt)
 | 
						|
            .FirstOrDefaultAsync();
 | 
						|
        if (subscription is null || !subscription.IsAvailable) return NotFound();
 | 
						|
 | 
						|
        return Ok(subscription);
 | 
						|
    }
 | 
						|
 | 
						|
    [HttpGet("{identifier}")]
 | 
						|
    [Authorize]
 | 
						|
    public async Task<ActionResult<SnWalletSubscription>> GetSubscription(string identifier)
 | 
						|
    {
 | 
						|
        if (HttpContext.Items["CurrentUser"] is not SnAccount 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 SnPaymentDetails 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<ActionResult<SnWalletSubscription>> CreateSubscription(
 | 
						|
        [FromBody] CreateSubscriptionRequest request,
 | 
						|
        [FromHeader(Name = "X-Noop")] bool noop = false
 | 
						|
    )
 | 
						|
    {
 | 
						|
        if (HttpContext.Items["CurrentUser"] is not SnAccount 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,
 | 
						|
                noop
 | 
						|
            );
 | 
						|
 | 
						|
            return subscription;
 | 
						|
        }
 | 
						|
        catch (ArgumentOutOfRangeException ex)
 | 
						|
        {
 | 
						|
            return BadRequest(ex.Message);
 | 
						|
        }
 | 
						|
        catch (InvalidOperationException ex)
 | 
						|
        {
 | 
						|
            return BadRequest(ex.Message);
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    [HttpPost("{identifier}/cancel")]
 | 
						|
    [Authorize]
 | 
						|
    public async Task<ActionResult<SnWalletSubscription>> CancelSubscription(string identifier)
 | 
						|
    {
 | 
						|
        if (HttpContext.Items["CurrentUser"] is not SnAccount 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<ActionResult<SnWalletOrder>> CreateSubscriptionOrder(string identifier)
 | 
						|
    {
 | 
						|
        if (HttpContext.Items["CurrentUser"] is not SnAccount currentUser) return Unauthorized();
 | 
						|
 | 
						|
        try
 | 
						|
        {
 | 
						|
            var order = await subscriptions.CreateSubscriptionOrder(currentUser.Id, identifier);
 | 
						|
            return order;
 | 
						|
        }
 | 
						|
        catch (InvalidOperationException ex)
 | 
						|
        {
 | 
						|
            return BadRequest(ex.Message);
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    public class RestorePurchaseRequest
 | 
						|
    {
 | 
						|
        [Required] public string OrderId { get; set; } = null!;
 | 
						|
    }
 | 
						|
 | 
						|
    [HttpPost("order/restore/afdian")]
 | 
						|
    [Authorize]
 | 
						|
    public async Task<IActionResult> RestorePurchaseFromAfdian([FromBody] RestorePurchaseRequest request)
 | 
						|
    {
 | 
						|
        var order = await afdian.GetOrderAsync(request.OrderId);
 | 
						|
        if (order is null) return NotFound($"Order with ID {request.OrderId} was not found.");
 | 
						|
 | 
						|
        var subscription = await subscriptions.CreateSubscriptionFromOrder(order);
 | 
						|
        return Ok(subscription);
 | 
						|
    }
 | 
						|
 | 
						|
    [HttpPost("order/handle/afdian")]
 | 
						|
    public async Task<ActionResult<WebhookResponse>> AfdianWebhook()
 | 
						|
    {
 | 
						|
        var response = await afdian.HandleWebhook(Request, async webhookData =>
 | 
						|
        {
 | 
						|
            var order = webhookData.Order;
 | 
						|
            await subscriptions.CreateSubscriptionFromOrder(order);
 | 
						|
        });
 | 
						|
 | 
						|
        return Ok(response);
 | 
						|
    }
 | 
						|
} |