← กรณีการใช้งาน

ห้องรอคิวสำหรับการเปิดขายบัตรและแฟลชเซล

วางคิวที่ยุติธรรมและอัปเดตแบบเรียลไทม์ไว้หน้าการขาย ผู้เข้าชมเห็นลำดับของตัวเองขยับ เข้าได้ตามอัตราที่คุณตั้ง และเว็บไซต์ของคุณตรวจพาสที่มีลายเซ็น ใช้ได้กับทุกโฮสต์ รวมถึง Laravel และ Filament

การเปิดขายบัตร สินค้าลิมิเต็ด หรือการเปิดพรีออร์เดอร์ ทำให้ทราฟฟิกทั้งวันมากองอยู่ในนาทีแรกของการขาย ระบบปรับขนาดอัตโนมัติตอบสนองไม่ทัน การชำระเงินช้าลงสำหรับทุกคน และสุดท้ายคนที่เน็ตเร็วที่สุดเป็นผู้ชนะ ห้องรอคิวจะตั้งแถวที่สงบและยุติธรรมไว้หน้าหน้าเว็บที่คนแน่น ผู้เข้าชมรับหมายเลขคิว ดูแถวขยับแบบเรียลไทม์ และเข้ามาตามอัตราที่เว็บไซต์ของคุณรับไหว สมัครและตั้งค่าได้เอง ไม่ต้องคุยกับฝ่ายขาย และไม่ต้องย้ายโฮสต์หรือ CDN

สิ่งที่ผู้เข้าชมได้รับ

  • หมายเลขคิวและข้อความ "มีคนรออยู่ข้างหน้า 4,812 คน" ที่อัปเดตแบบเรียลไทม์ระหว่างรอ
  • รีเฟรชหรือเปิดแท็บเพิ่มก็ไม่ทำให้เสียคิวหรือได้คิวเร็วขึ้น
  • หน้ารอคิวแบบโฮสต์ให้ รองรับภาษาอังกฤษ จีนตัวเต็ม จีนตัวย่อ ญี่ปุ่น เกาหลี ไทย และเวียดนาม
  • การนับถอยหลังที่ชัดเจนก่อนเริ่มขาย และข้อความ "ขายหมดแล้ว" ที่ชัดเจนเมื่อจบ
หน้ารอคิวแบบโฮสต์ให้: ลำดับคิว จำนวนคนข้างหน้า เวลารอโดยประมาณ และประกาศของร้าน แสดงเป็นภาษาของผู้เข้าชม
หน้ารอคิวแบบโฮสต์ให้: ลำดับคิว จำนวนคนข้างหน้า เวลารอโดยประมาณ และประกาศของร้าน แสดงเป็นภาษาของผู้เข้าชม

สิ่งที่คุณได้รับ

  • แถวที่ขยับตามอัตราที่คุณตั้ง เช่น 300 คนต่อนาที
  • ปุ่มสำหรับช่วงเวลาสำคัญ: หยุดแถวชั่วคราว ให้ 500 คนเข้าทันที และปิดเมื่อสต็อกหมด
  • ผู้เข้าชมที่ได้เข้าแต่ละคนจะได้พาสที่มีลายเซ็น ซึ่งเว็บไซต์ของคุณตรวจก่อนแสดงหน้าที่ป้องกันไว้ ใครเดา URL ก็แซงคิวไม่ได้
  • ถ้าต้องการ ใช้ ticket ID ในพาสเพื่อให้หนึ่งหมายเลขคิวชำระเงินได้ครั้งเดียว
  • ผู้ที่รอคิวไม่นับรวมในจำนวนการเชื่อมต่อของแอป ฟีเจอร์เรียลไทม์อื่นบนเว็บไซต์จึงยังมีที่ว่างพอในช่วงคนแน่น
  • ประกาศถึงทุกคนในคิว เช่น "โซน A ขายหมดแล้ว" แสดงบนหน้ารอคิวทันที
ห้องรอคิวในพอร์ทัล: จำนวนคนแบบเรียลไทม์ อัตราการปล่อยเข้า เวลาที่คิวจะหมด และปุ่มหยุดชั่วคราว ปล่อยเข้า ขายหมด และประกาศ
ห้องรอคิวในพอร์ทัล: จำนวนคนแบบเรียลไทม์ อัตราการปล่อยเข้า เวลาที่คิวจะหมด และปุ่มหยุดชั่วคราว ปล่อยเข้า ขายหมด และประกาศ

ห้องรอคิวเป็นส่วนเสริมที่เพิ่มได้กับทุกแผนของ Hibira ใช้ฟรี 1 ห้อง และผู้เข้าคิว 5,000 คนต่อเดือน แผนแบบชำระเงินเริ่มที่ $29 ต่อเดือน และบัตรอีเวนต์ครั้งเดียว $49 ใช้ได้ 72 ชั่วโมง ผู้เข้าชมที่เกินแผนจะหักจากเครดิต และไม่มีใครถูกปฏิเสธ ดูราคา

