Stripe Setup
Getting your secret key, webhook secret, and price IDs, and setting a statement descriptor.
Turning on real billing takes three environment variables and one setting inside Stripe itself. Nothing here is needed to develop against the app: the Local fake covers that. This is the checklist for the point where you're ready to charge a real card.
The three variables
STRIPE_SECRET_KEY: start with a test key (sk_test_...) while you're integrating, and swap to a live key only when you're ready to charge real cards.STRIPE_WEBHOOK_SECRET: from your webhook endpoint at/webhooks/stripe. Locally, runstripe listen --forward-to localhost:3000/webhooks/stripeand use the secret it prints.- A price id per plan, for example
STRIPE_PRICE_PRO: the id of a recurring Price you create in Stripe, either directly in the dashboard or through the Stripe MCP server (see.mcp.json).
Secrets resolve ENV first, then Rails encrypted credentials (bin/rails credentials:edit) as a
fallback. Locally, put them in .env (copy .env.example as a starting point); in production,
inject them via Kamal secrets. See
First Deploy Checklist.
Set your statement descriptor before you charge anyone
By default, a customer's card statement shows your Stripe account's registered business name, not
your product's name. Someone who bought from yourapp.com sees an unfamiliar legal entity on their
statement, doesn't recognize it, and disputes the charge. Unrecognized descriptors are a leading
cause of chargebacks, and the risk compounds if one Stripe account serves more than one product.
For subscriptions, the descriptor comes from the Stripe Product (max 22 characters, shown in capitals), with precedence running invoice, then product, then account default. Set it on each product:
Stripe::Product.update("prod_...", statement_descriptor: "YOURAPP.COM")
Your domain is usually the clearest choice: it's what the customer remembers paying. A product
created inline from price_data cannot carry a descriptor at all, which is the reason to create
real Products and Prices in Stripe and reference their ids from environment variables, rather than
building line items inline at checkout time.
Webhook events to enable
Your Stripe webhook endpoint needs to receive subscription lifecycle events: created, updated,
canceled, and payment failures, at minimum. Missing one of these means the app's local billing
state can drift from what Stripe actually has on file, since entitled? reads that local state
rather than calling Stripe on every request. See
First Deploy Checklist for the full list this kit's own
deploy expects.
Verify
With test-mode keys in place, walk through a full subscribe-and-cancel cycle on the pricing page and confirm the webhook fires and updates entitlement correctly, before pointing the same setup at live keys.
Next
If you want to run a limited-time discount instead of (or alongside) your standard pricing, see Launch Pricing.