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.
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
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
request
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