Skip to main content

Overview

The definePlan() function defines a subscription plan with optional compile-time feature key restriction. When used with a generic type parameter, it ensures that only features defined in your configuration can be referenced.

Function Signature

function definePlan<
  F extends Record<string, FeatureDefInput> = Record<string, FeatureDefInput>
>(
  config: Omit<PlanDefInput, "features" | "prices"> & {
    features: F extends Record<string, FeatureDefInput>
      ? Partial<Record<keyof F, PlanFeatureValue>>
      : Record<string, PlanFeatureValue>;
    prices?: Array<
      Omit<PriceDef, "overage_configuration"> & {
        overage_configuration?: F extends Record<string, FeatureDefInput>
          ? Partial<Record<keyof F, { overage_amount: number; overage_unit: number }>>
          : Record<string, { overage_amount: number; overage_unit: number }>;
      }
    >;
  }
): PlanDefInput

Type Parameters

F
Record<string, FeatureDefInput>
default:"Record<string, FeatureDefInput>"
Feature dictionary type. Pass typeof yourFeatures for strict key validation. Omit for loose mode (backward compatible).

Parameters

config
PlanDefInput
required
The plan configuration object.

Returns

plan
PlanDefInput
The plan configuration, normalized to PlanDefInput type.

Usage

Strict Mode (Type-Safe Feature Keys)

import { defineConfig, defineFeature, definePlan } from "@revstack/core";

const features = {
  seats: defineFeature({
    name: "Team Seats",
    type: "static",
    unit_type: "count",
  }),
  storage: defineFeature({
    name: "Storage",
    type: "metered",
    unit_type: "bytes",
  }),
};

export default defineConfig({
  features,
  plans: {
    pro: definePlan<typeof features>({
      name: "Pro",
      is_default: false,
      is_public: true,
      type: "paid",
      features: {
        seats: { value_limit: 10 },
        storage: { value_limit: 100_000_000_000 },
        // typo_feature: { value_bool: true }, // ❌ Compile error!
      },
    }),
  },
});

Loose Mode (Backward Compatible)

import { definePlan } from "@revstack/core";

export const starter = definePlan({
  name: "Starter",
  is_default: true,
  is_public: true,
  type: "free",
  features: {
    seats: { value_limit: 3 },
    anything: { value_bool: true }, // ✅ Any key accepted
  },
});

Free Plan

import { definePlan } from "@revstack/core";

export const free = definePlan({
  name: "Free",
  description: "Perfect for individuals and small projects",
  is_default: true,
  is_public: true,
  type: "free",
  features: {
    seats: { value_limit: 1 },
    projects: { value_limit: 3 },
    storage: { value_limit: 1_000_000_000 }, // 1 GB
    api_calls: { 
      value_limit: 1000, 
      reset_period: "monthly",
      is_hard_limit: true,
    },
  },
  // No prices array for free plans
});
import { definePlan } from "@revstack/core";

export const pro = definePlan({
  name: "Pro",
  description: "For growing teams",
  is_default: false,
  is_public: true,
  type: "paid",
  features: {
    seats: { value_limit: 10 },
    projects: { value_limit: 50 },
    storage: { value_limit: 100_000_000_000 }, // 100 GB
    api_calls: { 
      value_limit: 50000, 
      reset_period: "monthly",
      is_hard_limit: false, // Allow overage
    },
  },
  prices: [
    {
      amount: 2900,
      currency: "USD",
      billing_interval: "monthly",
      trial_period_days: 14,
      available_addons: ["extra_seats"],
    },
    {
      amount: 29000,
      currency: "USD",
      billing_interval: "yearly",
      trial_period_days: 14,
      available_addons: ["extra_seats"],
    },
  ],
});

Plan with Overage Pricing

import { definePlan } from "@revstack/core";

export const business = definePlan({
  name: "Business",
  is_default: false,
  is_public: true,
  type: "paid",
  features: {
    api_calls: { 
      value_limit: 100000, 
      reset_period: "monthly",
      is_hard_limit: false, // Allow overage
    },
  },
  prices: [
    {
      amount: 9900,
      currency: "USD",
      billing_interval: "monthly",
      overage_configuration: {
        api_calls: {
          overage_amount: 10, // $0.10 per unit
          overage_unit: 1000, // Per 1000 API calls
        },
      },
    },
  ],
});

Enterprise/Custom Plan

import { definePlan } from "@revstack/core";

export const enterprise = definePlan({
  name: "Enterprise",
  description: "Custom pricing and unlimited resources",
  is_default: false,
  is_public: true,
  type: "custom", // No prices array, redirects to "Contact Sales"
  features: {
    seats: { value_bool: true }, // Unlimited
    projects: { value_bool: true },
    storage: { value_bool: true },
    sso: { value_bool: true },
  },
});

Source

Location: packages/core/src/define.ts:41-60

Build docs developers (and LLMs) love