Live demo: https://applycoding.github.io/ai-receipt-tracker/ (demo mode, runs in your browser, no account or key needed)
Paste the text of a shop receipt and get structured data back: merchant, date, currency, line items, tax, total and a spending category. Receipts roll up into monthly totals per category and export to CSV. A free plan holds 10 receipts; the Pro upgrade goes through Stripe Checkout in test mode only.
This is a portfolio demo by Arpan Bom. It is not a production product: storage is in memory, the default receipt reader is a deterministic parser (the "mock LLM provider"), and no real payments can be taken.
| Path | What it is |
|---|---|
packages/core |
Shared TypeScript: receipt types, extractor, summaries, CSV, sample receipts |
apps/api |
TypeScript API on Hono: email and password auth, receipts CRUD, monthly summary, CSV export, Stripe test-mode checkout and signed webhook |
apps/web |
Next.js 14 app, statically exported. Demo mode keeps data in localStorage; set NEXT_PUBLIC_API_URL to use the API |
apps/mobile |
Flutter app (iOS and Android): sign in, add receipt with live preview, list and totals. Offline demo by default; --dart-define=API_URL=... to use the API |
flowchart LR
subgraph Clients
W[Next.js web<br/>static export]
M[Flutter app]
end
W -- "demo mode" --> C[(packages/core<br/>extractor + summaries)]
W -- "NEXT_PUBLIC_API_URL" --> A
M -- "API_URL" --> A
M -- "offline" --> D[Dart extractor port]
A[Hono API<br/>auth, receipts, billing] --> X{ReceiptExtractor}
X -- default --> C
X -- LLM_PROVIDER=openai --> O[OpenAI-compatible<br/>chat completions<br/>JSON + zod validation]
O -. "error / bad JSON" .-> C
A --> S[(Store<br/>in-memory adapter)]
A --> B{Billing}
B -- default --> MB[Mock billing]
B -- "sk_test_ key" --> ST[Stripe Checkout<br/>TEST MODE]
ST -- "signed webhook" --> A
More detail in docs/architecture.md.
npm install
npm test # core + API tests, no network
npm run dev:api # http://localhost:8787, mock LLM and mock billing
npm run dev:web # http://localhost:3000, demo mode
NEXT_PUBLIC_API_URL=http://localhost:8787 npm run dev:web # web against the API
cd apps/mobile && flutter test && flutter runOptional settings are in .env.example. With no settings everything runs offline.
Set STRIPE_SECRET_KEY=sk_test_..., STRIPE_PRICE_ID and STRIPE_WEBHOOK_SECRET, then forward webhooks with stripe listen --forward-to localhost:8787/billing/webhook. Pay with the test card 4242 4242 4242 4242. The API refuses to start with any key that is not sk_test_, and the webhook rejects live-mode events.
LLM_PROVIDER=openai with OPENAI_API_KEY sends the receipt text to a chat completions endpoint with JSON output. The response is validated against a schema; if the call fails or the JSON is wrong the API falls back to the deterministic parser and marks the provider as mock (fallback).
| Method | Path | Notes |
|---|---|---|
| GET | /health |
Shows which LLM and billing providers are active |
| POST | /auth/register, /auth/login |
{email, password} returns a session token |
| GET | /me |
Current user and plan |
| POST | /receipts |
{text}: extract and save; 402 when the free limit is reached |
| GET | /receipts, /receipts/summary, /receipts/export.csv |
List, monthly totals, CSV |
| DELETE | /receipts/:id |
Only the owner can delete |
| POST | /billing/checkout |
Stripe Checkout (test) or mock URL |
| POST | /billing/webhook |
Verifies Stripe-Signature, upgrades on checkout.session.completed |
GitHub Actions runs the TypeScript typecheck and tests, builds the static web app, runs flutter analyze and flutter test, and publishes the web demo to GitHub Pages from main.
MIT