Kill Bill

@crvouga/mockingbird-service-kill-bill

Stateful Kill Bill billing emulator with accounts, subscriptions, invoices, payments, credits, retries, catalog, and clock controls.

Parity: Accounts, subscriptions, and invoicesPayments16 of 16 operationsIn-process fetchNode servermockingbird-kill-bill
$
npm install -D @crvouga/mockingbird-service-kill-bill

Playground

Requests go to the real emulator running in this tab. State persists across requests, and the journal shows what it received. A green dot marks operations whose sample succeeds as-is; others need ids from earlier responses.

Loads the real @crvouga/mockingbird-service-kill-bill into this tab on first send
http://127.0.0.1:8080

Send a request to see the emulator's response.

Operations

16 of 16 operations in the vendored contract are emulated.

MethodPathOperationStatus
GET/1.0/kb/accounts/paginationPaginateAccounts
GET/1.0/kb/invoices/paginationPaginateInvoices
GET/1.0/kb/tenantsGetTenantByApiKey
POST/1.0/kb/tenantsCreateTenant
GET/1.0/kb/tenants/registerNotificationCallbackGetNotificationCallbacks
POST/1.0/kb/tenants/registerNotificationCallbackRegisterNotificationCallback
DELETE/1.0/kb/tenants/registerNotificationCallbackDeleteNotificationCallbacks
GET/1.0/kb/test/queuesWaitForQueues
GET/1.0/kb/{resource}ReadResource
POST/1.0/kb/{resource}CreateResource
GET/1.0/kb/{resource}/{id}GetResource
PUT/1.0/kb/{resource}/{id}UpdateResource
DELETE/1.0/kb/{resource}/{id}DeleteResource
PUT/1.0/kb/subscriptions/{subscriptionId}/undoChangePlanUndoChangeSubscriptionPlan
PUT/1.0/kb/invoices/{invoiceId}/voidInvoiceVoidInvoice
GET/1.0/kb/accounts/{accountId}/overdueGetAccountOverdue

Documentation

The package README, the same file that ships in the npm tarball.Edit on GitHub

Local emulators. Real API contracts. Part of Mockingbird.

Stateful Kill Bill REST emulator for billing integration tests. It models tenant-scoped accounts, payment methods, subscriptions, bundles, invoices, payments, credits, refunds, retries, catalogs, audit metadata, a test clock, account overdue state, and secret-protected lifecycle webhooks.

Install

Shell
npm install -D @crvouga/mockingbird-service-kill-bill

ESM only. Node 22+ or Bun 1.2+.

Usage

TypeScript
import { AccountApi, Configuration, apiKey } from "killbill"
import { createServer } from "@crvouga/mockingbird-service-kill-bill/server"

const emulator = await createServer()

// The Kill Bill client your application already uses. Only its basePath changes.
const config = new Configuration({
  basePath: emulator.url,
  username: "admin",
  password: "password",
  apiKey: apiKey("bob", "lazar"),
})
const accounts = new AccountApi(config)
await accounts.createAccount({ name: "Synthetic Customer", externalKey: "customer-1", currency: "USD" }, "example")
const { data: account } = await accounts.getAccountByKey("customer-1")
console.log(account.accountId, account.currency) // acc-… USD

await emulator.close()

The default credentials are Basic auth admin:password plus tenant headers X-Killbill-ApiKey: bob and X-Killbill-ApiSecret: lazar. Mutations also require X-Killbill-CreatedBy; optional reason and comment headers are recorded in the audit journal.

POST /1.0/kb/tenants creates another tenant with Basic auth and X-Killbill-CreatedBy only. Accounts created with that tenant's api key stay hidden from other tenants. POST /1.0/kb/tenants/registerNotificationCallback?cb= stores one push-notification URL for the tenant; invoice and payment events are POSTed there as Kill Bill notification JSON. The in-process onEvent hook still receives those events.

