import { Controller, Post, Get, Body, Param, HttpCode, HttpStatus, } from '@nestjs/common' import { SubscriptionsService } from './subscriptions.service' import type { CreateSubscriptionRequest, CreateSubscriptionWithPaymentRequest, CreateSubscriptionResponse, TierChangePreview, TierChangePreviewNewTierData, } from '@/providers/subscription.types' import { SubscriptionEntity } from '@/src/entities/subscription.entity' /** * Subscriptions Controller * * REST API for subscription lifecycle management. * Handles creation, cancellation, tier changes, 3DS flow, and provider sync. */ @Controller('subscriptions') export class SubscriptionsController { constructor(private readonly subscriptionsService: SubscriptionsService) {} /** * Create a subscription * * POST /subscriptions */ @Post() @HttpCode(HttpStatus.OK) async create(@Body() request: CreateSubscriptionRequest): Promise { return this.subscriptionsService.create(request) } /** * Create a subscription with inline card payment * * POST /subscriptions/with-payment */ @Post('with-payment') @HttpCode(HttpStatus.OK) async createWithPayment( @Body() request: CreateSubscriptionWithPaymentRequest, ): Promise { return this.subscriptionsService.createWithPayment(request) } /** * List subscriptions for a user * * GET /subscriptions/user/:userId * NOTE: Must be declared BEFORE :id route to avoid NestJS route shadowing */ @Get('user/:userId') async listByUser(@Param('userId') userId: string): Promise { return this.subscriptionsService.listByUser(userId) } /** * Get a subscription by ID * * GET /subscriptions/:id */ @Get(':id') async getById(@Param('id') id: string): Promise { return this.subscriptionsService.getById(id) } /** * Cancel a subscription * * POST /subscriptions/:id/cancel */ @Post(':id/cancel') @HttpCode(HttpStatus.OK) async cancel( @Param('id') id: string, @Body('cancelAtPeriodEnd') cancelAtPeriodEnd = true, ): Promise { return this.subscriptionsService.cancel(id, cancelAtPeriodEnd) } /** * Complete 3DS authentication for a pending subscription * * POST /subscriptions/:id/complete-3ds */ @Post(':id/complete-3ds') @HttpCode(HttpStatus.OK) async complete3DS(@Param('id') id: string): Promise { return this.subscriptionsService.complete3DS(id) } /** * Sync subscription state with payment provider * * POST /subscriptions/:id/sync */ @Post(':id/sync') @HttpCode(HttpStatus.OK) async sync(@Param('id') id: string): Promise { return this.subscriptionsService.sync(id) } /** * Change subscription tier * * POST /subscriptions/:id/change-tier */ @Post(':id/change-tier') @HttpCode(HttpStatus.OK) async changeTier( @Param('id') id: string, @Body('newTierId') newTierId: string, ): Promise { return this.subscriptionsService.changeTier(id, newTierId) } /** * Preview tier change (prorated amounts, effective dates) * * POST /subscriptions/:id/tier-change-preview/:newTierId */ @Post(':id/tier-change-preview/:newTierId') @HttpCode(HttpStatus.OK) async getTierChangePreview( @Param('id') id: string, @Param('newTierId') newTierId: string, @Body() newTierData?: TierChangePreviewNewTierData, ): Promise { return this.subscriptionsService.getTierChangePreview(id, newTierId, newTierData) } /** * Cancel a scheduled tier change * * POST /subscriptions/:id/cancel-tier-change */ @Post(':id/cancel-tier-change') @HttpCode(HttpStatus.OK) async cancelTierChange(@Param('id') id: string): Promise { return this.subscriptionsService.cancelTierChange(id) } }