Інженерія

Як ми звʼязали хмарне бронювання готелю з турнікетами Hikvision

Готель звернувся до нас із задачею, яка звучить просто, а на практиці впирається в архітектуру мереж: гість бронює номер онлайн — і система має сама видати йому карту-ключ на турнікет біля входу, а на виїзді автоматично її погасити. Жодного ресепшена, який руками заводить кожного гостя в панель контролю доступу.

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

Чому це не «просто HTTP-запит»

Дві частини системи живуть у різних світах:

  • PMS-система бронювання — у хмарі (QloApps на базі PrestaShop), доступна з інтернету.
  • Контролери доступу — турнікети Hikvision у локальній мережі готелю, за роутером і NAT, без білого IP.

Достукатися з хмари напряму до залізки в приватній мережі не можна: вхідні порти закриті, білої адреси немає. Прокидати порти на кожному обʼєкті — це і дірка в безпеці, і постійний головний біль на підтримці (у готелів змінюються провайдери, роутери, IP). Потрібне було рішення, яке працює за будь-яким NAT і нічого не вимагає від мережі готелю.

Архітектура: три ланки й одна ключова ідея

Ми розбили інтеграцію на три частини — модуль у хмарі, сервіс-шлюз і локальний агент:

[PMS у хмарі]                         [ПК ресепшена, локальна мережа]
  Модуль бронювання                     Локальний агент (Windows-служба)
       │                                          │
       ▼                                          ▼
  Сервіс-шлюз  ─── WSS (агент дзвонить сам) ──►  Контролер доступу
   (наш бекенд)                                 (турнікет / картрідер)

Ключова ідея — інверсія зʼєднання. Не хмара стукає в готель, а локальний агент сам відкриває вихідний канал назовні через WebSocket Secure (порт 443). Для роутера це звичайний вихідний HTTPS-трафік — нічого відкривати чи прокидати не треба, працює за будь-яким NAT «з коробки». Коли надходить бронювання, шлюз пише завдання в чергу й проштовхує його вже відкритим каналом до потрібного агента.

Життєвий шлях одного бронювання

  1. Гість оплачує бронь у PMS → модуль кладе завдання issue у чергу шлюзу (номер, період, які зони доступні).
  2. Шлюз знаходить агента потрібного готелю серед відкритих WSS-зʼєднань і надсилає завдання.
  3. Агент перекладає його у виклики до контролера: заводить гостя, генерує/привіʼязує карту, ставить розклад дії за датами заїзду й виїзду.
  4. Контролер підтверджує — агент повертає статус, шлюз оновлює запис у базі, у журналі зʼявляється рядок «карту видано».
  5. На check-out (або коли настає дата виїзду) — дзеркальний сценарій revoke: карта гаситься, користувач прибирається з контролера.

Все обладнання готелю адміністратор бачить і налаштовує в одному розділі — список турнікетів із їхніми адресами, зонами доступу й станом:

Список турнікетів в адмінці
Розділ керування турнікетами: кожен контролер — з адресою в мережі, зонами доступу та статусом.

Для кожного турнікета задаються мережеві реквізити, роль (вхід/вихід), групи прав і те, до яких зон готелю карта відкриває доступ:

Форма налаштування турнікета
Налаштування окремого турнікета — адреса, роль, зони доступу.

Що виявилося непростим

Стабільність каналу й черга з підтвердженнями

WSS-зʼєднання треба тримати живим годинами й переживати обриви мережі. Агент сам перепідключається, а завдання не губляться: вони лежать у черзі в базі зі статусами, і шлюз повторює доставку, поки агент не підтвердить виконання (ack). Якщо ПК ресепшена вимкнули на ніч — вранці агент піднявся, забрав усе накопичене й доробив по порядку. Кожна операція ідемпотентна, тож повтор не створює дублів карт.

ISAPI — це не той REST, до якого всі звикли

Агент спілкується з контролером по його рідному протоколу — ISAPI поверх HTTP Digest: своя автентифікація, специфічні JSON-структури під кожен endpoint, власні коди помилок, а документація протоколу — сотні сторінок. Ми обгорнули всю цю «кухню» в чистий внутрішній контракт, щоб решта системи не знала деталей контролера й оперувала простими командами issue / revoke / status. Якщо завтра зʼявиться інша модель або вендор — міняється тільки адаптер в агенті, а не вся система.

Карта живе рівно стільки, скільки бронь

Карта має зʼявитися саме на період проживання й зникнути після виїзду — ні днем раніше, ні днем пізніше. Ми привʼязали видачу й погашення до подій бронювання в PMS і ведемо журнал кожної операції: адміністратор завжди бачить, яка карта, кому і на який період видана, та може погасити її вручну достроково.

Оновлення без виїздів на обʼєкт

Агент — це Windows-служба на ПК ресепшена. Щоб не їздити на кожен готель заради нової версії, ми зробили механізм самооновлення: агент підтягує оновлення сам тим самим захищеним каналом і перезапускається як служба.

Безпека

  • Жодних вхідних портів у мережі готелю — тільки вихідний WSS на 443.
  • Агент автентифікується на шлюзі по токену; кожен готель бачить лише свої завдання.
  • Реквізити контролерів зберігаються на боці готелю, а не гуляють хмарою.
  • Повний журнал видач/погашень — для аудиту й розбору спірних ситуацій.

Стек

  • Шлюз: Node.js (WebSocket-сервер + REST для PMS-модуля), черга й журнал у MySQL, готовність масштабуватися на багато готелів через pub/sub.
  • Агент: Node.js, упакований у Windows-службу; ISAPI Digest до контролера доступу; самооновлення.
  • PMS: QloApps / PrestaShop — інтеграція через модуль на боці системи бронювання.
  • Тестування: E2E-сценарії наскрізь — бронювання → шлюз → агент → контролер → запис карти → статус назад у базу.

Результат

Готель отримав повністю автоматичний цикл: бронювання онлайн → карта на турнікеті → автопогашення на виїзді, без ручної роботи ресепшена й без діри в мережевій безпеці обʼєкта. Рішення тиражоване — підключити новий готель означає лише поставити агента на ПК ресепшена, решта інфраструктури вже є.

Потрібна інтеграція системи бронювання, CRM чи сайту з фізичним обладнанням — турнікетами, замками, касами, ПРРО? Напишіть нам — розберемо вашу задачу.

Поділитись

Готові обговорити реліз?

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

Залишити запит