Canton Receivables is a private invoice financing MVP for SMEs, funders, and auditors. It combines AI invoice parsing and deterministic risk scoring with a Canton DevNet workflow that records the key financial state transitions as Daml contracts.
The app is designed as a demo-ready MVP: SQLite powers the operational UI, while Canton DevNet is used for the multi-party financial workflow.
- Upload a PDF invoice and extract invoice fields with Gemini.
- Generate a deterministic invoice risk score and suggested financing terms.
- Let an auditor verify, request correction, or reject extracted invoices.
- Let an SME list verified invoices for funding.
- Let a funder submit private financing offers.
- Let the SME accept or reject offers.
- Track funding, debtor payment, and final settlement.
- Show role-based visibility for SME, Funder, and Auditor.
- Mirror the critical workflow states in Canton DevNet contracts.
Invoice
-> VerifiedInvoice
-> FundingListing
-> FundingOffer
-> FundingAgreement
-> FundedInvoice
-> SettlementPending
-> SettledInvoice
In the UI, the same lifecycle is shown as an invoice status:
Draft
-> Extracted
-> Verified
-> Listed
-> Offer Submitted
-> Offer Accepted
-> Funded
-> Settlement Pending
-> Settled
SQLite keeps the current operational view for the dashboard. Canton keeps the multi-party contract state for the important transitions.
Invoice extraction uses the Gemini API through @google/genai.
The configured prompt asks Gemini to return structured JSON with:
- invoice number
- seller and buyer information
- issue date and due date
- payment terms
- currency and amounts
- tax and total validation
- IBAN or payment reference when available
- extraction confidence
- missing fields and warnings
- deterministic risk score
- risk grade
- suggested discount
- suggested advance amount
The scoring is intentionally deterministic: the same extracted invoice fields should produce the same score. The score is based on field completeness, date quality, amount consistency, payment terms, identity quality, and explicit risk penalties.
The current integration targets Canton DevNet through the Canton JSON API. For the hackathon demo, the app is configured to use the 5n sandbox validator endpoint:
https://ledger-api.validator.devnet.sandbox.fivenorth.io
Implemented Canton-backed actions:
- Create invoice contract when a new invoice is saved.
- Mark invoice as verified.
- Request invoice correction.
- Reject invoice.
- List invoice for funding.
- Submit funding offer.
- Accept or reject funding offer.
- Mark invoice as funded.
- Record debtor payment.
- Confirm final settlement.
- Query and archive invoice-related contracts in development tools.
When a Canton action fails, the local SQLite state is not updated. This keeps the demo flow aligned with the ledger-backed workflow.
LocalNet can still be used for development, but the demo path is DevNet-first.
SME: uploads invoices, lists verified invoices, accepts/rejects offers, records debtor payment, and views settlement.Funder: reviews listed invoices, submits offers, funds accepted agreements, and views funded/settled positions.Auditor: verifies extracted invoices, requests correction, rejects invoices, and confirms settlement.
- Next.js 16
- React 19
- TypeScript
- Tailwind CSS
- Radix UI / shadcn-style components
- SQLite with
better-sqlite3 - Gemini API via
@google/genai - Daml contracts built with SDK
3.4.11 - Canton JSON API against DevNet sandbox
apps/web Next.js app, API routes, SQLite repositories, Gemini and Canton clients
contracts Daml package for the Canton Receivables workflow
.local Local generated SQLite data, ignored by git
Copy .env.example to .env and configure the Canton/OAuth values.
For the DevNet sandbox:
CANTON_JSON_API_URL=https://ledger-api.validator.devnet.sandbox.fivenorth.io
CANTON_ACT_AS_PARTY=your-validator-local-party-id
KEYCLOAK_TOKEN_URL=https://auth.sandbox.fivenorth.io/application/o/token/
KEYCLOAK_CLIENT_ID=validator-devnet-m2m
KEYCLOAK_CLIENT_SECRET=your-devnet-client-secret
KEYCLOAK_GRANT_TYPE=client_credentials
KEYCLOAK_SCOPE=daml_ledger_api
KEYCLOAK_AUDIENCE=validator-devnet-m2m
CANTON_REQUEST_TIMEOUT_MS=30000
For LocalNet development:
CANTON_JSON_API_URL=http://127.0.0.1:2975
CANTON_ACT_AS_PARTY=
KEYCLOAK_TOKEN_URL=http://keycloak.localhost:8082/realms/AppUser/protocol/openid-connect/token
KEYCLOAK_CLIENT_ID=app-user-validator
KEYCLOAK_CLIENT_SECRET=your-client-secret
KEYCLOAK_GRANT_TYPE=client_credentials
KEYCLOAK_SCOPE=openid
KEYCLOAK_AUDIENCE=
Do not commit .env; only .env.example should be committed.
The Gemini API key is configured from the frontend. On first load, the app opens a setup dialog and validates the key before saving it locally in SQLite.
cd apps/web
npm install
npm run db:reset
npm run devUseful database commands:
npm run db:init
npm run db:seed
npm run db:resetcd contracts
dpm buildThe DAR is generated under:
contracts/.daml/dist/
The development Canton dialog in the app can upload the DAR to DevNet, query packages/templates, list parties, create test contracts, and archive demo contracts.
The MVP intentionally keeps some data in SQLite for speed and simplicity:
- dashboard invoice rows
- role-specific views
- local balances
- Gemini settings
- extracted invoice JSON
Canton is used for the core multi-party commitments and state transitions. The
important demo message is that the app is not just changing a local status
field: each meaningful financial step is also represented as a Daml contract
transition on Canton DevNet.