14 · POS & Mobile POS

Complete inventory of checkout surfaces, register/shift, cart/tender/finalize, and the critical distinction between Mobile POS the UI and /api/mobile the alternate device API.

Canonical money path: /api/pos → Core Invoicing Deep trace: OAO 06 · Appendix B Updated from live code pass
One rule Almost every real sale UI (Modern, Mobile, Standard, Kiosk.jsx, Storefront) uses posApi/api/pos/* with a platform session. Do not build new checkout against /api/mobile or /api/kiosk unless you intentionally want the device-credential unattended path.

How you open POS

flowchart TB
  LOGIN[Platform login · session cookie] --> POS["/pos"]
  POS --> ENTRY[PosEntryRoute]
  ENTRY -->|viewport ≤768 or pref mobile| MOB[MobilePOS.jsx]
  ENTRY -->|desktop / pref modern| MOD[ModernPOS.jsx]
  BOOK["/pos/mobile"] --> MOB
  KURL["/pos/kiosk"] --> KIOSK[Kiosk.jsx]
  KDS["/pos/kitchen"] --> KITCHEN[KitchenDisplay · KDS]
  BAR["/pos/bar"] --> STUB1[Placeholder]
  REST["/pos/restaurant"] --> STUB2[Broken placeholder]
  HAND["/pos/handheld"] --> STUB3[Broken placeholder]
          
URLComponentStatus
/posPosEntryRoute → Modern or Mobileprimary
/pos/mobileMobilePOS alwaysprimary
/pos/kioskKiosk.jsx + KioskLayoutreal UI
/pos/kitchenKitchenDisplayKDS
/pos/barBarDisplayplaceholder
/pos/restaurantthrows / not built
/pos/handheldthrows / not built

Layout preference: localStorage posLayoutPreference = modern | mobile | auto. One-shot: /pos?layout=modern or ?layout=mobile. See lib/posLayoutPreference.js.

All POS-related surfaces

SurfaceFile~SizeAPINotes
Modern POS pages/pos/ModernPOS.jsx ~2.5k lines /api/pos Full register: gift, loyalty, seating, fuel module, platform catalog
Mobile POS pages/pos/MobilePOS.jsx ~2.4k lines /api/pos Touch layout; send-to-kitchen; no gift/loyalty/seating/fuel/catalog enrich UI
Standard POS pages/pos/StandardPOS.jsx ~1.7k lines /api/pos Older parallel full UI; gift/loyalty; offline-heavy
Kiosk pages/pos/Kiosk.jsx ~640 lines /api/pos via posApi Self-checkout style; still needs staff session today
KioskSelfServe KioskSelfServe.jsx None Demo only — hardcoded products
Kitchen (KDS) KitchenDisplay.jsx ~17k /api/kds Polling ~3s; seat chips from tickets
Bar BarDisplay.jsx ~10 lines Placeholder
Storefront pages/store/Storefront.jsx /api/pos cart finalize Customer e-com reuses POS spine
Open registers admin/operations/OpenRegisters.jsx register open-shifts / admin-close /admin/open-registers
Shift history RegisterShiftHistory.jsx shift-history /admin/register-shift-history

Modern vs Mobile — feature parity

Both share register ceremony and the same cart/tender/finalize API. Differences are UI capability.

CapabilityModernMobile
Platform session requiredyesyes
Register PIN + open/close shiftyesyes
canTransactOnRegister gateyesyes
Device selector / POS settingsyesyes
Catalog search + barcodeyesyes
Platform UPC catalog enrichyesno
Customer attach / searchyesyes
Cash / card / terminal / cryptoyesyes
Gift card tender UIyesno
Loyalty tender UIyesno
Seating / floor pickeryesno
Fuel / posModulesyesno
Manual send-to-kitchenlimitedexplicit button
Offline queue via posApiyesyes
Switch to other layout→ desktop Modern
Parity backlog (product) Mobile is missing gift, loyalty, seating, platform catalog prefill, and fuel modules. If floor staff run phone POS, those flows only work on Modern today.

Canonical sale flow (Modern & Mobile)

flowchart TD
  A[Platform login] --> B[Device context
deviceKey · location · catalogs] B --> C[Register authenticate
email + POS PIN] C --> D{Register open?} D -->|no| E[Open shift + float] D -->|yes + staff match| F[canTransactOnRegister] E --> F F --> G[POST /api/pos/carts] G --> H[Add items · barcode · customer] H --> I[Optional metadata seat/table · Modern] I --> J[Tender cash/card/crypto/…] J --> K{Covered?} K -->|no| J K -->|yes| L[POST .../finalize] L --> M[CoreInvoicingService] M --> N[(Invoices + payments)] L --> O[Inventory / recipe consumption] L --> P[kitchen_tickets if preparable] H --> Q[Mobile: send-to-kitchen] Q --> P

PIN session + active cart id: lib/posLocalPersistence.js (keys scoped by tenant + device). Deep step list: OAO Appendix B.

Backend API surface

A · Canonical — /api/pos

pos.routes.js (~4.2k lines) + pos-register.routes.js. Middleware: session + tenant + optional device + idempotency + rate limits.

AreaPaths
Cart GET/POST /carts · GET /carts/:id · PATCH .../metadata · POST .../customer · POST .../items · DELETE .../items/:lineId · POST .../items/:lineId/qty · POST .../recalc · POST .../tip · POST .../send-to-kitchen · GET .../invoice · POST .../finalize
Tenders cash · card · giftcard · loyalty · crypto · coinbase · coingate · stripe-terminal · square-terminal · ingenico · verifone · adyen · paypal
Many backends; only a subset have first-class UI
Register POST /register/authenticate · open · status · close · open-shifts · shift-history · admin-close · admin-force-signout
Device / catalog GET /catalog · GET /settings (+ posModules) · assign-catalog · assign-location · available-catalogs · available-locations

B · Client wrapper — frontend/src/pos/api.js

All mutating cart/tender/finalize calls go through tryOnlineThenQueue (offline mutation queue). Service worker for full offline shell is still disabled.

C · Parallel device APIs (not used by main UIs)

MountAuthUsed by MobilePOS / Kiosk.jsx?
/api/mobile/* deviceAuth (+ optional session) No — thin cart/sync/finalize via CartService
/api/kiosk/* deviceAuth No — Kiosk.jsx uses posApi + session

Naming traps

flowchart LR
  subgraph UI["What staff open"]
    MPOS[Mobile POS page
MobilePOS.jsx] KUI[Kiosk.jsx] end subgraph API["What they call"] POSAPI["/api/pos/* · session"] end subgraph ALT["Separate unattended routers"] MAPI["/api/mobile/*"] KAPI["/api/kiosk/*"] end MPOS --> POSAPI KUI --> POSAPI MAPI -.->|not used by MPOS| X[ ] KAPI -.->|not used by Kiosk.jsx| X
Do not confuse “Mobile POS” always means the React page on /pos / /pos/mobile. It does not mean the Express tree under backend/routes/api/mobile/.

Stubs, orphans, and product gaps

ItemTypeAction hint
Bar displayPlaceholder UIBuild or hide route
Restaurant / handheld routesThrowing placeholdersBuild or remove from App.jsx
KioskSelfServeHardcoded demo catalogWire to posApi or archive
SplitTenderDialogComponent exists, not imported in Modern/MobileWire multi-tender UX or delete
POSModeRouterLegacy ?mode= switcherPrefer PosEntryRoute + explicit paths
Mobile gift/loyalty/seating/catalog/fuelParity gapsProduct backlog
Register ceremony for solo opsICP frictionOptional simple mode (audit S6-3)
Offline service workerDisabledQueue works; full offline shell does not
Extra tender backendsAPI without UIDocument as integrate/defer

File anchors

ConcernPath
Entry routingfrontend/src/components/pos/PosEntryRoute.jsx
Modern / Mobile UIsfrontend/src/pages/pos/ModernPOS.jsx · MobilePOS.jsx
posApi clientfrontend/src/pos/api.js
PIN + cart persistencefrontend/src/lib/posLocalPersistence.js
Layout preferencefrontend/src/lib/posLayoutPreference.js
Register UIcomponents/POS/RegisterLoginScreen.jsx · RegisterOpeningDialog.jsx
Seating / fuelPosSeatingPicker.jsx · pos/modules/*
Cart + finalize backendbackend/routes/pos.routes.js
Register backendbackend/routes/pos-register.routes.js
Tenant POS SQL helpersbackend/utils/posTenantQueries.js
Invoicingbackend/services/coreInvoicing.service.js
Device API altsbackend/routes/api/mobile/index.js · api/kiosk/index.js
OAO chapters06-POS-SYSTEMS.md · Appendix B · KDS 08

Related DocHub pages