Marketplace · Track-Day Booking Case study

BookRevs

A European motorcycle track-day marketplace where riders discover and book events, organizers manage capacity and are paid, and the platform mediates the money between them.

Marketplace · Track-Day Booking

BookRevs BookRevs

Senior Full-Stack Engineer · Marketplace Engineering

System map · Platform topology

Production scope

  • Two parties, one ledger Bookings, payments and refunds stay reconcilable
  • Cannot be oversold Physical capacity, held by waitlist offers

Marketplace platform

  1. Who acts

    Rider

    Organizer

    Administrator

  2. Shared platform

    Discovery, track-day catalogue and booking workflows

  3. Platform services

    Capacity

    Waitlist offers

    Payments

    Connected accounts

    Cancellation

    Reviewed requests

  4. Payment ledger

    Movements recorded, never inferred from booking status

Production operations

  • Idempotent payment webhooks
  • Transactional email
  • Five-language routing
  • Structured logging
Product surface
The public BookRevs homepage: a track-day search across event name, dates, track and difficulty level, above a list of upcoming events.

Engineering

Next.js · TypeScript · PostgreSQL · Prisma · Stripe

Public product · Proprietary source code

Role
Senior Full-Stack Engineer · Marketplace Engineering
Disclosure
Public product · Proprietary source code

The problem

What the system had to solve.

A track-day marketplace moves money between two parties who do not know each other. A rider pays the platform, an organizer is paid by the platform, and the platform has to be able to explain every movement afterwards — during a refund, a cancellation or a dispute.

Capacity is physical and cannot be oversold, payment state is decided by an external provider that retries and duplicates its notifications, and the market spans several countries and languages.

What I owned

The responsibility behind the work.

Delivered customer-facing booking workflows, the payment and webhook integration, organizer and administrative functionality, and the production reliability of the transaction paths.

Built booking and discovery journeys, the marketplace payment integration with connected organizer accounts, the payment ledger and webhook handling, capacity and waitlist behaviour, cancellation and support workflows, and the administrative surface behind them.

Engineering approach

How it was built.

Payments run through connected organizer accounts with a dedicated ledger recording movements rather than inferring them from booking status. Incoming payment webhooks are verified and recorded before being applied, so redelivery cannot double-confirm a booking. Full events place riders on a waitlist and freed places become time-bounded offers rather than automatic charges, and cancellations are modelled as reviewable requests with a financial record.

Booking path

  1. Discover
  2. Book
  3. Pay
  4. Confirm
  5. Attend

A three-sided marketplace

Three audiences with opposing interests.

A rider wants to find and book a track day, and to get their money back if it is cancelled. An organizer wants to fill an event, control capacity and be paid. The platform has to hold both positions at once, take its cut, and stay correct when they conflict — because a booking is a financial commitment on both sides.

That is what separates a marketplace from a booking form. Money moves between two parties who do not trust each other, mediated by a platform that has to be able to explain every movement afterwards.

Who acts

Riders

  • Discover and filter events
  • Book and pay
  • Join waitlists
  • Request cancellation

Organizers

  • Create and schedule events
  • Set capacity and add-ons
  • Manage bookings
  • Receive payouts

Administrators

  • Oversee the platform
  • Invite organizers
  • Handle support tickets
  • Resolve disputes

Moving money between strangers

Why marketplace payments are their own architecture.

A rider pays the platform; an organizer is paid by the platform. Those are not the same transaction, and treating them as one is how marketplaces end up unable to explain their own balances. Payments run through Stripe with connected accounts for organizers and payment intents for riders, so the money has an owner at every point.

Payment path

  1. Rider books Payment intent created
  2. Payment confirmed
  3. Booking becomes firm
  4. Platform records the movement
  5. Organizer paid through their connected account
Two sides, one mediated flow. The platform is a party to the transaction rather than a passive redirect.

A dedicated payment ledger records movements rather than inferring them from booking status. A booking tells you what someone asked for; a ledger tells you what actually happened to the money, which is the only thing that helps during a dispute.

Webhooks are not requests

Taking payment events from an external system safely.

Payment state is decided by the payment provider, not by the application, and it arrives as webhooks. Webhooks retry, arrive out of order, and can be delivered more than once for the same event. A handler that assumes otherwise will double-confirm a booking or double-count a payment.

Received events are recorded, so a redelivered webhook is recognised as one already processed rather than applied a second time.

Webhook handling

  1. Webhook received
  2. Signature verified
  3. Already processed? Stop
  4. Record the event
  5. Apply the state change
Recording the event before acting on it is what makes at-least-once delivery safe on the payment path.

