P0 live payments: remove demo bypass and guard order status transitions #67

Closed
opened 2026-09-15 16:17:32 +00:00 by admin · 5 comments
Owner

Observed on canonical 0pt main 7555f3d (2026-09-15): app/bestall/forskott/page.tsx pay() falls back from failed Stripe Checkout to PATCH /api/orders/{id} status=in_production, then redirects as if production started. app/api/orders/[id]/route.ts accepts draft/quoted/deposit_pending/in_production/review/revision/accepted/delivered from an arbitrary PATCH; middleware.ts matches only /api/0pt, so this route has no upstream auth. The validation UI also PATCHes accepted and the balance Checkout route uses that state. These paths must be closed or explicitly isolated as non-production before SIAX Technology AB live Stripe keys are enabled. Dependency: PR #66 fixes verified paid-event advancement but deliberately does not change current demo/auth semantics without owner decision. Acceptance: unauthenticated/spoofed requests cannot advance paid/order-approval states; no client fallback can mark payment success; deposit and balance states advance only from matched, signed, paid Stripe events; customer acceptance has verified order/tenant identity; manual demo mode is impossible in NODE_ENV=production; full CI and staging paid/unpaid/cancelled webhook tests pass. No live keys or customer payments were changed by this issue.

Observed on canonical 0pt main 7555f3d (2026-09-15): app/bestall/forskott/page.tsx pay() falls back from failed Stripe Checkout to PATCH /api/orders/{id} status=in_production, then redirects as if production started. app/api/orders/[id]/route.ts accepts draft/quoted/deposit_pending/in_production/review/revision/accepted/delivered from an arbitrary PATCH; middleware.ts matches only /api/0pt, so this route has no upstream auth. The validation UI also PATCHes accepted and the balance Checkout route uses that state. These paths must be closed or explicitly isolated as non-production before SIAX Technology AB live Stripe keys are enabled. Dependency: PR #66 fixes verified paid-event advancement but deliberately does not change current demo/auth semantics without owner decision. Acceptance: unauthenticated/spoofed requests cannot advance paid/order-approval states; no client fallback can mark payment success; deposit and balance states advance only from matched, signed, paid Stripe events; customer acceptance has verified order/tenant identity; manual demo mode is impossible in NODE_ENV=production; full CI and staging paid/unpaid/cancelled webhook tests pass. No live keys or customer payments were changed by this issue.
Author
Owner

Update 2026-09-15: PR #66 now removes the client demo payment bypass, blocks PATCH writes to in_production/delivered, requires verified auth for other status transitions and balance Checkout, and adds atomic compare-and-set. 1041 local tests, typecheck and build pass. Issue stays OPEN: anonymous draft orders have no verified buyer/tenant binding; a verified caller could still act on someone else’s order, and the current UI has no production Zitadel session wiring for these API calls. Live Stripe keys/cutover must wait for owner-bound order authorization and staging paid/unpaid/cancelled E2E evidence.

Update 2026-09-15: PR #66 now removes the client demo payment bypass, blocks PATCH writes to in_production/delivered, requires verified auth for other status transitions and balance Checkout, and adds atomic compare-and-set. 1041 local tests, typecheck and build pass. Issue stays OPEN: anonymous draft orders have no verified buyer/tenant binding; a verified caller could still act on someone else’s order, and the current UI has no production Zitadel session wiring for these API calls. Live Stripe keys/cutover must wait for owner-bound order authorization and staging paid/unpaid/cancelled E2E evidence.
Author
Owner

