← Hướng dẫn sử dụng · tài liệu kỹ thuật, không chính thức; thông tin cá nhân đã được ẩn

9698 Photobooth v3.5.0 — Khảo sát giao diện, chức năng, API và kiến trúc

Nguồn: thao tác trực tiếp trên app đang chạy + đọc mã đã bundle trong app.asar (chỉ đọc) + log trong %APPDATA%\9698-photobooth.

1. Công nghệ

2. Giao diện đã chụp (kiểm tra thực tế)

Màn hìnhNội dung
Sự kiện (trang chủ)Danh sách thẻ sự kiện, tìm kiếm, "Tạo Sự Kiện", "Đồng bộ ngay"; thẻ có loại Đám cưới / Bản quán, số ảnh, số in, doanh thu, nút Cài đặt / Chạy / Sửa ảnh đại diện, QR album cả tiệc (anh.9698media.com/e/<eventId>)
Báo cáo (Tổng quan)Lọc Ngày/Tháng/Quý/Năm/Tự chọn; tổng phiên, tổng giấy in, doanh thu, biểu đồ; phiên bản + "Kiểm tra bản mới"; nơi lưu dữ liệu; "Mở thư mục lưu"
Cấu hình → Màn hình3 tab: chào / chụp / chia sẻ, có preview trực tiếp + toàn màn hình; nền, nút Chạm để bắt đầu, Xem lại, Cài đặt, lật ảnh, kiểu hiển thị, xoay camera, tự động chụp…
Cấu hình → Chủ đềNền app, ảnh ướm vào khung (thư mục AnhPreview)
Cấu hình → Máy ảnhWebcam/Canon, chọn webcam, lật, FPS khung hình
Cấu hình → SmartboothNhận in từ điện thoại khách (quét QR, chụp bằng máy khách, in tại booth), mã booth
Cấu hình → Âm thanhÂm theo luồng Photobooth / Ảnh thẻ / Ảnh AI, mỗi bước tải file riêng
Cấu hình → Máy inMáy in chính/phụ, số bản mặc định, in ngang, tự in, ICC, giới hạn lượt in (100–200/không giới hạn), đặt lại bộ đếm
Cấu hình → Thanh toánBật thu tiền (chỉ sự kiện Bản quán), bảng giá theo khổ giấy (2×6, 4×6…), tiền mặt / QR / voucher, SePay
Cấu hình → Thêm khungTrình thiết kế template (canvas, lớp, ảnh từ booth, QR, undo/redo, zoom)
Cấu hình → Bộ lọc9 bộ lọc (Gốc, Ngọt ngào, Vintage…), LUT .cube, chỉnh màu/da/sticker, chụp thử, in thử
Cấu hình → Mã APIHướng dẫn lấy và nhập key OpenAI
Hồ sơTài khoản ([email tài khoản]), gói "Dùng thử" còn 25 giờ, mã máy, đổi mật khẩu, đăng xuất, mua gói 1 năm 2.000.000đ / vĩnh viễn, add-on Ảnh thẻ và Photobooth AI
Ảnh thẻ / Ảnh AIBị khoá với tài khoản hiện tại (hộp thoại mời mua)
Chế độ chạy (kiosk)Màn chào → Chọn khung ảnh → Chụp → Chia sẻ (xem mã QR, in, chụp lại). Thoát bằng Ctrl+Shift+A

3. Kết quả test

Chức năngKết quả
Mở tất cả 10 tab Cấu hình, Báo cáo, Hồ sơChạy tốt, không lỗi
Ảnh thẻ, Ảnh AIHiện hộp thoại cần mua (đúng thiết kế)
Chạy sự kiện → màn chào → chạmVào "Chọn khung ảnh", trống vì sự kiện chưa có khung ("Vào Cài đặt → Thêm khung")
Chụp / in / thanh toán / chia sẻChưa test được: cần có khung ảnh, webcam và máy in; không tự tạo khung hay mua gói để tránh thay đổi dữ liệu và phát sinh chi phí
Đồng bộ ngayBấm một lần nhưng log chưa ghi dòng mới. Lần đồng bộ trước đó (08:47Z) ghi "ĐẨY OK"; chưa xác nhận lần bấm này
Mua gói, đổi mật khẩu, đăng xuấtKhông bấm (có tác dụng thật)

