Purchase is a provider-neutral billing SDK for Effect applications that want to keep commercial state inside their own system instead of pushing billing logic into scattered provider handlers or a hosted black box.
It is being designed for teams that need one commercial runtime across:
- Node services and Cloudflare-oriented deployments
- SQLite, Cloudflare D1, PostgreSQL, and MySQL-oriented systems
- multiple payment providers
- subscriptions, one-time purchases, and prepaid credits
Why Purchase exists
Most billing integrations look simple until product and operational requirements start compounding:
- more than one application needs billing behavior
- provider event semantics leak into business logic
- entitlements and credits need durable local state
- refunds, retries, and replay handling become real operational work
- one provider is no longer enough for every market or product line
Purchase is the layer intended to absorb that complexity.
The problem Purchase is solving
There are a few recurring failure modes in real payment systems:
- the provider dashboard becomes the only place people can reason about commercial state
- application code starts branching directly on raw provider event names
- checkout success redirects get mistaken for durable purchase completion
- entitlements, subscription state, refunds, and credits end up in separate ad hoc state machines
- every product team re-implements the same webhook, replay, and reconciliation logic
Purchase treats those as architecture problems, not as a collection of helper utilities.
The design position
Purchase makes a few strong choices:
- commercial ids belong to the application
- provider ids are references, not your primary keys
- webhooks are workflow inputs, not incidental callbacks
- customer entitlements are derived state
- the database is owned by the application
- provider adapters should be replaceable
That is why the package is built around catalog normalization, workflow receipts, read-side projections, and SQL-backed state instead of being a thin wrapper around one provider SDK.
What already exists in the repository
- catalog DSL for products, plans, features, quotas, and credit units
- shared runtime APIs for checkout, webhooks, subscriptions, refunds, credits, and portal sessions
- Stripe and Paddle integrations behind one workflow surface
- SQL-backed workflow and projection storage
- test coverage across core logic, provider fixtures, and end-to-end billing flows
- CLI support for catalog synchronization
Current focus
- public API hardening
- deployment and bootstrap ergonomics
- broader provider capability docs
- production migration and rollout guides
- richer examples for real app architectures
The docs are organized in two layers:
- runtime guides for the APIs and examples in this repository
- architecture guides for the commercial model, workflow boundaries, and operational shape
End-to-end model
Why invest in this extra structure
Purchase is not trying to be the smallest possible abstraction. It is trying to be the layer you do not need to rebuild every time billing becomes more important.
That matters when you want:
- one runtime for subscriptions, one-time purchases, and prepaid credits
- one architecture that can fit both Node and Cloudflare-oriented deployments
- one commercial model across SQLite, D1, PostgreSQL, and MySQL-oriented systems
- one application-owned boundary even when provider choices evolve
Start here
Purchase is meant to be read as a system, not a single API page.
- Browse the example app walkthrough
- Read the technical docs
- Check the roadmap