/** * Subscription DTOs * * Data Transfer Objects for subscription management API */ import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger'; import { IsString, IsEnum, IsNotEmpty, IsUrl, IsOptional, IsBoolean, IsInt, Min, } from 'class-validator'; /** * Subscription plan types */ export enum SubscriptionPlanDto { FREE = 'FREE', STARTER = 'STARTER', PRO = 'PRO', ENTERPRISE = 'ENTERPRISE', } /** * Subscription status types */ export enum SubscriptionStatusDto { ACTIVE = 'ACTIVE', PAST_DUE = 'PAST_DUE', CANCELED = 'CANCELED', INCOMPLETE = 'INCOMPLETE', INCOMPLETE_EXPIRED = 'INCOMPLETE_EXPIRED', TRIALING = 'TRIALING', UNPAID = 'UNPAID', PAUSED = 'PAUSED', } /** * Billing interval types */ export enum BillingIntervalDto { MONTHLY = 'monthly', YEARLY = 'yearly', } /** * Create Checkout Session DTO */ export class CreateCheckoutSessionDto { @ApiProperty({ example: SubscriptionPlanDto.STARTER, description: 'The subscription plan to purchase', enum: SubscriptionPlanDto, }) @IsEnum(SubscriptionPlanDto) plan: SubscriptionPlanDto; @ApiProperty({ example: BillingIntervalDto.MONTHLY, description: 'Billing interval (monthly or yearly)', enum: BillingIntervalDto, }) @IsEnum(BillingIntervalDto) billingInterval: BillingIntervalDto; @ApiPropertyOptional({ example: 'https://app.xpeditis.com/dashboard/settings/subscription?success=true', description: 'URL to redirect to after successful payment', }) @IsUrl() @IsOptional() successUrl?: string; @ApiPropertyOptional({ example: 'https://app.xpeditis.com/dashboard/settings/subscription?canceled=true', description: 'URL to redirect to if payment is canceled', }) @IsUrl() @IsOptional() cancelUrl?: string; } /** * Create Portal Session DTO */ export class CreatePortalSessionDto { @ApiPropertyOptional({ example: 'https://app.xpeditis.com/dashboard/settings/subscription', description: 'URL to return to after using the portal', }) @IsUrl() @IsOptional() returnUrl?: string; } /** * Sync Subscription DTO */ export class SyncSubscriptionDto { @ApiPropertyOptional({ example: 'cs_test_a1b2c3d4e5f6g7h8', description: 'Stripe checkout session ID (used after checkout completes)', }) @IsString() @IsOptional() sessionId?: string; } /** * Checkout Session Response DTO */ export class CheckoutSessionResponseDto { @ApiProperty({ example: 'cs_test_a1b2c3d4e5f6g7h8', description: 'Stripe checkout session ID', }) sessionId: string; @ApiProperty({ example: 'https://checkout.stripe.com/pay/cs_test_a1b2c3', description: 'URL to redirect user to for payment', }) sessionUrl: string; } /** * Portal Session Response DTO */ export class PortalSessionResponseDto { @ApiProperty({ example: 'https://billing.stripe.com/session/test_YWNjdF8x', description: 'URL to redirect user to for subscription management', }) sessionUrl: string; } /** * License Response DTO */ export class LicenseResponseDto { @ApiProperty({ example: '550e8400-e29b-41d4-a716-446655440000', description: 'License ID', }) id: string; @ApiProperty({ example: '550e8400-e29b-41d4-a716-446655440001', description: 'User ID', }) userId: string; @ApiProperty({ example: 'john.doe@example.com', description: 'User email', }) userEmail: string; @ApiProperty({ example: 'John Doe', description: 'User full name', }) userName: string; @ApiProperty({ example: 'ADMIN', description: 'User role (ADMIN users have unlimited licenses)', }) userRole: string; @ApiProperty({ example: 'ACTIVE', description: 'License status', }) status: string; @ApiProperty({ example: '2025-01-15T10:00:00Z', description: 'When the license was assigned', }) assignedAt: Date; @ApiPropertyOptional({ example: '2025-02-15T10:00:00Z', description: 'When the license was revoked (if applicable)', }) revokedAt?: Date; } /** * Plan Details DTO */ export class PlanDetailsDto { @ApiProperty({ example: SubscriptionPlanDto.STARTER, description: 'Plan identifier', enum: SubscriptionPlanDto, }) plan: SubscriptionPlanDto; @ApiProperty({ example: 'Starter', description: 'Plan display name', }) name: string; @ApiProperty({ example: 5, description: 'Maximum number of licenses (-1 for unlimited)', }) maxLicenses: number; @ApiProperty({ example: 49, description: 'Monthly price in EUR', }) monthlyPriceEur: number; @ApiProperty({ example: 470, description: 'Yearly price in EUR', }) yearlyPriceEur: number; @ApiProperty({ example: ['Up to 5 users', 'Advanced rate search', 'CSV imports'], description: 'List of features included in this plan', type: [String], }) features: string[]; } /** * Subscription Response DTO */ export class SubscriptionResponseDto { @ApiProperty({ example: '550e8400-e29b-41d4-a716-446655440000', description: 'Subscription ID', }) id: string; @ApiProperty({ example: '550e8400-e29b-41d4-a716-446655440001', description: 'Organization ID', }) organizationId: string; @ApiProperty({ example: SubscriptionPlanDto.STARTER, description: 'Current subscription plan', enum: SubscriptionPlanDto, }) plan: SubscriptionPlanDto; @ApiProperty({ description: 'Details about the current plan', type: PlanDetailsDto, }) planDetails: PlanDetailsDto; @ApiProperty({ example: SubscriptionStatusDto.ACTIVE, description: 'Current subscription status', enum: SubscriptionStatusDto, }) status: SubscriptionStatusDto; @ApiProperty({ example: 3, description: 'Number of licenses currently in use', }) usedLicenses: number; @ApiProperty({ example: 5, description: 'Maximum licenses available (-1 for unlimited)', }) maxLicenses: number; @ApiProperty({ example: 2, description: 'Number of licenses available', }) availableLicenses: number; @ApiProperty({ example: false, description: 'Whether the subscription is scheduled for cancellation', }) cancelAtPeriodEnd: boolean; @ApiPropertyOptional({ example: '2025-01-01T00:00:00Z', description: 'Start of current billing period', }) currentPeriodStart?: Date; @ApiPropertyOptional({ example: '2025-02-01T00:00:00Z', description: 'End of current billing period', }) currentPeriodEnd?: Date; @ApiProperty({ example: '2025-01-01T00:00:00Z', description: 'When the subscription was created', }) createdAt: Date; @ApiProperty({ example: '2025-01-15T10:00:00Z', description: 'When the subscription was last updated', }) updatedAt: Date; } /** * Subscription Overview Response DTO (includes licenses) */ export class SubscriptionOverviewResponseDto extends SubscriptionResponseDto { @ApiProperty({ description: 'List of active licenses', type: [LicenseResponseDto], }) licenses: LicenseResponseDto[]; } /** * Can Invite Response DTO */ export class CanInviteResponseDto { @ApiProperty({ example: true, description: 'Whether the organization can invite more users', }) canInvite: boolean; @ApiProperty({ example: 2, description: 'Number of available licenses', }) availableLicenses: number; @ApiProperty({ example: 3, description: 'Number of used licenses', }) usedLicenses: number; @ApiProperty({ example: 5, description: 'Maximum licenses allowed (-1 for unlimited)', }) maxLicenses: number; @ApiPropertyOptional({ example: 'Upgrade to Starter plan to add more users', description: 'Message explaining why invitations are blocked', }) message?: string; } /** * All Plans Response DTO */ export class AllPlansResponseDto { @ApiProperty({ description: 'List of all available plans', type: [PlanDetailsDto], }) plans: PlanDetailsDto[]; }