SeRental
Camera hire, run off a multi-tenant CMS
SeRental is a camera hire business: cameras, lenses, lighting, stabilisation, from one day. The storefront is a Next.js site with no database of its own — every product, price, category and payment method comes from Nexus, a multi-tenant CMS we built, over an API-key-authenticated public API. The rental vertical is one of seven the same platform serves.
Challenge
Hire is not retail. The same lens can be free next week and gone tomorrow, the price depends on how long you keep it, and two customers can book the same unit in the same second. On top of that the shop wanted volume discounts without re-typing a ladder onto 138 items, and card prepayment with money that goes back automatically when a booking is refused.
What we built
Availability is decided in the basket, against real reservations, and the calendar greys out the days a kit cannot start on. Discounts are one shared grid of percentages, addressed at everything, at a category or at a single item, with the narrowest addressee winning. Double booking is refused by the database itself — a Postgres exclusion constraint over time ranges — rather than by application code that can race. Card payments carry the callback address per invoice, and cancelling a paid booking returns the money in the same action.
Result
A live storefront on a platform the owner runs themselves: prices, tariffs, categories, services and payment methods are edited in the admin and live on the site without a deploy. Payment, refunds and availability are enforced server-side, so what a customer is shown and what they are charged cannot drift apart.
What it does
Every screen below is the live product, captured this week.
Storefront
A hire shop is chosen on what is in it and how quickly you can find the thing you need, so the entry point is search and category, not a brochure.
- 138 units across 13 categories, counted from the live catalogue
- Search by name, category chips straight from the admin
- Every figure on the page derived, never hard-coded
Catalogue and facets
Filters are built from the characteristics the operator defined — mount, sensor format, optics type — and every count is computed with the other filters applied and its own left off.
- Facet counts exclude their own filter, so a switch is never shown as zero
- Price, availability and characteristic filters share one query layer
- One page query plus one pass for every untouched axis, whatever the catalogue size
Pricing
The daily rate falls as the hire gets longer. The ladder is written once as percentages and prices every item in the shop, from a 100 UAH holder to a 4 500 UAH camera.
- One grid addressed at everything, a category, or a single item
- Narrowest addressee wins; one active grid per addressee, enforced by index
- Bands cannot overlap — a range exclusion constraint refuses it
Booking and payment
One period covers the whole kit. Availability is checked against real reservations, the discount is shown in money before anyone commits, and the card path ends in a booking the operator can refund in one action.
- Calendar greys out days a line of the kit is already gone
- Full price, discount and total stated separately, never folded together
- Card or cash; a refused booking refunds automatically
The basket is where dates, availability and price meet — built for a phone first, with the discount stated in money.
Under the hood
The stack, and the engineering decisions behind the build.
- One discount ladder priced against the whole catalogue — percentages, not a table re-typed onto every item.
- Overlapping bookings are refused by a Postgres exclusion constraint, so two simultaneous checkouts cannot both win.
- The basket asks the API for today's prices rather than trusting what it stored when the item was added.
- Cancelling a paid booking issues the refund in the same action; a gateway that refuses says so instead of reporting success.
Have a Similar Project?
Let's talk about how we can build something great for your business. We love a good challenge.
Start a Conversation