Skip to content

Prepaid credits

Usage credits represent service units, not money. Settlement of a usage_credits benefit issues a provenance-preserving credit lot.

import { Credits } from "@byfungsi/sapa";
const creditBenefit =
yield *
Credits.pack({
unit: "ai.credit",
quantity: 10_000n,
});
import { consumeCredits, releaseCredits, reserveCredits } from "@byfungsi/sapa";
import { Effect } from "effect";
const runJob = Effect.gen(function* () {
const reservation = yield* reserveCredits({
idempotencyKey: "job-018f-credit-reservation",
customerId: "customer-42",
unit: "ai.credit",
quantity: 60n,
});
const workSucceeded = true; // Replace with your external work result.
return yield* workSucceeded
? consumeCredits({ reservationId: reservation.id })
: releaseCredits({ reservationId: reservation.id });
});

Reserve before starting costly work. Consume only after success. Release after failure or cancellation so the units can be reserved again.

  • Reservation uses a stable idempotency key.
  • Consumption and release are mutually exclusive terminal outcomes.
  • An expired reservation cannot be consumed, but can be released.
  • Concurrent reservations cannot overspend the same balance.
  • Refund handling preserves lot provenance and does not invent negative units.

Use getCreditBalance({ customerId, unit }) for verified aggregates and getCreditHistory({ customerId, unit }) for sanitized authoritative movements.