📱 kioskgaming_cashier

Cashier App
Ứng Dụng Thu Ngân

Ứng dụng Android dành riêng cho thu ngân tại quầy gaming kiosk — quản lý ca làm việc, đếm tiền mặt theo mệnh giá, xử lý giao dịch credits với người chơi.

Android Native · Capacitor 7 React 18 CRA TypeScript 4.9 CRACO Config react-hook-form v7 Tailwind CSS 3 @kioskgaming/ui
7 Trang chính
13 Mệnh giá tiền
5 Bước trong ca
3 Phương thức TT
Android Native Shell
👤

Vai trò Thu Ngân

Thu ngân là người vận hành trực tiếp tại quầy gaming kiosk. Họ sử dụng ứng dụng này trên máy tính bảng Android chuyên dụng đặt tại quầy thu ngân, đóng vai trò cầu nối giữa tiền mặt thực tế và hệ thống credits của người chơi.

💰

Chuyển đổi tiền mặt → Credits

Nhận tiền mặt từ người chơi, nhập số tiền và phương thức thanh toán, hệ thống tự động cộng credits vào tài khoản người chơi.

💸

Thanh toán Credits → Tiền mặt

Khi người chơi muốn đổi credits ra tiền mặt, thu ngân xử lý lệnh redeem và chi tiền trực tiếp tại quầy.

🧾

Đếm tiền theo mệnh giá

Mỗi lần mở ca, đóng ca, hay nạp tiền giữa ca đều yêu cầu nhập số lượng từng mệnh giá — đảm bảo tính chính xác và trách nhiệm giải trình.

👥

Quản lý tài khoản người chơi

Tra cứu người chơi qua số điện thoại hoặc username. Tạo tài khoản mới nếu người chơi chưa có — ngay tại quầy, không cần rời đi.

📊

Theo dõi lịch sử ca

Xem lại toàn bộ danh sách ca làm việc của mình, bao gồm tổng giao dịch, số tiền qua quỹ, và trạng thái bàn giao.

🔒

Trách nhiệm giải trình

Mọi giao dịch đều được ghi nhận theo ca. Device fingerprint đảm bảo mỗi thiết bị Android đều được định danh duy nhất.

🔄

Vòng đời Ca Làm việc

Mỗi ca làm việc đi qua 5 bước có thứ tự bắt buộc. Ứng dụng hướng dẫn thu ngân từng bước và ngăn không cho bỏ qua bước nào.

Bước 1 · OpeningCashPage

Mở quỹ đầu ca

Thu ngân đếm toàn bộ tiền trong két trước khi ca bắt đầu. Nhập số lượng từng mệnh giá vào Denomination Grid. Hệ thống tính tổng tự động và tạo bản ghi ca mới.

DenominationGrid computeDenominationTotal() cashierGetShift()
Bước 2 · ActiveShiftPage

Ca đang hoạt động

Màn hình làm việc chính. Thu ngân tìm kiếm người chơi, nạp credits, đổi credits, tạo người chơi mới. Danh sách giao dịch của ca hiển thị realtime bên cạnh.

cashierFetchPlayers() cashierDepositCredits() cashierRedeemCredits() cashierCreatePlayer() cashierListTransactions()
Bước 3 (tùy chọn) · CashDropPage

Nạp tiền giữa ca

Khi két cần thêm tiền mặt trong ca, thu ngân ghi nhận lần nạp này bằng Denomination Grid. Mỗi cash drop được ghi lại với đầy đủ chi tiết mệnh giá.

cashierCreateCashDrop() DenominationGrid
Bước 4 · ClosingCountPage

Đóng quỹ cuối ca

Cuối ca, thu ngân đếm lại toàn bộ tiền mặt và nhập vào Denomination Grid. Hệ thống so sánh với số dư kỳ vọng, ghi nhận chênh lệch (nếu có).

DenominationGrid emptyDenominationGrid()
Bước 5 · HandoverPage

Bàn giao ca

Chuyển giao ca cho thu ngân tiếp theo. Bản ghi bàn giao được lưu lại bao gồm thông tin người nhận, ghi chú, và trạng thái két tiền.

HandoverPage CashierProtectedRoute

* CashDropPage có thể được thực hiện nhiều lần trong một ca, không phải bước bắt buộc. Các bước còn lại phải theo đúng thứ tự.

💵

Denomination Grid — Lưới Đếm Tiền

