All worknestonexStart a project
SELECTED WORK/16/BACKEND ENGINEERING

Food delivery marketplace

A three-sided food-delivery marketplace, end to end.

Project
Backend engineering
Year
2024–25
Our focus
Marketplace backendPaymentsNotifications

How do you serve diners, restaurants and riders from a single backend?

Give each audience its own front door and one shared API behind it. Diners, restaurants and riders authenticate through separate surfaces but operate on the same orders, described by a single OpenAPI spec. Rider payouts run through Stripe Connect, with transfers gated on onboarding eligibility.

Food delivery marketplace architecture: three auth surfaces over one API, an order state machine, Stripe Connect payouts gated on eligibility, and per-audience notifications
System architecture · Food delivery

01 /Why three auth surfaces instead of one with roles?

Because the three audiences have genuinely different signup journeys. A diner needs to order in under a minute; a rider needs identity documents and a payout account before they can earn anything; a restaurant sits somewhere between.

Forcing all three through one flow makes it wrong for everyone. Separate surfaces over one API keeps the onboarding appropriate to each while the orders underneath stay shared.

02 /What does an order state machine buy you?

Predictability in a system where three parties are all changing the same record. Placed, accepted, prepared, picked up, delivered: each transition is explicit, and invalid ones are impossible rather than merely unlikely.

Without it, concurrent updates from a restaurant and a rider produce states nobody designed. A state machine is the cheapest way to make that class of bug not exist.

03 /Why must rider transfers wait on eligibility?

Because paying someone who has not completed onboarding fails at the payment provider, and by then the order is delivered and the rider is expecting money.

Stripe Connect requires an onboarded account with verified details before transfers are permitted. Gating on eligibility checks that before promising anything, so the failure surfaces during onboarding rather than at payout.

04 /Why give each audience its own notification model?

Because the same event means different things. An accepted order is reassurance to a diner, a task to a restaurant, and an opportunity to a rider.

One generic notification table serving all three collapses into conditional logic everywhere. Dedicated models per audience keep the right message going to the right app without a growing pile of special cases.

05 /What does the OpenAPI spec do for a three-client platform?

It keeps three clients honest against one contract. With a diner app, a restaurant app and a rider app all consuming the same API, the spec is the shared reference that stops each drifting toward its own assumptions.

An admin console sits over the whole thing, so operations has one place to see orders, users and payouts rather than three.

06 /What we delivered

  • Three-sided marketplace API
  • Stripe Connect rider onboarding and payouts
  • Eligibility-gated transfers with webhooks
  • Multi-audience push notifications

07 /The outcome

A complete food-delivery backend with an OpenAPI spec, connecting diners, restaurants and riders under one admin console.

08 /Build at a glance

SidesDiners, restaurants, riders
AuthenticationThree separate surfaces over one API
OrdersExplicit state machine from placed to delivered
DispatchRider assignment with live tracking
PaymentsDiner checkout plus Stripe Connect rider payouts
Payout controlTransfers gated on onboarding eligibility
WebhooksPayout confirmation handling
NotificationsDedicated model per audience
ContractOpenAPI spec, single admin console
COMMON QUESTIONS

Questions people actually ask

How do you handle authentication in a three-sided marketplace?

Separate signup surfaces over a shared API. Diners need to order in under a minute, riders need identity documents and a payout account first, and restaurants sit in between. One flow for all three is wrong for everyone, but the orders underneath should stay shared.

Why use an order state machine in a delivery platform?

Because three parties change the same record concurrently. Making each transition explicit means invalid states are impossible rather than merely unlikely, which removes an entire class of bug that otherwise appears only under load.

How does Stripe Connect handle rider payouts?

Riders are onboarded as connected accounts with verified details, and transfers are only permitted once that onboarding is complete. Checking eligibility before promising payment means the failure surfaces during onboarding rather than after a delivery is already done.

Should notifications be shared across marketplace audiences?

Better to model them per audience. The same event means different things to a diner, a restaurant and a rider, and one generic table serving all three turns into conditional logic spread everywhere. Dedicated models keep the right message going to the right app.

THE WORK BEHIND IT

Services this project drew on

ANOTHER PERSPECTIVE / NEXT PROJECTWen

One account for perps, spot and copy-trading — and an AI that can trade for you.