05 · Money spine (POS → Invoice)

The architectural backbone. Almost every sale should end as an Invoice through Core Invoicing.

Verified backbone

Canonical sale flow

flowchart TD
  A[Platform session + tenant] --> B{Register ready?}
  B -->|Modern POS| C[Open shift + PIN staff]
  B -->|Some modes| D[Cart may still require session]
  C --> E[POST /api/pos/carts]
  D --> E
  E --> F[POST carts/:id/items]
  F --> G[Optional: seatId/tableId metadata]
  G --> H[Tender cash/card/crypto/gift]
  H --> I[POST carts/:id/finalize]
  I --> J[CoreInvoicingService.createPOSSaleInvoice]
  J --> K[(Invoices + line items tenant main)]
  I --> L[Kitchen tickets if preparable stations]
  I --> M[Inventory / recipe consumption]
  I --> N[Transaction tracking / analytics hooks]
  H --> O{Crypto?}
  O -->|yes| P[Crypto invoice + watchers]
  P --> I
          

Deep trace: OAO Appendix B · backend/routes/pos.routes.js · coreInvoicing.service.js

Who reuses this path

ClientCart APINotes
ModernPOS / StandardPOS/api/pos/*Primary
MobilePOS / Kiosk.jsxSame POS cart API + sessionNot the unattended kiosk router by default
StorefrontPOS createCart + finalizeGood reuse — no parallel checkout stack
/api/kiosk · /api/mobileSeparate routersDevice-auth for unattended; hardened with secrets
Retired anti-pattern Legacy Stripe/Coinbase router that wrote a separate payments table was disabled (ENABLE_LEGACY_3P=0). Do not resurrect outside invoicing.

Register ceremony

stateDiagram-v2
  [*] --> Closed
  Closed --> Open: POST /register/open + float
  Open --> Authenticated: POST /register/authenticate PIN
  Authenticated --> Selling: canTransactOnRegister
  Selling --> Authenticated: idle
  Authenticated --> Closed: POST /register/close
  Open --> Closed: admin-close
          

Hardcoded for all Modern POS tenants today — may be heavy for solo farm stands (product question, not silent bug).

Full POS inventory Modes, Mobile parity, register, and API naming traps: 14 · POS & Mobile POS