Labels.io

Stripe billing

The kit ships Stripe billing built in. /app/billing shows the current plan, payment method, and invoice history; the pricing page and landing plan grid have checkout buttons. Until you add the Stripe keys, everything runs in demo mode — the pricing buttons fall back to their demo links (/register · /contact) and /app/billing shows mock data. This page turns it on.

What you get once it is wired:

  • Checkout — pricing CTAs open Stripe Checkout for the selected plan.
  • Customer Portal — the "Manage subscription" button opens Stripe's hosted portal (change card, upgrade/downgrade, cancel, download invoices).
  • Live billing state — current plan, payment method, and invoices read from Stripe.
  • Webhook — subscription changes sync back to the app.

1. Install + keys

Stripe ships with the kit, so there is nothing to install. Create a Stripe account, then copy this file's keys into .env:

VITE_STRIPE_PUBLISHABLE_KEY=pk_test_xxxxxxxxxxxxxxxxxxxxxxxx
STRIPE_SECRET_KEY=sk_test_xxxxxxxxxxxxxxxxxxxxxxxx
STRIPE_WEBHOOK_SECRET=whsec_xxxxxxxxxxxxxxxxxxxxxxxx
  • VITE_STRIPE_PUBLISHABLE_KEY is the browser's signal that billing is live — when it is unset, every pricing CTA stays in demo mode.
  • STRIPE_SECRET_KEY and STRIPE_WEBHOOK_SECRET are server-only; never expose them to the client.

Find both keys in the Stripe dashboard under Developers → API keys. Use the test mode keys while developing.

2. Create prices and map them

In the Stripe dashboard, open Product catalog and create a recurring Price for each plan you sell, then copy each price_… id into .env. The key format is STRIPE_PRICE_<PLAN>_<INTERVAL>:

STRIPE_PRICE_STARTER_MONTHLY=price_xxxxxxxxxxxx
STRIPE_PRICE_PRO_MONTHLY=price_xxxxxxxxxxxx
STRIPE_PRICE_PRO_YEARLY=price_xxxxxxxxxxxx
STRIPE_PRICE_TEAM_MONTHLY=price_xxxxxxxxxxxx
STRIPE_PRICE_BUSINESS_MONTHLY=price_xxxxxxxxxxxx
STRIPE_PRICE_BUSINESS_YEARLY=price_xxxxxxxxxxxx

The pricing page tiers (Starter / Pro / Team) use the *_MONTHLY ids; the landing plan grid (Pro / Business) uses MONTHLY or YEARLY based on its monthly/yearly toggle. Only set the keys for plans you actually offer — extras can stay unset.

3. Set up the webhook

The kit exposes a webhook route at POST /api/stripe/webhook. It verifies the signature with STRIPE_WEBHOOK_SECRET and keeps the user's plan in sync when a subscription is created, updated, or canceled.

Locally, forward events with the Stripe CLI and use the signing secret it prints:

stripe listen --forward-to localhost:5173/api/stripe/webhook

In production, add an endpoint in Developers → Webhooks pointing at https://yourdomain.com/api/stripe/webhook, subscribe to checkout.session.completed and customer.subscription.updated / customer.subscription.deleted, then copy that endpoint's signing secret into STRIPE_WEBHOOK_SECRET.

4. Test the setup

  1. Restart the dev server after editing .env.
  2. Open /pricing — the plan buttons now start a real Stripe Checkout instead of the demo link.
  3. Subscribe with the test card 4242 4242 4242 4242 (any future expiry, any CVC).
  4. After checkout, /app/billing shows the live plan, payment method, and invoice.
  5. Click Manage subscription — the Stripe Customer Portal opens; cancel or switch plans there.

Production checklist

Before going live:

  • Swap the test keys (pk_test_… / sk_test_…) for live keys (pk_live_… / sk_live_…).
  • Recreate your prices in live mode and update the STRIPE_PRICE_* ids.
  • Add the production webhook endpoint and set STRIPE_WEBHOOK_SECRET to its signing secret.
  • Confirm the Customer Portal is enabled in Settings → Billing → Customer portal.

Useful docs:

Other providers

The billing UI is provider-shaped (hosted checkout, customer portal, subscription state, invoices, webhooks). If you prefer Lemon Squeezy, Polar, or Paddle, keep the same /app/billing screen and map that provider's hosted checkout, portal, and webhook onto the same seams.

On this page