mirror of
https://github.com/Memo-2023/mana-monorepo.git
synced 2026-05-17 07:59:41 +02:00
- Add StripeService for PaymentIntent creation and webhook verification - Add credit purchase flow (POST /credits/purchase) - Add stripe_customers table for Stripe customer mapping - Add subscriptions schema (plans, subscriptions, invoices) - Add SubscriptionsService with Checkout, Portal, Cancel, Reactivate - Add subscription plans (Free: 150 Mana, Pro: €9.99, Enterprise: €49.99) - Handle subscription and invoice webhooks - Update roadmap with completed tasks Credit pricing: 1 Mana = 1 Cent (no volume discounts)
103 lines
4 KiB
TypeScript
103 lines
4 KiB
TypeScript
import { Controller, Get, Post, Body, Param, UseGuards, Query } from '@nestjs/common';
|
|
import { ApiTags, ApiOperation, ApiResponse, ApiBearerAuth } from '@nestjs/swagger';
|
|
import { SubscriptionsService } from './subscriptions.service';
|
|
import { JwtAuthGuard } from '../common/guards/jwt-auth.guard';
|
|
import { CurrentUser } from '../common/decorators/current-user.decorator';
|
|
import type { CurrentUserData } from '../common/decorators/current-user.decorator';
|
|
import { CreateCheckoutSessionDto } from './dto/create-checkout-session.dto';
|
|
import { CreatePortalSessionDto } from './dto/create-portal-session.dto';
|
|
|
|
@ApiTags('subscriptions')
|
|
@Controller('subscriptions')
|
|
export class SubscriptionsController {
|
|
constructor(private readonly subscriptionsService: SubscriptionsService) {}
|
|
|
|
// ============================================================================
|
|
// PUBLIC ENDPOINTS
|
|
// ============================================================================
|
|
|
|
@Get('plans')
|
|
@ApiOperation({ summary: 'Get all available subscription plans' })
|
|
@ApiResponse({ status: 200, description: 'Returns list of active plans' })
|
|
async getPlans() {
|
|
return this.subscriptionsService.getPlans();
|
|
}
|
|
|
|
@Get('plans/:planId')
|
|
@ApiOperation({ summary: 'Get a specific plan' })
|
|
@ApiResponse({ status: 200, description: 'Returns plan details' })
|
|
@ApiResponse({ status: 404, description: 'Plan not found' })
|
|
async getPlan(@Param('planId') planId: string) {
|
|
return this.subscriptionsService.getPlan(planId);
|
|
}
|
|
|
|
// ============================================================================
|
|
// PROTECTED ENDPOINTS
|
|
// ============================================================================
|
|
|
|
@Get('current')
|
|
@UseGuards(JwtAuthGuard)
|
|
@ApiBearerAuth('JWT-auth')
|
|
@ApiOperation({ summary: 'Get current subscription' })
|
|
@ApiResponse({ status: 200, description: 'Returns current subscription and plan' })
|
|
async getCurrentSubscription(@CurrentUser() user: CurrentUserData) {
|
|
return this.subscriptionsService.getCurrentSubscription(user.userId);
|
|
}
|
|
|
|
@Post('checkout')
|
|
@UseGuards(JwtAuthGuard)
|
|
@ApiBearerAuth('JWT-auth')
|
|
@ApiOperation({ summary: 'Create Stripe checkout session for subscription' })
|
|
@ApiResponse({ status: 201, description: 'Returns checkout session URL' })
|
|
async createCheckoutSession(
|
|
@CurrentUser() user: CurrentUserData,
|
|
@Body() dto: CreateCheckoutSessionDto
|
|
) {
|
|
return this.subscriptionsService.createCheckoutSession(
|
|
user.userId,
|
|
dto.planId,
|
|
dto.billingInterval,
|
|
dto.successUrl,
|
|
dto.cancelUrl
|
|
);
|
|
}
|
|
|
|
@Post('portal')
|
|
@UseGuards(JwtAuthGuard)
|
|
@ApiBearerAuth('JWT-auth')
|
|
@ApiOperation({ summary: 'Create Stripe Customer Portal session' })
|
|
@ApiResponse({ status: 201, description: 'Returns portal URL for billing management' })
|
|
async createPortalSession(
|
|
@CurrentUser() user: CurrentUserData,
|
|
@Body() dto: CreatePortalSessionDto
|
|
) {
|
|
return this.subscriptionsService.createPortalSession(user.userId, dto.returnUrl);
|
|
}
|
|
|
|
@Post('cancel')
|
|
@UseGuards(JwtAuthGuard)
|
|
@ApiBearerAuth('JWT-auth')
|
|
@ApiOperation({ summary: 'Cancel subscription at period end' })
|
|
@ApiResponse({ status: 200, description: 'Subscription scheduled for cancellation' })
|
|
async cancelSubscription(@CurrentUser() user: CurrentUserData) {
|
|
return this.subscriptionsService.cancelSubscription(user.userId);
|
|
}
|
|
|
|
@Post('reactivate')
|
|
@UseGuards(JwtAuthGuard)
|
|
@ApiBearerAuth('JWT-auth')
|
|
@ApiOperation({ summary: 'Reactivate a canceled subscription' })
|
|
@ApiResponse({ status: 200, description: 'Subscription reactivated' })
|
|
async reactivateSubscription(@CurrentUser() user: CurrentUserData) {
|
|
return this.subscriptionsService.reactivateSubscription(user.userId);
|
|
}
|
|
|
|
@Get('invoices')
|
|
@UseGuards(JwtAuthGuard)
|
|
@ApiBearerAuth('JWT-auth')
|
|
@ApiOperation({ summary: 'Get invoice history' })
|
|
@ApiResponse({ status: 200, description: 'Returns list of invoices' })
|
|
async getInvoices(@CurrentUser() user: CurrentUserData, @Query('limit') limit?: number) {
|
|
return this.subscriptionsService.getInvoices(user.userId, limit);
|
|
}
|
|
}
|