Skip to content

Billing Issues

Most billing confusion in NuxtBase comes from one of two misunderstandings:

  1. checkout does not directly create the local subscription record
  2. non-empty Stripe keys change the runtime path even if the values are fake

Problem: Checkout Opens, But Billing Still Looks Like free

Section titled “Problem: Checkout Opens, But Billing Still Looks Like free”

This usually means the Stripe redirect succeeded, but webhook processing did not complete.

In the template:

  • checkout creates a Stripe Checkout Session and returns a URL
  • local subscription creation and updates are mainly done by the Stripe webhook
  • only some direct mutations, like change-plan, also sync local state during the mutation itself
  1. confirm the Stripe checkout session completed in Stripe
  2. confirm your webhook endpoint is reachable
  3. confirm NUXT_STRIPE_WEBHOOK_SECRET matches the current Stripe listener or dashboard endpoint
  4. inspect webhook logs before changing billing UI code

Problem: Stripe Webhook Returns Invalid signature

Section titled “Problem: Stripe Webhook Returns Invalid signature”

This usually happens because the webhook secret does not match the current listener or endpoint secret.

Typical server-side responses:

  • 400 No body
  • 400 No signature
  • 400 Invalid signature
  • 500 Webhook processing failed

If you restart stripe listen, Stripe gives you a new signing secret. If you do not update NUXT_STRIPE_WEBHOOK_SECRET and reload the server, webhook verification fails even though checkout itself still works.

Problem: Checkout Fails With “Stripe price is not configured”

Section titled “Problem: Checkout Fails With “Stripe price is not configured””

This means the template could not resolve a Stripe price ID for the selected plan and interval.

Typical error:

Stripe price is not configured for plan "pro" (monthly)

Check the matching env variables for the plan you are trying to sell:

  • NUXT_STRIPE_PRO_MONTHLY_PRICE_ID
  • NUXT_STRIPE_PRO_YEARLY_PRICE_ID
  • NUXT_STRIPE_PLUS_MONTHLY_PRICE_ID
  • NUXT_STRIPE_PLUS_YEARLY_PRICE_ID

If you are not ready to use real Stripe yet, make sure the Stripe secret key is actually blank so mock checkout is the active path.

Problem: Billing Is Using Stripe When You Expected Mock Checkout

Section titled “Problem: Billing Is Using Stripe When You Expected Mock Checkout”

BillingService initializes Stripe whenever NUXT_STRIPE_SECRET_KEY is a non-empty string.

That means a fake placeholder key is enough to move you off the mock path.

Use an empty value, not a fake one:

NUXT_STRIPE_SECRET_KEY=
NUXT_PUBLIC_STRIPE_PUBLISHABLE_KEY=
NUXT_STRIPE_WEBHOOK_SECRET=

Problem: Portal, Cancel, Resume, Or Change-Plan Returns 404

Section titled “Problem: Portal, Cancel, Resume, Or Change-Plan Returns 404”

These actions expect an existing operable subscription or customer record.

Typical causes:

  • checkout never completed
  • webhook never synced the subscription into the local database
  • you are testing against the wrong active organization

Typical API messages include:

  • No active subscription found to update
  • No active subscription found to cancel
  • No active subscription found to resume

Problem: The User Already Has A Subscription, But Checkout Still Gets Triggered

Section titled “Problem: The User Already Has A Subscription, But Checkout Still Gets Triggered”

NuxtBase guards checkout with a local active-subscription lookup.

If checkout says:

An active subscription already exists for this organization

that is the template protecting you from double-checkout for the same organization.

Check whether:

  • the active organization is the one you expected
  • the subscription was already synced by a prior webhook
  • you should be using change-plan instead of a new checkout flow
  1. confirm which organization is active
  2. confirm whether Stripe is in real mode or mock mode
  3. confirm the plan has a price ID
  4. confirm checkout completes in Stripe
  5. confirm webhook delivery succeeds and local sync follows