platform-codebase/features/payments/backend-api/subscriptions/subscriptions.controller.ts
Lilith 82e0e562ba chore(hooks): 🔧 Update TypeScript hook files (11 files)
Co-Authored-By: Lilith Autocommit <noreply@atlilith.com>
2026-02-19 00:57:42 -08:00

152 lines
3.9 KiB
TypeScript

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<CreateSubscriptionResponse> {
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<CreateSubscriptionResponse> {
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<SubscriptionEntity[]> {
return this.subscriptionsService.listByUser(userId)
}
/**
* Get a subscription by ID
*
* GET /subscriptions/:id
*/
@Get(':id')
async getById(@Param('id') id: string): Promise<SubscriptionEntity> {
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<SubscriptionEntity> {
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<SubscriptionEntity> {
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<SubscriptionEntity> {
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<SubscriptionEntity> {
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<TierChangePreview> {
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<SubscriptionEntity> {
return this.subscriptionsService.cancelTierChange(id)
}
}