152 lines
3.9 KiB
TypeScript
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)
|
|
}
|
|
}
|