Back to BlogIndustry Guides

Car Rental Software for Small Fleets: Bookings, Deposits, Availability

Rupak Amin

Founder & Lead Engineer, RAITHub

15 min read

Car rental software for a small fleet, in any market, needs five things: online bookings, live fleet availability, a card deposit hold, damage and extras on the rental agreement, and pricing in your customers' currencies. Off-the-shelf tools start around $300 a year for up to 25 vehicles. Build custom when your pricing, multi-location rules or marketplace model don't fit, and enforce no double-booking in the database.

This guide is for owners of small rental fleets, from 5 to a few hundred vehicles, and for founders building rental or car-sharing products. It covers what to buy, what it costs, and how the two hard parts work: availability and deposits. RAITHub has not shipped a car rental product, so this is engineering guidance. It draws on patterns RAITHub has shipped elsewhere: Stripe rent collection on PropDesk, backed by 1,024 automated tests, and stock and role-based access on Sundor Skin.

What does car rental software need to do for a small fleet?

It has to take a booking, guarantee the car is free, secure payment and a deposit, record the car's condition at pickup and return, and charge correctly for extras, fuel, late return and damage. Everything else is convenience.

ModuleWhat it doesWhere it goes wrong
BookingsDates, times, pickup and return locations, driver details, online and at the deskTime zones and one-way rentals between locations
Fleet availabilityWhich vehicles, or which categories, are free for a date rangeDouble-booking when two customers book the last car at once
Payments and depositsRental charge up front or at pickup; a deposit held on the cardThe card hold expires before the car comes back
Damage and conditionPhotos and a checklist at pickup and return, damage charges against the depositNo timestamped evidence, so disputes are lost
Extras and pricingChild seats, extra drivers, insurance options, mileage caps, seasonal ratesStock limits on extras, and rate rules nobody can explain
Fleet operationsServicing, cleaning turnaround, blocking a car for repairsA car booked while it is in the workshop

What does car rental software cost?

For a small fleet, subscription tools run from a few hundred dollars a year to a few hundred euros a month, priced by vehicles, users and locations. Published examples, checked on 1 October 2026:

ProductPublished priceWhat the price coversSource
Easy Web Rent$299.99, $399.99 or $499.99 a yearUp to 25, up to 100, or 100+ vehicles; same features on every plan, including online reservations and a booking websiteEasy Web Rent pricing
TopRentApp€99, €139 or €299 a month (monthly billing)10, 30 or unlimited vehicles; extra vehicles from €1.49 a month; an online booking widget is a €59 a month add-on; pay-as-you-go at €1 a month plus €4.99 a bookingTopRentApp pricing
Booqable (general rental, not car-specific)From $29 a month on its entry plan, higher tiers at $69 and $149Unlimited inventory and orders, no commission on bookings; users and API access vary by planBooqable pricing
Rent CentricQuote from a calculatorPriced from your number of vehicles, users and locationsRent Centric pricing

Prices change, may exclude tax, and differ by billing period, so confirm on the vendor's page. Add card processing fees on top of any subscription. For most fleets under about 50 cars with standard rules, one of these tools is cheaper than any custom build.

When does a custom car rental system make sense?

When the software is the product, or when your rules are the business. A tool you rent is built for the average rental desk; custom pays when you are not average.

  • A marketplace or peer-to-peer model. Several owners list cars, and you split payments between them. That is a marketplace, and it needs payouts to third parties.
  • Car subscriptions or long-term rentals. Monthly billing, swaps between vehicles and mileage bundles do not fit a day-rate engine.
  • Pricing as your edge. Rates by demand, lead time, customer tier or corporate contract, which a settings screen cannot express.
  • Several brands or franchisees. Each location needs its own prices, staff and reports, with data kept separate.
  • Integration with your own systems. Telematics, an accounting package, a corporate booking portal, or an existing customer base.

If one of those applies, the build is closer to a SaaS product than a website. The MVP cost estimator gives a first range, and the marketplace split payments guide covers the payout side.

How do you stop two customers booking the same car?

Let the database refuse it. In PostgreSQL, an exclusion constraint on vehicle and time range rejects any second booking that overlaps the first, however the requests race. An application check ("is it free?" then "insert") leaves a gap in which two requests both see the car as free.

