Skip to content

Publish unified invoice API contract for FE and BE (#446) - #571

Open
woahwhattheheck wants to merge 10 commits into
Quittance-Labs:mainfrom
woahwhattheheck:latch/quittance-446-invoice-contract
Open

woahwhattheheck wants to merge 10 commits into
Quittance-Labs:mainfrom
woahwhattheheck:latch/quittance-446-invoice-contract

Conversation

@woahwhattheheck

@woahwhattheheck woahwhattheheck commented Sep 24, 2026 •

Copy link
Copy Markdown
Contributor

Summary

Closes #446.

Publishes a complete invoice API contract (create, get, list, cancel, verify, stats) as shared schemas with TypeScript types used by Express handlers and the Next.js client. Invalid create payloads fail consistently on both sides; list, detail, and verify share response types while the runtime client tolerates response-shape drift; strict shared parsers are exercised by contract tests.

Done when

  • Invalid create payloads fail consistently on client and server
  • List, detail, and verify share response types; strict shared parsers remain covered by contract tests while the runtime client stays tolerant
  • Contract tests run with the same commands used locally for CI
  • No parallel invoice type system remains in app code

Changes

  • shared/invoice-contract.ts — request/response shapes, shared error envelope (ApiSuccess / ApiFailure / ValidationFailureBody), runtime parsers for create/get/list/payment-info/cancel/verify/stats, and INVOICE_OPENAPI_SPEC
  • shared/invoice.ts — settlement fields and typed late-payment / settlement enums on InvoiceDto / PublicInvoiceDto
  • backend/src/types/api.ts — re-exports envelopes from the shared contract; Express send helpers stay backend-only
  • frontend/lib/api.ts — invoiceApi methods use shared response types and tolerate runtime response-shape drift; strict shared parsers remain in contract tests
  • InvoiceCard / pay-page.types / export / payment-page-state — drop parallel invoice interfaces in favor of InvoiceDto
  • backend/tests/invoice-contract.test.ts + tests/invoice-contract.test.mjs — fail when required fields diverge; exercise the in-memory MVP router

Test plan

  • cd backend && node --import tsx --test tests/invoice-contract.test.ts tests/create-invoice-validation.test.ts tests/shared-verification-contract.test.ts — pass
  • node --test tests/invoice-contract.test.mjs — pass
  • cd frontend && node --test tests/invoice-form-validation.test.js tests/api-runtime.test.js — pass
  • npm run typecheck (backend + frontend) — clean
  • In-memory MVP create / list / get / payment-info / stats / cancel paths remain compatible

Original-contributor compensation claim

/claim #446

I am the original author of this existing PR implementing issue #446. I request sponsor review, acceptance and consideration for any eligible GrantFox OSS reward under its current campaign terms. If accepted and eligible, please confirm the approved amount (if any), allocation, payment mechanism and payment date. This is an affirmative contributor claim, not a representation that a Maybe Rewarded listing guarantees payment.

Closes Quittance-Labs#446.

Share create/get/list/cancel/verify/stats request and response shapes plus
the error envelope in shared/invoice-contract.ts, with runtime parsers and
an OpenAPI 3.0.3 document. Express re-exports the envelopes; the Next.js
client validates responses through the same parsers so invalid creates fail
consistently on both sides and list/detail/verify/stats stay aligned.
Require complete numeric statistics and preserve explicit zero counts. Reject malformed success envelopes, unsafe or negative counts, non-finite or nonnumeric revenue, and missing or ambiguous storage rows. Keep raw object and canonical one-row responses compatible; publish their numeric bounds and wire shapes in OpenAPI.

Validation: 66 focused backend checks, 21 shared checks, and 15 frontend checks pass. Backend typecheck and TypeScript 5.9.3 scoped frontend API-client check pass. The same contract file on exact parent reports 16 pass / 19 fail. Actual invoiceApi.getStats with locked Axios 1.12.2 completes 18 local HTTP cases: parent has 13 mismatches, candidate none; real Express/memory empty, pending, and paid controls pass both.

Boundary: no full frontend build/typecheck, browser, wallet, external Horizon, or PostgreSQL runtime rerun. No dependency or storage behavior changes.
@CemAyyildiz

Copy link
Copy Markdown
Collaborator

I reviewed this locally. The shared contract is a good direction, but six frontend tests fail on this branch as submitted, before any merge with main:

  • pay-return.test.js: a valid ?tx= return starts verification without a paste, a malformed ?tx= is ignored…, a stored session hash shows the resume copy… and a stored session for another invoice is not applied. The pay page never renders ("no payment result region rendered", "verify input missing").
  • wallet-session-surfaces.test.js: switching accounts replaces the previous seller rows… and disconnecting hides the seller rows…. The dashboard shows "Quittance API is unreachable".

The cause is in frontend/lib/api.ts: every call now runs a strict parse*Response and throws when the payload doesn't match the contract. Any response with a missing or extra-typed field takes the whole pay page or dashboard down, which is what the fixtures hit. In production that's risky too: a backend that's one field ahead or behind breaks payment. Consider validating in tests and CI (the contract tests you added are great for that) and keeping the runtime client tolerant, or at least logging instead of throwing.

main has also moved on since this was opened. The pay-link artifact (#557) added copyValue and networkPassphrase to the create and payment-info payloads, and InvoiceCard, pay-page.types and export.ts now conflict. Please rebase, add those two fields to CreateInvoiceResult, PaymentInfoResult, both parsers and the OpenAPI schemas, and get npm test in frontend/ green.

woahwhattheheck and others added 8 commits October 4, 2026 08:32
Merge current main without rewriting the submitted branch. Keep shared
contract validation strict in tests, retain typed tolerant client responses,
preserve existing seller-statistics row normalization, and carry pay-link
copyValue/networkPassphrase through types, parsers and OpenAPI.

Preserve current-main request correlation, search, canonical proof delivery
and payment-session types. Add artifact-contract and partial-stats coverage.
Reject malformed pagination objects and require nonnegative safe-integer limit, offset, and total values in the strict shared parser. Reflect that contract in the OpenAPI schema. Continue normalizing legacy responses that omit pagination, and preserve the frontend client's tolerant runtime behavior.

Two focused contract cases pass; the parent source reproduces acceptance of malformed supplied metadata.
Use one shared discriminant guard for create, get, list, payment-info, cancel and verify responses. A supplied success value must be literal true; supported legacy payloads without the flag retain their existing normalization. Preserve the stats and pagination repairs, OpenAPI document and all payment/identity logic.

Two focused regression tests execute against the complete production contract and its real invoice-validation dependency, transpiled with TypeScript 5.8.3 and run with Node 22.16.0. Original: 1 passed, 1 failed. Patched: 2 passed, 0 failed. This is not a full backend/frontend typecheck, HTTP integration or database run.
Make the existing bare-array normalization reachable while retaining the strict supplied-success discriminant and pagination validation. Empty and populated lists use the same envelope parser; invalid invoice items remain rejected.

Extend the existing compatibility case with the list exception, preserving empty-array rejection for all other response parsers. No new test file, transport gate or workflow enters the product branch. Preserve the documentation-only successor 9577eeb as parent.

Run https://github.com/woahwhattheheck/Quittance0/actions/runs/37207722383 executed both existing response-contract tests against the complete production contract and real validation dependency using native type stripping and temporary ESM import-path adaptation: 2 passed, 0 failed. Paired legacy-list cases improved 0/2 to 2/2, with five pagination/error/item controls retained. This is not a project typecheck, HTTP/database check or full repository suite. Checked source blob fc9ac68; test blob 9474aa1.
@woahwhattheheck

Copy link
Copy Markdown
Contributor Author

@CemAyyildiz Your October 3 findings have been addressed in this original branch, now at 34c3c299a4e1ba46e986bc5b3e2e25aef9b62057 and mergeable with main@282c75ad933651a93b89a8412ab649b48714c739.

  • frontend/lib/api.ts keeps shared response types and returns tolerant runtime responses instead of invoking strict response parsers on each request. Strict parser validation remains in the contract tests. The PR description now reflects this runtime behavior.
  • copyValue and networkPassphrase are present in the create/payment-info result types, parsers and OpenAPI schemas.
  • The current-main invoice, proof and payment-session changes were preserved during integration.

The committed review-resolution record retains the implementation revision's passing project commands and later focused results. It separately records the failed clean-worktree wrappers; those workflows are not being represented as green CI. No checks were rerun for this follow-up.

Please re-review this head and identify any concrete remaining requirement. I maintain my original #446 contribution claim and request payment; please confirm acceptance, the compensation amount and settlement route.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Unified invoice DTO and OpenAPI-style contract published for FE and BE

2 participants