docs(readme): revise backend overview, add ingest/payment API docs and seed data details
This commit is contained in:
@@ -1,6 +1,6 @@
|
|||||||
# monk-code-screen
|
# monk-code-screen
|
||||||
|
|
||||||
Billing/subscriptions/invoicing backend skeleton. Bun + Hono + `bun:sqlite` (zero network deps at runtime; TS runs natively, no build step).
|
Billing/invoicing/payments backend skeleton. Bun + Hono + `bun:sqlite` (zero network deps at runtime; TS runs natively, no build step).
|
||||||
|
|
||||||
## ⚡ START COMMAND (sticky)
|
## ⚡ START COMMAND (sticky)
|
||||||
|
|
||||||
@@ -20,8 +20,10 @@ bunx tsc --noEmit # typecheck
|
|||||||
|
|
||||||
```
|
```
|
||||||
src/index.ts Hono app + routes
|
src/index.ts Hono app + routes
|
||||||
src/db.ts opens data.db, applies schema, seeds if empty (runs on import)
|
src/ingest.ts CSV parser/normalizer + transactional insert (shared by seed and POST /ingest)
|
||||||
src/schema.sql customers / plans / subscriptions / invoices (idempotent)
|
src/db.ts opens data.db, applies schema, seeds from invoices.csv if empty (runs on import)
|
||||||
|
src/schema.sql customers / invoices / payments (idempotent)
|
||||||
|
invoices.csv seed data — deliberately messy; DO NOT MODIFY
|
||||||
```
|
```
|
||||||
|
|
||||||
DB is `data.db` at repo root (WAL, FK on). Boot always re-applies schema, so adding a table = edit schema.sql + restart.
|
DB is `data.db` at repo root (WAL, FK on). Boot always re-applies schema, so adding a table = edit schema.sql + restart.
|
||||||
@@ -31,19 +33,29 @@ DB is `data.db` at repo root (WAL, FK on). Boot always re-applies schema, so add
|
|||||||
```sh
|
```sh
|
||||||
curl localhost:3000/health
|
curl localhost:3000/health
|
||||||
curl localhost:3000/customers
|
curl localhost:3000/customers
|
||||||
curl localhost:3000/customers/cus_ada/invoices
|
curl localhost:3000/customers/<cus_id>/invoices
|
||||||
curl -X POST localhost:3000/subscriptions \
|
curl localhost:3000/invoice/INV-1011 # by invoice_number or surrogate inv_ id
|
||||||
|
curl -X POST localhost:3000/ingest --data-binary @invoices.csv
|
||||||
|
curl -X POST localhost:3000/payment/INV-1011 \
|
||||||
-H 'content-type: application/json' \
|
-H 'content-type: application/json' \
|
||||||
-d '{"customer_id":"cus_grace","plan_id":"plan_basic"}'
|
-d "{\"idempotency_key\":\"$(uuidgen)\",\"amount\":1500000,\"currency\":\"USD\"}"
|
||||||
```
|
```
|
||||||
|
|
||||||
`POST /subscriptions` is the happy path: validates with zod, creates subscription + first invoice in one transaction, returns both (201).
|
- `POST /ingest`: CSV as raw request body. Rows failing validation (negative values, empty currency, missing/unparseable amount or dates) are still stored, with status `corrupted`. Returns `{ status, ingested, errors, rows }`. No idempotency — re-posting the same file appends duplicates.
|
||||||
|
- `GET /invoice/:id`: invoice (with status) + its payments (with PSP-owned status). Resolves surrogate id or invoice_number (newest wins on duplicate numbers).
|
||||||
|
- `POST /payment/:invoice_id`: zod-validated `{idempotency_key: uuid, amount, currency}`; only `open`/`partially_paid` invoices are payable (409 otherwise — includes `corrupted`), repeated key replays the original payment (200), else inserts a `pending` payment (201).
|
||||||
|
|
||||||
|
Money is stored as integer minor units (cents): CSV `150` → `15000`.
|
||||||
|
|
||||||
## Seed data
|
## Seed data
|
||||||
|
|
||||||
- customers: `cus_ada`, `cus_grace`
|
Seeded by ingesting `invoices.csv` (25 rows → 22 open + 3 corrupted, 21 customers):
|
||||||
- plans: `plan_basic` ($10/mo), `plan_pro` ($50/mo)
|
|
||||||
- `cus_ada` has an active `sub_1` on pro with open `inv_1`
|
- `INV-1006` (line 7): empty currency code
|
||||||
|
- `INV-1010`: negative quantity and amount
|
||||||
|
- `INV-1020`: missing amount
|
||||||
|
|
||||||
|
Duplicate invoice numbers (`INV-1001` ×2, `INV-1006` reused) are ingested as-is — not a validation rule on the canvas. Customers dedupe by email case-insensitively (`Acme Corp`/`ACME CORPORATION`/`acme corp` → one customer); rows without email get a customer keyed by name.
|
||||||
|
|
||||||
## Extension cheatsheet (interview)
|
## Extension cheatsheet (interview)
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user