Stripe setup
Beluga uses Stripe Checkout Sessions: the hosted page owns the card fields, 3-D Secure, wallets and address collection, which keeps a Beluga store at PCI SAQ-A.
What lives where
Section titled “What lives where”| In Stripe | In Beluga |
|---|---|
| A Product per published product, a Price per variant | The catalogue, drafts included, and every image |
| Payments, refunds, disputes | Orders, fulfilment status, tracking |
| Promotion codes and coupons | The discount amount an order used |
| Tax registrations, rates, calculation | Whether tax is on, and how prices are quoted |
| Customers, if you enable them | Customer accounts and address books |
| Inventory. Stripe Prices have no stock concept. |
Three environment variables. STRIPE_SECRET_KEY is server-only and never reaches
a browser. STRIPE_PUBLISHABLE_KEY is public by design and is served to the
storefront through /api/store. STRIPE_WEBHOOK_SECRET verifies inbound events.
npm run setup validates the secret key against Stripe before storing it and says
plainly whether it is a live one. A store without any key browses and carts but
cannot take money, and the admin says so.
Test and live keys have separate catalogues. Everything published under a test key exists only in the test account. Going live covers the swap.
Publishing
Section titled “Publishing”Products reach Stripe only when explicitly published. Saving a product never writes to Stripe. Publishing creates or updates a Product and one Price per variant, with the store’s tax behaviour and the product’s tax code, and records the ids.
Prices are immutable in Stripe. Changing an amount, or the tax behaviour, mints a new Price and archives the old one; historic orders keep resolving against the archived one. Nothing republishes itself.
Webhooks
Section titled “Webhooks”Locally, forward events with the CLI and put the printed secret in .env:
stripe listen --forward-to localhost:4000/api/webhooks/stripeDeployed, create an endpoint in the dashboard at
https://your-store/api/webhooks/stripe for checkout.session.completed,
checkout.session.expired and charge.refunded. Restart the API after setting
the secret. Stripe webhooks (inbound) has what
each one does.
What Beluga deliberately does not do with Stripe
Section titled “What Beluga deliberately does not do with Stripe”- No
ui_mode: 'elements'. Owning the checkout page would put card fields, 3-D Secure and PCI scope back on your server. This is what rules out live carrier rates; see Shipping. - No Stripe Shipping Rates objects. Beluga’s rates are passed inline on the session, so there is nothing to keep in sync.
- No Stripe-side inventory. There is no such thing.
- No subscriptions. One-off payments only.