Build your first store
By the end of this you will have a store with a product you made, a completed test
payment, and an order in the admin that Stripe’s webhook marked paid. It takes about
twenty minutes, most of it waiting for npm install.
You need Node 22, a Stripe account in test mode, and the Stripe CLI.
1. Run it
Section titled “1. Run it”git clone https://github.com/binx/beluga-v2.git my-storecd my-storenvm usenpm installnpm run setupSetup asks for a database (accept the SQLite default), your Stripe test secret key and publishable key, a public URL (leave the default while developing), an admin email and password, and a store name. Say yes to the demo catalogue; you will delete it in a moment, and it is useful to see a populated store first.
npm run dev:allOpen http://localhost:5173. That is the demo store. Open http://localhost:5173/admin and sign in.
2. Forward Stripe’s webhook
Section titled “2. Forward Stripe’s webhook”In a second terminal:
stripe listen --forward-to localhost:4000/api/webhooks/stripeCopy the whsec_… it prints into .env:
STRIPE_WEBHOOK_SECRET=whsec_...Restart npm run dev:all. Without this step everything will look like it works
and no order will ever be marked paid, because
only the webhook does that.
3. Make a product
Section titled “3. Make a product”Admin → Products → New product. The editor is one form that saves itself as you type; the indicator in the corner says when. Fill in:
- A name and a description. The slug is generated from the name.
- Type: physical.
- One option axis, say Size with values
SmallandLarge. The variant table below fills itself in with one row per value. Give each a price and a weight. - An image. Drop a JPEG or PNG; it is re-encoded to WebP, stripped of EXIF, and
resized copies are written for
srcset.
Set the product live, then click Publish. That is the first and only time anything is written to Stripe: a Product and one Price per variant appear in your test dashboard. Until you publish, a live product shows in the storefront but cannot be bought, and the dashboard says so.
Delete the demo products now if you like. Products → ⋯ → Delete names what is about to go and refuses an unknown id.
4. Add a shipping rate
Section titled “4. Add a shipping rate”A store with no rates ships everything free and nothing at checkout says so. The
admin Overview warns you. Admin → Shipping → Add rate: name it Standard, give
it a price, leave the zone and bounds empty. An unpinned, unbounded rate applies
everywhere, which is the whole configuration a flat-rate store needs.
5. Buy it
Section titled “5. Buy it”Back in the storefront, add your product to the cart. The cart asks which country you are shipping to; that is how a zone-priced store knows the postage before Stripe’s page exists. Check out, and on Stripe’s page use:
4242 4242 4242 4242 any future expiry any CVC any postcodeYou land on /confirm, which polls the order’s status until the webhook has
recorded payment. Your stripe listen terminal shows checkout.session.completed
arriving and a 200 going back.
6. See the order
Section titled “6. See the order”Admin → Orders. The order is paid, with the shipping address Stripe collected,
the line as it was bought, and stock decremented on the variant. Mark it
processing, then shipped with a carrier and tracking number. Each transition
sends an email, or logs one if SMTP_URL is not set yet.
Replay the webhook and prove idempotency to yourself:
stripe events resend <event id from the listen output>Stock does not move a second time.
Where to next
Section titled “Where to next”- Deploy it, because there is no store until it is somewhere.
- Restyle the storefront so it stops looking like the demo.
- Go live when it is real.