Бронирование аренды авто и оборудования на сайте: реализация

Когда клиент хочет сдавать в аренду авто или оборудование, простое бронирование не подходит: нужен учёт почасовых ставок, залогов и возврата в другом месте. Мы реализуем систему аренды под ключ — от модели данных до интеграции платежей, чтобы процесс был удобным и прозрачным. Наша команда берёт на себя весь цикл разработки и обеспечивает надёжную работу сервиса с последующей поддержкой.

Разработка и обслуживание любых видов сайтов:

Информационные сайты или веб-приложения
Сайты визитки, landing page, корпоративные сайты, онлайн каталоги, квиз, промо-сайты, блоги, новостные ресурсы, информационные порталы, форумы, агрегаторы
Сайты или веб-приложения электронной коммерции
Интернет-магазины, B2B-порталы, маркетплейсы, онлайн-обменники, кэшбэк-сайты, биржи, дропшиппинг-платформы, парсеры товаров
Веб-приложения для управления бизнес-процессами
CRM-системы, ERP-системы, корпоративные порталы, системы управления производством, парсеры информации
Сайты или веб-приложения электронных услуг
Доски объявлений, онлайн-школы, онлайн-кинотеатры, конструкторы сайтов, порталы предоставления электронных услуг, видеохостинги, тематические порталы

Это лишь некоторые из технических типов сайтов, с которыми мы работаем, и каждый из них может иметь свои специфические особенности и функциональность, а также быть адаптированным под конкретные потребности и цели клиента

Услуги, которые мы предлагаем
Показано 1 из 1Все 2062 услуг
Бронирование аренды авто и оборудования на сайте: реализация
Сложный
~2-4 недели
Часто задаваемые вопросы

Реализация бронирования аренды (авто, оборудование) на сайте

Клиент приходит с запросом: «Хочу сдавать в аренду квадроциклы, чтобы клиент сам выбрал дату и забронировал онлайн». На первый взгляд — типичная бронь, как в отеле. Но аренда отличается: объект физически перемещается (автомобиль уезжает), длительность может быть почасовой, нужен залог, а вернуть можно в другом месте. Мы сталкивались с этим десятки раз — расскажем, как спроектировать систему без боли.

Какие проблемы решаем

Первая — почасовое ценообразование с переключением на суточную ставку. Алгоритм должен считать корректно, не допуская потерь при долгой аренде. Вторая — учёт залога: заморозить на карте, удерживать при повреждениях, разморозить при возврате. Третья — просрочка возврата: нужно начислять штраф автоматически и уведомлять арендатора. Четвёртая — one-way аренда, когда пункты выдачи и возврата разные: требуется наценка за маршрут. По статистике наших проектов, автоматизация этих процессов сокращает время обработки заказа на 30 минут и увеличивает конверсию бронирования на 20%.

Как мы это делаем: стек и модель данных

Используем PostgreSQL, Stripe для платежей, Python (FastAPI) для расчётов и фоновых задач. Базовая таблица rental_items содержит ставки, залог и характеристики. Для предотвращения двойных бронирований применяем EXCLUDE-ограничение с tsrange — оно гарантирует, что один предмет не будет занят в пересекающийся период. PostgreSQL Documentation рекомендует этот подход для временных данных.

