Orders and payments
Orders are durable domain transactions, not customer profile fields. A draft uses a stable idempotency key across retries. A timeout does not mean failure or success; the app reconciles against the server before retrying.
Design reasons
- Duplicate HTTP requests must not create duplicate kitchen orders or charges.
- The client must not supply a trusted price; MariaDB server-side catalogue prices are authoritative.
- A Stripe PaymentIntent is not settled solely because a customer saw a success animation.
- Webhook signatures, event replay protection and payment-provider status checks are mandatory.
Current boundary
M6 includes draft and outbox logic, but no native SQLite driver. M7 adds inert payment tables and verification adapters, not a live native PaymentSheet or working charged-order pipeline. Five-venue Stripe account topology and saved-card sharing are unresolved.
Trade-off
Payment integration takes longer than a simulated checkout, but separating drafts, pricing and settlement now avoids fragile financial reconciliation later. No card PAN or CVC enters MariaDB.
Financial authority and the meaning of a draft
The customer device composes an intended basket, but the server owns product prices, discounts, taxes, fees and the authoritative venue identity. An order draft records a proposed set of items and table information. A server draft is neither a charge nor a kitchen acceptance. The REST response in the current API explicitly says that it is saved only; no interface may interpret it as an accepted paid order. The browser presenter is allowed to demonstrate the visual steps but must label the responses fictional.
Idempotency and uncertainty
A network timeout leaves the outcome unknown. The client should retain the original idempotency key, ask the API for the matching draft and only retry the same logical request when reconciliation proves that behaviour safe. The backend can reject reuse of a key with different draft contents. This is cheaper and safer than the apparent simplicity of issuing new requests after each timeout. A future local SQLite outbox must persist key and state before transmission, survive restart, and distinguish queued, sent, acknowledged, unresolved and failed records. The driver-injected source outbox is not a verified native driver.
Five-venue payments
HSLG operates distinct venue identities. A payment quote, merchant account, basket, card-token scope, refund and reconciliation event must be associated with the same venue. The exact Stripe connected/independent-account topology and saved-card portability remain owner/provider decisions. Do not assume a payment method can be used by every venue merely because it was saved once. The pricing path must validate current catalogue data, order ownership, quote expiry and server-created totals before generating provider payment instructions.
Why Stripe PaymentSheet needs both sides
The mobile app should use the provider's supported native UI for sensitive card handling. Secret provider credentials live on the server, never in the APK. A client callback can inform the UI that a flow completed, but is not final settlement proof. The server must verify provider webhook signatures, deduplicate repeated webhook deliveries and correlate provider events to the correct local order. A customer-visible paid/accepted state should require the appropriate server evidence. The existing PHP gateway and verifier foundations are not evidence that PaymentSheet and webhook ingestion were successfully deployed.
Important failure scenarios
A customer may cancel at the provider sheet, a card may be declined, the API may time out after the provider accepts an operation, the webhook may arrive twice or late, and the kitchen may reject an item after payment. Each case requires a defined refund/cancellation and customer-message flow. Avoid issuing a second charge while any prior payment is uncertain. Do not log raw card data or store it in MariaDB. Only use provider-approved tokens and server-side references where necessary, with clear retention and consent policy.
Solo-operator trade-off
Implementing one boring, auditable payment path is less costly than supporting multiple custom payment UIs, five sets of undocumented exceptions and manually repairing duplicated orders. The price of doing this carefully is extra test cases, server reconciliation logic and provider configuration per venue. Those costs are justified because a duplicate charge or wrong-venue settlement creates direct customer harm and administrative work. No release shortcut may convert a demo confirmation into financial proof.
Acceptance criteria
Use Stripe's supported sandbox/test mode. Verify quote creation, correct account selection, client PaymentSheet launch, success, decline, cancellation, duplicate request, delayed webhook, bad signature, replay and offline-before-confirmation. Reconcile provider and local identifiers. Validate that changing venue invalidates an incompatible basket/quote. Retain audit evidence without secrets. Do not enable live payments until the owner approves provider topology and the end-to-end tests pass on an actual device and HTTPS staging server.