billing-kit-adapters
Synced from billing-kit-adapters/README.md — the repo is canonical.
Community-contributed provider adapters for billing-kit, and the contract every one of them conforms to.
billing-kit is provider-agnostic by design: metering, aggregation, pricing and the double-entry ledger are its own; a provider handles customers, subscriptions, settlement of a closed period, capture, refunds and webhook verification. Stripe and Paddle ship in the core. Everything else lives here.
flowchart LR
core["billing-kit core<br/>metering · pricing · ledger"]
subgraph iface["BillingProvider<Caps>"]
m["ensureCustomer · findCustomer<br/>ensureSubscription · settle<br/>refund · verifyWebhook"]
end
subgraph adapters["adapters/* (this repo)"]
a1["Chargebee"]
a2["Recurly"]
a3["Braintree"]
a4["…yours"]
end
core -->|reads capabilities| iface
adapters -.implements.-> iface
classDef c fill:#0d9488,stroke:#0f766e,color:#fff
class core c
Every branch in billing-kit reads a provider's declared capabilities, never its name — so a new provider is an adapter and a folder, not a change to the core.
Where an adapter sits
The core owns the math; the adapter owns the conversation with the provider. When a billing period closes, billing-kit asks the adapter to settle it and hands the returned amounts back to its own double-entry ledger — the provider never touches the ledger, and the core never touches the provider's API.
sequenceDiagram
participant BK as billing-kit core
participant A as adapter (this repo)
participant P as provider API
BK->>A: settle(closed period)
A->>P: create invoice / capture
P-->>A: settlement result
A-->>BK: exact amounts (idempotent)
BK->>BK: post balanced ledger
Note over A,P: verifyWebhook confirms<br/>settlement out-of-band
Idempotency is the load-bearing rule: every ensure* and settle is called more
than once (retries, redelivered webhooks) and must converge to one result — the
conformance testkit checks exactly this.
Adapters
| Provider | Status | Settlement | Merchant of record |
|---|---|---|---|
| Stripe | in billing-kit core | lines / quantity | no |
| Paddle | in billing-kit core | quantity | yes |
| your provider | open a PR | — | — |
(This table grows as adapters land. adapters/_template is the starting point.)
Using an adapter
Each adapter is a small package that exports a create<Provider>Provider(config)
returning a BillingProvider. Point billing-kit at it:
import { createChargebeeProvider } from 'billing-kit-adapters/chargebee';
const provider = createChargebeeProvider({
apiKey: process.env.CHARGEBEE_KEY!,
webhookSecret: process.env.CHARGEBEE_WEBHOOK_SECRET!,
});
// hand `provider` to billing-kit's settlement / webhook paths
Contributing an adapter
Read CONTRIBUTING.md — it walks the contract method by method, and the one rule that matters most (every create is idempotent). The short version:
cp -r adapters/_template adapters/<your-provider>
# implement each method; the billing-kit types tell you every shape
pnpm typecheck # your adapter must satisfy BillingProvider<Caps>
pnpm test # run the conformance testkit
The template already type-checks against billing-kit's real BillingProvider
interface, so you start from a correct skeleton and fill it in.
Licence
Apache-2.0 — the same as billing-kit, so an adapter can be used anywhere the core can. See LICENSE.