Розробка системи онлайн-бронювання для сайту

Наша компанія займається розробкою, підтримкою та обслуговуванням сайтів будь-якої складності. Від простих односторінкових сайтів до масштабних кластерних систем, побудованих на мікро сервісах. Досвід розробників підтверджено сертифікатами від вендорів.

Розробка та обслуговування будь-яких видів сайтів:

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

Це лише деякі з технічних типів сайтів, з якими ми працюємо, і кожен із них може мати свої специфічні особливості та функціональність, а також бути адаптованим під конкретні потреби та цілі клієнта.

Пропоновані послуги
Показано 1 з 1 послугУсі 2065 послуг
Розробка системи онлайн-бронювання для сайту
Складна
~2-4 тижні
Часті питання

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

Етапи розробки

Останні роботи

  • image_website-b2b-advance_0.png
    Розробка сайту компанії B2B ADVANCE
    1262
  • image_web-applications_feedme_466_0.webp
    Розробка веб-додатків для компанії FEEDME
    1171
  • image_websites_belfingroup_462_0.webp
    Розробка веб-сайту для компанії БЕЛФІНГРУП
    874
  • image_ecommerce_furnoro_435_0.webp
    Розробка інтернет магазину для компанії FURNORO
    1094
  • image_crm_enviok_479_0.webp
    Розробка веб-додатків для компанії Enviok
    831
  • image_bitrix-bitrix-24-1c_fixper_448_0.png
    Розробка веб-сайту для компанії ФІКСПЕР
    851

Розроблення системи онлайн-бронювання для веб-сайту

Система бронювання — це не просто форма з вибором дати. Це управління слотами, перевірка доступності в реальному часі, утримання брони, обробка одночасних запитів і ланцюг сповіщень. Неправильна реалізація призводить до подвійних бронювань або «завислих» слотів.

Ключові сутності

-- Ресурс бронювання (зал, спеціаліст, номер, стіл тощо)
CREATE TABLE bookable_resources (
    id              SERIAL PRIMARY KEY,
    type            VARCHAR(50) NOT NULL,  -- 'specialist', 'room', 'table', 'car'
    name            VARCHAR(255) NOT NULL,
    config          JSONB,                 -- специфічні налаштування ресурсу
    is_active       BOOLEAN DEFAULT TRUE
);

-- Розклад доступності ресурсу
CREATE TABLE resource_schedules (
    id              SERIAL PRIMARY KEY,
    resource_id     INTEGER REFERENCES bookable_resources(id),
    weekday         SMALLINT,              -- 0=пн, 6=нд; NULL = конкретна дата
    specific_date   DATE,
    start_time      TIME NOT NULL,
    end_time        TIME NOT NULL,
    slot_duration   INTERVAL,             -- NULL = ресурс не поділяється на слоти
    is_available    BOOLEAN DEFAULT TRUE
);

-- Реальні бронювання
CREATE TABLE bookings (
    id              BIGSERIAL PRIMARY KEY,
    resource_id     INTEGER REFERENCES bookable_resources(id),
    user_id         INTEGER,
    guest_name      VARCHAR(255),
    guest_email     VARCHAR(255),
    guest_phone     VARCHAR(50),
    starts_at       TIMESTAMP NOT NULL,
    ends_at         TIMESTAMP NOT NULL,
    status          VARCHAR(20) DEFAULT 'pending',
    -- pending | confirmed | cancelled | no_show | completed
    notes           TEXT,
    metadata        JSONB,
    created_at      TIMESTAMP DEFAULT NOW(),
    confirmed_at    TIMESTAMP,
    cancelled_at    TIMESTAMP,
    CONSTRAINT no_overlap EXCLUDE USING gist (
        resource_id WITH =,
        tsrange(starts_at, ends_at, '[)') WITH &&
    ) WHERE (status NOT IN ('cancelled'))
);

Обмеження EXCLUDE USING gist із tsrange — найнадійніший спосіб запобігти подвійному бронюванню на рівні БД. Він працює атомарно і не залежить від логіки додатку.