Wave 48 source hardening: anonymous GET /api/orders/{id} and deposit Checkout creation now require route-auth (PR #66, cecc2bc). Full suite 1043 passed / 184 infra skipped. ADR-001 records the remaining production decision: new orders must bind verified tenant + buyer subject at creation; scoped SQL reads/mutations, UI SSO and a verified migration runner are still required. Auth alone does not prove ownership; issue remains open and no live Stripe cutover is authorized.

Wave 48 source hardening: anonymous GET /api/orders/{id} and deposit Checkout creation now require route-auth (PR #66, cecc2bc). Full suite 1043 passed / 184 infra skipped. ADR-001 records the remaining production decision: new orders must bind verified tenant + buyer subject at creation; scoped SQL reads/mutations, UI SSO and a verified migration runner are still required. Auth alone does not prove ownership; issue remains open and no live Stripe cutover is authorized.
Author
Owner

Wave 49 backend implementation prepared as stacked PR #69 (base #66), not merged/live. Verified owner tenant + buyer subject at creation, tenant-scoped SQL buyer routes, legacy-unowned webhook fail-closed, tenant-namespaced idempotency and additive sql/011. Smoke-tested migration twice on disposable pgvector/Postgres 16; local 1051 passed / 184 infra skipped, typecheck/build green. Remaining critical path: merge #66 after independent review; migrate real 0pt DB with evidence; real Zitadel browser SSO and credential propagation for order APIs; staging purchase/cancel/refund and cross-tenant HTTP proof. Current UI would fail order creation if #69 merged alone, so PR explicitly blocked from cutover.

Wave 49 backend implementation prepared as stacked PR #69 (base #66), not merged/live. Verified owner tenant + buyer subject at creation, tenant-scoped SQL buyer routes, legacy-unowned webhook fail-closed, tenant-namespaced idempotency and additive sql/011. Smoke-tested migration twice on disposable pgvector/Postgres 16; local 1051 passed / 184 infra skipped, typecheck/build green. Remaining critical path: merge #66 after independent review; migrate real 0pt DB with evidence; real Zitadel browser SSO and credential propagation for order APIs; staging purchase/cancel/refund and cross-tenant HTTP proof. Current UI would fail order creation if #69 merged alone, so PR explicitly blocked from cutover.
Author
Owner

STATUS 2026-09-22: #67 (P0 payments-demo-bypass + status-transition-guard) RÖRDE SÄKERHETEN och är fixad — nuvarande main verifierar: order-PATCH kräver Zitadel-principal + tenant-predikat i SQL, klienten kan aldrig skriva in_production/delivered, deposit/release kräver verified auth, webhook flyttar order endast efter verifierad signatur + belopp + valuta (se ARCHITECTURE.md §8 S3/S4 som omnumrerade luckorna efter fixarna). KVARSTÅR enligt issue-text: demo-fallback i app/bestall/forskott/page.tsx (förlitar sig på PATCH:en som nu kräver auth — anonym fallback kan inte längre avancera betald status) + full staging paid/unpaid/cancelled webhook-triage E2E med riktiga Stripe-creds. Stripe-creds saknas (Simon-gate, vaultet innehåller inga Stripe-keys) — E2E kan EJ verifieras autonomt. Behåller öppen med reducerat scope: demo-pathen är nu aut-gated; riktiga betalningar får inte aktiveras innan staging-triage görs med live-keys. Ingen kodändring denna pass — väntar på creds.

STATUS 2026-09-22: #67 (P0 payments-demo-bypass + status-transition-guard) RÖRDE SÄKERHETEN och är fixad — nuvarande main verifierar: order-PATCH kräver Zitadel-principal + tenant-predikat i SQL, klienten kan aldrig skriva in_production/delivered, deposit/release kräver verified auth, webhook flyttar order endast efter verifierad signatur + belopp + valuta (se ARCHITECTURE.md §8 S3/S4 som omnumrerade luckorna efter fixarna). KVARSTÅR enligt issue-text: demo-fallback i app/bestall/forskott/page.tsx (förlitar sig på PATCH:en som nu kräver auth — anonym fallback kan inte längre avancera betald status) + full staging paid/unpaid/cancelled webhook-triage E2E med riktiga Stripe-creds. Stripe-creds saknas (Simon-gate, vaultet innehåller inga Stripe-keys) — E2E kan EJ verifieras autonomt. Behåller öppen med reducerat scope: demo-pathen är nu aut-gated; riktiga betalningar får inte aktiveras innan staging-triage görs med live-keys. Ingen kodändring denna pass — väntar på creds.
Author
Owner

KOD-KRAVEN i denna issue är nu alla verifierade på live-prod (main 3d89cec, 2026-09-22):

✅ pay() i app/bestall/forskott/page.tsx har INGEN demo-fallback — kommentaren säger ordagrant "never simulate a successful payment or redirect to a demo order"; misslyckad/avstängd Stripe → paymentError, kunden får retry.
✅ POST /api/orders kräver verifierad Zitadel-principal (anonym = 401, mätt live).
✅ PATCH /api/orders/[id] nekar in_production/delivered (403 payment_event_required) — betalda states ägs exklusivt av den signaturverifierade Stripe-webhooken.
✅ Deposit/release kräver route-auth + owned order (tenant-predikat i SQL).
✅ Webhook flyttar order endast efter verifierad signatur + payment_status=paid + exakt belopp + valuta SEK; utan konfig → 503 (aldrig 200-ack, mätt live).
✅ createOrder i lib/order-client.ts: 401 → null → router.push(/bestall) (ingen simulerad order).

KRÄVS FÖRE RIKTIGA BETALNINGAR (blockerar, Simon-gate): live Stripe-keys i edge-envs + full staging paid/unpaid/cancelled webhook-triage — det kan ej autonomt eftersom creds saknas i vaultet. Stänger kod-delen; triage öppnas igen som nytt issue om/ när creds landar.

KOD-KRAVEN i denna issue är nu alla verifierade på live-prod (main 3d89cec, 2026-09-22): ✅ pay() i app/bestall/forskott/page.tsx har INGEN demo-fallback — kommentaren säger ordagrant "never simulate a successful payment or redirect to a demo order"; misslyckad/avstängd Stripe → paymentError, kunden får retry. ✅ POST /api/orders kräver verifierad Zitadel-principal (anonym = 401, mätt live). ✅ PATCH /api/orders/[id] nekar in_production/delivered (403 payment_event_required) — betalda states ägs exklusivt av den signaturverifierade Stripe-webhooken. ✅ Deposit/release kräver route-auth + owned order (tenant-predikat i SQL). ✅ Webhook flyttar order endast efter verifierad signatur + payment_status=paid + exakt belopp + valuta SEK; utan konfig → 503 (aldrig 200-ack, mätt live). ✅ createOrder i lib/order-client.ts: 401 → null → router.push(/bestall) (ingen simulerad order). KRÄVS FÖRE RIKTIGA BETALNINGAR (blockerar, Simon-gate): live Stripe-keys i edge-envs + full staging paid/unpaid/cancelled webhook-triage — det kan ej autonomt eftersom creds saknas i vaultet. Stänger kod-delen; triage öppnas igen som nytt issue om/ när creds landar.
admin closed this issue 2026-09-22 08:13:38 +00:00
Sign in to join this conversation.
No labels
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: admin/0pt#67