PostgreSQL's own documentation shows the pattern for meeting rooms: an EXCLUDE USING GIST constraint with room WITH = and during WITH &&, which needs the btree_gist extension to combine equality on a plain column with range overlap (PostgreSQL range types). A vehicle is a meeting room with wheels.

CREATE EXTENSION IF NOT EXISTS btree_gist;

CREATE TABLE vehicle (
  id          bigint GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
  location_id bigint NOT NULL,
  category    text   NOT NULL,          -- e.g. 'economy', 'suv'
  plate       text   NOT NULL UNIQUE,
  active      boolean NOT NULL DEFAULT true
);

CREATE TABLE booking (
  id              bigint GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
  vehicle_id      bigint NOT NULL REFERENCES vehicle (id),
  -- Pickup to return plus cleaning turnaround, computed by the app.
  -- Default bounds are [) so back-to-back bookings do not collide.
  blocked         tstzrange NOT NULL CHECK (NOT isempty(blocked)),
  status          text NOT NULL
                  CHECK (status IN ('held', 'confirmed', 'on_rent', 'returned', 'cancelled')),
  hold_expires_at timestamptz,
  currency        char(3) NOT NULL,     -- ISO 4217, e.g. 'EUR'
  total_minor     bigint NOT NULL CHECK (total_minor >= 0),
  deposit_minor   bigint NOT NULL DEFAULT 0 CHECK (deposit_minor >= 0),
  deposit_intent  text UNIQUE,          -- Stripe PaymentIntent ID
  CONSTRAINT no_double_booking EXCLUDE USING gist (
    vehicle_id WITH =,
    blocked    WITH &&
  ) WHERE (status IN ('held', 'confirmed', 'on_rent'))
);

-- Find a free car in a category, then try to take it.
SELECT v.id
FROM vehicle v
WHERE v.location_id = $1 AND v.category = $2 AND v.active
  AND NOT EXISTS (
    SELECT 1 FROM booking b
    WHERE b.vehicle_id = v.id
      AND b.status IN ('held', 'confirmed', 'on_rent')
      AND b.blocked && tstzrange($3, $4, '[)')
  )
LIMIT 5;

INSERT INTO booking (vehicle_id, blocked, status, hold_expires_at, currency, total_minor, deposit_minor)
VALUES ($5, tstzrange($3, $4, '[)'), 'held', now() + interval '15 minutes', $6, $7, $8);
-- If another request took the car first, this fails with
-- SQLSTATE 23P01 (exclusion_violation): try the next candidate.

Four details make it work in practice:

  • Book categories, assign cars. Customers usually book "an economy car", not a plate. Try candidate vehicles in order and catch the exclusion error; if every candidate fails, the category is sold out for those dates.
  • Short holds. A held row reserves the car for 15 minutes while the customer pays. A partial constraint cannot reference now(), so a scheduled job must flip expired holds to cancelled; until it runs, they still block the car.
  • Turnaround and repairs are bookings too. Add the cleaning buffer to blocked, and put a workshop visit in as a booking with its own status, so the same constraint protects it.
  • Use timestamptz. Store instants in UTC and show them in the location's time zone. A one-way rental across a time-zone line is otherwise wrong by an hour or more.

Extras with limited stock, such as three child seats per location, are an oversell problem; the patterns are in how to prevent inventory oversells.

How do you take a rental deposit with Stripe?

Place a hold, not a charge. A PaymentIntent created with capture_method: 'manual' authorises the deposit on the customer's card without taking the money; at return you capture part or all of it for damage or fuel, or cancel the PaymentIntent to release it (Stripe: place a hold on a payment method).

The constraint that shapes the whole design is how long a hold lasts. From Stripe's documentation, checked on 1 October 2026:

Hold typeHow long it is valid
Online card, customer-initiated7 days for Visa, Mastercard, American Express and Discover
Online card, merchant-initiated7 days, except Visa at 5 days (exactly 4 days and 18 hours)
In person (card-present)2 days for Mastercard, American Express and Discover; Visa 5 days
Extended authorisation, onlineUp to 30 days, for Visa, Mastercard, American Express and Discover; vehicle rental is a named eligible category for Visa, American Express and Discover
Japan-based accounts, JPYUp to 30 days for several card brands