Superseded payment intents are retained rather than deleted. When a payment is retried or replaced, the abandoned attempt is still part of the story of that booking.

Capacity, waitlists and offers

What happens when an event is full.

A track day has hard physical capacity — a finite number of riders can be on a circuit safely. Selling one place twice is not a bug that can be smoothed over afterwards. When capacity is reached, riders join a waitlist rather than being turned away.

When a place frees up

  1. A place becomes available
  2. Next waitlist entry identified
    1. Offer made Time-bounded
    2. Claimed → booking
    1. Offer expires
    2. Place returns to the pool
A waitlist entry is not a booking. An offer is a time-bounded right to claim a place, which then either converts or returns to the pool.

Events carry schedules, images, links and optional add-ons that riders select alongside the booking itself, so an add-on is priced and recorded as part of the same commercial transaction rather than bolted on beside it.

Cancellations

The path that has to work when everything else went wrong.

Track days are cancelled — weather, circuit availability, an organizer withdrawing. Cancellation is where a marketplace either keeps its riders or loses them, and it is a request with a decision and a financial consequence rather than a button that deletes a row.

Cancellation path

  1. Cancellation requested
  2. Reviewed against event and policy
  3. Decision recorded
  4. Refund movement recorded in the ledger
  5. Both parties notified
Modelled as a request with a lifecycle, so there is a record of what was asked, what was decided and what was refunded.

Support tickets are part of the same system rather than an external inbox, so a dispute about a booking sits next to the booking it concerns.

Getting in, and getting invited

Two different onboarding problems.

Riders self-register and verify with a one-time code. Organizers cannot: an organizer can take other people’s money, so they arrive by invitation with a single-use token rather than by signing up. The two paths are deliberately different because the trust required is different.

Two ways onto the platform

Riders

  • Self-registration
  • One-time code verification
  • Session-based authentication

Organizers

  • Invitation only
  • Single-use invite token
  • Connected payment account required
Open where it is safe to be open, closed where money and capacity are at stake.

Built for more than one market

Internationalisation as a structural choice.

European track days are not a single-language market. The application is internationalised across five languages from the routing layer down, rather than having translation retrofitted, which is the difference between adding a market and rebuilding for one.

Supported languages

  • English
  • German
  • Spanish
  • French
  • Italian

Also structural

  • GDPR cookie consent with granular preferences
  • Transactional email templates
  • Generated booking documents
  • Structured logging

Engineering scope

What I worked on.

  1. Rider booking and discovery journeys
  2. Marketplace payments with connected organizer accounts
  3. Payment ledger and idempotent webhook handling
  4. Capacity, waitlists and time-bounded offers
  5. Cancellation, refund and support workflows
  6. Organizer invitation and administrative functionality
  7. Internationalisation across five languages

Why these decisions mattered

The trade-offs behind the marketplace.

  • A payment ledger, not derived state

    Decision
    Money movements are recorded in their own ledger.
    Why
    Booking status tells you what was asked for. Only a ledger tells you what happened to the money.
  • Webhook events recorded before applied

    Decision
    Every received payment event is recorded and checked for reprocessing.
    Why
    Providers retry and duplicate. Without this, a redelivery double-confirms a booking.
  • Waitlist offers, not automatic booking

    Decision
    A freed place becomes a time-bounded offer.
    Why
    Nobody should be charged for a place they no longer want, and a place should not be held indefinitely.
  • Organizers by invitation

    Decision
    Organizer accounts are created through single-use invitations.
    Why
    An organizer takes riders’ money; that is not a self-service capability.
  • Cancellation as a modelled request

    Decision
    Cancellations have a lifecycle and a record.
    Why
    It is the path most likely to be disputed, so it is the one that most needs an audit trail.
  • Internationalised from the routing layer

    Decision
    Five languages built in rather than retrofitted.
    Why
    A European marketplace that assumes one language has to be rebuilt to enter its second market.

Impact

What the design holds together.

Bookings, payments, refunds and cancellations stay reconcilable across two parties and an external payment provider, with capacity that cannot be oversold and payment notifications that are safe to receive twice.

Technology

The technical surface.

Application

  • Next.js
  • TypeScript
  • React Query

Data

  • PostgreSQL
  • Prisma

Payments

  • Connected accounts
  • Payment intents
  • Webhooks

Interface

  • Tailwind CSS
  • Radix primitives
  • Schema validation

Reach

  • Internationalised routing
  • Five languages
  • GDPR consent

Operations

  • Transactional email
  • Structured logging

Next: EducoGym

Back to all work Start a conversation