Test bổ sung: đã tạo mẫu khung (1 ô "Ảnh từ booth") ở Thêm khung, lưu thành công ("Đã lưu mẫu", tự lưu sau mỗi thay đổi). Chạy kiosk → Chọn khung ảnh hiện khung mới (có ảnh minh hoạ) → Tiếp tục → màn chụp báo "Không mở được camera" (webcam Logitech C922 đã cài driver nhưng đang không cắm, máy không có camera khác). Nút Thử lại vẫn lỗi như cũ; nút Thoát/Quay lại hoạt động. Vì vậy đếm ngược, chụp, chọn bộ lọc, màn chia sẻ/QR, in và thanh toán vẫn chưa test được; cần cắm webcam. Máy in có sẵn: EPSON WF-C5290BA, EPSON L8050, Microsoft Print to PDF.

Lưu ý nhỏ: nút "Chạy" ở dock Hồ sơ cần bấm 2 lần mới vào (lần đầu chỉ hiện tooltip) — có thể là lỗi UX do hộp thoại "Ảnh thẻ" vừa đóng.

3.1 Test đầy đủ với Canon EOS 70D + DNP DS-RX1 (đã chạy thật)

BướcKết quả
Nhận CanonApp tự đổi sang "Canon DSLR (EDSDK)", live view hiển thị, đọc thông số (AE Manual, 4800K, ISO 4000, 1/100, f/5.6), có "Chụp thử", "Chẩn đoán chi tiết"
Chọn máy in DS-RX1 + "In thử"Báo "Đã gửi in thử"; in-log.txt: XONG, máy=DS-RX1
Kiosk: chào → chọn khung → chụpLive view Canon, đếm ngược 3, tự chụp, ảnh gốc may-anh-goc\IMG_0001.JPG (7,1 MB)
Màn chia sẻHiện 9 bộ lọc, ảnh ghép vào khung, QR lấy ảnh, số tấm in, "In ảnh", "Chụp lại tất cả", "Hoàn tất"
Bộ lọcChọn "Ngọt ngào" áp dụng, nhãn "Đã in – không đổi được" sau khi in
In ảnhluong-log: IN so_to=1, cách=miễn phí; in-log: XONG, ban=1, DS-RX1; hàng đợi Windows trống sau khi in
TimelapseTự dựng _tl\*.mp4 (360 KB, 52 khung)
Đồng bộ sau phiêndongbo-log: ĐẨY OK (cập nhật=1), album đẩy 1 mã, "ĐẨY KHUNG VỪA LƯU OK — 1 khung"
Hàng đợi uploadLúc kiểm tra còn 4 job chờ (f/<mã>/1.jpg, anh.jpg, tl.mp4; trang p/<mã> đã XONG 13 KB / 1,3 s) — chưa xác nhận đã đẩy hết lên R2

Cấu trúc file một phiên: <Sự kiện>\<mã>.jpg (ảnh in), _web\…-3600.jpg, _thumb\, _don\ (ảnh đơn), _tl\ (timelapse), may-anh-goc\ (RAW/JPG gốc từ Canon). Mã phiên 8 ký tự (vd <mã>) là khoá chung cho QR, f/<mã>/… và p/<mã> trên R2.

Nhật ký luồng cho thấy các trạng thái: TAM-VE → CHUP-XONG → DOI-ANH → VAO-SUA → DOI-LOC → GHEP-IN → IN → DON-LUOT (đáng dùng khi thiết kế máy trạng thái).

3.2 Test thu tiền, voucher, Smartbooth, QR (không in thêm)