Extended holds come with conditions: Stripe offers them on IC+ pricing (blended-pricing accounts must ask support), and for American Express you must capture no later than the end of the rental (Stripe extended authorisations). Stripe also recommends reading the capture_before field on each charge rather than trusting a table, because network rules change.

What that means for a rental desk:

  • Authorise at pickup, not at booking. A booking made three weeks ahead cannot hold a deposit until the car goes out. Take the rental payment at booking; place the deposit hold at the counter or on the day.
  • Rentals longer than the window need a plan. Request an extended authorisation where available, and for anything longer, re-authorise before capture_before or save the card for a later charge with the customer's agreement.
  • One capture only. For most payments you can capture once; a partial capture releases the rest, and you cannot capture the difference later. Settle damage in one decision.
  • Tell the customer. Some card statements do not distinguish a hold from a charge, Stripe notes, so say on the agreement and the receipt that it is a hold and when it will be released.

A minimal server-side sample with the official stripe Node library. It was written for this post as an illustration, not taken from a client codebase:

import Stripe from 'stripe'

const stripe = new Stripe(process.env.STRIPE_SECRET_KEY as string)

// At pickup: hold the deposit on the customer's saved card.
export async function holdDeposit(b: {
  id: number
  customerId: string
  paymentMethodId: string
  depositMinor: number // minor units: 30000 = 300.00 EUR; 30000 = 30,000 JPY
  currency: string     // lowercase ISO code, e.g. 'eur', 'gbp', 'jpy'
}) {
  const intent = await stripe.paymentIntents.create(
    {
      amount: b.depositMinor,
      currency: b.currency,
      customer: b.customerId,
      payment_method: b.paymentMethodId,
      payment_method_types: ['card'],
      capture_method: 'manual',
      confirm: true,
      payment_method_options: { card: { request_extended_authorization: 'if_available' } },
      expand: ['latest_charge'],
      metadata: { bookingId: String(b.id), kind: 'deposit' },
    },
    { idempotencyKey: 'deposit-hold-' + b.id },
  )
  const charge = intent.latest_charge as Stripe.Charge
  // Store this: the hold must be captured or released before it.
  const captureBefore = charge.payment_method_details?.card?.capture_before
  return { intentId: intent.id, status: intent.status, captureBefore }
}

// At return: capture damage or fuel costs, or release the whole hold.
export async function settleDeposit(intentId: string, chargeMinor: number) {
  if (chargeMinor <= 0) return stripe.paymentIntents.cancel(intentId)
  // A partial capture releases the remainder automatically.
  return stripe.paymentIntents.capture(intentId, { amount_to_capture: chargeMinor })
}

Around it: a status of requires_capture means the hold succeeded; anything else (for example a card needing authentication) sends the customer through a confirmation step. A daily job lists holds whose captureBefore is within 24 hours and alerts staff, so no deposit lapses by accident. Webhooks, not the API response alone, should update the booking; why a Stripe webhook is not firing and testing payments and webhooks cover the traps.

How should damage and extras be recorded?

As evidence and as line items. Damage is a dispute waiting to happen, and the side with timestamped photos usually wins; extras are small amounts that add up, and they need the same rigour as the rental charge.

  • Pickup and return inspections. A checklist per vehicle area, photos with server timestamps, fuel level and mileage, and the customer's acknowledgement on the agreement.
  • Damage as a claim, not a number. A record with photos, a repair estimate, the amount charged against the deposit and the decision, all kept on the booking with an audit trail of who changed what.
  • Extras as priced line items. Per day or per rental, with caps (a child seat charged for 30 days is a complaint), tax per line, and stock per location.
  • Your terms decide what you may charge. What you can charge for damage after return, and how, depends on your rental terms, consumer law where you operate and card network rules; Stripe's own guidance on extended holds says you are responsible for compliance with network rules. This is general information; confirm with your adviser.

How do you handle more than one currency?

