Готель звернувся до нас із задачею, яка звучить просто, а на практиці впирається в архітектуру мереж: гість бронює номер онлайн — і система має сама видати йому карту-ключ на турнікет біля входу, а на виїзді автоматично її погасити. Жодного ресепшена, який руками заводить кожного гостя в панель контролю доступу.
Нижче — як ми звʼязали хмарну систему бронювання з фізичним обладнанням у локальній мережі готелю, які підводні камені трапилися і чому рішення тиражується на інші обʼєкти без болю.
Чому це не «просто HTTP-запит»
Дві частини системи живуть у різних світах:
- PMS-система бронювання — у хмарі (QloApps на базі PrestaShop), доступна з інтернету.
- Контролери доступу — турнікети Hikvision у локальній мережі готелю, за роутером і NAT, без білого IP.
Достукатися з хмари напряму до залізки в приватній мережі не можна: вхідні порти закриті, білої адреси немає. Прокидати порти на кожному обʼєкті — це і дірка в безпеці, і постійний головний біль на підтримці (у готелів змінюються провайдери, роутери, IP). Потрібне було рішення, яке працює за будь-яким NAT і нічого не вимагає від мережі готелю.
Архітектура: три ланки й одна ключова ідея
Ми розбили інтеграцію на три частини — модуль у хмарі, сервіс-шлюз і локальний агент:
[PMS у хмарі] [ПК ресепшена, локальна мережа]
Модуль бронювання Локальний агент (Windows-служба)
│ │
▼ ▼
Сервіс-шлюз ─── WSS (агент дзвонить сам) ──► Контролер доступу
(наш бекенд) (турнікет / картрідер)
Ключова ідея — інверсія зʼєднання. Не хмара стукає в готель, а локальний агент сам відкриває вихідний канал назовні через WebSocket Secure (порт 443). Для роутера це звичайний вихідний HTTPS-трафік — нічого відкривати чи прокидати не треба, працює за будь-яким NAT «з коробки». Коли надходить бронювання, шлюз пише завдання в чергу й проштовхує його вже відкритим каналом до потрібного агента.
Життєвий шлях одного бронювання
- Гість оплачує бронь у PMS → модуль кладе завдання
issueу чергу шлюзу (номер, період, які зони доступні). - Шлюз знаходить агента потрібного готелю серед відкритих WSS-зʼєднань і надсилає завдання.
- Агент перекладає його у виклики до контролера: заводить гостя, генерує/привіʼязує карту, ставить розклад дії за датами заїзду й виїзду.
- Контролер підтверджує — агент повертає статус, шлюз оновлює запис у базі, у журналі зʼявляється рядок «карту видано».
- На 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 чи сайту з фізичним обладнанням — турнікетами, замками, касами, ПРРО? Напишіть нам — розберемо вашу задачу.