Chức năngKết quả
Link công khaianh.9698media.com/e/<eventId> (album), /p/<mã> (trang ảnh), /f/<mã>/anh.jpg, 1.jpg, tl.mp4 đều trả 200 với đúng dung lượng (219.696 / 1.531.108 / 360.521 B) → hàng đợi upload đã đẩy xong lên R2
Thu tiềnChỉ bật được với loại sự kiện Bản quán (tôi đổi tạm rồi trả về Đám cưới). Bật thu tiền → chọn dịch vụ (Photobooth / Ảnh thẻ / Photobooth AI), thu lúc nào, bảng giá theo khổ (4×6: 25.000đ…)
VoucherBật Voucher → "Tạo thêm voucher" sinh 10 mã 6 số, mỗi mã 1 tờ, đồng bộ lên server (/api/voucher-tu-may). Mã sai (111111) báo "Mã không có trong danh sách"; mã đúng được chấp nhận, màn chia sẻ hiện "1 bản · đã thanh toán"
Luồng Bản quánChào → Chọn kích thước ảnh (4×6, 25.000đ/tấm) → Chọn khung → Thanh toán (Tiền mặt / Chuyển khoản / Nhập mã) → chụp 8 ảnh → chọn ảnh + bộ lọc → chia sẻ (video kỷ niệm, ảnh sẽ in, QR) → Xong. Không in, hàng đợi in trống
Chuyển khoản / SePayKhông test: cần nhập số tài khoản và token SePay thật, tôi không nhập thông tin tài chính
Tiền mặtKhông bấm (chờ người vận hành xác nhận)
SmartboothBật + mã booth tạo link khách https://tk.9698media.com/b/<mã> và mã QR (tải/copy được); trang trả 200 ("9698 Smartbooth"). Mã phải ≥ 2 ký tự, báo lỗi khi quá ngắn. Chưa test chụp từ điện thoại thật (cần máy khách)
Lưu ý lỗiÔ nhập "Mã booth" mất focus sau mỗi ký tự (gõ tự động chỉ nhận ký tự đầu/cuối) → nên sửa debounce/giữ focus

Đã khôi phục: tắt thu tiền, tắt Smartbooth, loại sự kiện về Đám cưới. Còn lại: 10 voucher thử chưa dùng (mỗi mã 1 tờ) trong cài đặt thanh toán, mã booth "1t" trên server, phiên Bản quán 8 ảnh trong thư mục dữ liệu.

Chưa test: quét QR bằng điện thoại thật, chuyển khoản/SePay, Smartbooth từ điện thoại, Ảnh thẻ và Ảnh AI (khoá theo gói).

3.3 Màn hình/chức năng bổ sung (rà soát lần 2)

Danh sách màn trong mã nguồn (20 chunk) đều đã được xem, trừ các màn khoá theo gói: AnhTheSettings, ManAnhThe, AiSettings, ManPhotoboothAi, và DangNhap (đăng nhập, không test vì đang đăng nhập). Đã xem thêm: hộp thoại Tạo sự kiện mới (5 bước: tên + loại, ảnh bìa, frame, 300/600 dpi, giới hạn lượt in), Sửa ảnh đại diện, trang Xem lại ảnh (xoá từng ảnh, mở thư mục), trang Chọn sự kiện, cài đặt PIN quản trị / PIN doanh thu / Tên booth / Yêu cầu mật khẩu khi thu nhỏ, và web event.9698media.com ("Tạo sự kiện & khung ảnh", cổng web đồng bộ với app, cần đăng nhập, chưa test).

Không có file hướng dẫn chính thức; đã lập huong-dan-su-dung.md.

3.4 Web quản lý event.9698media.com ("9698 Event — Tạo sự kiện & khung ảnh")

Web dạng SPA mobile-first (thanh dưới 4 tab, nút tròn "+"), đã đăng nhập bằng [email tài khoản]. Kiểm tra bằng thao tác xem và mở form; không lưu dữ liệu nào (thử bấm Lưu gói chụp thì hệ thống chặn thao tác ghi lên dữ liệu dùng chung, tôi không làm cách khác để vượt qua).

