import { IsString, IsOptional, IsEnum, IsDateString, IsInt, } from "class-validator"; import { ApiProperty, ApiPropertyOptional } from "@nestjs/swagger"; import { Exclude, Expose, Type } from "class-transformer"; export enum PlanType { FREE = "free", PLUS = "plus", PREMIUM = "premium", } export enum BillingIntervalType { MONTHLY = "monthly", YEARLY = "yearly", } /** * Plan feature limits configuration */ export const PLAN_LIMITS: Record< PlanType, { maxAnalyses: number; maxCoupons: number } > = { [PlanType.FREE]: { maxAnalyses: 3, maxCoupons: 1 }, [PlanType.PLUS]: { maxAnalyses: 25, maxCoupons: 5 }, [PlanType.PREMIUM]: { maxAnalyses: 999, maxCoupons: 999 }, }; /** * Plan display information */ export interface PlanInfo { id: PlanType; name: string; description: string; monthlyPrice: number; yearlyPrice: number; currency: string; features: string[]; limits: { maxAnalyses: number; maxCoupons: number }; highlighted: boolean; } export const PLANS: readonly PlanInfo[] = [ { id: PlanType.FREE, name: "Free", description: "Temel analiz özellikleri", monthlyPrice: 0, yearlyPrice: 0, currency: "TRY", features: ["Günlük 3 analiz", "Günlük 1 kupon", "Temel maç istatistikleri"], limits: PLAN_LIMITS[PlanType.FREE], highlighted: false, }, { id: PlanType.PLUS, name: "Plus", description: "Detaylı analiz ve daha fazla kupon", monthlyPrice: 99, yearlyPrice: 999, currency: "TRY", features: [ "Günlük 25 analiz", "Günlük 5 kupon", "AI detaylı analiz", "H2H karşılaştırma", "Reklamsız deneyim", ], limits: PLAN_LIMITS[PlanType.PLUS], highlighted: true, }, { id: PlanType.PREMIUM, name: "Premium", description: "Sınırsız erişim ve özel özellikler", monthlyPrice: 249, yearlyPrice: 2499, currency: "TRY", features: [ "Sınırsız analiz", "Sınırsız kupon", "AI detaylı analiz", "H2H karşılaştırma", "Kupon Builder", "Spor Toto analiz", "Reklamsız deneyim", "Öncelikli destek", ], limits: PLAN_LIMITS[PlanType.PREMIUM], highlighted: false, }, ] as const; // ── Response DTOs ── @Exclude() export class UsageLimitResponseDto { @Expose() analysisCount: number; @Expose() couponCount: number; @Expose() maxAnalyses: number; @Expose() maxCoupons: number; } @Exclude() export class SubscriptionResponseDto { @Expose() id: string; @Expose() plan: string; @Expose() billingInterval: string | null; @Expose() currentPeriodStart: Date | null; @Expose() currentPeriodEnd: Date | null; @Expose() cancelledAt: Date | null; @Expose() cancelEffectiveDate: Date | null; @Expose() paddlePriceId: string | null; @Expose() createdAt: Date; @Expose() updatedAt: Date; } // ── Request DTOs ── export class CreateCheckoutDto { @ApiProperty({ enum: PlanType, example: PlanType.PLUS, description: "Target plan", }) @IsEnum(PlanType) plan: PlanType; @ApiProperty({ enum: BillingIntervalType, example: BillingIntervalType.MONTHLY, description: "Billing interval", }) @IsEnum(BillingIntervalType) billingInterval: BillingIntervalType; } export class CancelSubscriptionDto { @ApiPropertyOptional({ description: "Reason for cancellation", example: "Too expensive", }) @IsOptional() @IsString() reason?: string; }