Skip to main content
Version: 2.x

Stores

Every Bursar facade is built on a store — the adapter that owns the database. PostgresStore is the only bundled credit store; PostgresBillingStore is the bundled billing store; CreditStore is the abstract contract a custom backend must implement.

PostgresStore

from bursar import PostgresStore

store = PostgresStore(
database_url,
tenant_id=tenant_id,
provider_environment="test",
)

Connects through psycopg2 and runs every mutation as a canonical SQL function (RPC). Requires the bursar[postgres] extra (pip install "bursar[postgres]").

ParameterTypeMeaning
database_urlstrPostgres connection string (positional)
tenant_idstr | UUIDRequired keyword argument scoping every transaction to one tenant
provider_environment"live" | "test" | "sandbox"Required financial provider namespace
max_pool_sizeint = 20Upper bound of the internal ThreadedConnectionPool
poolThreadedConnectionPool | NoneReuse an existing pool instead of creating one

The store owns its pool: call store.close() to drain it. database_url is exposed as a read-only property.

PostgresBillingStore

from bursar import PostgresBillingStore

billing_store = PostgresBillingStore(
database_url,
tenant_id=tenant_id,
provider_environment="test",
)

Same shape: database_url (positional), required tenant_id and provider_environment keywords, and an optional pool (defaults to a fresh pool, max 10). It wraps all billing repositories (offer, topup, customer, subscription, event, payment, refund, invoice, dispute, config) behind one interface. Pass it as billing_store= to enable billing. Commerce additionally requires commerce_options= with the provider environment and factories. The store is lazily imported from the top level and requires the [postgres] extra.

The CreditStore contract

CreditStore is the abstract base class for a custom backend. Implementations must preserve, for every capability they expose:

  • Atomicity — each mutation commits or rolls back as one unit; balances and their ledger rows never diverge.
  • Idempotency — a replayed idempotency_key returns the original result instead of applying a second time.
  • Append-only ledger — history is immutable; corrections are new entries (adjustment/refund), never updates.
  • Credit-lot allocation — credits are consumed lot-by-lot by priority and expiry (FEFO), with sweep_expired_credits expiring eligible lots.
  • Stable cursor ordering — every list view orders by (created_at, entry_id) so cursor pagination never skips or duplicates rows.

Optional capabilities (usage analytics, team management, usage-charge lists, get_ledger_entry) raise CapabilityNotSupportedError by default on the ABC; override them if your backend supports them. Billing is a separate hierarchy (BillingStore/PostgresBillingStore) with its own repositories.

Migrations

The package never applies SQL on construction — neither PostgresStore nor Bursar runs migrations. Apply the bundled migrations with the bursar CLI during application setup, before constructing any store:

Terminal
BURSAR_MIGRATION_DATABASE_URL=postgres://... bursar migrate

The migration command reads BURSAR_MIGRATION_DATABASE_URL. See CLI for the separate tenant, runtime/config, and local-validation credentials.

See Storage backends for S3/ClickHouse adapters and the storage runtime, and custom stores for a worked example of a custom CreditStore.