Billing Setup
NuxtBase ships with a Stripe-based subscription flow for the example SaaS inside the template.
At the code level, the template expects:
- a Stripe secret key
- a Stripe publishable key
- a Stripe webhook secret
- four recurring Stripe Price IDs
Those Price IDs map the template’s paid sample plans:
promonthlyproyearlyplusmonthlyplusyearly
Start in Stripe Sandboxes
Section titled “Start in Stripe Sandboxes”Stripe now recommends Sandboxes for testing. They are the preferred testing environment over the older test mode.
Use a sandbox while you build and verify your integration:
- sandbox API keys do not move real money
- sandbox products, prices, customers, and subscriptions are isolated from live mode
- sandbox settings are better isolated than the older shared test-mode behavior
- sandbox works with the same test card numbers and webhook testing flows you already expect
For this setup page, treat sandbox as your default development environment. Do not start with live mode.
Required Billing Variables
Section titled “Required Billing Variables”NUXT_STRIPE_SECRET_KEY=sk_test_...NUXT_STRIPE_WEBHOOK_SECRET=whsec_...NUXT_PUBLIC_STRIPE_PUBLISHABLE_KEY=pk_test_...
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_...Without the Price IDs, checkout cannot resolve which Stripe price belongs to which plan and interval.
In sandbox, these values usually look like:
NUXT_STRIPE_SECRET_KEY=sk_test_...NUXT_PUBLIC_STRIPE_PUBLISHABLE_KEY=pk_test_...
In production, you must switch to the live equivalents:
NUXT_STRIPE_SECRET_KEY=sk_live_...NUXT_PUBLIC_STRIPE_PUBLISHABLE_KEY=pk_live_...
Before You Skip Billing Setup
Section titled “Before You Skip Billing Setup”If you are not enabling Stripe yet, do not leave the copied placeholder key in .env.
After copying .env.example, remove or blank out the billing values:
# NUXT_STRIPE_SECRET_KEY=# NUXT_STRIPE_WEBHOOK_SECRET=# NUXT_PUBLIC_STRIPE_PUBLISHABLE_KEY=# NUXT_STRIPE_PRO_MONTHLY_PRICE_ID=# NUXT_STRIPE_PRO_YEARLY_PRICE_ID=# NUXT_STRIPE_PLUS_MONTHLY_PRICE_ID=# NUXT_STRIPE_PLUS_YEARLY_PRICE_ID=This matters because the billing service checks whether NUXT_STRIPE_SECRET_KEY
is non-empty. A fake placeholder like sk_test_... is still non-empty, so the
template will try to initialize Stripe instead of using the mock fallback.
How Plan Mapping Works
Section titled “How Plan Mapping Works”The mapping is static in the template source:
pro->stripeProMonthlyPriceId/stripeProYearlyPriceIdplus->stripePlusMonthlyPriceId/stripePlusYearlyPriceId
That means your Stripe dashboard must contain four recurring prices that match those sample plans.
Step 1: Create Products and Prices in a Sandbox
Section titled “Step 1: Create Products and Prices in a Sandbox”Inside your Stripe sandbox, create your paid plans and add recurring prices for:
promonthlyproyearlyplusmonthlyplusyearly
Then copy each price_... ID into the matching environment variable.
Step 2: Install Stripe CLI
Section titled “Step 2: Install Stripe CLI”Stripe CLI is the standard tool for local webhook testing.
Official install guide:
On macOS with Homebrew:
brew install stripe/stripe-cli/stripeThen authenticate:
stripe loginStep 3: Start the App
Section titled “Step 3: Start the App”pnpm devnpm run devyarn devStep 4: Forward Stripe Webhooks Locally
Section titled “Step 4: Forward Stripe Webhooks Locally”NuxtBase verifies Stripe signatures from the raw request body, so webhook forwarding must be correct.
Use Stripe CLI:
stripe listen --forward-to localhost:3000/api/billing/webhookStripe CLI prints a temporary webhook secret. Copy that value into:
NUXT_STRIPE_WEBHOOK_SECRET=whsec_...If you restart stripe listen, you usually get a new secret. Update .env again before testing more webhook flows.
Step 5: Test the Checkout Flow in a Sandbox
Section titled “Step 5: Test the Checkout Flow in a Sandbox”Stripe’s official testing environments support mock payment behavior without real charges.
Use a sandbox card for a basic successful payment:
4242 4242 4242 4242Stripe also provides additional test card numbers and test scenarios for:
- card declines
- authentication flows such as 3DS
- insufficient funds
- other common payment outcomes
Official testing docs:
The intended local test path is:
- register a user
- open the dashboard billing area
- start checkout for a paid plan
- complete the Stripe test checkout
- confirm Stripe sends the webhook
- verify subscription data appears back in the app
The return URLs in the template go back to the billing page with query params such as:
/dashboard/billing?checkout=success/dashboard/billing?checkout=canceledIf you want to test recurring billing behavior over time, Stripe also recommends using sandbox tools such as test clocks where applicable.
Important Local Fallback Behavior
Section titled “Important Local Fallback Behavior”If NUXT_STRIPE_SECRET_KEY is blank or removed, the billing service falls back to mock checkout and mock portal URLs.
Leaving the placeholder sk_test_... in .env does not count as “missing”.
That is useful for:
- exploring the UI
- avoiding hard blockers on day one
But it is not enough for validating:
- real subscription creation
- webhook processing
- Stripe customer portal behavior
- billing event logs
For real billing verification, you need a real Stripe sandbox account and real Stripe test objects inside that sandbox.
Production Go-Live
Section titled “Production Go-Live”After your sandbox flow works, switch to production deliberately.
That means:
- use live API keys instead of sandbox keys
- create or verify the live products and four live recurring prices
- register a real production webhook endpoint in Stripe
- replace the local CLI webhook secret with the production endpoint secret
- redeploy the app with live billing configuration
- run a real production smoke test yourself
Your production smoke test should confirm:
- checkout opens from the real app
- payment completes with your real production configuration
- Stripe sends the production webhook successfully
- the app reflects the subscription correctly after the webhook
- customer portal and billing pages still work
Stripe API Versions: v1 and v2
Section titled “Stripe API Versions: v1 and v2”Stripe currently has both API v1 and API v2 namespaces.
For this billing setup page, the important distinction is:
- the common billing resources used here, such as Products, Prices, Checkout Sessions, Subscriptions, Customers, and most webhook flows, are still documented in Stripe’s established API v1 namespace
- Stripe API v2 exists and Stripe supports using v1 and v2 in the same integration, but v2 follows different request and event patterns
The practical guidance for NuxtBase is:
- keep this billing setup aligned with the Stripe Billing resources the template already uses
- prefer the latest official Stripe SDK and Stripe CLI versions
- prefer the latest Stripe API version that your account and integration are ready to support
- if you intentionally adopt newer Stripe features that depend on v2-specific behavior, review the v2 docs separately before changing your integration assumptions
Official versioning docs:
Billing Setup Checklist
Section titled “Billing Setup Checklist”Use this checklist before you trust the integration:
- you are building in a Stripe sandbox, not live mode
NUXT_STRIPE_SECRET_KEYis a sandbox or live key from the correct Stripe accountNUXT_PUBLIC_STRIPE_PUBLISHABLE_KEYmatches the same account- all four
price_...IDs exist and are recurring prices - sandbox test cards work for the expected payment scenarios
- Stripe CLI forwards events to
/api/billing/webhook NUXT_STRIPE_WEBHOOK_SECRETmatches the current CLI session or production endpoint- production uses separate live keys, live prices, and a live webhook endpoint
- you have personally tested the production billing flow after deployment
Official Stripe Docs
Section titled “Official Stripe Docs”Stripe changes quickly, so keep the official docs close while configuring billing: