Розробка платформи для оренди житла: пошук, бронювання, платежі
Розробка rental-платформи — не просто верстка лістингів та форма бронювання. Ключова складність — логіка доступності, платежів і синхронізації. Якщо в момент підтвердження броні на один і той самий об'єкт приходять два запити, без правильного блокування обидва пройдуть — і хазяїн отримає подвійну переплату, а гість — відмову. Ми проєктуємо транзакційні системи, де race condition виключені на рівні СУБД. Наприклад, у проєкті для мережі апартаментів у Мінську ми обробляємо 500+ бронювань на добу без жодного конфлікту за 2 роки експлуатації. Рішення — на Laravel + PostgreSQL з SELECT FOR UPDATE та скінченним автоматом статусів. Нижче — конкретний устрій ключових модулів.
Окрім коректності даних, гостро стоїть питання продуктивності. Core Web Vitals — LCP, CLS, INP — безпосередньо впливають на конверсію. Ми досягаємо LCP < 1.5 за допомогою серверного рендерингу (Next.js), оптимізації зображень та CDN. Для складних карт з кластеризацією використовуємо vector tiles та lazy loading.
Чому модель доступності — ядро будь-якої rental-платформи?
Жоден available: boolean на об'єкті не працює. Потрібна окрема таблиця періодів з типами блокувань — booked, owner_blocked, maintenance, external_ical. Тільки так можна коректно перевіряти доступність на перетин дат. Різниця між PostGIS та MySQL Spatial — у 3 рази вища продуктивність на радіусі 50 км завдяки GiST-індексам.
CREATE TABLE availability_blocks (
id BIGSERIAL PRIMARY KEY,
listing_id BIGINT NOT NULL REFERENCES listings(id),
start_date DATE NOT NULL,
end_date DATE NOT NULL,
block_type VARCHAR(20) NOT NULL,
booking_id BIGINT REFERENCES bookings(id),
CHECK (end_date > start_date)
);
CREATE INDEX idx_availability_listing_dates
ON availability_blocks (listing_id, start_date, end_date);
Запит перевірки доступності під блокуванням SELECT FOR UPDATE — обов'язкова умова, інакше при одночасних запитах на один об'єкт виникає race condition.
Пошук з геофільтрацією: PostGIS vs MySQL Spatial
Для geo-пошуку PostGIS в 3 рази швидше MySQL Spatial: підтримка SRID 4326, функції ST_DWithin та ST_Distance з географічними типами, індекси GiST. На фронті використовуємо Mapbox GL JS — векторні тайли дають кращий UX.
| Параметр | PostGIS (GiST index) | MySQL Spatial (R-tree) |
|---|---|---|
| Час виконання (радіус 50 км, 1 млн точок) | 120ms | 380ms |
| Підтримка географічних типів (geography) | Так | Ні |
| Функції для відстані в метрах | ST_DWithin, ST_Distance | ST_Distance_Sphere (повільніше) |
| Індексація | GiST (швидкий для перетинів) | R-tree (менш ефективний) |
SELECT
l.id,
l.title,
l.price_per_night,
ST_Distance(l.location, ST_MakePoint($1, $2)::geography) AS distance_meters
FROM listings l
WHERE ST_DWithin(
l.location,
ST_MakePoint($1, $2)::geography,
$3
)
AND l.guests_max >= $4
AND l.bedrooms >= $5
AND NOT EXISTS (
SELECT 1 FROM availability_blocks ab
WHERE ab.listing_id = l.id
AND ab.block_type IN ('booked', 'owner_blocked')
AND ab.start_date < $7
AND ab.end_date > $6
)
ORDER BY distance_meters
LIMIT 50;
Система бронювання: скінченний автомат на Laravel
| Статус | Допустимі переходи |
|---|---|
| pending_payment | confirmed, cancelled |
| confirmed | cancelled_by_host, cancelled_by_guest, active |
| active | completed, disputed |
class Booking extends Model
{
public function confirm(): void
{
if ($this->status !== BookingStatus::PendingPayment) {
throw new InvalidBookingTransitionException(
"Cannot confirm booking in status: {$this->status->value}"
);
}
DB::transaction(function () {
$this->update(['status' => BookingStatus::Confirmed]);
AvailabilityBlock::create([
'listing_id' => $this->listing_id,
'start_date' => $this->check_in,
'end_date' => $this->check_out,
'block_type' => 'booked',
'booking_id' => $this->id,
]);
event(new BookingConfirmed($this));
});
}
}
Платежі та утримання коштів: Stripe Connect
Ключова особливість rental: гроші утримуються при бронюванні та зараховуються хазяїну після check-in (або checkout). Stripe Connect з capture_method: manual дозволяє захопити кошти пізніше. Періодичні задачі (scheduled jobs) обробляють виплати та повернення. Помилка в логіці виплат може коштувати до 15% обороту через комісії та повернення — тому ми тестуємо кожен сценарій вручну. Економія на комісіях при власній платформі може досягати 10–15% від обороту — при 1000 бронюваннях на місяць із середнім чеком $200 це $24 000–36 000 на рік.
Як уникнути конфліктів при синхронізації з Airbnb та Booking?
Підключаємо iCal (RFC 5545): хазяїн вказує URL календаря, система імпортує зовнішні блокування. Експорт — навпаки. Laravel Scheduler запускає синхронізацію кожні 15–30 хвилин. Тоді об'єкт не буде заброньований одночасно на різних платформах.
Деталі синхронізації iCal
Імпорт парсить ICS-файл, витягує події з типом VEVENT і створює блокування з типом external_ical. Експорт генерує ICS-рядок із блокуваннями з внутрішніх бронювань. Для уникнення дублювання ми зберігаємо external_event_uid та оновлюємо лише змінені записи.
Система відгуків із двосторонньою анонімністю
Відгуки публікуються лише після того, як обидві сторони залишили відгук, або після закінчення 14 днів з checkout. Це виключає тиск і підвищує довіру. Реалізація з published_at та periodic job.
Що таке Core Web Vitals і чому вони важливі для rental-платформ?
Google ранжує сайти за метриками LCP, CLS, INP. Для rental-платформи критичні: швидкість завантаження сторінки лістингу (LCP), стабільність макету при підвантаженні зображень (CLS) та чуйність фільтрів (INP). Ми досягаємо LCP < 1.5 за допомогою серверного рендерингу (Next.js), оптимізації зображень та CDN. Для складних карт з кластеризацією використовуємо vector tiles та lazy loading.
Як ми реалізуємо платіжну систему: покроково
- Проєктування схеми платежів: вибір Stripe Connect, налаштування
capture_method: manual - Реалізація ендпоінтів для створення PaymentIntent та підтвердження
- Обробка вебхуків (payment_intent.succeeded, charge.disputed)
- Scheduled jobs для виплат хазяям (із затримкою після check-in)
- Тестування всіх кейсів: успішна оплата, скасування, повернення, спір
Що входить в роботу
- Проєктування архітектури БД, API, схеми платежів
- Розробка лістингів, пошуку, бронювання, чатів
- Інтеграція Stripe Connect, iCal, email-повідомлень
- Розгортання на інфраструктурі замовника (VPS, Docker)
- Документація, навчання адміністраторів, 6 місяців гарантії
Строки розробки
Базова версія (пошук, бронювання, Stripe, кабінети): 10–12 тижнів. З iCal, відгуками, картою з кластеризацією: 14–18 тижнів. Повний функціонал (модерація, верифікація, dispute resolution, аналітика): 20–24 тижні. Тестування граничних випадків з виплатами — найтриваліший етап, кожен баг або втрата грошей, або юридичний ризик.
Замовте консультацію — проаналізуємо ваш проєкт і запропонуємо архітектуру. Отримайте оцінку протягом дня. Зв'яжіться з нами, щоб обговорити деталі.







