Add Stripe controller routes and full Plans CRUD in backoffice

This commit is contained in:
Tiago Yamamoto 2025-12-26 10:30:10 -03:00
parent ee5a680468
commit 03827302e5
4 changed files with 175 additions and 7 deletions

View file

@ -1,11 +1,30 @@
import { Controller, Get, Param } from '@nestjs/common';
import { ApiTags, ApiOperation } from '@nestjs/swagger';
import { PlansService } from './plans.service';
import { Controller, Get, Post, Patch, Delete, Param, Body, NotFoundException } from '@nestjs/common';
import { ApiTags, ApiOperation, ApiBody, ApiBearerAuth } from '@nestjs/swagger';
import { PlansService, Plan } from './plans.service';
class CreatePlanDto {
id: string;
name: string;
description: string;
monthlyPrice: number;
yearlyPrice: number;
features: string[];
popular?: boolean;
}
class UpdatePlanDto {
name?: string;
description?: string;
monthlyPrice?: number;
yearlyPrice?: number;
features?: string[];
popular?: boolean;
}
@ApiTags('Plans')
@Controller('plans')
export class PlansController {
constructor(private readonly plansService: PlansService) {}
constructor(private readonly plansService: PlansService) { }
@Get()
@ApiOperation({ summary: 'Get all plans' })
@ -16,6 +35,42 @@ export class PlansController {
@Get(':id')
@ApiOperation({ summary: 'Get plan by ID' })
getPlanById(@Param('id') id: string) {
return this.plansService.getPlanById(id);
const plan = this.plansService.getPlanById(id);
if (!plan) {
throw new NotFoundException(`Plan with ID ${id} not found`);
}
return plan;
}
@Post()
@ApiBearerAuth()
@ApiOperation({ summary: 'Create a new plan' })
@ApiBody({ type: CreatePlanDto })
createPlan(@Body() body: CreatePlanDto) {
return this.plansService.createPlan(body);
}
@Patch(':id')
@ApiBearerAuth()
@ApiOperation({ summary: 'Update a plan' })
@ApiBody({ type: UpdatePlanDto })
updatePlan(@Param('id') id: string, @Body() body: UpdatePlanDto) {
const plan = this.plansService.updatePlan(id, body);
if (!plan) {
throw new NotFoundException(`Plan with ID ${id} not found`);
}
return plan;
}
@Delete(':id')
@ApiBearerAuth()
@ApiOperation({ summary: 'Delete a plan' })
deletePlan(@Param('id') id: string) {
const deleted = this.plansService.deletePlan(id);
if (!deleted) {
throw new NotFoundException(`Plan with ID ${id} not found`);
}
return { message: `Plan ${id} deleted successfully` };
}
}

View file

@ -12,7 +12,7 @@ export interface Plan {
@Injectable()
export class PlansService {
private readonly plans: Plan[] = [
private plans: Plan[] = [
{
id: 'starter',
name: 'Starter',
@ -53,7 +53,32 @@ export class PlansService {
getAllPlans(): Plan[] {
return this.plans;
}
getPlanById(id: string): Plan | undefined {
return this.plans.find((p) => p.id === id);
}
createPlan(plan: Plan): Plan {
this.plans.push(plan);
return plan;
}
updatePlan(id: string, updates: Partial<Plan>): Plan | undefined {
const index = this.plans.findIndex((p) => p.id === id);
if (index === -1) {
return undefined;
}
this.plans[index] = { ...this.plans[index], ...updates };
return this.plans[index];
}
deletePlan(id: string): boolean {
const index = this.plans.findIndex((p) => p.id === id);
if (index === -1) {
return false;
}
this.plans.splice(index, 1);
return true;
}
}

View file

@ -0,0 +1,85 @@
import { Controller, Get, Post, Body, Param } from '@nestjs/common';
import { ApiTags, ApiOperation, ApiBody, ApiBearerAuth } from '@nestjs/swagger';
import { StripeService } from './stripe.service';
class CreateCustomerDto {
email: string;
name: string;
}
class CreateSubscriptionDto {
customerId: string;
priceId: string;
}
class CreateCheckoutDto {
customerId: string;
priceId: string;
successUrl: string;
cancelUrl: string;
}
class BillingPortalDto {
customerId: string;
returnUrl: string;
}
class CancelSubscriptionDto {
subscriptionId: string;
}
@ApiTags('Stripe')
@ApiBearerAuth()
@Controller('stripe')
export class StripeController {
constructor(private readonly stripeService: StripeService) { }
@Post('customers')
@ApiOperation({ summary: 'Create a Stripe customer' })
@ApiBody({ type: CreateCustomerDto })
async createCustomer(@Body() body: CreateCustomerDto) {
return this.stripeService.createCustomer(body.email, body.name);
}
@Post('subscriptions')
@ApiOperation({ summary: 'Create a subscription' })
@ApiBody({ type: CreateSubscriptionDto })
async createSubscription(@Body() body: CreateSubscriptionDto) {
return this.stripeService.createSubscription(body.customerId, body.priceId);
}
@Get('subscriptions/:customerId')
@ApiOperation({ summary: 'List subscriptions for a customer' })
async listSubscriptions(@Param('customerId') customerId: string) {
return this.stripeService.listSubscriptions(customerId);
}
@Post('subscriptions/cancel')
@ApiOperation({ summary: 'Cancel a subscription' })
@ApiBody({ type: CancelSubscriptionDto })
async cancelSubscription(@Body() body: CancelSubscriptionDto) {
return this.stripeService.cancelSubscription(body.subscriptionId);
}
@Post('checkout')
@ApiOperation({ summary: 'Create a checkout session' })
@ApiBody({ type: CreateCheckoutDto })
async createCheckoutSession(@Body() body: CreateCheckoutDto) {
return this.stripeService.createCheckoutSession(
body.customerId,
body.priceId,
body.successUrl,
body.cancelUrl,
);
}
@Post('billing-portal')
@ApiOperation({ summary: 'Create a billing portal session' })
@ApiBody({ type: BillingPortalDto })
async createBillingPortalSession(@Body() body: BillingPortalDto) {
return this.stripeService.createBillingPortalSession(
body.customerId,
body.returnUrl,
);
}
}

View file

@ -1,11 +1,14 @@
import { Module, Global } from '@nestjs/common';
import { ConfigModule } from '@nestjs/config';
import { StripeService } from './stripe.service';
import { StripeController } from './stripe.controller';
@Global()
@Module({
imports: [ConfigModule],
controllers: [StripeController],
providers: [StripeService],
exports: [StripeService],
})
export class StripeModule {}
export class StripeModule { }