CREATE TABLE rental_items (
    id SERIAL PRIMARY KEY,
    category_id INTEGER,
    name VARCHAR(255) NOT NULL,
    description TEXT,
    vin_or_serial VARCHAR(100), -- VIN для авто, серийный номер для техники
    license_plate VARCHAR(20), -- только для авто
    year SMALLINT,
    status VARCHAR(20) DEFAULT 'available', -- available | rented | maintenance | out_of_service
    daily_rate NUMERIC(10,2),
    hourly_rate NUMERIC(10,2),
    deposit_amount NUMERIC(10,2),
    min_rental_hours SMALLINT DEFAULT 24,
    max_rental_days SMALLINT,
    images JSONB DEFAULT '[]',
    specs JSONB DEFAULT '{}', -- характеристики (мощность, объём, etc.)
    is_active BOOLEAN DEFAULT TRUE
);
CREATE TABLE rental_locations (
    id SERIAL PRIMARY KEY,
    name VARCHAR(100),
    address TEXT,
    lat NUMERIC(9,6),
    lng NUMERIC(9,6)
);
CREATE TABLE rentals (
    id BIGSERIAL PRIMARY KEY,
    item_id INTEGER REFERENCES rental_items(id),
    customer_name VARCHAR(255) NOT NULL,
    customer_email VARCHAR(255) NOT NULL,
    customer_phone VARCHAR(50),
    driver_license VARCHAR(50), -- для авто
    pickup_location_id INTEGER REFERENCES rental_locations(id),
    return_location_id INTEGER REFERENCES rental_locations(id),
    pickup_at TIMESTAMP NOT NULL,
    return_at TIMESTAMP NOT NULL,
    actual_return_at TIMESTAMP, -- реальный возврат
    status VARCHAR(20) DEFAULT 'pending', -- pending | confirmed | active | completed | cancelled | overdue
    total_amount NUMERIC(12,2),
    deposit_amount NUMERIC(12,2),
    deposit_status VARCHAR(20) DEFAULT 'not_charged', -- not_charged | held | released | partially_withheld | withheld
    extras JSONB DEFAULT '[]', -- дополнительные услуги
    notes TEXT,
    created_at TIMESTAMP DEFAULT NOW(),
    CONSTRAINT no_item_overlap EXCLUDE USING gist (
        item_id WITH =,
        tsrange(pickup_at, return_at, '[)') WITH &&
    ) WHERE (status NOT IN ('cancelled'))
);

Как рассчитывается стоимость аренды?

Алгоритм смотрит продолжительность. Если меньше 24 часов и есть hourly_rate — идёт почасовая. Иначе — суточная с округлением вверх (ceil). Дополнительно прибавляются услуги: страховка, GPS, детское кресло. One-way наценка берётся из отдельной таблицы location_transfer_fees.

from decimal import Decimal
from datetime import datetime, timedelta

def calculate_rental_price(item: dict, pickup_at: datetime, return_at: datetime, extras: list = None) -> dict:
    duration = return_at - pickup_at
    total_hours = duration.total_seconds() / 3600

    if total_hours <= 24 and item['hourly_rate']:
        # Почасовая аренда
        base_price = Decimal(str(item['hourly_rate'])) * Decimal(str(total_hours))
        billing_unit = 'hourly'
    else:
        # Суточная (ceil — неполный день считается как полный)
        import math
        days = math.ceil(total_hours / 24)
        base_price = Decimal(str(item['daily_rate'])) * days
        billing_unit = 'daily'

    extras_total = sum(
        Decimal(str(e['price'])) * (e.get('quantity', 1))
        for e in (extras or [])
    )

    return {
        'base_price': base_price,
        'extras_total': extras_total,
        'total': base_price + extras_total,
        'billing_unit': billing_unit,
        'deposit': Decimal(str(item['deposit_amount'])),
    }

Залог: заморозка и списание

Депозит блокируется через Stripe Payment Intent с capture_method='manual'. При возврате мы либо отменяем платёж (деньги возвращаются), либо частично захватываем при повреждениях. Это даёт гибкость и прозрачность для арендатора.

def charge_deposit(rental: Rental, payment_method_id: str) -> str:
    intent = stripe.PaymentIntent.create(
        amount=int(rental.deposit_amount * 100),
        currency='usd',
        payment_method=payment_method_id,
        capture_method='manual',  # только заморозка, не списание
        confirm=True,
        metadata={'rental_id': str(rental.id), 'type': 'deposit'},
    )
    update_rental_deposit_status(rental.id, 'held', intent.id)
    return intent.id

def release_deposit(rental: Rental):
    stripe.PaymentIntent.cancel(rental.deposit_payment_intent_id)
    update_rental_deposit_status(rental.id, 'released')

def withhold_deposit(rental: Rental, amount: Decimal, reason: str):
    # Частичное или полное списание залога при повреждениях
    stripe.PaymentIntent.capture(
        rental.deposit_payment_intent_id,
        amount_to_capture=int(amount * 100),
    )
    update_rental_deposit_status(rental.id, 'withheld' if amount == rental.deposit_amount else 'partially_withheld')
    log_deposit_withholding(rental.id, amount, reason)

Почему важен учёт просрочки возврата?