ใช้อะไรบ้าง

  • ห้องรอคิว ในแอป Hibira ของคุณ จัดการได้จากพอร์ทัลหรือด้วยการเรียก HTTP API ที่มีลายเซ็น
  • หน้ารอคิวแบบโฮสต์ให้ ws.hibira.io/wait/APP_KEY/ROOM หรือหน้าของคุณเองด้วย @hibira-io/waiting-room
  • cache channel หนึ่งช่องต่อห้อง (cache-waitroom-ROOM): ข้อความเดียวบอกผู้ที่รอทุกคนว่าแถวขยับไปถึงไหน แล้วแต่ละเบราว์เซอร์คำนวณลำดับของตัวเอง
  • พาส ที่ลงลายเซ็นด้วย app secret ของคุณ (HMAC-SHA256) เว็บไซต์ของคุณตรวจเองได้โดยไม่ต้องเรียก Hibira
ผู้เข้าชมเปิด /drop
ไม่มีพาส: เปลี่ยนเส้นทาง
Hibiraหน้ารอคิวแบบโฮสต์ให้
ถึงคิว: ?hibira_pass=…
เว็บไซต์ของคุณตรวจพาส ตั้ง cookie

ทำให้แถวขยับ

ทีมของคุณพอร์ทัล Filament หรือ API
เปิด หยุด ปล่อยเข้า ปิด
Hibiracache-waitroom-presale
อัปเดตครั้งเดียว
ผู้ที่รอทุกคนคำนวณลำดับของตัวเอง
ผู้เข้าชมที่ไม่มีพาสต้องเข้าคิว เมื่อถึงคิวจะกลับมาพร้อมพาสที่มีลายเซ็น

เลือกวิธีเชื่อมต่อ

เลือกตามที่ที่เว็บไซต์ของคุณทำงานอยู่ ยิ่งอยู่สูงในรายการ ยิ่งเลี่ยงได้ยาก

ขั้นที่ 1: สร้างห้อง

สร้างในพอร์ทัล หรือด้วยคำขอ HTTP API ที่ลงลายเซ็นแบบเดียวกับการเผยแพร่อีเวนต์:

POST /apps/APP_ID/waiting_rooms/presale
{
  "admit_per_minute": 300,
  "state": "paused",
  "starts_at": "2026-12-04T12:00:00+09:00",
  "title": "Winter tour presale",
  "return_urls": ["https://shop.example.com/"]
}

open ปล่อยคนเข้าตามอัตรา paused หยุดแถวไว้ และ closed หยุดแจกหมายเลขเมื่อขายหมด ผู้ที่มาก่อน starts_at จะได้หมายเลขและเห็นการนับถอยหลัง แถวจะเริ่มขยับเมื่อถึงเวลานั้น

ขั้นที่ 2: ป้องกันหน้าเว็บที่ edge

บน Cloudflare Workers, middleware ของ Next.js, Deno หรือ Bun เรียกครั้งเดียวก็จบ:

// middleware.js (Next.js)
import { NextResponse } from 'next/server';
import { protect } from '@hibira-io/waiting-room/server';

export async function middleware(request) {
  const blocked = await protect(request, {
    appKey: process.env.HIBIRA_KEY,
    secret: process.env.HIBIRA_SECRET,
    room: 'presale',
  });
  return blocked ?? NextResponse.next();
}

export const config = { matcher: ['/drop/:path*'] };

ผู้เข้าชมที่ไม่มีพาสจะถูกส่งไปหน้ารอคิว เมื่อถึงคิวจะกลับมาพร้อมพาส ซึ่ง protect() ตรวจแล้วเก็บไว้ใน HttpOnly cookie บน Express ให้ใช้ expressWaitingRoom() จากแพ็กเกจเดียวกัน

ขั้นที่ 3: Laravel

ด้วย hibira-io/laravel เวอร์ชัน 0.4 ขึ้นไป (ซึ่งคุณอาจใช้ทำ broadcasting อยู่แล้ว) สร้างห้องจากคำสั่ง แล้วป้องกัน route ด้วย middleware ตัวเดียว:

composer require hibira-io/laravel:^0.4
php artisan hibira:waiting-room presale create --rate=300 --title="Winter presale"
Route::middleware('hibira.waiting:presale')->group(function () {
    Route::get('/drop', [DropController::class, 'show']);
    Route::post('/drop/checkout', [DropController::class, 'checkout']);
});

ผู้เข้าชมที่ไม่มีพาสจะถูกส่งไปหน้ารอคิวในภาษาเดียวกับแอปของคุณ พาสที่นำกลับมาจะถูกตรวจด้วย app secret ของคุณโดยไม่ต้องเรียก API และเก็บไว้ในคุกกี้ HttpOnly จนหมดอายุ คำขอ POST ที่ไม่มีพาสจะได้ 403 ใน controller อ่านพาสได้จาก request:

$pass = $request->attributes->get('hibira_pass');
$pass->number;   // queue number
$pass->ticketId; // record it at checkout: one order per number

ควบคุมห้องได้จากโค้ด งานในคิว หรือ command line:

use Hibira\Laravel\Facades\Hibira;

