Процес перевірки гостей
У цьому посібнику пояснено, як використовувати список гостей, які проживають у готелі, у captive-порталі: як зіставляти введені гостем дані зі списком, коли має завершуватися сеанс Wi-Fi, як часто оновлювати список і які поля зберігати для записів за турецьким законом № 5651.
Що запитувати на порталі#
Оскільки список містить одночасно дані про номер і особу, найпоширеніша форма — номер кімнати + прізвище. Одного номера кімнати недостатньо: увійти зміг би будь-хто, хто знає номер сусідньої кімнати. Альтернативи — номер кімнати + номер посвідчення особи чи паспорта або прізвище + дата народження; останній варіант залиште як запасний для гостей, у яких номер документа порожній.
Правила зіставлення#
Перш ніж порівнювати введені гостем дані зі значеннями зі списку, нормалізуйте обидві сторони:
- Номер кімнати: обріжте пробіли й переведіть у верхній регістр.
roomName— рядок (це може бути"104","A-12"або"Villa 3"); не перетворюйте його на число й не відкидайте нулі на початку. - Прізвище: переведіть у верхній регістр (з урахуванням турецького відповідника
i → İ) і замініть повторювані пробіли одним. Значення в списку зазвичай зчитано з документа великими літерами. - Номер документа: приберіть пробіли й переведіть у верхній регістр. Турецький національний ідентифікаційний номер складається з 11 цифр; номер паспорта містить і літери, і цифри.
- Дата народження: у списку вона у форматі
DD.MM.YYYY; якщо ваша форма збирає інший формат, конвертуйте.
const norm = (s) => String(s ?? "")
.replace(/\s+/g, " ")
.trim()
.toLocaleUpperCase("tr-TR");
function findGuest(list, room, lastName) {
return list.find((g) =>
norm(g.roomName) === norm(room) && norm(g.lastName) === norm(lastName)
) || null;
}
const guest = findGuest(data.otelde, form.room, form.lastName);
if (!guest) {
// «Номер кімнати й прізвище не збігаються» — рахуйте невдалі спроби, після 5-ї обмежуйте частоту
}Значення unique знайденого запису — це ідентифікатор користувача hotspot. Коли той самий гість входить із другого пристрою, використовуйте обліковий запис, прив’язаний до цього значення, а не створюйте новий, і застосовуйте обмеження кількості пристроїв до цього облікового запису.
Тривалість сеансу#
checkout — запланована дата виїзду, що вказує на 00:00 (UTC) цього дня; часу доби вона не містить. Якщо відкрити сеанс до цього моменту, гість утратить доступ уранці в день від’їзду. Обчислюйте кінець сеансу, додаючи до дати розрахунковий час готелю:
const CHECKOUT_TIME = "12:00"; // розрахунковий час готелю
const day = new Date(guest.checkout * 1000)
.toLocaleDateString("en-CA", { timeZone: "UTC" }); // "2026-09-10"
const sessionEnd = new Date(`${day}T${CHECKOUT_TIME}:00+03:00`);Якщо гість виїжджає раніше розрахункового часу, він зникає зі списку; закрийте обліковий запис під час наступної синхронізації. Якщо проживання продовжено, checkout змінюється; порівнюйте це поле під час синхронізації й оновлюйте кінець сеансу. У разі пізнього виїзду безпечніше залишати доступ відкритим, доки гість є в списку.
Періодична синхронізація#
Список — це знімок стану; сповіщень про зміни (вебхуків) немає. Отримуйте список із фіксованим інтервалом і порівнюйте його з попереднім за unique:
- Інтервал: для більшості готелів достатньо 5 хвилин. Якщо гості мають підключатися до мережі одразу після заселення, можна зменшити до 1–2 хвилин; менше ніж 1 хвилина — не варто.
- Новий
unique: підготуйте обліковий запис; гостя буде зіставлено під час першого входу на порталі. - Зниклий
unique: гість виселився або бронювання скасовано. Завершіть сеанс і закрийте обліковий запис. - Той самий
unique, іншийroomNameабоcheckout: переселення в інший номер або продовження проживання. Оновіть обліковий запис. - Невдалий запит: у разі тайм-ауту,
401або5xxзалишайте останній успішно отриманий список; ніколи не закривайте облікові записи на підставі відповіді з помилкою.
async function sync(prev) {
const res = await fetch(BASE + "/public/json/customer/inhotel", {
method: "POST",
headers: { "ApiKey": process.env.HMS_API_KEY, "HotelCode": HOTEL_CODE }
});
if (!res.ok) return prev; // 401 / 5xx: залишаємо старий список
const { success, otelde } = await res.json();
if (success !== 1) return prev;
const next = new Map(otelde.map((g) => [g.unique, g]));
for (const [id] of prev) {
if (!next.has(id)) await closeAccount(id); // гість виселився
}
for (const [id, g] of next) {
const old = prev.get(id);
if (!old) await openAccount(g); // новий гість
else if (old.checkout !== g.checkout || old.roomName !== g.roomName) await updateAccount(g);
}
return next;
}Кілька гостей в одному номері#
Кожен гість у номері — окремий запис: rezervasyon_id у них однаковий, а unique різний. Відкривайте облікові записи для кожного гостя, а не для номера, щоб у кожного були власні пристрої, сеанс і запис за законом № 5651. Запит «номер кімнати + прізвище» може повернути кілька записів для гостей з однаковим прізвищем (родини); замість того щоб брати перший збіг, уточнюйте за ім’ям або датою народження чи відкрийте для них спільний обліковий запис.
Записи за законом № 5651#
Обов’язок вести журнали доступу згідно з турецьким законом № 5651 покладено на систему hotspot; HMS лише надає ідентифікаційні дані. Коли відкривається сеанс, зберігайте разом з MAC/IP-адресою й міткою часу такі поля: unique, rezervasyon_id, firstName, lastName, identityNumber, birthDate, roomName, checkin, checkout. Щойно гість зникає зі списку, HMS більше не надає цих даних, тож зберігайте запис на своєму боці.