Розробка платформи для аренди автомобілів
Прокат автомобілів відрізняється від аренди жилля в кількох принципіальних речах: короткі періоди (години, не дні), строгий облік страховки та водійського посвідчення, обов'язкова перевірка стану автомобіля до та після аренди, паливна політика. Це значно змінює архітектуру.
Модель даних
Автомобіль — це конкретна одиниця з історією, а не просто «тип авто»:
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'
);
Верифікація водія
Це обов'язковий блок — без перевірки документів платформа несе юридичну відповідальність. Інтеграція зі Stripe Identity або Jumio:
class DriverVerificationService
{
public function initiateVerification(User $user): string
{
$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;
}
}
Ціноутворення: почасова та посуточна моделі
Прокат може бути почасовим, посуточним або комбінованим. Потрібен гнучкий розрахунок:
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,
};
return new PricingResult(
base: $base,
subtotal: $base * $multiplier * (1 - $discount),
total: $subtotal + ($subtotal * $this->platformFeeRate),
);
}
}
Акт приєму-передачі з фото
Перед видачею та після повернення — обов'язкова фіксація стану. Це знімає спори:
Schema::create('vehicle_inspections', function (Blueprint $table) {
$table->id();
$table->foreignId('booking_id')->constrained();
$table->enum('type', ['pre_rental', 'post_rental']);
$table->json('damage_map'); // координати пошкоджень на схемі авто
$table->json('photo_urls'); // S3 URLs фотографій
$table->integer('fuel_level'); // 0-100%
$table->integer('mileage_km');
$table->text('notes')->nullable();
$table->string('renter_signature_url')->nullable();
$table->string('owner_signature_url')->nullable();
$table->timestamp('signed_at')->nullable();
});
Підпис реалізується через canvas на фронте (signature_pad.js), зображення base64 → S3. Після підписання обома сторонами статус переходить в signed та документ стає юридично значимим.
GPS-трекинг та телематика
Для парків від 20+ автомобілів часто потрібен live-трекинг. Інтеграція з GPS-трекерами через MQTT:
class VehicleTelematicsHandler
{
public function handle(string $topic, string $payload): void
{
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);
}
});
}
}
Страховий депозит та утримання
Депозит блокується на карті при бронюванні, списується лише при пошкодженнях:
$paymentIntent = $stripe->paymentIntents->create([
'amount' => $booking->security_deposit_cents,
'currency' => 'eur',
'capture_method' => 'manual',
'confirm' => false,
]);
// Без пошкоджень — скасуємо hold
$stripe->paymentIntents->cancel($paymentIntent->id);
// Є пошкодження — захоплюємо потрібну суму
$stripe->paymentIntents->capture($paymentIntent->id, [
'amount_to_capture' => $damageAmount,
]);
Терміни розробки
MVP з пошуком авто, бронюванням, верифікацією водія та базовим кабінетом власника: 8–10 тижнів.
Додавання актів осмотру, GPS-інтеграції, гнучкого ціноутворення, депозитної логіки: ще 4–5 тижнів.
Повний запуск з корпоративними аккаунтами, fleet management та аналітикою: 16–18 тижнів разом.







