Skip to content

Cloudflare reference deployment

The private infra/cloudflare application is SAPA’s official reference composition root. It demonstrates one concrete installation; it is not a separately published runtime package.

flowchart TB
  accTitle: Cloudflare reference deployment topology
  accDescr: Authenticated traffic reaches the SAPA Worker, which coordinates D1, Queues, Workflows, retained R2 artifacts, dashboard assets, payment providers, and Resend.
  Internet["Merchant operators and signed customer actions"] --> Access["Cloudflare Access and route authentication"]
  Access --> Worker["SAPA Worker"]
  Assets["Dashboard static assets"] --> Worker

  Worker --> D1["D1 · history, snapshots, projections, leases"]
  Worker --> Dispatch["Workflow dispatch Queue"]
  Dispatch --> ProviderWorkflow["Provider-event Workflow"]
  Dispatch --> DLQ["Dead-letter Queue"]
  Worker --> Checkpoints["R2 · retained checkpoints"]
  Worker --> Shadows["R2 · reconstructed shadows"]

  Worker --> Midtrans["Optional Midtrans adapter"]
  Worker --> Polar["Optional Polar adapter"]
  Worker --> Resend["Resend transactional email"]

R2 checkpoints and shadow artifacts do not replace authoritative D1 history. The Worker verifies request-bound identities and creates a coherent request-scoped SAPA runtime; D1 handles must not be shared across requests.

  • A Cloudflare account with Workers, D1, Queues, Workflows, R2, and Zero Trust Access.
  • Node.js 22 and the repository’s pinned pnpm version.
  • A durable Alchemy state backend and ALCHEMY_PASSWORD.
  • A verified Resend sender domain.
  • Independent production secrets for this SAPA installation.
  • Optional Midtrans or Polar credentials when those providers are enabled.

The Worker binds SAPA_PROVIDER_NOTIFICATION_BASE_URL to a public hostname outside operator-only Cloudflare Access and exposes provider-specific raw webhook endpoints:

  • /api/provider-notifications/midtrans
  • /api/provider-notifications/polar

The Polar endpoint requires POLAR_WEBHOOK_SECRET, preserves the raw body and Standard Webhooks headers, rejects signatures outside a five-minute freshness window, and accepts USD one-time settlement only after exact amount and currency comparison.

Run commands from the repository root. Always include the stage; do not let a local Alchemy identity choose the production state implicitly.

Terminal window
pnpm --filter @sapa/infra-cloudflare run deploy --stage staging
pnpm --filter @sapa/infra-cloudflare run deploy --stage production

Review the Alchemy plan before applying it. Deploy and verify staging before production. The production Worker disables its workers.dev origin and binds its configured custom domains.

The isolated production E2E environment uses a separate stage and independent secrets:

Terminal window
pnpm --filter @sapa/infra-cloudflare run deploy --stage sandbox

Sandbox routes and capabilities are not composed into the production runtime.

sequenceDiagram
  accTitle: Checkout settlement and access sequence
  accDescr: The merchant creates a checkout, SAPA retains terms around provider creation, accepts an authenticated settlement, executes durable work, and answers an access query.
  autonumber
  participant App as Merchant application
  participant Sapa as SAPA runtime
  participant DB as D1
  participant Provider as Payment provider
  participant Worker as Durable worker

  App->>Sapa: Create checkout with idempotency key
  Sapa->>DB: Atomically accept command and snapshot terms
  Sapa->>Provider: Create hosted checkout
  Provider-->>Sapa: Provider creation identity and URL
  Sapa->>DB: Retain creation receipt
  Sapa-->>App: Checkout ID and hosted URL
  Provider->>Sapa: Authenticated payment notification
  Sapa->>DB: Atomically append settlement, access, and owed work
  Worker->>DB: Lease owed work with fencing
  Worker->>Provider: Execute external work with stable idempotency
  Worker->>DB: Append succeeded, retry, or terminal outcome
  App->>Sapa: Check access
  Sapa->>DB: Verify history and bounded projection
  Sapa-->>App: Access decision

The complete create-checkout and access operations in this sequence are currently library calls inside a composition root, not general production HTTP endpoints.