PurchaseDocumentation
Purchase

Runtimes and Databases

How Purchase fits Node, Cloudflare, and SQL backends.

Runtime model

Purchase is not tied to one web framework. The integration boundary is the Effect layer graph plus a SQL client plus one payment provider implementation.

That makes it a good fit for:

Node API services, worker-based deployments, and systems that want one billing runtime across multiple applications.

In practice, the runtime has three major ingredients:

Catalog and SDK Tag Managed Runtime Provider Layer SQL Client Layer Checkout Webhooks Customer Reads

Database model

The SDK persists commercial data in app-owned SQL tables. Today the codebase clearly supports SQLite, PostgreSQL, and Cloudflare D1 over HTTP.

The broader project direction also targets MySQL-compatible deployments.

Why SQL first

Billing is operational state. Checkout intents, normalized events, subscriptions, entitlements, invoices, provider references, and credit ledgers all need durable semantics. SQL is the most natural fit for idempotency, replay handling, querying current customer state, auditability, and app-specific joins and reporting.

Cloudflare direction

Purchase is being designed so the same commercial model can run in Cloudflare-oriented systems rather than only in traditional long-lived Node servers.

That matters for teams that want:

edge-adjacent webhook and API handling, D1-backed commercial state, and one billing layer across worker and server environments.

The example app already demonstrates this shape using Cloudflare D1:

const makeRuntimeLayer = (provider: PurchaseProviderTag) =>
  Layer.provideMerge(
    Pay.Layer,
    Layer.mergeAll(
      resolveProviderLayer(provider),
      D1Client.layer({
        db: getDatabase(),
        transformQueryNames: EffectString.camelToSnake,
        transformResultNames: EffectString.snakeToCamel
      })
    )
  )

That is a good illustration of the intended boundary:

the app composes the runtime, the provider is chosen by environment, the SQL layer is chosen by deployment target, and the billing workflows stay the same.

Storage ownership

Purchase owns the runtime logic, but your application still owns the schema boundary. The storage adapter APIs are designed so you can keep Purchase tables aligned with your own naming and model conventions instead of being forced into a hosted black box.

Why SQL-first state is the right trade-off

When billing state lives only in remote provider objects, teams repeatedly have to re-fetch provider objects to answer product questions, rebuild entitlements from transport-layer data, invent patch tables for local product behavior, and create replay and audit tooling after incidents happen.

Purchase takes the opposite approach: normalize once, persist once, and let the application read commercial state locally.

On this page