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&lt;Caps&gt;"]
        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.