Skip to content
Ricardo Brossard
Projects

Zizu

A delivery platform for every shop in a city, with online payments.

Multi-merchant delivery, ArgentinaIn production

The customer screen with the delivery address, the categories, a free delivery promotion and the open shops with their delivery times
end-to-end tests on every change, plus 57 unit tests
152
versioned migrations that rebuild the whole database
118
tables with role-based permissions, with 64 policies
30
stages delivered, audited and billed
4
Order XS6JA goes through Zizu's real screens. The customer orders from La Nonna Pizzeria, the shop receives and accepts it, the courier takes it and the customer follows it live.Order XS6JA goes through Zizu's real screens. The customer orders from La Nonna Pizzeria, the shop receives and accepts it, the courier takes it and the customer follows it live.Order XS6JA goes through Zizu's real screens. The customer orders from La Nonna Pizzeria, the shop receives and accepts it, the courier takes it and the customer follows it live.Order XS6JA goes through Zizu's real screens. The customer orders from La Nonna Pizzeria, the shop receives and accepts it, the courier takes it and the customer follows it live.
One order on the customer, shop and courier screens, with every change shown instantly.

The problem

In a small city, ordering food for delivery means calling or messaging each shop separately on WhatsApp. The shop writes it down by hand, looks for someone to take the order and collects payment however it can. The customer has no idea where their order is, and nobody knows how much each shop is owed.

The brief was a single app for every shop in the city, with its own couriers and payment through Mercado Pago or in cash.

What I built

Zizu, a delivery platform where the customer orders, the shop prepares, the courier delivers and the admin oversees everything, each on their own screen and watching the same order update live. I built it from scratch, end to end, and delivered it in four stages, each one audited and billed.

It is a single web application in React 18 with Vite and TypeScript, installable on a phone, with four areas depending on the role. Each area loads separately and is turned on with a switch per stage: the code of an area that is turned off never even reaches the browser.

The entire backend lives in Supabase, with 90 database functions, 5 Edge Functions and 3 scheduled jobs with pg_cron:

  • The rules live in the database, not in the screens. Row Level Security on all 30 tables, with 64 policies: a shop cannot read another shop’s orders, and a courier who is removed instantly loses access to their customers’ data.
  • The order state machine runs on the server. Pending, accepted, preparing, ready, on the way, delivered: each role can only run its own transitions, even if someone calls the API by hand.
  • Real time. A new order rings on the shop’s screen without reloading, “ready” shows up in the couriers’ list and the customer sees every step.
  • The database calculates the price, with options and extras per product. The total always comes from the server.

It also handles the day-to-day operation: batches of up to 4 orders per courier, opening hours with exceptions that expire on their own, settlements that count only what was collected, and append-only auditing on 9 tables.

The hardest part

Charging only once, with every peso accounted for. The payment notice reaches an Edge Function that validates the signature, discards duplicates (Mercado Pago retries every 15 minutes), checks the payment directly with the Mercado Pago API and hands the fact to a database function that decides: credit it, reject it, record a chargeback or a refund. The notice carries; the database decides.

If the customer retries the payment or opens two tabs, they get the same charge: I store the live attempt in the database and, if it exists, Mercado Pago is not even called. I decided this after testing it, because Mercado Pago’s idempotency key does not cover creating the charge. Everything ends up in an append-only payment ledger, protected by triggers: an entry cannot be edited or deleted, not even with direct database access, and the commission is sealed into each order.

Getting the courier to the right door. To turn a typed address into a point on the map, I built two layers with a cache: OpenStreetMap first and Google as a fallback. In a system like this, the dangerous failure is the false positive: a query with only a house number returned a door in another city 500 km away, and Google flagged it with its highest precision. That is why every result is validated against the town, the distance to the center, the street name and the house number, and if it does not pass, it moves to the next layer. No house number, no pin: a doubtful point is worse than none, because the courier trusts it. The checks are pure functions, with unit tests on the city’s real street map.

How it is tested

Every change runs on GitHub Actions: 57 unit tests, type checking, a build and 152 end-to-end tests with Playwright in a real browser. They run against a separate test environment identical to production, built with the same 118 migrations. Every push to the main branch is published automatically on Vercel.

  • Real concurrency. One test has two couriers take the same order at once: taking it is a single atomic operation in the database, and only one wins.
  • Payments, case by case. Underpayment and overpayment, chargebacks, refunds that respect the valid payment, retries and the abandoned checkout that cancels itself after 30 minutes.
  • Isolated data. The suite creates its own test data, deletes it when it finishes and fails if the real payment ledger grew during the run.

How it works

  1. 01The customer orderscart and options
  2. 02Total in the databaseserver-side price
  3. 03Locates the addresscache, OSM, Google
  4. 04Mercado Pagoor cash payment
  5. 05Payment noticevalidates, dedupes
  6. 05Shop acceptslive alert
  7. 06Payment ledgerappend-only
  8. 06Courier takes itatomic operation
  9. 07Deliveredevery step live
  10. 07Settlementonly what was paid

Results

  • Four screens, one order

    Customer, shop, courier and admin see every status change instantly, without reloading.

  • Payments that add up

    Mercado Pago confirmed server to server, with refunds and chargebacks, and a payment ledger that cannot be edited.

  • One order, one courier

    If two couriers tap "Take" at the same time, only one wins. An automated test checks it.

  • Reliable addresses

    Every location is validated before it is accepted, and the courier reaches the door the customer typed.

  • Up to 4 orders per trip

    The shop groups orders into a batch, and the courier takes the whole batch in a single operation.

  • Installs like an app

    It works on Android and iPhone without going through the app stores, and updates itself after every release.

Stack

Application
React 18ViteTypeScriptTailwind CSSTanStack QueryZustandLeafletPWA with Workbox
Data and backend
SupabasePostgreSQLRow Level SecurityRealtimeAuthStorageEdge Functions on Denopg_cron
Integrations
Mercado PagoOpenStreetMapGoogle GeocodingResend
Quality and deployment
Playwrightnode:testGitHub ActionsVercel
On the left, the shop screen receives a new order to accept or reject; on the right, the customer follows the same order step by step, live