Price each location in its own currency, store every amount as an integer in minor units with its ISO currency code, and charge the customer in the currency you quoted. Stripe expects amounts in the currency's minor unit, for example 1099 for 10.99 USD but 10 for 10 JPY, and says you can charge in over 135 currencies (Stripe supported currencies).

  • Never store floats. Integer minor units and a currency column, on every price, booking, deposit and damage charge.
  • Know the zero-decimal currencies. JPY and KRW have no minor unit in Stripe's API; a deposit of 30000 means 30,000 yen, not 300. Convert with a lookup table, not by multiplying by 100 everywhere.
  • Minimums differ. Stripe's minimum charge is 0.50 USD or 0.30 GBP, for example, and 50 JPY, which matters for small extras charged on their own.
  • Tourists pay in their own currency sometimes. If you show converted prices, decide who carries the exchange-rate risk between quote and capture, and say so on the booking.

Why RAITHub for this

  • Payments are built and tested. PropDesk, the property management platform RAITHub built, collects rent through Stripe and carries 1,024 automated tests (PropDesk case study). A car rental deposit flow is new work, and RAITHub says so, but the webhook, idempotency and money-in-minor-units discipline is the same.
  • Stock and roles, shipped. Sundor Skin runs stock, 12 staff roles and 88 permission codes on PostgreSQL with row-level security, which is the shape of a multi-location fleet with desk staff, managers and owners.
  • Correctness in the database. Double-booking prevented by a constraint, not by hope, with concurrency tests that fire simultaneous bookings at the same car.
  • Hours by agreement. RAITHub works from Dhaka (UTC+6). US and Canada clients get a daily 2-hour evening overlap, Dhaka 19:00 to 21:00; the UK gets about 3 hours in winter and 4 in summer, Central Europe 4 and 5, Dubai 7 and Sydney 5 (4 in daylight saving). The working week is agreed per client, with written daily handoffs.
  • Fixed scope. A free 15-minute technical audit, then a written fixed quote. You own the code, and an NDA is standard.

When you don't need us

  • A standard fleet under about 50 cars. Subscribe to one of the tools above and spend the difference on marketing.
  • You need a native mobile app for drivers. RAITHub builds responsive web applications and PWAs, not mobile apps.
  • You want insurance or legal terms drafted. That is work for your insurer and a lawyer in your market.
  • You want developers placed in your team by the hour. RAITHub offers fixed-scope builds and dedicated teams, not staff augmentation.

If rental software is your product, the SaaS development service covers multi-location, multi-currency builds. Book the free 15-minute technical audit with your fleet size, locations, currencies, and the booking or deposit case your current tool gets wrong.

Last reviewed: 1 October 2026. Vendor prices and Stripe documentation checked on 1 October 2026.

Frequently asked questions

What is the cost of car rental software for a small business?

Published prices for small fleets range from about $300 a year (Easy Web Rent, up to 25 vehicles) to €99 to €299 a month (TopRentApp), with some vendors quoting by fleet size. Card processing fees come on top. A custom build costs more and pays off only when your model does not fit these tools.

How do I prevent double bookings in a rental system?

Enforce it in the database. A PostgreSQL exclusion constraint on vehicle ID and a time range rejects any overlapping active booking, even when two requests arrive at the same moment. The application catches the error and offers another car in the same category.

How long can a card deposit hold last?

With Stripe, an online card authorisation usually lasts 7 days (Visa 5 days for merchant-initiated payments), and in-person holds 2 to 5 days. Extended authorisations can reach 30 days for eligible cards and categories, including vehicle rental. Check the capture_before field on each charge.

Should I take the deposit at booking or at pickup?

At pickup, in most cases. A hold placed weeks ahead would expire before the rental ends. Take the rental payment at booking and place the deposit hold when the car goes out, then capture or release it at return.

Can I charge a deposit in different currencies?

Yes. Create the hold in the currency the customer was quoted and store the amount in minor units with its currency code. Remember that JPY and KRW are zero-decimal in Stripe, so 30000 means 30,000 yen.

Has RAITHub built car rental software?

No. RAITHub has not shipped a car rental product, so this guide is engineering guidance. It has shipped the underlying patterns: Stripe rent collection on PropDesk with 1,024 tests, and stock with role-based access on Sundor Skin.

car rental softwarecar rental software for small businessfleet booking systemrental deposit holdstripe manual capturepostgres exclusion constraint

Ready to discuss your project?

Book a free 15-minute technical audit with our engineering team.