Payments
elements man payments Read as markdownTake card payments with Stripe Checkout. Stripe hosts the checkout page and holds the card data, so the app never touches a card number.
The owner's whole part is one value: a Stripe secret key. Everything else the app does itself:
- Prices come from the app's own rows. Checkout Sessions carry them inline
(
price_data), so nobody creates products or copies price ids in the Stripe dashboard. - The app fulfills a payment when the buyer returns, by asking Stripe for the session's state, and again when the webhook arrives. Both paths call one idempotent function, so development needs no webhook at all.
- In production the app registers its own webhook endpoint through the Stripe
API, the first time a buyer starts a checkout, and keeps the signing secret.
Nobody copies a
whsec_value.
Until the key is set, the app still takes payments in development. The pay button opens a test checkout page inside the app: the order summary, the total, and a Pay button that records the payment through the same function a real payment uses. Every flow after payment (paid status, receipts, live updates) works and can be tested before Stripe exists. Once the key is set, the same button goes to real Stripe Checkout. Production never runs without a key: the build fails when it is missing, and the app refuses to start when it is empty.
The official stripe npm package works as is: elements install stripe. Build
the pages in order: setup, checkout, webhook, then subscriptions if the
app sells recurring plans.
Never ask the user to paste a secret key into chat or into a committed file, and never put a secret key in browser-reachable code. Never collect a card number in the app, not even on the test checkout page.