PAYGO PLATFORM
Stratus Frontend
How the Cloudflare dashboard connects to subscriptions-api. Three checkout systems, two HTTP client patterns, one API gateway. The concrete implementation layer behind the billing architecture.
The stratus monorepo houses three distinct checkout implementations. All converge at subscriptions-api through the API gateway — but through different HTTP clients, different Stripe integrations, and different state management patterns.
Modern stack. React Query + Stripe PaymentElement. Custom fetch wrapper with typed errors. Gated per-product.
Legacy stack. Redux + Stripe CardElement. 31 product SKUs. Three-view flow: review → payment → confirm.
Internal admin tool. /api/v3/ with ADMIN-JWT auth. Admin-only actions: unban, sync-entitlements, lock/unlock.
Products are migrating individually from Multisku to Unified Checkout. Each product gets its own feature gate (e.g. billing-unified-checkout-load-balancer). When enabled, the product's checkout route renders UC instead of Multisku. The gate inventory controls which checkout system a customer sees for each product.
The Unified Checkout purchase flow traces five steps from product page to confirmation. Each step hands off to the next — cookie to nonce to API call to client secret to entitlement. The entire sequence is orchestrated by useSubmitPurchase.ts.
Product page writes cart to cookie, navigates to checkout. No server-side session — cookie is the only state.
Stripe PaymentElement collects card/wallet. Frontend confirms to get a nonce — raw card data never touches our code.
Cart splits: subscriptions → bulkCreate, registrar → billingCheckout, single upgrades → updateSubscription.
Each client_secret confirmed with Stripe. pi_ → confirmPayment, seti_ → confirmSetup. May trigger 3DS iframe.
Polls entitlements until active. Patches new zones from initializing → pending. Navigates to confirmation.
user_is_on_session flag is transitional. After PM gate removal, client secrets become the default path.
The frontend never calls subscriptions-api directly. All API calls use relative URLs starting with /api/v4/ which hit the API gateway. The gateway routes to subscriptions-api endpoints grouped into three categories.
| Endpoint | Method | Returns | Called By |
|---|---|---|---|
/bulk/subscriptions | POST | client_secrets[] | UC + Multisku |
/subscriptions/{id}/action/append | POST | client_secrets[] | UC (upgrades) |
/billing/checkout | POST | client_secrets[] | UC (registrar) |
/billing/profile | GET/POST/PUT | Profile object | UC + Multisku |
/payment-methods | GET/POST/DELETE | PM list | UC + Multisku |
/billing/profile/payment-method | POST | Stripe client secret | UC (new PM) |
/billing/pre_auth/stripe_payment_intent | POST | client_secret + intent_type | UC (pre-auth) |
/entitlements | GET | Entitlement list | Both + product pages |
Two HTTP client patterns coexist. The modern pattern was purpose-built for Unified Checkout. The legacy pattern predates React Query and uses Redux for all async state. Both prepend /api/v4 to all URLs. Error handling diverges completely.
Native fetch() with auto-unwrap of {result: T}. Throws typed ApiError(status, errors[]). React Query for cache and retries.
makeActionCreator generates thunk triples. fetchWithoutErrorNotifications suppresses the red error bar — errors route through Redux instead.
Two distinct gates serve different purposes. The frontend billing-on-session-payment gate controls whether the dashboard handles payment confirmation via client secrets. The server-side PM gate at changes_calculator.go blocks subscription changes without a payment method on file. Their removal follows a strict dependency chain.
When the server-side PM gate fires, the frontend receives HTTP 402 with error code 1210. Both checkout systems handle this as a blocking error — but differently. Multisku suppresses the global error bar via fetchWithoutErrorNotifications and routes through Redux. UC catches it as an ApiError with status 402. Both paths must be updated before gate removal — after Deferred Collection, this error code should never appear.
| Gate | Type | Controls |
|---|---|---|
billing-unified-checkout | Frontend | Master UC gate — enables Unified Checkout for eligible products |
billing-on-session-payment | Frontend | 3DS/SCA confirmation flow via client secrets |
billing-routes | Frontend | Visibility of the entire billing section at /:accountId/billing/* |
billing-zone-creation | Frontend | Zone creation flows |
| Per-product UC gates | Frontend | Individual product migration (e.g. billing-unified-checkout-load-balancer) |
| PM gate (402/1210) | Server | Blocks subscription changes without a PM on file — Deferred Collection target |
The frontend is where the billing architecture meets the customer. Three checkout systems converging to one. Two HTTP client patterns collapsing to one. The gate dependency chain is the timeline — Act 1 completes, the PM gate falls, and client secrets become the default path.