Skip to content

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Repository files navigation

Metered API Billing

Core of a SaaS metering + billing system: ingest usage events → aggregate into hourly windows → tiered-price into monthly invoices → a signed payment webhook, with a customer dashboard and an internal ops console.

Stack: Django + DRF + Postgres · React + Vite + TypeScript · a Postgres-advisory-locked job runner. The reasoning behind every non-obvious decision is in DESIGN.md (read that first — it counts as much as the code).

Quick start

docker compose up --build

That's it — no config step. up migrates the DB and auto-seeds realistic demo data on first run (it's idempotent, so repeated ups are safe; first run takes ~1 minute — watch the seed logs), then starts the API, worker, and frontend.

Demo logins (also printed in the seed logs):

  • Ops console: opsadmin / opsadmin
  • Customer dashboard: any seeded email (e.g. acme0@example.com) / demo1234
  • Anomaly-test accounts (anomaly-usage-spike@…, -per-key@…, -spend@…, -drop@…) appear in the ops Anomalies panel; Acme is left with an unpaid May invoice to test the payment module.

Config comes from the environment with local-dev defaults baked into docker-compose.yml, so nothing real is committed; override anything via a .env (copy .env.example) or real env vars. Reseed from scratch with make down-v && docker compose up.

Tests

make test      # full pytest suite against Postgres (RLS active)

78 tests focused on the correctness boundaries the brief calls out — idempotency, concurrency (real threaded races), tenant isolation, reconciliation/late events, and money — not getters.

What's implemented

Customer API (/v1, scoped to the authenticated customer)

  • POST /v1/events — batched, idempotent ingestion (API-key auth)
  • GET /v1/usage — page-number paginated (jump-to-page), filter by date range + api key; GET /v1/usage/summary
  • GET /v1/invoices, GET /v1/invoices/{id}
  • POST /v1/auth/login, GET /v1/me

Ops API (/ops, staff auth, audited, cross-tenant)

  • GET /ops/customers, GET /ops/customers/{id} (usage + invoices + anomaly)
  • POST /ops/customers/{id}/credits (requires Idempotency-Key)
  • PATCH /ops/invoices/{id}/line-items/{id} (override + audit trail)

Webhook

  • POST /webhooks/payments — HMAC-signed, replay-safe, marks invoices paid

Background jobs (the worker service; advisory-locked, idempotent)

  • aggregate_usage, issue_invoices, reconcile — run on a loop by run_scheduler, and individually runnable: docker compose run --rm migrate python manage.py <command>

Key design properties (one-liners; details in DESIGN.md)

  • Tenant isolation at the database via Postgres Row-Level Security and an app-layer scoped manager — a customer guessing another's invoice id gets a 404.
  • Idempotency everywhere it matters: ON CONFLICT on request_id (ingestion) and provider_event_id (webhook), Idempotency-Key on credits, recompute-based aggregator.
  • Money in integer minor units (cents), sub-cent rates in micro-dollars, banker's rounding.
  • Immutable audit log (DB trigger + revoked grants) for every credit/override.
  • No secrets in the repo — webhook secret, API-key pepper, DB creds are env-based.

Layout

backend/   Django project (apps: customers, usage, billing, audit, webhooks, jobs, ops, common)
frontend/  Vite + React + TS SPA (src/app = customer, src/ops = ops console)
docker/    Postgres role bootstrap
DESIGN.md  the written reasoning (data model, idempotency, scaling, threat model, trade-offs)

Stack note

The brief's stack is Django + DRF + Postgres + React, and that's what this uses — but the choice is also deliberate: the properties being graded (tenant scoping at the right layer, constraints over comments, idempotent jobs, integer money) are idiomatic in Postgres, so the database does the heavy lifting and the application stays thin. Where it would break first at production scale, and the migration path, are spelled out in DESIGN.md §4.

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages