A car rental website looks simple from the outside: pick dates, pick a car, pay. Under the hood, almost every problem comes back to one question: is this car actually free for these dates? Get that wrong and you get double bookings, angry customers at the airport and a team that stops trusting the website.
I'm a freelance WordPress and Flutter developer in Famagusta, North Cyprus, where a lot of small rental companies still take bookings over WhatsApp. Here is how I approach a booking site for them, from a developer's point of view.
Model the fleet, not just "products"
The tempting shortcut is to treat each car as a WooCommerce product with stock. It breaks quickly, because rental stock is about time, not quantity. I usually model it like this:
- Car group (e.g. "Economy automatic"): what the customer books, with photos, seats, luggage and transmission.
- Vehicle: the physical car with its plate, linked to a group, which can be blocked for service.
- Booking: vehicle, pick-up datetime, return datetime, locations, extras, status.
Customers book a group and the system assigns a free vehicle. That way one car in the workshop doesn't make the whole group "sold out" on the website.
The overlap query
Two bookings clash when one starts before the other ends and ends after the other starts. In SQL that is the classic interval check:
SELECT COUNT(*) FROM wp_rental_bookings
WHERE vehicle_id = %d
AND status IN ('confirmed', 'pending')
AND pickup_at < %s -- requested return
AND return_at > %s; -- requested pick-up
Small details that matter:
- Store datetimes in UTC and convert for display. Rental days cross midnight, and daylight saving changes will bite you otherwise.
- Add a buffer after each return (cleaning, fuel, delivery time) before the car is free again.
- Count
pendingbookings too, so two people paying at the same time can't both get the last car.
Prevent the race condition
Checking availability and inserting the booking are two steps. If two requests run between them, both pass the check. Wrap the check and the insert in a transaction and lock the vehicle row (SELECT ... FOR UPDATE on InnoDB), or hold a short reservation that expires if payment isn't completed in, say, 15 minutes. It's boring code, and it's the code that saves the business.
Prices are rules, not numbers
Rental pricing is rarely "price × days". There are seasonal rates, discounts for longer rentals, a minimum number of days, one-way fees and extras like a child seat or full insurance. I keep these as small, testable rules and calculate the total on the server, never only in JavaScript. The customer sees the full breakdown before confirming, which also reduces the "why is it more at the desk?" conversations.
Airport delivery and the booking form
Many visitors here land at Ercan Airport or arrive at the port in Kyrenia. Delivery locations, a delivery fee and a flight number field belong in the booking form itself. A booking that arrives with all the details means one message for the staff instead of five.
Speed and languages
Most people book on a phone, often on roaming data, so the search results page has to be light: real image sizes, WebP, lazy-loading below the fold, and no heavy slider. On a four-language WordPress site I improved homepage PageSpeed from 51 to 75 on mobile and 81 to 97 on desktop without touching the design, mostly through this kind of asset discipline. For languages I use WPML with real URLs per language and hreflang, so the German page can rank for German searches.
Where a Flutter app fits
The website is for customers. The part rental companies love is a small staff app: at pick-up and return the employee takes photos of the car, records fuel and mileage, and the customer signs on the screen. Built in Flutter, it runs on Android and iPhone and talks to the same WordPress database through the REST API, so bookings and check-ins stay in one place.
I wrote a client-facing version of this, for rental company owners rather than developers, here: Car Rental Website Design in North Cyprus.
How do you handle availability in booking systems: database locks, short reservations, or something else?
Top comments (0)