ขั้นตอนการตรวจสอบผู้เข้าพัก

คู่มือนี้อธิบายวิธีใช้รายชื่อผู้เข้าพักในโรงแรมกับ captive portal ได้แก่ การจับคู่ข้อมูลที่ผู้เข้าพักกรอกกับรายการ เวลาที่ควรสิ้นสุดเซสชัน 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 จะเปลี่ยน ให้เปรียบเทียบฟิลด์นี้ระหว่างการซิงค์และอัปเดตเวลาสิ้นสุดเซสชัน สำหรับการเช็กเอาต์ล่าช้า การเปิดการเข้าถึงไว้ตราบที่ผู้เข้าพักยังอยู่ในรายการจะปลอดภัยกว่า

การซิงค์เป็นระยะ#

รายการเป็นภาพข้อมูล ณ ขณะหนึ่ง (snapshot) และไม่มีการแจ้งเตือนการเปลี่ยนแปลง (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 เป็นของระบบ Hotspot ส่วน HMS เพียงให้ข้อมูลระบุตัวตนเท่านั้น เมื่อเปิดเซสชัน ให้จัดเก็บฟิลด์ต่อไปนี้พร้อมกับที่อยู่ MAC/IP และเวลา: unique, rezervasyon_id, firstName, lastName, identityNumber, birthDate, roomName, checkin, checkout เมื่อผู้เข้าพักหายไปจากรายการ HMS จะไม่ให้ข้อมูลนี้อีก จึงต้องเก็บบันทึกไว้ในฝั่งของคุณ