Алгоритм перевірки доступності

def get_available_slots(resource_id: int, date: date) -> list[TimeSlot]:
    # 1. Отримати розклад ресурсу на цей день
    schedule = get_schedule(resource_id, date)
    if not schedule or not schedule.is_available:
        return []

    # 2. Згенерувати всі теоретичні слоти
    all_slots = generate_slots(
        start=schedule.start_time,
        end=schedule.end_time,
        duration=schedule.slot_duration or timedelta(hours=1),
    )

    # 3. Отримати вже заняті інтервали з БД
    booked = get_booked_intervals(resource_id, date)

    # 4. Відфільтрувати занятих
    return [
        slot for slot in all_slots
        if not any(slot.overlaps(b) for b in booked)
    ]

Тимчасове утримання слота (hold)

Між виборомслота та оплатою проходить час. Щоб слот не змогли зайняти в цей час, реалізується механізм hold:

HOLD_TTL = 600  # 10 хвилин

def hold_slot(resource_id: int, starts_at: datetime, session_id: str) -> str:
    hold_key = f"hold:{resource_id}:{starts_at.isoformat()}"
    # SET NX — тільки якщо ще не занято
    success = redis.set(hold_key, session_id, nx=True, ex=HOLD_TTL)
    if not success:
        existing = redis.get(hold_key)
        if existing and existing.decode() != session_id:
            raise SlotAlreadyHeld("Слот зайнятий іншим користувачем")
    return hold_key

def confirm_booking(hold_key: str, booking_data: dict) -> Booking:
    session_id = redis.get(hold_key)
    if not session_id:
        raise HoldExpired("Час утримання слота закінчився")

    with db.transaction():
        booking = create_booking(booking_data)
        redis.delete(hold_key)
    return booking

Обробка одночасних запитів

Навіть із обмеженням EXCLUDE, можлива гонка: два запити перевіряють доступність одночасно, обидва бачать слот вільним. Обмеження впіймає другий INSERT і викине ExclusionViolationError. Додаток повинен це обробити:

try:
    booking = create_booking(data)
except ExclusionViolationError:
    raise BookingConflict("Цей слот щойно забронював. Виберіть інший час.")

Сповіщення

Подія Кому Канал
Бронювання створено Клієнт Email + SMS
Бронювання підтверджено Клієнт Email
Нагадування за 24 години Клієнт Email + SMS
Нагадування за 1 годину Клієнт SMS
Нове бронювання Адміністратор Email
Скасування Клієнт + Адміністратор Email

Нагадування відправляються через запланізовані завдання — cron щогодини вибирає брони, у яких starts_at через 24 години або через 1 годину, і відповідне сповіщення не було відправлено.

Правила скасування та змін

Гнучка система політик скасування:

{
  "cancellation_policy": {
    "free_cancellation_hours": 24,
    "partial_refund_hours": 12,
    "partial_refund_percent": 50,
    "no_refund_hours": 2
  }
}

Політика зберігається на рівні ресурсу або типу бронювання та застосовується автоматично при розрахунку повернення коштів.

Інтеграція з оплатою

Бронювання може вимагати передоплату або повну оплату. Інтеграція з Stripe:

def create_payment_intent(booking: Booking, amount: int, currency: str):
    intent = stripe.PaymentIntent.create(
        amount=amount,
        currency=currency,
        metadata={
            'booking_id': str(booking.id),
            'resource_id': str(booking.resource_id),
        },
        capture_method='manual',  # автооплата при підтвердженні
    )
    booking.payment_intent_id = intent.id
    booking.save()
    return intent.client_secret

При capture_method='manual' гроші замораживаються на карті, але списуються лише при виклику capture() — це зручно для бронювань із ручним підтвердженням.

Строки реалізації

Базова система з одним типом ресурсу, без оплати — 8–10 робочих днів. Декілька типів ресурсів, управління розкладом через CMS, інтеграція з оплатою, SMS-сповіщення, політики скасування, мобільний вид — 14–18 робочих днів.