179 lines
3.4 KiB
TypeScript
179 lines
3.4 KiB
TypeScript
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;
|
||
}
|