Files
baya-monorepo/mvp/blocker-phases/07-card-payment-redirect.md
T
2026-08-02 20:37:14 +03:30

2.1 KiB

Phase 07 — Card payment can never complete

Blocker: blockers.md § "Payments" (the card-payment dead end). Depends on: nothing.


Root cause. MockPaymentProvider.cs:16-21 returns RedirectUrl: https://mock-psp.local/pay/{reference} — a host that doesn't exist. client/.../checkout/page.tsx:137-140 does a hard browser navigation (window.location.assign) to that URL with no catch, so the user is stranded outside the SPA.

The page that used to catch this redirect and bounce back into the app — client/.../bookings/checkout/gateway/page.tsx — was deleted in commit 64f6aa4 ("refinement phase 4") and never replaced. The exact same pattern still lives today for BNPL: client/.../bookings/checkout/bnpl/gateway/page.tsx (env-gated to NODE_ENV=development via notFound(), pay-success/pay-fail buttons, redirects into ROUTES.CHECKOUT_BNPL_RETURN). The target it should redirect to already exists and already works: client/.../bookings/checkout/return/page.tsx (useConfirmGatewayReturn + bounded backoff poll → success routes to /checkout/confirmation).

Nothing about the money logic is broken — ZarinPalPaymentProvider.cs (the real provider) is fully implemented and simply unreachable pre-e-namad (blockers.md §B.5), which is a legal/business gate, not a code gap.

The fix (two parts, both needed)

  1. Restore client/.../bookings/checkout/gateway/page.tsx, mirroring the still-live BNPL harness pattern (env-gate with notFound() outside development), targeting ROUTES.CHECKOUT_RETURN.
  2. Point MockPaymentProvider.InitPaymentAsync's RedirectUrl at that harness (a relative or client-origin-absolute URL) instead of the dead mock-psp.local host.

Flag, don't guess (latent, not blocking the mock fix)

Once a real gateway is ever switched on, ZarinPal's callback redirects the browser with its own query params (Authority, Status) — nothing under Controllers/V1 today translates that into what checkout/return/page.tsx expects (request_id/transaction_id/outcome). This needs a translating endpoint before the real path can work, separate from the mock-harness fix above.