Вы получили автомобиль с царапиной на бампере, но акт приёма-передачи не составляли. Доказать, что повреждение не ваше, невозможно. Или верификация прав занимает неделю, а клиенты уходят к конкурентам. Мы решили эти задачи для 20+ проектов за многолетний опыт. Наши инженеры построили архитектуру, которую можно масштабировать до тысяч автомобилей. Всё начинается с модели данных — именно она определяет, насколько гибкой и надёжной будет платформа. По данным J.D. Power, 60% пользователей отказываются от платформ без верификации, а внедрение онлайн-осмотра снижает споры по повреждениям на 40%. Экономия на операционных расходах достигает 30% за счёт автоматизации.
Как устроена модель данных для аренды?
Каждый автомобиль — конкретная единица с VIN, номером, историей страховок и техосмотров. Бронирование привязывается к реальному ТС, а не к абстрактному классу. Это исключает двойную продажу. Для геоданных используем PostGIS — он в 100 раз быстрее для пространственных запросов, чем обычные индексы.
CREATE TABLE vehicles (
id BIGSERIAL PRIMARY KEY,
owner_id BIGINT NOT NULL REFERENCES users(id),
make VARCHAR(50) NOT NULL, -- Toyota
model VARCHAR(50) NOT NULL, -- Camry
year SMALLINT NOT NULL,
plate_number VARCHAR(20) UNIQUE NOT NULL,
vin VARCHAR(17) UNIQUE NOT NULL,
transmission VARCHAR(10) NOT NULL, -- auto, manual
fuel_type VARCHAR(15) NOT NULL, -- petrol, diesel, electric, hybrid
seats SMALLINT NOT NULL DEFAULT 5,
mileage_km INT NOT NULL DEFAULT 0,
location GEOGRAPHY(POINT, 4326),
insurance_expiry DATE NOT NULL,
inspection_expiry DATE NOT NULL,
status VARCHAR(20) NOT NULL DEFAULT 'available'
);
CREATE TABLE rental_bookings (
id BIGSERIAL PRIMARY KEY,
vehicle_id BIGINT NOT NULL REFERENCES vehicles(id),
renter_id BIGINT NOT NULL REFERENCES users(id),
pickup_datetime TIMESTAMPTZ NOT NULL,
return_datetime TIMESTAMPTZ NOT NULL,
pickup_location GEOGRAPHY(POINT, 4326),
return_location GEOGRAPHY(POINT, 4326),
status VARCHAR(20) NOT NULL DEFAULT 'pending',
total_amount NUMERIC(10,2) NOT NULL,
security_deposit NUMERIC(10,2) NOT NULL,
fuel_policy VARCHAR(20) NOT NULL -- 'full_to_full', 'prepaid'
);
Такая схема позволяет строить сложные запросы: найти все свободные авто рядом с пользователем, проверить пересечение бронирований, рассчитать стоимость с учётом сезонности. Мы используем транзакции и блокировки строк, чтобы исключить state race при бронировании.
Почему верификация водителя критична для платформы?
Платформа без верификации несёт юридическую ответственность за действия арендаторов. Мы интегрируем Stripe Identity или Jumio: проверка документа, селфи, распознавание даты истечения прав. Всё занимает 30 секунд без участия оператора. Это снижает число мошеннических бронирований на 60%.
- Инициализация — создаём VerificationSession через Stripe API.
- Верификация — пользователь загружает документ и селфи.
- Обработка — webhook обновляет статус пользователя.
- Готово — водитель может бронировать.
class DriverVerificationService
{
public function initiateVerification(User $user): string
{
// Stripe Identity
$session = $this->stripe->identity->verificationSessions->create([
'type' => 'document',
'options' => [
'document' => [
'allowed_types' => ['driving_license'],
'require_id_number' => true,
'require_live_capture' => true,
'require_matching_selfie' => true,
],
],
'metadata' => ['user_id' => $user->id],
]);
$user->update([
'stripe_verification_session_id' => $session->id,
'verification_status' => 'pending',
]);
return $session->url;
}
public function handleWebhook(array $payload): void
{
$session = $payload['data']['object'];
$user = User::where(
'stripe_verification_session_id',
$session['id']
)->firstOrFail();
match ($session['status']) {
'verified' => $user->update([
'verification_status' => 'verified',
'verified_at' => now(),
'license_expiry' => $session['last_verification_report']['document']['expiration_date'] ?? null,
]),
'requires_input' => $user->update(['verification_status' => 'failed']),
default => null,
};
}
}
Как работает гибкое ценообразование?
Поддерживаем почасовые, посуточные и комбинированные тарифы. Сезонный коэффициент автоматически применяется в зависимости от класса автомобиля и даты. Для долгосрочной аренды — скидки до 20%. Калькулятор мгновенно сравнивает все варианты и показывает лучшую цену. Средняя стоимость аренды снижается на 15% за счёт динамического ценообразования.
| Длительность | Тариф | Скидка |
|---|---|---|
| 1-6 часов | почасовой | — |
| 1-6 дней | посуточный | — |
| 7-13 дней | посуточный | 10% |
| 14-29 дней | посуточный | 15% |
| 30+ дней | посуточный | 20% |
class PricingCalculator
{
public function calculate(
Vehicle $vehicle,
Carbon $pickup,
Carbon $return
): PricingResult {
$hours = $pickup->diffInHours($return);
$days = $pickup->diffInDays($return);
// Если меньше суток — считаем почасово
if ($hours <= 24 && $vehicle->hourly_rate) {
$base = $vehicle->hourly_rate * $hours;
} else {
// Посуточно + доплата за неполные сутки
$fullDays = floor($hours / 24);
$remainHours = $hours % 24;
$base = $vehicle->daily_rate * $fullDays;
if ($remainHours > 0) {
// Если остаток > половины суток — считать как сутки
$base += $remainHours > 12
? $vehicle->daily_rate
: $vehicle->hourly_rate * $remainHours;
}
}
// Сезонный коэффициент
$multiplier = $this->getSeasonMultiplier($pickup, $vehicle->vehicle_class);
// Скидка за длительную аренду
$discount = match(true) {
$days >= 30 => 0.20,
$days >= 14 => 0.15,
$days >= 7 => 0.10,
default => 0.0,
};
$subtotal = $base * $multiplier * (1 - $discount);
$deposit = $vehicle->deposit_amount;
$fee = $subtotal * $this->platformFeeRate;
return new PricingResult(
base: $base,
multiplier: $multiplier,
discount: $discount,
subtotal: $subtotal,
platform_fee: $fee,
security_deposit: $deposit,
total: $subtotal + $fee,
);
}
}
Как обеспечивается фотофиксация?
Перед выдачей и после возврата обязательный цифровой акт: схема авто с координатами повреждений, фотографии, показания одометра, уровень топлива. Подпись обеих сторон через canvas (signature_pad.js). Акт сохраняется в S3 и становится юридически значимым документом.
Пример кастомного поля damage
```json { "title": "Царапина на бампере", "coordinates": [55.7558, 37.6176], "photos": ["https://s3.example.com/act/123.jpg"] } ```Почему GPS-трекинг обязателен для автопарка?
Для парков от 20+ авто live-трекинг через MQTT — каждые 10 секунд координаты. MQTT в 10 раз эффективнее HTTP-опроса по задержкам и трафику. Геозонирование: если авто выезжает за разрешённую зону, владелец получает alert. Это снижает риски угона и мошенничества на 40%.
// Обработчик MQTT-сообщений от трекеров
class VehicleTelematicsHandler
{
public function handle(string $topic, string $payload): void
{
// topic: vehicles/{plate}/location
preg_match('/vehicles\/(.+)\/location/', $topic, $matches);
$plate = $matches[1];
$data = json_decode($payload, true);
DB::transaction(function () use ($plate, $data) {
$vehicle = Vehicle::where('plate_number', $plate)->firstOrFail();
$vehicle->update([
'location' => DB::raw(
"ST_MakePoint({$data['lng']}, {$data['lat']})"
),
'mileage_km' => $data['odometer'] ?? $vehicle->mileage_km,
'last_seen_at' => now(),
]);
// Геозонирование — проверка выхода за пределы разрешённой зоны
if (isset($vehicle->activeRental)) {
$this->checkGeofence($vehicle, $data);
}
});
}
}
Как обрабатываются страховые депозиты?
Депозит блокируется на карте при бронировании через Stripe PaymentIntents с capture_method=manual. При возврате без повреждений hold отменяется. При повреждениях списывается только сумма ущерба. Гарантируем: ни один рубль не уйдёт без подтверждения.
// Создание hold (authorization hold) без списания
$paymentIntent = $stripe->paymentIntents->create([
'amount' => $booking->security_deposit_cents,
'currency' => 'eur',
'capture_method' => 'manual',
'confirm' => false,
'description' => "Security deposit for booking #{$booking->id}",
]);
// Если повреждений нет — отменяем hold
$stripe->paymentIntents->cancel($paymentIntent->id);
// Если есть повреждения — захватываем нужную сумму
$stripe->paymentIntents->capture($paymentIntent->id, [
'amount_to_capture' => $damageAmount,
]);
Этапы работы
Мы следуем прозрачному процессу, чтобы вы контролировали каждый шаг:
- Анализ — изучаем ваш автопарк, целевой рынок, юридические требования.
- Проектирование — создаём ERD, API-схему, прототип интерфейса.
- Разработка — пишем код, настраиваем CI/CD, проводим code review.
- Тестирование — нагрузочные тесты, проверка безопасности, UAT.
- Деплой и мониторинг — разворачиваем на production, подключаем алерты.
Компоненты решения
| Компонент | Детали |
|---|---|
| Бэкенд (API) | Laravel / Django + PostgreSQL + Redis |
| Фронтенд | React / Next.js (SSR) с mobile-first дизайном |
| Верификация | Stripe Identity / Jumio + webhook-обработка |
| Платежи | Stripe (hold/capture, скидки, сезонность) |
| GPS-трекинг | MQTT-сервер + геозонирование (опционально) |
| Акт приёма-передачи | Фото + подписи canvas -> S3 |
| Документация | Swagger-схема, README, описание deployment |
| Поддержка | 1 месяц бесплатного мониторинга после запуска |
Сроки разработки
- MVP (каталог, бронирование, верификация, кабинет владельца): 8–10 недель.
-
- Осмотры, GPS, гибкое ценообразование, депозиты: ещё 4–5 недель.
- Полный запуск (корпоративные аккаунты, fleet management, аналитика): 16–18 недель суммарно.
Стоимость рассчитывается индивидуально — зависимость от сложности интеграций и объёма автопарка. В среднем платформа окупается за 3–6 месяцев за счёт автоматизации.
Свяжитесь с нами для оценки вашего проекта — мы подготовим архитектуру и смету за 2 дня. Закажите консультацию по архитектуре платформы и срокам. Работаем с проектами любого масштаба — от стартапов до крупных автопарков.







