A production-shaped Stripe subscription billing backend on FastAPI that gets the parts everyone gets wrong right: hybrid seat + metered pricing, trials, upgrade/downgrade proration, a dunning email sequence, Stripe Tax, the customer portal, and a Connect marketplace with split payouts and refunds. All of it sits behind a webhook layer that survives duplicate, out-of-order, and dropped deliveries with zero double charges. It is strictly test-mode: the default gateway is a fully offline Stripe fake, the real adapter refuses live keys, and the client deploys with their own keys. A chaos harness fires perturbed webhook storms at the live app and writes its own results table.
- The correctness claim is machine-generated.
docs/chaos-report.mdis produced by a real local run of the app, not typed by hand. Latest run: 5 scenarios, 45 webhook deliveries, 0 double charges. Reproduce withpython -m billing.chaos. - Two independent idempotency layers back that claim: event-level dedup (
processed_eventsprimary key) and a money-level partial unique index (onechargeledger row per invoice). The database itself refuses a second charge even if the application logic above it were wrong. - Offline-green: the Stripe SDK is faked in tests; the suite needs no network and no keys and runs in well under a second.
Stripe owns the money; this service keeps a SQLite projection written from exactly one place:
the webhook processor. Full write-up in docs/architecture.md.
flowchart LR
API[REST API] --> GW[Stripe gateway<br/>fake or test-mode real]
GW -- signed events --> V[verify signature]
V --> D[dedupe processed_events]
D --> P[projection handlers]
P --> DB[(SQLite projection)]
API -->|reads| DB
CHAOS[chaos harness] -. duplicate / out-of-order / dropped .-> V
uv venv --python 3.12 .venv
source .venv/bin/activate
uv pip install -e '.[dev]'
python -m billing.demo # scripted end-to-end walkthrough of every flow
python -m billing.chaos # webhook chaos storms + pass/fail verdict
python scripts/serve.py # run the API on http://127.0.0.1:8000 (GET /health)The billing console command exposes the same three entry points:
billing demo
billing chaos --write docs/chaos-report.md
billing serve| Capability | Where |
|---|---|
| Hybrid seat + metered pricing, overage math | pricing.py, plans.py |
| Proration (upgrade/downgrade, mid-cycle, half-up rounding) | proration.py, money.py |
| Trials | gateway_fake.py, gateway_stripe.py |
| Dunning state machine + email sequence + outbox | dunning.py, outbox.py, email_backend.py |
| Stripe Tax (jurisdiction lookup / real Tax Calculation) | tax.py, gateway_stripe.py |
| Customer portal | app.py, gateways |
| Connect marketplace: onboarding/KYC, split payouts, refunds | webhooks.py, store.py, gateways |
| Webhook layer: signature verify, idempotency, dedupe, replay tolerance | signing.py, webhooks.py, store.py |
Chaos harness → docs/chaos-report.md |
chaos.py |
Email uses a file/console/memory backend. No real mail is sent.
uv run pytest65 tests, fully offline. The suite fakes the Stripe SDK (test_gateway_stripe.py), drives real
HTTP requests through the ASGI app (test_app.py, test_fixtures.py), replays recorded-style
event fixtures (tests/fixtures/), and asserts the chaos harness never double-charges
(test_chaos.py). CI additionally re-runs the chaos harness as a hard gate.
There is no live-mode path. StripeApiGateway raises on any sk_live_ key. To run the same
flows against a real Stripe test-mode account, follow
docs/test-mode-runbook.md: it is a human checklist covering key
setup, product/price provisioning, stripe listen webhook forwarding, and Connect onboarding.
Stripe work is point-in-time reference material; the client deploys with their own keys and owns
their production configuration.
src/billing/ billing domain, gateways, webhook layer, chaos + demo harnesses
scripts/ runnable entry points (serve, demo, chaos, test-mode setup/smoke)
tests/ offline suite + recorded event fixtures
docs/ architecture note, generated chaos report, test-mode runbook
I make money-critical and AI-era backends production-safe. If you need Stripe billing that holds up under real webhook chaos, or a second pair of eyes on the one you have, reach me through my portfolio: https://amin-ale.github.io/portfolio-site, or email amin.ale.business@gmail.com