import { Controller, Get, Post, Body, Headers, HttpCode, HttpStatus, NotFoundException, } from '@nestjs/common'; import { SubscriptionRepository } from '../../../infrastructure/repositories/subscription.repository'; import { PlanRepository } from '../../../infrastructure/repositories/plan.repository'; import { ChangePlanUseCase } from '../../../application/use-cases/change-plan.use-case'; import { CancelSubscriptionUseCase } from '../../../application/use-cases/cancel-subscription.use-case'; import { CheckTokenQuotaUseCase } from '../../../application/use-cases/check-token-quota.use-case'; import { InvoiceCurrency } from '../../../domain/entities/invoice.entity'; @Controller('api/v1/billing') export class SubscriptionController { constructor( private readonly subscriptionRepo: SubscriptionRepository, private readonly planRepo: PlanRepository, private readonly changePlan: ChangePlanUseCase, private readonly cancelSubscription: CancelSubscriptionUseCase, private readonly checkQuota: CheckTokenQuotaUseCase, ) {} @Get('subscription') async getSubscription(@Headers('x-tenant-id') tenantId: string) { const sub = await this.subscriptionRepo.findByTenantId(tenantId); if (!sub) throw new NotFoundException('No active subscription'); const plan = await this.planRepo.findById(sub.planId); const nextPlan = sub.nextPlanId ? await this.planRepo.findById(sub.nextPlanId) : null; return { id: sub.id, status: sub.status, plan: plan ? { name: plan.name, displayName: plan.displayName } : null, nextPlan: nextPlan ? { name: nextPlan.name, displayName: nextPlan.displayName } : null, currentPeriodStart: sub.currentPeriodStart, currentPeriodEnd: sub.currentPeriodEnd, trialEndsAt: sub.trialEndsAt, cancelAtPeriodEnd: sub.cancelAtPeriodEnd, cancelledAt: sub.cancelledAt, }; } @Post('subscription/upgrade') @HttpCode(HttpStatus.OK) async upgradePlan( @Headers('x-tenant-id') tenantId: string, @Body() body: { planName: string; currency?: 'USD' | 'CNY' }, ) { await this.changePlan.execute({ tenantId, newPlanName: body.planName, currency: body.currency === 'CNY' ? InvoiceCurrency.CNY : InvoiceCurrency.USD, }); return { success: true }; } @Post('subscription/cancel') @HttpCode(HttpStatus.OK) async cancelPlan( @Headers('x-tenant-id') tenantId: string, @Body() body: { immediate?: boolean }, ) { await this.cancelSubscription.execute(tenantId, body.immediate ?? false); return { success: true }; } @Get('usage/current') async getCurrentUsage(@Headers('x-tenant-id') tenantId: string) { return this.checkQuota.execute(tenantId); } }