Màn hìnhNội dung
Tổng quan (/)Lọc Hôm nay/7 ngày qua/Tháng này/Quý này/Năm nay/Tất cả/Tự chọn; 4 số: tổng sự kiện (2), đang hoạt động (0), lượt khách (1), doanh thu (0đ); ô tìm kiếm; 3 danh sách: Bản quán, Đám cưới chưa triển khai, Đám cưới đã triển khai. Thẻ sự kiện: ảnh bìa, số ảnh, số tấm đã in, số khung, máy được gán; nút Quản lý khung, Link ảnh, Xoá (không bấm Xoá)
Sửa sự kiện (Quản lý khung)Loại sự kiện, tên (dùng làm {couple} trên khung), ngày chụp ({date}), ảnh bìa, 300/600 dpi, giới hạn lượt in, danh sách máy thấy sự kiện (chỉ xem; máy tự nhận bản mới trong 30 giây), Khung ảnh: "Chọn file khung (nhiều file)", PNG khoét ô trong suốt hoặc tô ruột màu xanh lá, web tự cắt ruột và xếp lượt chụp. Sự kiện tạo ở máy booth thì ảnh/phiên/doanh thu nằm ở máy, web chỉ xem và thêm khung
Nút "+""Tạo sự kiện cho khách nào?": chọn đơn trong sổ khách (điền sẵn tên, ngày) hoặc "Tạo sự kiện rời, không gắn đơn"
Khách (/danh-sach-khach)Sổ đơn khách: Danh sách / Lịch tháng / Bảng giá gói / "+ Lên đơn"; lọc tháng, tìm tên/sđt/địa điểm; tổng tiền, đã thu, còn phải thu. Lên đơn mới: tên khách, sđt, ngày cưới, gói chụp, giờ bắt đầu/kết thúc (tự tính theo số giờ của gói), địa điểm, tổng tiền, đã cọc, thanh toán (Chưa cọc / Đã cọc / Đã thanh toán), ghi chú
Lịch thángLưới tháng 11/2026, bấm ô trống để lên đơn, bấm đơn để sửa, đánh dấu "Hôm nay"
Bảng giá gói (/goi-chup)"+ Thêm gói": tên gói (vd PRO), giá, số giờ chụp (dùng dấu phẩy cho số lẻ), mô tả. Đổi giá gói sau này không đổi các đơn đã chốt. Hiện 0 gói
Voucher (/voucher)"Chép mã", "+ Tạo mã", xuất .txt; mỗi mã 1 lượt chụp miễn phí, máy nhận qua đồng bộ. Hiện 0 mã, xác nhận 10 voucher thử đã xoá khỏi server
Tài khoảnTên, email, "Đăng xuất" (không bấm)

Test ghi dữ liệu (được bạn cho phép, đã dọn sạch):

Chức năngKết quả
Thêm gói (TEST Claude, 100.000đ, 2 giờ)OK, toast "Đã lưu gói", hiện trong bảng giá
Lên đơn (tên giả, sđt giả 0900000000, 15/11/2026, 09:00)OK, "Đã thêm đơn". Chọn gói thì giờ kết thúc tự tính (11:00) và tổng tiền tự điền (100.000đ); đơn hiện đúng trên Lịch tháng ở ngày 15 và trong danh sách (tổng/đã thu/còn phải thu). Bấm đơn trên lịch mở lại form sửa (không có nút xoá ở đó)
Xoá đơnTừ danh sách (nút "Xoá" cuối hàng) → hộp thoại "Xoá đơn?" → OK, về 0 đơn
Xoá góiNút "Xoá" trong bảng giá, không hỏi xác nhận → về 0 gói

Còn lại chưa test: nút "+ Tạo sự kiện" gắn đơn, upload khung, tạo/xuất voucher trên web, "Đặt tên" máy, "+ Thêm mẫu" ảnh AI.

Hai màn bổ sung chỉ hiện ở menu bản desktop (bản mobile không có nút, truy cập qua /may và /prompt):