Hibira::waitingRoom('presale')->pause();
Hibira::waitingRoom('presale')->admit(500);
Hibira::waitingRoom('presale')->notice('Zone A is sold out');
Hibira::waitingRoom('presale')->close();

ระหว่างพัฒนาบนเครื่อง ตั้ง HIBIRA_WAITING_ROOMS=false เพื่อข้ามคิว

ขั้นที่ 4: ควบคุมแถวจาก Filament

ในวันขาย ทีมของคุณไม่ต้องเปิดแดชบอร์ดเพิ่มอีก ด้วย hibira-io/filament เวอร์ชัน 0.5 ขึ้นไป บรรทัดเดียวก็เพิ่มหน้า "ห้องรอคิว" ให้แผงของคุณ:

HibiraPlugin::make()->waitingRooms(
    authorize: fn ($user) => $user->is_admin,
)

หน้านี้แสดงจำนวนคนที่รอ ความคืบหน้าของคิว และอัตราการปล่อยเข้าของแต่ละห้องแบบเรียลไทม์ ทีมสามารถหยุดและเปิดคิว ปล่อยคนเข้าทันที โพสต์ประกาศ ปิดห้องเมื่อสินค้าหมด และสร้างหรือแก้ไขห้องได้จากหน้านี้ ถ้าไม่ใส่ authorize ผู้ใช้แผงทุกคนจะเข้าได้

ขั้นที่ 5: หน้ารอคิวของคุณเอง

หน้าแบบโฮสต์ให้เหมาะกับเว็บไซต์ส่วนใหญ่ ถ้าต้องการให้ตรงกับแบรนด์ทุกจุด ก็สร้างเองได้:

import { joinWaitingRoom } from '@hibira-io/waiting-room';

joinWaitingRoom({
  key: 'APP_KEY',
  room: 'presale',
  onUpdate: ({ ahead, etaMinutes }) => render(ahead, etaMinutes),
  onTurn: (turn) => turn.redirect('https://shop.example.com/drop'),
  onClosed: () => showSoldOut(),
});

ไคลเอนต์เก็บตั๋วของผู้เข้าชมไว้ในเบราว์เซอร์ รีเฟรชแล้วคิวจึงไม่หาย หากเปิดการเชื่อมต่อไม่ได้ จะเปลี่ยนไปเช็กสถานะทุกไม่กี่วินาทีแทน ไม่มีใครถูกกันไว้ข้างนอก

ขั้นที่ 6: ในแอปของคุณ

ไคลเอนต์มือถือหรือเกมใช้ endpoint สาธารณะชุดเดียวกับหน้าแบบโฮสต์ให้: เข้าคิว ติดตาม cache channel ของห้อง ขอพาสเมื่อถึงหมายเลข แล้วส่งพาสไปกับการเรียก API API ของคุณตรวจพาสด้วย app secret โดยไม่ต้องเรียก Hibira บน Laravel ใช้ WaitingRoom::verifyPass($pass, 'presale')

POST /waitroom/APP_KEY/presale/join         -> {"ticket": "…", "number": 5121}
GET  /waitroom/APP_KEY/presale              -> {"admitted": 4980, "state": "open", …}
POST /waitroom/APP_KEY/presale/pass         {"ticket": "…"}  -> {"pass": "…"}

เทียบกับ Queue-it และ Cloudflare Waiting Room

อ้างอิงหน้าราคาและแผนที่เปิดเผยต่อสาธารณะของแต่ละรายเมื่อพฤศจิกายน 2026 แผนอาจเปลี่ยนได้ โปรดตรวจสอบกับแต่ละรายก่อนตัดสินใจ

เปรียบเทียบฉบับเต็ม พร้อมราคาและกรณีที่แต่ละรายเหมาะกว่า: Queue-it, Cloudflare Waiting Room, CrowdHandler และ Queue-Fair

เคล็ดลับ

  • ป้องกันหน้าที่ของหมดได้จริง: หน้าสินค้า ตะกร้า และการชำระเงิน ปล่อยหน้าแรกและรูปภาพไว้นอกห้องรอคิว
  • ตรวจพาสในคำขอที่สำคัญ ไม่ใช่แค่ในเบราว์เซอร์ ผู้เข้าชมแก้สคริปต์บนหน้าของตัวเองได้ แต่ปลอมลายเซ็นไม่ได้
  • ถ้าหนึ่งหมายเลขคิวซื้อได้ครั้งเดียว ให้บันทึก ticket ID ของพาสตอนชำระเงิน และปฏิเสธคำสั่งซื้อที่สองที่ใช้ ID เดิม
  • เปิดห้องก่อนเริ่มขาย 15 ถึง 30 นาทีและตั้ง starts_at ผู้ที่มาก่อนจะรอที่หน้านับถอยหลังแทนการกดรีเฟรชรัว ๆ บนเว็บไซต์ของคุณ
  • เริ่มที่อัตราที่ระบบชำระเงินรับได้สบาย ๆ แล้วค่อยเพิ่มขณะดูอัตราข้อผิดพลาด การหยุดแถวชั่วคราวอยู่ห่างแค่คลิกเดียวเสมอ