DenominationGrid.tsx là component trung tâm của ứng dụng — được dùng ở cả 3 trang đếm tiền (Opening, Cash Drop, Closing). Hỗ trợ 7 mệnh giá tờ và 6 mệnh giá xu theo chuẩn USD.

💵 Tờ giấy — BILL_DENOMINATIONS

$1 × $1.00
$2 × $2.00
$5 × $5.00
$10 × $10.00
$20 × $20.00
$50 × $50.00
$100 × $100.00
Tổng tờ giấy $760.00

🪙 Xu — COIN_DENOMINATIONS

¢1 × $0.01
¢5 × $0.05
¢10 × $0.10
¢25 × $0.25
¢50 × $0.50
$1 × $1.00
Tổng xu $9.50
Tổng cộng (Bills + Coins) $769.50

Utils — denominations.ts

// Hằng số mệnh giá const BILL_DENOMINATIONS = [1,2,5,10,20,50,100] const COIN_DENOMINATIONS = [0.01,0.05,0.1,0.25,0.5,1.0] // Tạo grid rỗng emptyDenominationGrid() // Tính tổng từ grid computeDenominationTotal(grid) // Format nhãn: "$20", "¢25" formatDenominationLabel(denomination) // Format tiền tệ: "$1,234.56" formatCurrency(amount)

Lý do dùng Denomination Grid

  • Loại bỏ hoàn toàn việc tính nhẩm — thu ngân chỉ cần đếm số lượng tờ/xu
  • Tổng tiền được cập nhật tức thì khi nhập số lượng
  • Dữ liệu chi tiết theo mệnh giá được lưu cho mục đích kiểm toán
  • Phù hợp với quy trình kiểm quỹ chuẩn của ngành
  • Hỗ trợ phát hiện chênh lệch khi so sánh Opening vs Closing
🎮

Giao dịch với Người Chơi

ActiveShiftPage.tsx là màn hình làm việc chính. Thu ngân thao tác tất cả giao dịch với người chơi từ đây — tìm kiếm, nạp credits, đổi credits, tạo tài khoản mới.

Active Shift · Ca đang hoạt động
NV
nguyen.van
📞 (555) 012-3456
$248.00
credits
💰 Nạp Credits
💸 Đổi Credits
Phương thức: Tiền mặt Thẻ ngân hàng Chuyển khoản

Luồng Nạp Credits (Deposit)

1️⃣ Tìm kiếm người chơi
2️⃣ Nhập số tiền nạp
3️⃣ Chọn phương thức (cash/card/bank)
4️⃣ Xác nhận → cashierDepositCredits()
5️⃣ Credits cộng vào tài khoản người chơi

Luồng Đổi Credits (Redeem)

1️⃣ Tìm kiếm người chơi
2️⃣ Nhập số credits muốn đổi
3️⃣ Xác nhận → cashierRedeemCredits()
4️⃣ Thu ngân chi tiền mặt cho người chơi
5️⃣ Credits trừ khỏi tài khoản

Tạo Người Chơi Mới — cashierCreatePlayer()

📱 PhoneInput (số điện thoại)
📧 Email
react-hook-form validate
🎮 Tạo tài khoản ngay tại quầy

Component PhoneInput từ @kioskgaming/ui xử lý format số điện thoại chuẩn quốc tế, thu ngân không cần lo về định dạng.

🤖

Android Native với Capacitor 7

Ứng dụng được đóng gói thành APK Android qua Capacitor 7, chạy trực tiếp trên máy tính bảng Android chuyên dụng đặt tại quầy. Điều này mang lại trải nghiệm native app trong khi vẫn phát triển bằng React/TypeScript.

@capacitor/android v7

Chuyển đổi React web app thành APK Android native. Codebase duy nhất, triển khai như app Android thực thụ.

🔑

Device Fingerprint

deviceFingerprint.ts tạo Device ID duy nhất cho mỗi thiết bị Android. Gắn kết thiết bị với ca làm việc.

⚙️

CRACO Config

Create React App được mở rộng qua CRACO — thêm alias, plugins Tailwind, cấu hình build tùy chỉnh mà không eject.

Lợi ích của Android Native Shell

  • WebView tải nội dung từ server.url remote — cần mạng ổn định
  • Device fingerprint UUID lưu localStorage, gắn ca + đăng ký thiết bị
  • APK build qua npm run apk:dev / apk:prod + gradlew
  • Package id: club.kioskservice.kioskgaming.cashier
  • Thiết bị mới cần quản lý cửa hàng duyệt trong Shop Portal
  • CRACO patch source-map-loader từ packages/ui/scripts/

