Skip to content

Stripe

Stripe is the kit’s default payment provider. To have Polar take payments instead, as merchant of record, see its page.

Without Stripe keys the kit runs with billing off: everyone is on the free plan. This page connects Stripe. To decide what you sell and gate features by plan, see Charge for a feature.

You need a Stripe account and the Stripe CLI.

  1. Put your test secret key from the dashboard in .env:

    Terminal window
    STRIPE_SECRET_KEY=sk_test_...
  2. Stripe cannot reach your machine, so forward its webhooks. Leave this running:

    Terminal window
    bun run billing:listen

    It prints a signing secret. Put it in .env as STRIPE_WEBHOOK_SECRET=whsec_.... The API refuses to start with the key and without this.

  3. Create your plans in Stripe:

    Terminal window
    bun run billing:sync

    It creates a product per paid plan with a price per interval, and for each meter a plan charges for, a meter and a metered price. Run it again whenever you change PLANS.

  4. Restart bun dev, open Settings → Billing and upgrade with the card 4242 4242 4242 4242, any future date and any CVC.

The plan changes a moment after you return from Stripe, when the webhook arrives. If it does not, billing:listen is not running. The receipt lands in Mailpit.

  1. In Stripe’s live mode, copy your live secret key.
  2. Add a webhook endpoint for https://<your domain>/api/billing/webhook with these events:
    • checkout.session.completed
    • every customer.subscription.* event
    • invoice.paid, which sends receipts
  3. Set STRIPE_SECRET_KEY to the live key and STRIPE_WEBHOOK_SECRET to that endpoint’s signing secret in production. A secret from test mode, or from the CLI, will not verify.
  4. Run bun run billing:sync once with the live key in your environment.
  5. Turn on the customer portal and allow plan changes. People change plan, update their card and cancel there.
  6. Buy a plan on the live site with a real card, change it, and cancel it.

Step 6 is the one that proves your own setup: your keys, your webhook and your prices together. Do it before you announce.

The units beyond a plan’s allowance are sent as meter events and appear on the next invoice, as a second line beside the plan’s. Stripe’s dashboard shows each customer’s events under Billing → Meters. Events can be sent up to 35 days late, so a day of retries loses nothing. How usage works: Charge for usage.

Stripe prices cannot be edited. billing:sync creates a new price and archives the old one. New subscriptions get the new price; people already subscribed keep paying the old one until you move them in Stripe.

If something does not work, see Troubleshooting.