GET /1.0/kb/accounts/pagination and GET /1.0/kb/invoices/pagination enumerate the authenticated tenant's existing billing records as bare arrays. offset defaults to 0 and limit to 100; positive limits use creation order, and negative limits reverse it. Invoice updates retain their original position. Responses carry X-Killbill-Pagination-CurrentOffset, TotalNbRecords and MaxNbRecords. NextOffset and NextPageUri are present only when another page exists. Next-page URLs retain the selected namespace and configured admin prefix; continue sending Basic auth and tenant headers. Empty/exhausted pages return []. A zero limit also returns [], as the real SQL pagination does; clients should stop on an empty page. Account balances default to null; use accountWithBalance=true or accountWithBalanceAndCBA=true for the live invoice balance/CBA. These reads use the same collections as invoice creation, payment and voiding, and participate in the standard namespace, clock, reset, snapshot and fault controls.

The oracle is Kill Bill's account resource, invoice resource, pagination response, and ordering implementation. Counts are exact for the emulator's finite collections; the vendor's large-dataset count heuristic, audit projections and search endpoints are unmodelled. Invalid pagination uses the emulator's existing error envelope; exact live error text remains unverified. Live parity requires an isolated tenant and is not enabled by the current runner.

Controls

  • POST /__admin/payments/decline-next and /pending-next select the next payment outcome.
  • POST /__admin/payments/:id/retry succeeds the latest failed or pending transaction.
  • POST /__admin/catalog/plans seeds a plan.
  • GET /__admin/billing inspects billing state and audit entries. GET /__admin/state is the shared collection view.
  • GET /1.0/kb/test/clock returns Kill Bill's clock JSON (currentUtcTime, localDate, timeZone). PUT /1.0/kb/test/clock?requestedDate=... advances recurring billing.
  • PUT /1.0/kb/subscriptions/:id/undoChangePlan cancels a pending plan change. With no pending change it returns 400 and Kill Bill error code 1071.
  • GET /1.0/kb/test/queues?timeoutSec= returns 200 when no due notification or bus event is outstanding, and 412 after that many seconds when work remains. The default timeout is 5 seconds. A notification counts only when its effectiveDate is at or before the clock; rows in kb_bus_events always count. Both collections are visible through GET /__admin/state.
  • Fault presets cover plugin failure, rate limiting, network loss, and webhook duplicate/reorder/drop delivery.

API

  • KillBillAPI, KillBillAPIOptions, KillBillEvent: REST handler and event contract.
  • Account, subscription, bundle, invoice, payment, transaction, and catalog state types.
  • createRuntime, KillBillRuntime, KillBillRuntimeOptions: full runtime and webhook hub.
  • KILL_BILL_NAMESPACE, KILL_BILL_PRESETS: namespace and fault controls.
  • document, operationIds, supportedOperationIds: generated OpenAPI metadata.
  • createServer, KillBillServerOptions, DEFAULT_PORT, serveTarget from ./server.

Fidelity boundary

The emulator targets deterministic adapter and lifecycle tests. It does not run Kill Bill plugins, tax engines, databases, notification workers, or production entitlement and dunning algorithms. Invoice payments with externalPayment=true are the exception: they are stored on the built-in __EXTERNAL_PAYMENT__ method and are not sent to a gateway. GET /1.0/kb/invoices/{invoiceId}/payments returns those payments with targetInvoiceId. GET /1.0/kb/invoicePayments/{paymentId} returns that payment plus targetInvoiceId. POST /1.0/kb/invoicePayments/{paymentId}/refunds adds a REFUND on the same payment (Kill Bill 0.24.10). externalPayment=true on that refund records a separate credit payment instead, and leaves the original refundedAmount unchanged. GET /1.0/kb/test/queues only reports whether due notifications and bus events are still outstanding. A future-dated notification, including an end-of-term plan change whose effective date is still ahead of the clock, does not count. GET /1.0/kb/accounts/{accountId}/overdue uses a fixed Kill Bill 0.24.10 config: the synthetic clear state __KILLBILL__CLEAR__OVERDUE_STATE__, and state OD1 once the earliest unpaid invoice (positive balance, not void) is at least one day old (timeSinceEarliestUnpaidInvoiceEqualsOrExceeds). Uploading a replacement overdue config is not modelled. A subscription priceOverrides entry whose phaseName is {planName}-evergreen (or whose phaseType is EVERGREEN when the name is omitted) replaces the catalog amount on the recurring invoice item, and GET /1.0/kb/subscriptions/{id} returns the overrides that were sent. Other catalog phases are not modelled.

↑↓ to navigate↵ to open