ゲスト認証フロー

このガイドでは、キャプティブポータルで滞在中のゲスト一覧を使う方法を説明します。ゲストの入力と一覧の照合、Wi-Fi セッションを終了するタイミング、一覧を更新する頻度、トルコ法第5651号に基づく記録のために保存するフィールドを取り上げます。

ポータルで入力を求める項目#

一覧には部屋の情報と身元情報がまとめて含まれるため、最も一般的なフォームは部屋番号 + 姓です。部屋番号だけでは不十分です。隣の部屋の番号を知っていれば誰でもログインできてしまいます。代替として部屋番号 + ID/パスポート番号姓 + 生年月日があります。後者は、ID 番号が空のゲスト向けの予備手段として残しておいてください。

照合ルール#

ゲストの入力と一覧の値を比較する前に、両方を正規化してください。

  • 部屋番号:空白を除去し、大文字に変換します。roomName は文字列です("104""A-12""Villa 3" などがありえます)。数値に変換したり、先頭のゼロを削除したりしないでください。
  • 姓:大文字に変換し(トルコ語の i → İ の対応に注意)、連続するスペースを1つにまとめます。一覧の値は通常、身分証明書から大文字で読み取られています。
  • ID 番号:スペースを除去し、大文字に変換します。トルコ国民 ID 番号は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 の値が、ホットスポットユーザーの識別子になります。同じゲストが2台目の端末からログインした場合は、新しいアカウントを作成せずにこの値に紐づくアカウントを再利用し、そのアカウントに端末数の上限を適用してください。

セッションの有効期間#

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ゲストがチェックアウトしたか、予約がキャンセルされました。セッションを終了し、アカウントを停止します。
  • 同じ uniqueroomName または checkout が異なる:部屋の移動または滞在の延長です。アカウントを更新します。
  • リクエストの失敗:タイムアウト、4015xx の場合は最後に成功した一覧を保持します。エラーレスポンスに基づいてアカウントを停止してはいけません。
同期ループ
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 アドレスとタイムスタンプとともに次のフィールドを保存してください:uniquerezervasyon_idfirstNamelastNameidentityNumberbirthDateroomNamecheckincheckout。ゲストが一覧から外れると、HMS はこれらの情報を提供しなくなります。記録は自社側で保持してください。