← All posts
September 11, 2026SaaSAIBuilding products

Book Sân Tập: building a two-sided marketplace out of my own frustration

My third product solves a problem I personally kept running into: booking a sports court quickly, without double-booked schedules. Here's how I built it.

Why I wanted to do this

My first two products (content-factory-web, cv-nhanh-web) both started from watching other people struggle. Book Sân Tập was different — it started from my own frustration. Trying to book a badminton or football court for the weekend meant back-and-forth texting with the court owner, waiting for confirmation, and more than once getting a double-booked slot because the owner was managing everything with a notebook or an Excel sheet.

I wanted to build a real marketplace — not one-sided (just players), but two-sided: players find and book courts, owners manage schedules and revenue. This also turned out to be the most complex of the three products, because it has a genuinely hard problem: two people tap "book" on the same time slot at nearly the same instant — who wins?

The steps and the tools

Step 1 — Minimize friction for players as much as possible. I designed it so browsing and searching for courts requires no login at all — sign-up is only forced the moment a user taps "Book." To avoid breaking their flow, I carried the context through a ?next= parameter across sign-up → OTP → login, so they land back on exactly the page they were viewing instead of starting over.

Step 2 — Solve double-booking properly, not superficially. This is where I spent the most time. I used a BEGIN IMMEDIATE transaction at the database layer to lock exactly the moment a slot is checked and recorded as booked, guaranteeing two people can't book the same slot even if they tap almost simultaneously. I tested this against a real simulated race condition (firing concurrent requests) before trusting it in production.

Step 3 — Let court owners set their availability in plain natural language. Instead of forcing owners to fill out a complex form (opening hours, closing hours, days off, time-based pricing), I let them type something like "open 6am to 9am Monday to Friday mornings, open all day on weekends," and used Claude (a small, fast model) to parse that into structured schedule data. But I always show a confirmation card in Vietnamese before saving — never let the AI silently decide on its own — and I kept a manual form as a fallback for owners who aren't comfortable typing free-form text.

Step 4 — Handle geocoding and distance without fully depending on a paid service. I integrated Goong Maps to geocode court addresses, but built in a fallback using manually entered coordinates plus the Haversine formula for distance — so the product still works even before there's budget for a paid API.

Step 5 — Reuse the registration/OTP/wallet/Telegram foundation from the first two products, but this time with its own separate domain, its own Telegram bot, and its own SSH deploy key — because this is an independent brand, and I didn't want any cross-product risk.

Main tools used: Claude Code, Claude (a small, fast model) for parsing free-text availability schedules, FastAPI + SQLite with carefully controlled transactions, the Goong Maps API, and a Hetzner VPS with a dedicated domain and Telegram bot per product.

If you want the details

Double-booking, or letting an AI parse free-text schedules, can both be solved in several different ways, each with its own trade-offs. If you're building something booking- or scheduling-related and want to dig into the details, feel free to reach out by email or on LinkedIn.