Процесс проверки гостей
В этом руководстве описано, как использовать список проживающих гостей в 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 найденной записи — это идентификатор пользователя хотспота. Когда тот же гость входит со второго устройства, используйте учётную запись, привязанную к этому значению, а не создавайте новую, и применяйте к ней ограничение на число устройств.
Длительность сессии#
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; сравнивайте это поле при синхронизации и обновляйте окончание сессии. При позднем выезде надёжнее держать доступ открытым, пока гость остаётся в списке.
Периодическая синхронизация#
Список — это снимок текущего состояния; уведомлений об изменениях (webhook) нет. Запрашивайте список с фиксированным интервалом и сравнивайте его с предыдущим по 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 лежит на системе хотспота; HMS лишь предоставляет личные данные. При открытии сессии сохраняйте вместе с MAC/IP-адресом и временем следующие поля: unique, rezervasyon_id, firstName, lastName, identityNumber, birthDate, roomName, checkin, checkout. Когда гость исчезает из списка, HMS больше не предоставляет эти данные, поэтому храните записи на своей стороне.