capacitor.config.ts

const remoteUrl = process.env.CAP_SERVER_URL?.trim() || 'https://cashier.kioskservice.club/'; const config: CapacitorConfig = { appId: 'club.kioskservice.kioskgaming.cashier', appName: 'Cashier - Kiosk Gaming', // hoặc [Dev] ... webDir: 'build', server: { url: remoteUrl, // ★ WebView tải web live allowNavigation: ['dev-cashier.kioskservice.club', 'cashier.kioskservice.club'], }, }

Build flow: npm run buildnpx cap sync androidnpx cap run android → APK triển khai lên thiết bị

🛡️

Bảo mật & Trách nhiệm Giải trình

Ứng dụng thu ngân xử lý tiền mặt thực tế, vì vậy mọi lớp bảo mật và audit trail đều là yêu cầu bắt buộc, không phải tùy chọn.

🔐

CashierProtectedRoute

Mọi trang đều được bảo vệ bởi CashierProtectedRoute.tsx. Không có phiên hợp lệ → redirect về trang đăng nhập.

🪪

useCashierAuth Hook

Hook riêng biệt useCashierAuth.tsx quản lý trạng thái xác thực, token, và session của thu ngân.

📱

Device Fingerprint

deviceFingerprint.ts tạo Device ID không thể giả mạo. Mỗi ca làm việc được gắn với thiết bị cụ thể.

📋

Denomination Audit Trail

Mọi lần đếm tiền đều lưu chi tiết theo từng mệnh giá. Không thể sửa lịch sử sau khi submit — mọi chênh lệch đều bị phát hiện.

🔄

Shift Lifecycle Enforcement

Ứng dụng bắt buộc đi qua đúng thứ tự: Opening → Active → Closing → Handover. Không thể bỏ qua bước nào.

🔑

axios Client

cashierApi.ts dùng singleton axios với interceptor Bearer token và bắt 401 → logout. Base URL đã gồm /api.

MyShiftsPage.tsx — Thu ngân có thể xem toàn bộ lịch sử ca của mình: số tiền qua quỹ, tổng giao dịch, trạng thái, và thời điểm bàn giao. Đây là nguồn chứng minh nếu có tranh chấp về tiền.

⚙️

Technical Stack

Cấu trúc Services

// cashierApi.ts — tất cả API calls cashierGetShift() // Lấy thông tin ca hiện tại cashierListTransactions() // Danh sách giao dịch trong ca cashierFetchPlayers(query) // Tìm người chơi theo phone/username cashierDepositCredits(playerId, amount, method) // Nạp credits cho người chơi cashierRedeemCredits(playerId, amount) // Đổi credits của người chơi cashierCreateCashDrop(denomGrid) // Ghi nhận nạp tiền giữa ca cashierCreatePlayer(phone, email) // Tạo người chơi mới

Toàn bộ Pages

FileMục đích
CashierLoginPageĐăng nhập thu ngân
OpeningCashPageĐếm tiền mở quỹ
ActiveShiftPageGiao dịch trong ca
CashDropPageNạp tiền giữa ca
ClosingCountPageĐếm tiền đóng quỹ
HandoverPageBàn giao ca
MyShiftsPageLịch sử ca làm việc
Thư viện / Tool Phiên bản Vai trò
React 18 UI framework chính
TypeScript 4.9 Type safety cho toàn bộ codebase
CRACO Extend CRA config (aliases, plugins) không eject
Capacitor 7 Android native shell — đóng gói React thành APK
@capacitor/android v7 Android platform bridge
react-hook-form v7 Form validation (login, create player, reset password)
react-hot-toast Toast notifications cho giao dịch thành công/thất bại
Tailwind CSS 3 Styling utility-first
@kioskgaming/ui file:../packages/ui Modal, PhoneInput — shared UI (symlink packages/ui)
@kioskgaming/page-loading file:../packages/page-loading PageLoadingIndicator cho route guard
axios ^1.3 HTTP client singleton + interceptors

Components Chia sẻ

CashierLayout.tsx DenominationGrid.tsx CashierProtectedRoute.tsx useCashierAuth.tsx denominations.ts deviceFingerprint.ts cashierApi.ts