Skip to content

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.

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.

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.

Locally, forward events with the CLI and put the printed secret in .env:

Terminal window
stripe listen --forward-to localhost:4000/api/webhooks/stripe

Deployed, 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.