StudioOps Admin: an operations console for subscription + credits SaaS
Subscriptions, a credit ledger, orders, abuse and fraud cases, RBAC and audit in one internal console — built for the subscription + credits model most AI products use.
Capability case built on synthetic data — not paid client work.
- Role
- Solo — product, design, architecture, full-stack development, testing
- Stack
- Next.js 16React 19TypeScriptPostgreSQLPrisma 7Better AuthZodRechartsVitestPlaywright
Overview
- Challenge: a product that sells subscriptions plus usage credits can keep everything in PostgreSQL and still be impossible to operate — credits that must reconcile, API abuse and bonus farming, failed renewals, and no record of who changed what.
- Solution: one staff-only console for users, plans, a three-bucket credit wallet with an immutable ledger, orders with idempotent payment events, risk cases, five-role RBAC and an append-only audit log.
- Results: 65 unit/integration tests, 6 browser E2E tests and 17 security and risk acceptance checks passing; desktop and mobile layouts.
The challenge
Once a SaaS has paying users, the hard questions are no longer about features:
- A user says their credits were deducted twice. Can support prove what happened?
- Someone scripts the API or registers 50 accounts to farm signup credits. Who notices, and who decides what to do?
- A renewal fails three times. Does anyone follow up?
- An admin changes a price or compensates credits. Six weeks later, can anyone say who, when and why?
Most teams answer these by querying the database by hand. That works until money, permissions and audits start to matter.
What I built
A risk queue instead of a risk score. Payment failures, credential attacks, API abuse and bonus farming open cases through deterministic rules. An analyst claims a case, reviews the evidence and releases, restricts or escalates it — always with a reason.
Risk Center: from wireframe to final UI
Credits that always reconcile. Subscription, purchased and bonus credits live in separate buckets with their own expiry rules. Every change writes an append-only ledger entry; a manual adjustment requires a reason and writes the audit record in the same transaction.
Permissions enforced on the server. Owner, Admin, Operations, Risk Analyst and Auditor share one typed permission registry used by navigation, buttons, services and APIs. Hiding a button is never treated as security — an Auditor's write request really returns 403.
Usable during an incident. Dense tables become summary cards on a phone instead of shrinking.
Technical highlights
- One guard for every write: session → named permission → rate-limit policy, before any domain service runs.
- Money in integer cents, with a product snapshot on every order so history survives price changes.
- Idempotent payment events: a replayed webhook returns the earlier result instead of charging, activating or granting twice.
- Exact restore: restricting an account snapshots its prior state; releasing restores exactly that state.
- Optimistic concurrency: two analysts acting on the same case get a 409, not a silent overwrite.
- Redacted security events: no passwords, cookies, tokens or card data are ever stored.
Quality & testing
| Layer | Result |
|---|---|
| Unit / integration (Vitest) | 27 files, 65 tests passing |
| Browser E2E (Playwright) | 6 tests passing on desktop Chromium and an iPhone 13 viewport |
| Security acceptance | SEC-001 – SEC-008 passing (401 / 403 / 429 paths, redaction, headers) |
| Risk acceptance | RISK-001 – RISK-009 passing (escalation, dedup, exact restore, session revocation) |
| Build | Lint, type check and production build passing |
Results & limits
- A complete, demonstrable console on a deterministic synthetic dataset: 200 users, 190 accounts, 240 orders, 12 risk cases, 90 days of metrics.
- Payments are simulated; there is no customer-facing checkout in this case.
- Not included: SSO, custom roles, 2FA, WAF / DDoS protection, real payment-webhook signature verification.
- Next: model-usage cost and margin tracking, the operating metric that matters most for AI products.
How I delivered it
| Step | What was produced |
|---|---|
| Requirements | Written spec: three identities, problems to solve, explicit out-of-scope list |
| Prototype | Page flow and wireframes for the dashboard, risk queue and case detail |
| UI | Design system: one action color, state colors with text labels, mobile rules |
| Architecture | Modular Next.js monolith + PostgreSQL, one guard for every write |
| Build | Spec-first with AI coding agents; each module had a written plan |
| Testing | Unit, integration, E2E, security and risk acceptance cases |
| Launch | Vercel + managed PostgreSQL with synthetic data reset daily (in progress) |
I write about this process in What it really takes to deliver a web product from requirements to launch.


