Skip to content
Victor Ly

Balobrador ordering and payments

The ordering and card-payment system behind balobrador.es, a cheesecake shop near Madrid: catalogue, cart, pickup scheduling and Stripe Checkout. I built it alone, it has taken real payments since October 2025, and I still run it.

Visit balobrador.es
  • Stripe
  • Astro
  • React
  • TypeScript
  • Netlify Functions
  • Node.js
  • Zustand
  • Tailwind

What it does

Balobrador sells cheesecakes in three sizes, and cookies, for collection from its shop in Rivas-Vaciamadrid. The site carries the whole order: a catalogue, a cart that survives a closed tab, a pickup calendar that offers only dates the kitchen can meet, a Stripe-hosted card checkout, and confirmation emails to the customer and the shop once payment completes. Pages are static Astro with React only where something has to respond; the server side is a handful of Netlify Functions. There is no database: product files in the repository are the source of truth, and every price change, new flavour or closure ships as a commit that deploys on push.

The server decides what things cost

Checkout does not trust the basket the browser sends. A build step turns the product files into a price catalogue keyed by product and size, and the checkout function rebuilds every Stripe line item from it: price, name and category come from the server, quantities are clamped integers, the minimum order is checked against catalogue prices, and a pickup date inside a closure is refused before any payment session exists. Prices include 10% VAT and convert exactly to the whole cents Stripe charges. The webhook verifies Stripe's signature and builds the order emails only from the session it verified.

Pickup rules as code

The kitchen prepares weekend orders as one batch from Friday, so Saturday, Sunday and Monday collections share a single cutoff: Thursday at 18:30, Madrid time. Tuesday to Friday collections need two days' notice, with the same 18:30 cutoff. I wrote the rule as days of notice rather than weekday names, so it holds across week boundaries. A closure is one dated entry in a data file: it greys out the calendar, puts a notice on every page and in the confirmation email, makes the checkout function refuse those dates, and publishes special opening hours in the structured data. A malformed range fails the build.

Running it

I have run it since launch, through a price rise, seasonal flavours and a two-week summer closure, and two lessons are now part of the process. Carts are saved in the browser with a price snapshot, so a price rise also bumps a schema version that empties saved baskets, rather than showing one price and charging another. Three deploys once failed silently while Netlify kept serving the previous build: my installed packages had drifted ahead of the lockfile, and I had used a type the pinned version does not have. Type-level changes now start from a clean install, and the build check has to finish with zero errors, warnings and hints.