feat: Invoice Model & Core CRUD API (/invoices)#17
Conversation
|
Hello @josephchimebuka |
|
Alright I am on it and I am fixing CI now |
Expands the Invoice model and adds the merchant-facing CRUD endpoints described in ShadeProtocol#12. All routes are protected and scoped to the authenticated merchant. - POST /invoices: create an invoice with a unique, url-safe paymentSlug; status DRAFT when isDraft is true, otherwise PENDING - GET /invoices: paginated list (limit default 20, max 100) with status, token, startDate and endDate filters, scoped to the merchant - GET /invoices/:id: fetch a single invoice, 404 when missing or owned by another merchant - PATCH /invoices/:id/void: cancel a PENDING invoice, 400 otherwise - Validate positive integer amount and non-empty token - Expand Invoice schema (paymentSlug, description, payerEmail, expiresAt, status default, DRAFT enum value) and serialize BigInt amount as string - Add unit and integration tests covering all acceptance criteria Stacked on top of ShadeProtocol#16 (merchant auth middleware). Co-authored-by: Cursor <cursoragent@cursor.com>
05efc2a to
b2440c8
Compare
|
Important Review skippedAuto reviews are disabled on base/target branches other than the default branch. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Plus Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
✨ Finishing Touches🧪 Generate unit tests (beta)
Comment |
codebestia
left a comment
There was a problem hiding this comment.
LGTM!
Nice Implementation
Thank you for your contribution.
Summary
Implements the invoice model expansion and merchant-facing CRUD described in #12: create, list, get, and void — all protected and scoped to the authenticated merchant.
Note
Rebased onto the latest
main(now includes the merged merchant-auth work from #16 and the auth-verify endpoint from #15). No conflicts; the diff is limited to invoice files.Endpoints
All routes live under
/api/v1/invoicesand require a valid merchant session (Authorization: Bearer <token>).POST/invoicesGET/invoicesGET/invoices/:idPATCH/invoices/:id/voidPENDINGinvoiceCreate
Accepts
description,amount,token, and optionalpayerEmail,expiresAt,isDraft. A unique, url-safepaymentSlug(base64url) is generated server-side. Status isDRAFTwhenisDraft: true, otherwisePENDING.List
Query filters:
status,token,startDate,endDate. Pagination:limit(default 20, clamped to max 100) andoffset. Returns{ data, pagination: { limit, offset, total } }, scoped to the authenticated merchant and ordered newest-first.Changes
prisma/schema.prisma): addsDRAFTto theStatusenum and expandsInvoicewithpaymentSlug(@unique),description,payerEmail,expiresAt, astatusdefault ofPENDING, makesinvoiceId/refoptional, and indexesmerchantId/(merchantId, status).src/services/invoice.services.ts):createInvoice,listInvoices,getInvoice,voidInvoice. Ownership is enforced viamerchantIdon every query.sanitizeInvoiceserializes theBigIntamount to a string (JSON-safe). Slug creation retries on the rare unique-constraint collision.src/utils/invoice.validation.ts): positive-integer amount parsing, non-empty token, optional email/date checks, and list-query parsing with limit clamping.src/utils/slug.ts): base64url payment slug generator (~96 bits of entropy).src/controllers/invoice.controllers.ts,src/routes/invoice.routes.ts): wired under/invoicesbehindauthenticateMerchantand mounted insrc/routes/index.ts.Important
@prisma/clientis imported type-only throughout the invoice modules (enum/status handled via local string constants, Prisma error detection via duck-typing oncode === 'P2002'). This keeps the test runtime free of the generated client — consistent with the project's fully-mocked Jest setup, where CI does not runprisma generate.Acceptance criteria
POST /invoicesreturns201with the created invoice includingpaymentSlugpaymentSlugis unique and url-safeisDraft: truecreates an invoice withstatus = DRAFTGET /invoicesreturns a paginated list scoped to the authenticated merchantGET /invoices/:idreturns404when the invoice doesn't exist or belongs to another merchantPATCH /invoices/:id/voidon a non-PENDINGinvoice →400Test plan
npm test— full suite green (8 suites, including the merchant/auth suites already onmainand the new invoice suites). New tests:tests/unit/invoice.services.test.tsandtests/integration/invoice.routes.test.tscover each acceptance criterion (auth, create PENDING/DRAFT, validation, scoped list + filter + limit clamping, 404 ownership, void rules).prettier --check "src/**"— passes.Notes / follow-ups
Invoiceschema changed, so a migration must be generated and applied (npm run prisma:migrate) in environments with a database. No migration files are committed because the repo has no migrations baseline yet.PENDING→PAID, settinginvoiceId/datePaid) is out of scope here and left for a follow-up.Closes #12
Summary by CodeRabbit
New Features
Bug Fixes