Subscription Plans
Configure the subscription plans available to users.
Configure Subscription Plans
Subscription plans are defined in packages/utils/src/constants/billing.ts:
const _plans = [
// ! DO NOT REMOVE THE FREE PLAN
// (you can customize it)
{
name: "Free", // Can be customized. IMPORTANT: If you change this value, you must also update the default plan in the database schema (schema.prisma, line 27) so it matches.
lookupKey: "free", // ! DO NOT CHANGE THIS VALUE
features: ["Basic features", "Community support", "1 project"], // Can be customized
// The application reacts to the intervals configured in the Free plan
// Available options:
// 1. `intervals: false` - No intervals (no different prices for month/year)
// 2. `intervals: { [BillingInterval.MONTH]: {}, [BillingInterval.YEAR]: {} }` - Different prices for month/year (lookupKey will be automatically generated as `free_month` and `free_year`)
// 3. `intervals: { [BillingInterval.MONTH]: { lookupKey: "pro_custom_month" }, [BillingInterval.YEAR]: { lookupKey: "pro_custom_year" } }` - Different prices for month/year with custom lookup keys (you can add a custom lookup key only for one interval)
intervals: {
[BillingInterval.MONTH]: {},
[BillingInterval.YEAR]: {},
},
},
// Your plans
// (you can add/remove/customize them)
{
name: "Pro",
lookupKey: "pro",
features: ["Everything in Free", "Priority support", "10 projects"],
intervals: {
[BillingInterval.MONTH]: {},
[BillingInterval.YEAR]: {},
},
},
{
name: "Ultimate",
lookupKey: "ultimate",
features: ["Everything in Pro", "Dedicated support", "Unlimited projects"],
intervals: {
[BillingInterval.MONTH]: {},
[BillingInterval.YEAR]: {},
},
},
] as const satisfies readonly Plan[];The Free plan is required and must not be removed.
You can customize its name and features, but if you change the name, update the default plan in the database schema (schema.prisma, line 27) so it matches. Do not change the lookup key of the Free plan.
| Property | Description |
|---|---|
name | Display name of the plan. |
lookupKey | Used to match the plan with Stripe products. Lookup keys for intervals are auto-generated (e.g., pro_month, pro_year). |
features | List of features displayed on the pricing page. |
intervals | Billing intervals. Set to false for no intervals. You can optionally provide custom lookup keys per interval. |
The subscription plans are flexible. You can add, remove, or customize them as you need (e.g., change the name, lookup key, features, or intervals).
The application reacts to the intervals configured in the Free plan.
If you set intervals to false in the Free plan but set { [BillingInterval.MONTH]: {}, [BillingInterval.YEAR]: {} } in any other plan, the application will not have intervals.
Let's look at a few examples.
Example 1: No Intervals
const _plans = [
{
name: "Free",
lookupKey: "free",
features: ["Basic features", "Community support", "1 project"],
intervals: false,
},
{
name: "Pro",
lookupKey: "pro",
features: ["Everything in Free", "Priority support", "10 projects"],
intervals: false,
},
{
name: "Ultimate",
lookupKey: "ultimate",
features: ["Everything in Pro", "Dedicated support", "Unlimited projects"],
intervals: false,
},
] as const satisfies readonly Plan[];In this example, the application will not have intervals.
When pushing the plans to Stripe, the user will be prompted to choose the interval applied to all plans and the price of each plan.
Example 2: Different Prices for Month and Year
const _plans = [
{
name: "Free",
lookupKey: "free",
features: ["Basic features", "Community support", "1 project"],
intervals: {
[BillingInterval.MONTH]: {},
[BillingInterval.YEAR]: {},
},
},
{
name: "Pro",
lookupKey: "pro",
features: ["Everything in Free", "Priority support", "10 projects"],
intervals: {
[BillingInterval.MONTH]: {},
[BillingInterval.YEAR]: {},
},
},
{
name: "Ultimate",
lookupKey: "ultimate",
features: ["Everything in Pro", "Dedicated support", "Unlimited projects"],
intervals: {
[BillingInterval.MONTH]: {},
[BillingInterval.YEAR]: {},
},
},
] as const satisfies readonly Plan[];In this example, the application will have different prices for month and year.
When pushing the plans to Stripe, the user will be prompted to choose the price of each plan for each interval.
The lookup keys for the prices will be generated automatically as pro_month, pro_year, ultimate_month, and ultimate_year. If you set a custom lookup key for a plan, it will generate the lookup keys for the intervals as custom_month and custom_year.
Example 3: Different Prices for Month and Year with Lookup Keys
const _plans = [
{
name: "Free",
lookupKey: "free",
features: ["Basic features", "Community support", "1 project"],
intervals: {
[BillingInterval.MONTH]: {
lookupKey: "free_custom_month",
},
[BillingInterval.YEAR]: {
lookupKey: "free_custom_year",
},
},
},
{
name: "Pro",
lookupKey: "pro",
features: ["Everything in Free", "Priority support", "10 projects"],
intervals: {
[BillingInterval.MONTH]: {
lookupKey: "pro_custom_month",
},
[BillingInterval.YEAR]: {
lookupKey: "pro_custom_year",
},
},
},
{
name: "Ultimate",
lookupKey: "ultimate",
features: ["Everything in Pro", "Dedicated support", "Unlimited projects"],
intervals: {
[BillingInterval.MONTH]: {
lookupKey: "ultimate_custom_month",
},
[BillingInterval.YEAR]: {
lookupKey: "ultimate_custom_year",
},
},
},
] as const satisfies readonly Plan[];In this example, the application will have different prices for month and year.
When pushing the plans to Stripe, the user will be prompted to choose the price of each plan for each interval.
In this case, the lookup keys for the prices will be pro_custom_month, pro_custom_year, ultimate_custom_month, and ultimate_custom_year. The application will not generate lookup keys.
Example 4: Mixed
const _plans = [
{
name: "Free",
lookupKey: "free",
features: ["Basic features", "Community support", "1 project"],
intervals: {
[BillingInterval.MONTH]: {},
[BillingInterval.YEAR]: {},
},
},
{
name: "Pro",
lookupKey: "pro",
features: ["Everything in Free", "Priority support", "10 projects"],
intervals: {
[BillingInterval.MONTH]: {
lookupKey: "pro_custom_month",
},
[BillingInterval.YEAR]: {},
},
},
{
name: "Ultimate",
lookupKey: "ultimate",
features: ["Everything in Pro", "Dedicated support", "Unlimited projects"],
intervals: {
[BillingInterval.MONTH]: {},
[BillingInterval.YEAR]: {
lookupKey: "ultimate_custom_year",
},
},
},
] as const satisfies readonly Plan[];In this example, the application will have different prices for month and year.
When pushing the plans to Stripe, the user will be prompted to choose the price of each plan for each interval.
The lookup keys you configured will be used as you defined them and the ones you did not configure will be generated automatically.
In this case, the lookup keys for the prices will be pro_custom_month, pro_year, ultimate_month, and ultimate_custom_year.
Example 5: Invalid Configuration
const _plans = [
{
name: "Free",
lookupKey: "free",
features: ["Basic features", "Community support", "1 project"],
intervals: {
[BillingInterval.MONTH]: {},
[BillingInterval.YEAR]: {},
},
},
{
name: "Pro",
lookupKey: "pro",
features: ["Everything in Free", "Priority support", "10 projects"],
intervals: false,
},
{
name: "Ultimate",
lookupKey: "ultimate",
features: ["Everything in Pro", "Dedicated support", "Unlimited projects"],
intervals: {
[BillingInterval.MONTH]: {},
[BillingInterval.YEAR]: {
lookupKey: "ultimate_custom_year",
},
},
},
] as const satisfies readonly Plan[];In this example, the configuration is invalid because the application reacts to the intervals configured in the Free plan and the Pro plan is missing the intervals configuration.