Skip to content

Plans & Prices

NuxtBase ships with three internal plan keys:

  • free
  • plus
  • pro

Those keys are not just display labels. They are used across billing logic, Stripe price mapping, subscription sync, and some feature entitlements.

The default plan definitions look like this:

const plans = [
{ key: "free", intervals: [], defaultCreditsQuota: 100 },
{ key: "plus", intervals: ["monthly", "yearly"], defaultCreditsQuota: 500 },
{ key: "pro", intervals: ["monthly", "yearly"], defaultCreditsQuota: 2000 },
];

What matters here:

  • free has no recurring intervals, so it is not a checkout target
  • plus and pro are the two paid plans the billing APIs understand out of the box
  • plus sits between free and pro in the default quota model
  • monthly and yearly billing are supported for both paid plans

The template resolves paid plans from four runtime config values:

Terminal window
NUXT_STRIPE_PRO_MONTHLY_PRICE_ID=price_...
NUXT_STRIPE_PRO_YEARLY_PRICE_ID=price_...
NUXT_STRIPE_PLUS_MONTHLY_PRICE_ID=price_...
NUXT_STRIPE_PLUS_YEARLY_PRICE_ID=price_...

At runtime, checkout and webhook sync use those price IDs to answer two questions:

  1. which Stripe price should be used for a new checkout
  2. which internal plan and interval a Stripe subscription belongs to

If the mapping is wrong, the app can still receive Stripe events, but it will not resolve plan identity correctly.

Billing is not editable by every member of an organization.

The template uses requireActiveBillingManager(), which means only these roles can start checkout or mutate subscriptions for the active organization:

  • owner
  • admin

Regular members can still open the billing page, but they get a view-only state in the dashboard UI.

The dashboard billing screen combines three kinds of data:

  1. the current effective plan for the active organization
  2. the current billing interval if the organization is on a paid plan
  3. usage UI such as project count and AI credits quota

When the API cannot find an operable subscription, it returns a free-plan response instead of an error:

const FREE_RESPONSE = {
status: "none",
planId: "free",
planName: "Free Plan",
isPro: false,
interval: null,
};

That is why a fresh organization still renders a usable billing page.

The plan-change endpoint supports an explicit priceId override only in development and test.

That is a useful local escape hatch when you are testing Stripe price experiments, but it is not part of the normal production flow. In production, the app expects the canonical env-based mapping to be correct.

If you want to customize plans, do it in this order:

  1. decide whether you are keeping free, plus, and pro or replacing the model
  2. update Stripe products and recurring prices
  3. update the env price ID mapping
  4. verify checkout creates the expected subscription
  5. verify the billing page shows the correct plan and interval after webhook sync