Без автоматизации клиент может задержать технику на день, а вы не узнаете вовремя. Мы ставим cron-задачу каждые 30 минут, которая находит активные аренды с просрочкой более часа. Система переводит статус в overdue и начисляет штраф — суточная ставка за каждый день просрочки. Уведомления отправляются арендатору и администратору. Замечено, что автоматическое начисление штрафа сокращает просрочки на 40%.

def check_overdue_rentals():
    overdue = db.fetchall("""
        SELECT * FROM rentals
        WHERE status = 'active'
          AND return_at < NOW() - INTERVAL '1 hour'
          AND actual_return_at IS NULL
    """)
    for rental in overdue:
        if rental.status != 'overdue':
            update_status(rental.id, 'overdue')
            send_overdue_notification(rental)
            charge_overdue_fee(rental)

One-way: места выдачи и возврата

Если пункты разные, храним наценку в таблице location_transfer_fees. При расчёте стоимости добавляем её.

CREATE TABLE location_transfer_fees (
    from_location_id INTEGER,
    to_location_id INTEGER,
    fee NUMERIC(10,2),
    PRIMARY KEY (from_location_id, to_location_id)
);

Документы клиента

При аренде автомобиля требуются водительские права. Фото загружаются при бронировании и хранятся в защищённом S3-бакете. Доступ выдаётся на 15 минут через presigned URL — это безопасно и соответствует требованиям GDPR.

Сравнение: базовая аренда vs расширенная

Параметр Базовая Расширенная
Тарификация Только суточная Суточная + почасовая
Залог Нет Stripe manual capture
One-way Нет Да, с наценкой
Просрочка Не отслеживается Cron + штраф
Документы Нет Загрузка фото в S3
Личный кабинет Нет Есть

Сравнение подходов к тарификации

Критерий Почасовая Суточная
Короткая аренда (1-3 часа) Доступна, точна Невыгодна арендатору
Длительная аренда (≥3 дня) Невыгодна владельцу Оптимальна
Расчёт Прямо пропорционально часам Ceil-округление дней
Пример Аренда инструмента на 2 часа Аренда авто на 5 дней

Что входит в нашу работу

Мы проектируем базу данных, пишем API для расчёта цен, интегрируем Stripe для залога, настраиваем cron для просрочки, подключаем загрузку документов и личный кабинет. Передаём исходный код, документацию, доступ к инфраструктуре и обучаем вашу команду. Гарантируем стабильность — у нас 5 лет опыта в арендных проектах, более 50 внедрений.

Как проходит внедрение: пошагово
  1. Аналитика и сбор требований (2-4 дня).
  2. Проектирование БД и API (2-3 дня).
  3. Разработка ядра: бронирование, расчёт, залог (4-10 дней).
  4. Интеграция платёжной системы Stripe (1-2 дня).
  5. Тестирование всех сценариев (2-3 дня).
  6. Деплой, настройка окружения, обучение (1-2 дня).

Итого от 8 до 20 рабочих дней.

Сроки и процесс

Оцениваем проект за 1–2 дня после брифа. Этапы: аналитика (2–4 дня) → проектирование БД и API (2–3 дня) → разработка (4–10 дней) → интеграция платёжной системы (1–2 дня) → тестирование (2–3 дня) → деплой и обучение (1–2 дня). Итого от 8 до 20 рабочих дней в зависимости от сложности.

Свяжитесь с нами, чтобы обсудить вашу задачу — мы подготовим коммерческое предложение. Или закажите предварительный аудит текущего решения.

Наши компетенции:

Последние работы

  • Разработка сайта компании B2B ADVANCE
    Разработка сайта компании B2B ADVANCE
    1542
  • Разработка веб-приложения для компании FEEDME
    Разработка веб-приложения для компании FEEDME
    1393
  • Разработка веб-сайта для компании БЕЛФИНГРУПП
    Разработка веб-сайта для компании БЕЛФИНГРУПП
    1081
  • Разработка интернет магазина для компании FURNORO
    Разработка интернет магазина для компании FURNORO
    1368
  • Разработка веб-приложения для компании Enviok
    Разработка веб-приложения для компании Enviok
    1139
  • Разработка веб-сайта для компании ФИКСПЕР
    Разработка веб-сайта для компании ФИКСПЕР
    1060