Skip to content

basalt / subscriptions/src / PaymentLedger

Class: PaymentLedger ​

Defined in: subscriptions/src/payment.ts:248

Ties a PaymentStore to webhook idempotency so a retried callback is applied once. Record a payment on create, then feed every verified PaymentEvent through apply — it dedupes by event.id and updates the ledger.

ts
const ledger = new PaymentLedger()
const inst = await gateway.createPayment(req)
await ledger.created(inst, req)              // pending
// in the webhook route:
const event = gateway.verifyWebhook(raw, sig)
if (event) {
  const { fresh, record } = await ledger.apply(event)
  if (fresh && record?.status === 'paid') activate(record.billableId!)
}

Constructors ​

Constructor ​

> new PaymentLedger(options?): PaymentLedger

Defined in: subscriptions/src/payment.ts:256

Parameters ​

options? ​

PaymentLedgerOptions = {}

Returns ​

PaymentLedger

Methods ​

apply() ​

> apply(event, onFresh?): Promise<PaymentApplyResult>

Defined in: subscriptions/src/payment.ts:318

Apply a verified PaymentEvent idempotently. Dedupes by event.id; on a fresh event, flips the ledger record to paid/failed. If persisting fails the dedupe claim is released so the gateway's retry can reprocess.

onFresh runs inside the idempotency claim, after the ledger is updated — use it for domain side effects (activate a subscription, mark a booking paid) that must apply exactly once with the payment. If it throws, the claim is released so the whole thing reprocesses on the gateway's retry.

State machine: pending → paid | failed, failed → paid | failed (a retry that succeeds), and paid is terminal. An event for an already-paid payment — a late payment.failed, or a second payment.succeeded under a new event id — changes nothing, does not run onFresh and returns fresh: false, so a payment is never un-paid nor its side effects (activating a period) run twice.

Parameters ​

event ​

PaymentEvent

onFresh? ​

(record, event) => void | Promise<void>

Returns ​

Promise<PaymentApplyResult>


created() ​

> created(instruction, request): Promise<void>

Defined in: subscriptions/src/payment.ts:289

Record a just-created payment as pending. Call after createPayment.

Parameters ​

instruction ​

PaymentInstruction

request ​

PaymentRequest

Returns ​

Promise<void>


get() ​

> get(id): Promise<PaymentRecord | undefined>

Defined in: subscriptions/src/payment.ts:362

Parameters ​

id ​

string

Returns ​

Promise<PaymentRecord | undefined>


on() ​

> on<K>(event, listener): () => void

Defined in: subscriptions/src/payment.ts:268

Subscribe to a lifecycle event (recorded/confirmed/failed). Listeners are best-effort: they run after the payment is safely persisted and a throwing one never rolls it back (it's reported via onListenerError). Returns an unsubscribe function.

Type Parameters ​

K ​

K extends keyof PaymentLedgerEvents

Parameters ​

event ​

K

listener ​

PaymentLedgerListener<K>

Returns ​

() => void

Released under the MIT License.