API của web (cùng Bearer/cookie đăng nhập): POST /api/dang-nhap, GET /api/me, /api/may, /api/su-kien, /api/anh-khung?key=, /api/dat-lich?thang=&tim=, /api/goi-chup, /api/voucher; POST /cdn-cgi/rum là đo hiệu năng của Cloudflare. Web và app dùng chung backend event.9698media.com (mục 4.2).

3.5 Màn hình và link chưa vào được / chưa kiểm tra (rà soát lần 3)

MụcTình trạng
App: Ảnh thẻ, Ảnh AI (và cài đặt AnhTheSettings, AiSettings, màn khách ManAnhThe, ManPhotoboothAi)Không vào được: tài khoản chưa mua add-on, app hiện hộp thoại mời mua. Cần mua (1.000.000đ mỗi add-on) mới xem được
App: màn Đăng nhập/Đăng ký (DangNhap), đổi mật khẩu, quên mật khẩuKhông test: app đang đăng nhập, không muốn đăng xuất/đổi mật khẩu
App: chuyển khoản/SePay, Tiền mặt, tạo QR ngân hàngKhông test (cần số tài khoản và token thật)
App: cập nhật phiên bảnChưa test, https://app.9698media.com/app/ và latest.yml đều trả 200
https://tk.9698media.com/ ("Quản trị khách — 9698 Photobooth")Có trang nhưng cần mật khẩu quản trị (sổ khách của nhà cung cấp), không có mật khẩu nên không vào được; không nhập mật khẩu hộ
https://9698media.com, www.9698media.comKhông phân giải được tên miền (DNS lỗi), không có trang web giới thiệu/hướng dẫn
https://anh.9698.vn (tên miền ngắn dùng cho QR trong mã nguồn)Không phân giải được (DNS lỗi) — QR hiện tại dùng anh.9698media.com, nên tên miền ngắn có vẻ chưa được cấu hình
https://anh.9698media.com/ và https://app.9698media.com/ (gốc)Trả 404 là bình thường (chỉ phục vụ /e/, /p/, /f/ và /app/)
Web event.9698media.com: /tao, /sua, /dat-lich, /may, /prompt, /voucher, /goi-chup, /danh-sach-khachVào được. /tao là form tạo sự kiện (loại, tên, ngày, ảnh bìa, dpi, giới hạn, gán máy một lần, lưu xong là khoá, chọn khung). Mọi đường dẫn lạ vẫn trả trang SPA 200 (không có trang 404 riêng). /api/* trả 401 nếu chưa đăng nhập
Web: tải khung, tạo voucher, đặt tên máy, thêm mẫu AIChưa test thao tác ghi
my.sepay.vnTrang đăng nhập SePay mở được (không đăng nhập)

4. API gọi lên server

Ba host chính, đều HTTPS, Bearer token (Authorization: Bearer <the>), JSON; helper goi() timeout 12 s mặc định, trả về {ma: httpStatus, ...body}.

4.1 tk.9698media.com — tài khoản, giấy phép, in từ xa (qua Cloudflare)

EndpointPhương thứcDùng cho
/api/khoa-cong-khaiGETLấy khoá công khai để xác minh chữ ký giấy phép
/api/kiem-mayPOST {maMay}Kiểm tra máy
/api/goi?sanPham=photoboothGETDanh sách gói
/api/phien-banGETPhiên bản mới nhất
/api/dang-ky, /api/xac-thuc, /api/gui-lai-maPOSTĐăng ký, xác thực mã email, gửi lại mã
/api/dang-nhapPOSTĐăng nhập, trả the (token) + giayPhep đã ký
/api/giay-phepPOSTLàm mới giấy phép
/api/sua-ho-so, /api/doi-mat-khauPOSTHồ sơ, mật khẩu
/api/mua, /api/gia-nang-cap, /api/mua-nang-cap, /api/kiem-tra-don, /api/huy-donPOSTMua gói / add-on, hỏi giá, kiểm tra và huỷ đơn
/api/in/dang-ky-booth, /api/in/hang-doi, /api/in/xongPOSTSmartbooth: đăng ký booth, lấy hàng đợi lệnh in từ điện thoại khách (poll), báo đã in
/api/in/khung, /api/in/khung-anh?key=POSTĐẩy manifest và ảnh khung cho trang khách
/api/tai-anh?key=&ct=&cache=POST (octet-stream)Đẩy ảnh/video lên kho (Cloudflare R2) qua máy chủ, hàng đợi có retry

Giấy phép là chuỗi đã ký, app kiểm tra bằng crypto.subtle.verify với khoá công khai (nên chạy offline được trong thời hạn); mã máy (maMay) sinh theo phần cứng (dạng XXXX-XXXX-XXXX).

4.2 event.9698media.com — web sự kiện, đồng bộ đa máy

EndpointPhương thứcDùng cho
/api/dong-bo?tu=<ts>&maMay=&tenMay= (+chiDem=1)GETKéo sự kiện, khung, mẫu AI thay đổi từ mốc tu (log: "tải về — 1 sự kiện, 0 khung, 22 mẫu AI")
/api/su-kien-tu-mayPOSTĐẩy sự kiện, khung, ngày từ máy lên ("ĐẨY OK … mới / cập nhật")
/api/anh-khungPOST/GETTải/đẩy ảnh khung
/api/anh-thieuPOST {keys}Hỏi server ảnh nào còn thiếu
/api/prompt, /api/prompt-xoa, /api/prompt-thu-tuGET/POSTQuản lý prompt AI
/api/voucher-tu-may, /api/voucher-xoa, /api/voucher-dungPOSTĐồng bộ, xoá, báo dùng voucher
/api/album-maPOSTĐẩy danh sách mã album khách

4.3 Dịch vụ khác

5. Kiến trúc để triển khai app tương tự

┌──────────────── Máy booth (Electron) ───────────────┐
│ Renderer (React): Admin / Kiosk / Designer          │
│   │ contextBridge (preload) — 192 kênh IPC          │
│ Main: config, license, sync, upload queue, in ấn,   │
│       Canon SDK, SePay poll, OpenAI, local HTTP     │
│ Lưu: JSON + file ảnh + upload-queue.json            │
└───────┬───────────────┬────────────────┬────────────┘
        │               │                │
   tk.* (Auth/License   event.* (Sync    anh.* (R2 CDN)
   /Billing/Print)      sự kiện, voucher)   ▲
        │               │                │
        └── Cloudflare Workers + D1/KV ──── R2 ◄── trang khách (/e/…, /f/…)

Nguyên tắc thiết kế nên giữ:

1. Offline-first: mọi thứ chạy từ dữ liệu cục bộ; server chỉ để tài khoản, giấy phép, đồng bộ, chia sẻ ảnh. Giấy phép ký bất đối xứng (Ed25519/ECDSA), máy tự xác minh, có hạn dùng thử.

2. Hàng đợi upload bền vững (upload-queue.json: jobs, sessions, albums, mã ảnh) với retry; ảnh đẩy lên qua /api/tai-anh vào R2, khách xem bằng mã ngắn (QR).

3. Đồng bộ theo mốc thời gian (tu=<ts>): kéo và đẩy delta, server trả số mới/cập nhật; ảnh khung đồng bộ riêng, hỏi "ảnh thiếu" trước khi đẩy.

4. Tách host theo vai trò (tài khoản/giấy phép, sự kiện, ảnh) để mở rộng và cache độc lập.

5. Tích hợp bên thứ ba bằng key của khách (OpenAI, SePay) để không gánh chi phí.

6. Smartbooth: máy booth poll /api/in/hang-doi, khách chụp bằng điện thoại và tải ảnh lên, booth tải về và in, báo /api/in/xong.

Stack đề xuất để dựng lại: Electron + electron-vite + React; backend Cloudflare Workers + D1 (tài khoản, đơn, sự kiện) + R2 (ảnh) + KV (voucher, hàng đợi in); thanh toán chuyển khoản VietQR + webhook/poll SePay; cập nhật bằng electron-updater trỏ vào thư mục tĩnh.

6. Việc chưa xác nhận