POS Android
POS Android is the handheld client for mobile retail operations.
It is not the primary supermarket checkout client. Use it for:
- handheld cashier
- small merchants
- queue-busting
- supervisor approvals
- mobile inventory-adjacent flows
- generic Bluetooth, permission-bound USB-host printer-class and private-LAN raw-TCP ESC/POS receipt testing, external HID keyboard-wedge scanning, configurable foreground scanner broadcasts, offline camera barcode scanning, and future integrated-printer adapters
Current Responsibilities
- barcode scan and basket building
- item image confirmation after scan
- customer lookup and loyalty redemption
- mixed tender capture
- receipt view and reprint
- authenticated creation and opening of a signed public digital receipt link after server synchronization
- shift control
- offline-first local state with later sync
Offline Position
Android is the offline-first POS client in the product family.
- local Room storage
- cached catalog and item images
- local sale queue
- deterministic dependency-ordered sync and replay when connectivity returns
- exact-body-signed batches of up to 50 offline-safe shift, sale, cash and refund mutations, with per-item outcomes and stable idempotency keys
- a manager-gated refund return tray that selects original sale items, derives the partial amount from their remaining quantities, and can return only those quantities to stock
- stale-job recovery, capped transient retries, and a blocked state for permanent conflicts
- terminal-scoped manager retry/discard decisions consumed from a sanitized server queue; Room v15 preserves conflict metadata without storing raw server bodies or secrets
- durable marked-reprint jobs with explicit operator retry after Bluetooth reconnect or permission repair
- rapid external HID scan capture that rejects normal slow keyboard typing
- exact action/category/extra-key scan-intent capture that requires a valid test scan before reporting integrated-scanner readiness
- permission-on-use CameraX analysis with a bundled on-device ML Kit decoder and no image persistence or first-run model download
- real battery, network, printer and scanner-readiness heartbeats at enrollment and periodic sync
Sales can remain offline, but supervisor-controlled actions cannot. Android sends the POS-specific supervisor PIN only to the authenticated POS API, never stores a PIN hash, and fails closed if the service is unavailable. This deliberately prevents a stolen or rooted handheld from brute-forcing a cached four-digit hash.
Customer-app prompt cancellation is also online-only. Android sends the pending
tender UUID through the enrolled terminal request-proof contract, reloads the
provider-authoritative sale, and keeps the tender captured if the customer paid
before cancellation. It never queues an offline cancellation or offers the
action for a provider that lacks a safe cancellation adapter. Room v16 persists
the selected customer-app adapter's advertised cancellation operation with a
false-by-default, data-preserving migration, so an older install never guesses
support from the generic WALLET tender name.
Receipt Printer Foundation
The app now isolates printing behind a ReceiptPrinter interface. Generic
Bluetooth Classic SPP, USB printer-class bulk OUT and private-LAN raw-TCP
adapters emit a conservative ESC/POS stream with
selectable 58 mm/32-column and 80 mm/48-column profiles, explicit reprint
marking, bounded payload length, and control-character filtering. Room v12
retains print status, attempt count,
safe failure guidance, printer identity, and printed time. This software path is
verified by unit tests and debug/release builds; physical YCP-58, Ciontek, Sunmi,
paper-out, reconnect, and Unicode/code-page behavior still require the hardware
pilot.
Authoritative Close-of-Day Pack
After a supervisor closes and submits a shift, Android can retrieve its country-neutral server close pack. The client validates the current signing-key id/public key, exact canonical-payload SHA-256, detached Ed25519 signature, pack version, and store/terminal/session binding before showing it as verified. Room v14 preserves the canonical payload plus signature evidence and verification time for offline review. Device-generated CSV/PDF reports remain provisional working papers and are never presented as the authoritative finance record.
Printer readiness is based on actual configuration and a transport-specific
probe: Android permission, Bluetooth state and bonded-device visibility for
SPP; temporary device permission plus a successful USB-interface claim for USB;
or a bounded connection to a private resolved address for raw TCP. A printer-driven
drawer test is disabled by default, emits one bounded ESC/POS pulse and never
enters the at-least-once print spool. Automatic cash-sale and cash-refund
opening is a separate setting: it remains disabled until that one-shot pulse
succeeds and the operator physically confirms that the exact drawer opened.
Room v18 inserts an audit row before hardware I/O, uses a SHA-256
business-event digest to suppress duplicates, records confirmed,
sent-unconfirmed, failed, or crash-ambiguous outcomes, and never replays a
drawer action. A supervisor no-sale is online-only: the cashier submits a
reason and supervisor credential, the server binds the approved override to the
same organization, store, terminal, open shift, cashier, and
SUPERVISOR_NO_SALE action for five minutes, then records an immutable
zero-value NO_SALE cash movement before Android attempts one drawer pulse.
The authorization is single-use and the movement/pulse identities are
idempotent. If drawer automation is not physically qualified, the server audit
still succeeds and Android sends no pulse. Scanner readiness is true only for an
attached HID scanner, a scan-intent adapter that has received a valid scan, or
a camera with runtime permission that has successfully decoded a valid barcode
in the current process. Merely having a camera or saving an intent profile does
not count. Integrated printer SDKs and physical drawer qualification remain
hardware/pilot work.
Room v17 caches authoritative refund headers and line allocations. This lets the handheld subtract already completed return quantities after reconnect, hide a second request while provider reconciliation is active, and generate a new retry-stable idempotency key for each cumulative partial refund. Drawer expectation changes use the server-reported cash-tender refund delta rather than assuming every refund was paid entirely in cash.
Hardware-backed terminal enrollment
New Android enrollments generate the terminal signing key with a server-derived, domain-separated attestation challenge and send the leaf-first DER certificate chain with the existing proof-of-possession request. Exact activation retries reuse the same pre-enrollment key; a new activation rotates only an unbound pre-enrollment key. An already bound terminal is never silently rotated.
The locally persisted binding is reconciled against the authenticated bootstrap after every successful sign-in and during the 15-minute WorkManager cycle. A server-revoked terminal, or one no longer present in the operator's authoritative merchant/store view, is marked inactive locally without deleting offline transaction history. Re-enrollment can then create a fresh attested key and terminal identity. Matching a lane or terminal code is never enough to adopt a replacement device: once issued, the opaque server terminal UUID is the binding identity.
The POS service verifies evidence on the server. A verified result requires the submitted terminal key to match the attested leaf, a valid chain to one of the configured Android attestation roots, a fresh successful revocation lookup, the expected activation challenge, TEE or StrongBox security levels, signing purpose with SHA-256, generated-key origin, locked verified boot, and the configured Android package and signing-certificate digest. The certificate chain is not stored; only status, security level, trust-anchor hash, and verification time are retained.
Rollout modes are DISABLED, AUDIT, and
REQUIRED_ANDROID_HARDWARE. Test defaults to AUDIT, which never labels
unverified evidence as trusted. Required mode fails closed and refuses to start
without an Android signing-certificate digest. Web terminals and vendor
HARDWARE_POS devices remain outside Android enforcement so future vendor
attestation adapters can use the same country-neutral boundary.
When to Use Android Instead of Cashier Web
Use Android when the lane is not a fixed supermarket counter:
- mobile checkout
- aisle or queue-busting checkout
- supervisor device
- branch manager backup terminal
- smaller merchants without a countertop POS station
Design Rule
Do not force supermarkets to use handheld terminals as their primary cashier interface. Countertop lanes belong to POS Cashier Web; Android remains the mobile companion.