using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using Tiku.Api.Contracts; using Tiku.Application.PlatformBilling; using Tiku.Application.Security; using Tiku.Domain.Platform; namespace Tiku.Api.Controllers; [ApiController] [Route("api/tenant-billing")] [Authorize(Policy = BackendPermissions.TenantBillingManage)] public sealed class TenantBillingController( ITenantBillingService billingService, ICurrentAccessContext accessContext) : ControllerBase { [HttpGet("catalog")] public async Task Catalog(CancellationToken cancellationToken) => await billingService.GetCatalogAsync(await ActorAsync(cancellationToken), cancellationToken); [HttpPost("quotes")] public async Task Quote(CreatePlatformBillingQuoteDto request, CancellationToken cancellationToken) => await billingService.CreateQuoteAsync(await ActorAsync(cancellationToken), request.ToCommand(), cancellationToken); [HttpPost("orders")] public async Task CreateOrder(CreatePlatformBillingOrderDto request, CancellationToken cancellationToken) => await billingService.CreateOrderAsync(await ActorAsync(cancellationToken), request.ToCommand(), cancellationToken); [HttpPost("orders/{orderNo}/payments")] public async Task CreatePayment(string orderNo, CreatePlatformBillingPaymentDto request, CancellationToken cancellationToken) => await billingService.CreatePaymentAsync(await ActorAsync(cancellationToken), request.ToCommand(orderNo), cancellationToken); [HttpGet("orders")] public async Task> Orders(int limit = 100, CancellationToken cancellationToken = default) => await billingService.GetOrdersAsync(await ActorAsync(cancellationToken), limit, cancellationToken); [HttpGet("orders/{orderNo}")] public async Task Order(string orderNo, CancellationToken cancellationToken) => await billingService.GetOrderAsync(await ActorAsync(cancellationToken), orderNo, cancellationToken); [HttpGet("subscription")] public async Task Subscription(CancellationToken cancellationToken) => await billingService.GetSubscriptionAsync(await ActorAsync(cancellationToken), cancellationToken); [HttpPost("subscription/change")] public async Task Change(ChangeTenantSubscriptionDto request, CancellationToken cancellationToken) => await billingService.ChangeSubscriptionAsync(await ActorAsync(cancellationToken), request.ToCommand(), request.IdempotencyKey, cancellationToken); [HttpPost("subscription/renew")] public async Task Renew(IdempotentTenantBillingDto request, CancellationToken cancellationToken) => await billingService.RenewSubscriptionAsync(await ActorAsync(cancellationToken), request.IdempotencyKey, cancellationToken); [HttpPost("subscription/cancel")] public async Task Cancel(CancellationToken cancellationToken) => await billingService.CancelSubscriptionAsync(await ActorAsync(cancellationToken), cancellationToken); [HttpGet("usage")] public async Task> Usage(CancellationToken cancellationToken) => await billingService.GetUsageAsync(await ActorAsync(cancellationToken), cancellationToken); [HttpGet("invoices")] public async Task> Invoices(int limit = 100, CancellationToken cancellationToken = default) => await billingService.GetInvoicesAsync(await ActorAsync(cancellationToken), limit, cancellationToken); private async Task ActorAsync(CancellationToken cancellationToken) { var access = await accessContext.GetAsync(cancellationToken); return access.UserId is { } userId && access.TenantId is { } tenantId && access.IsCurrentTenantMember ? new TenantBillingActor(userId, tenantId) : throw new PlatformBillingException("Tenant billing actor was not resolved.", "tenant_access_denied"); } }