Web app đăng ký vé sự kiện kèm thanh toán và QR check-in tại cổng.
Khách chọn hạng vé → điền thông tin → nhận QR. Vé miễn phí xuất QR ngay, vé trả phí tạo đơn payOS và chỉ chuyển sang trạng thái hợp lệ sau khi webhook xác nhận thanh toán. Nhân sự tại sự kiện quét QR (hoặc nhập mã vé) để check-in, và tải được file Excel đối soát.
Đăng ký vé và nhận QR check-in — chọn hạng vé, điền thông tin; vé miễn phí xuất QR ngay, vé trả phí chờ webhook xác nhận.
Check-in tại cổng — nhân sự quét QR hoặc nhập mã vé để xem thông tin khách và xác nhận cho vào.
Thanh toán — chế độ mock checkout cho phép test toàn bộ luồng khi chưa cấu hình payOS.
| Nhóm | Chi tiết |
|---|---|
| Đăng ký | 3 hạng vé cấu hình sẵn (free, standard, mastery), validate họ tên / email / SĐT |
| Thanh toán | payOS payment link + QR, chế độ mock checkout khi chưa cấu hình payOS |
| Xác nhận | Webhook payOS có kiểm tra chữ ký HMAC-SHA256, kèm API đối soát trạng thái |
| Vé | QR check-in theo mã đơn, mở lại vé bằng link /?order=<mã đăng ký> hoặc /?orderCode=<mã đơn> |
| Check-in | Trang /checkin cho nhân sự: quét QR hoặc nhập mã, chặn check-in trùng |
| Đối soát | Xuất Excel tự động cập nhật (/api/export/registrations.xlsx) |
| Gửi email vé qua HTTP email API hoặc SMTP (tuỳ chọn, tắt được) | |
| Lưu trữ | Supabase (khuyến nghị) hoặc file JSON data/registrations.json khi chạy local |
Trình duyệt ──► React 19 + Vite (src/main.tsx)
│ /api/*
▼
Express 5 (server/index.mjs)
├──► Supabase (bảng registrations, RLS bật)
├──► payOS API (tạo payment link + webhook)
├──► ExcelJS (file đối soát)
└──► email API / SMTP (gửi vé)
Frontend và backend nằm cùng một tiến trình Node: Express phục vụ thư mục dist/ sau khi build.
Yêu cầu Node 22 trở lên.
npm install
npm run build
npm run startMở http://127.0.0.1:8080. Khi chưa cấu hình payOS, các gói trả phí dùng mock checkout để test toàn bộ luồng mà không cần tiền thật.
Chế độ phát triển giao diện:
npm run devSao chép .env.example thành .env rồi điền dần. Tất cả đều tuỳ chọn khi chạy local.
| Biến | Bắt buộc | Mô tả |
|---|---|---|
PORT |
không | Cổng server, mặc định 8080 |
PUBLIC_BASE_URL |
nên có | URL public, dùng để tạo link vé và link webhook |
ADMIN_TOKEN |
nên có khi deploy | Khoá cho các endpoint quản trị. Xem mục Bảo mật |
CORS_ORIGINS |
không | Danh sách origin được phép, cách nhau dấu phẩy. Bỏ trống = không mở CORS |
PAYOS_CLIENT_ID / PAYOS_API_KEY / PAYOS_CHECKSUM_KEY |
không | Bật payOS thật. Thiếu thì dùng mock checkout |
SUPABASE_URL / SUPABASE_SERVICE_ROLE_KEY |
không | Bật lưu trữ Supabase. Thiếu thì ghi ra file JSON |
EMAIL_API_* |
không | Gửi email vé qua HTTP API |
SMTP_* / MAIL_FROM |
không | Gửi email vé qua SMTP |
Các endpoint dưới đây trả về dữ liệu cá nhân của khách hoặc thay đổi trạng thái vé, nên được
khoá bằng ADMIN_TOKEN:
GET /api/export/registrations.xlsxPOST /api/checkin/:id/confirmPOST /api/mock/pay/:idPOST /api/email/test
Gửi kèm một trong hai header:
Authorization: Bearer <ADMIN_TOKEN>
X-Admin-Token: <ADMIN_TOKEN>
Nếu ADMIN_TOKEN để trống, các endpoint này vẫn mở để tiện chạy local, và server sẽ in cảnh
báo lúc khởi động. Luôn đặt ADMIN_TOKEN trước khi public app ra internet.
Ngoài ra:
- Webhook payOS luôn kiểm tra chữ ký HMAC-SHA256 và so sánh bằng
crypto.timingSafeEqual. Khi chưa cấu hình payOS, endpoint webhook trả503thay vì bỏ qua kiểm tra. - CORS mặc định không mở. Chỉ bật khi khai báo
CORS_ORIGINS. - Có rate limit cho đăng ký, tra cứu vé và các endpoint quản trị.
- Supabase dùng
service_rolekey ở server, RLS được bật sẵn trongsupabase-schema.sql. Không bao giờ đưa key này vào code frontend.
Xem DEPLOY.md. Bản ngắn: dùng Dockerfile và render.yaml có sẵn để deploy
lên Render, cấu hình webhook payOS trỏ về https://<domain>/api/payos/webhook.
src/main.tsx giao diện đăng ký và trang check-in
src/styles.css style dùng chung
server/index.mjs API Express, thanh toán, webhook, Excel, email
server/migrate-local-to-supabase.mjs chuyển dữ liệu JSON lên Supabase
supabase-schema.sql schema + index + RLS
DEPLOY.md hướng dẫn deploy chi tiết
| Lệnh | Việc |
|---|---|
npm run dev |
Vite dev server |
npm run build |
Build frontend vào dist/ |
npm run start |
Chạy server production |
npm run typecheck |
Kiểm tra TypeScript |
npm run test |
Chạy smoke test API (node:test) |
npm run lint |
Chạy ESLint |
npm run format |
Định dạng bằng Prettier |
npm run migrate:supabase |
Chuyển dữ liệu JSON local lên Supabase |


