MicroAI Paygate
An x402-style payment gateway for AI requests. Sign EIP-712, get a signed receipt, verify the signature client-side.
Fotografiază bonul, restul îl facem noi.
Translation pending
FinanceTypeScript · JavaScriptGitHub @madalincmcOnlineOwner unconfirmed
Have you tried it? One tap is enough. No sign-in needed.
No updates detected yet. GitHub releases will appear here.
Owners of public repositories under a personal GitHub account can sign in to verify ownership and manage the introduction themselves. No administrator approval is needed.
Sign in, generate a verification file, and commit it to this repository’s default branch within 30 minutes. Files on other branches or forks do not count. Then check the file below to manage the project without administrator approval. Keep the file to retain your access.
If needed, you will go to sign-in. After signing in, return here and press your chosen verification button again.
Run /nomorevibe verify in your project folder to verify ownership and manage the introduction yourself.
Verify in one minuteAsk about other verification options
The maker's introduction consists of the line above. Read the repository README for more details. Repository README ↗
Loading the full README.
Read the full README ↗Fotografiază bonul, restul îl facem noi.
Track spending by photographing receipts — no manual entry. Snap a photo, OCR pulls out the merchant, amount and date, and the spend categorizes itself. Cash goes in by hand, rent adds itself on a schedule, and a whole household pools into one set of totals.
Try the demo — invented data, no account, nothing to sign up for.
[!NOTE] The product UI is in Romanian and amounts are in lei (RON), as the screenshots show. Code, comments and docs are in English.
The accent hue and the warmth of the neutrals move together; every surface keeps the same lightness, so a screen tuned in one palette is tuned in all four. Category colours deliberately don't move — they identify a category, and repainting Transport because you changed accent would break what you already learned.
| Layer | What |
|---|---|
| Frontend | Next.js 16 (App Router) · React 19 · TypeScript (strict) · Tailwind CSS v4 · shadcn/ui on Base UI |
| Backend | Supabase — Postgres, Auth, Storage — with row-level security on every table |
| Scheduling | pg_cron inside Postgres: a nightly sweep mints due recurring expenses, so no service-role key is needed to run it |
| OCR | Google Vision API |
| Resend — household invitations; the send sits behind one module, so the relay is swappable | |
| Payments | Stripe — env vars are scaffolded (see below), but billing isn't wired into the product yet |
| Hosting | Vercel — preview per PR, production on merge to main |
The 12-month analysis calls a monthly_spending RPC that groups and sums in the database. A
year of a shared household's expenses is exactly the set that should never be pulled into a
serverless function to be added up there.
Household visibility, budget scoping and every read path go through row-level security policies. The UI filters are a convenience on top; removing them wouldn't leak a row. Five end-to-end scripts (113 checks) exercise those policies against a real project — see Testing.
The eight category hues are validated as a categorical palette: worst adjacent pair ΔE 12.9
(light) / 10.5 (dark) under simulated deuteranopia, against a target of 8. Every -ink step
clears 4.5:1 as text on its own surface. Accents sit at a fixed lightness with chroma fitted to
the sRGB gamut per hue, which is what keeps white-on-accent readable in all four palettes.
Status colours (within / near / over budget) are reserved and never reused for a category — binding "within budget" to the accent would have made the Apus palette read good → warning → over as coral → amber → red, three warm hues with no severity gradient between them.
An inline script in <head> resolves the stored preference and writes data-theme and
data-palette onto <html> before the browser paints, so reopening the app never flashes the
wrong colours. "System" is a preference, never a rendered state — the DOM only ever carries a
concrete light or dark, so CSS never has to express "follow the OS".
Every chart renders a visually hidden table of the same values alongside the plot, and columns
are reachable with the arrow keys. All motion is switched off wholesale under
prefers-reduced-motion.
app/ # App Router routes (dashboard, history, household, recurring, budgets, analytics, receipts)
app/globals.css # Design tokens: the four palettes, category + status colours, motion primitives
components/ # UI components — shadcn/ui primitives in components/ui, plots in components/charts
lib/ # Supabase clients, OCR, categorization, dashboard aggregation, email, theme
__tests__/ # Vitest unit tests for the pure date/money/parsing logic
e2e/ # Playwright browser tests
supabase/migrations/ # SQL: schema, RLS policies, pg_cron job, aggregate RPC
scripts/ # Migration runner and end-to-end RLS/recurring checks
types/ # Shared TypeScript types
docs/screenshots/ # Images used in this README
npm install
Copy the example file and fill in the values:
cp .env.example .env.local
| Variable | Description |
|---|---|
NEXT_PUBLIC_SITE_URL | Base URL of the app (http://localhost:3000 locally) |
NEXT_PUBLIC_SUPABASE_URL | Supabase project URL |
NEXT_PUBLIC_SUPABASE_ANON_KEY | Supabase anon/public key |
SUPABASE_SERVICE_ROLE_KEY | Supabase service role key (server-side only, never expose to the client) |
POSTGRES_URL_NON_POOLING | Direct (non-pooled) Postgres connection string, used by npm run db:migrate and the test scripts |
GOOGLE_CLIENT_ID / GOOGLE_CLIENT_SECRET | Google OAuth credentials, wired into Supabase Auth's Google provider |
GOOGLE_VISION_CREDENTIALS_BASE64 | Base64-encoded Google Cloud service account JSON, used for receipt OCR |
RESEND_API_KEY / EMAIL_FROM | Invitation email. Leave unset and invitations are still created — only the email is skipped, and the screen says so |
STRIPE_SECRET_KEY / NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY | Stripe API keys |
STRIPE_WEBHOOK_SECRET | Signing secret for the Stripe webhook endpoint |
STRIPE_PRICE_ID | Price ID for the SnapBudget subscription plan |
Run each file in supabase/migrations/ in filename order:
npm run db:migrate supabase/migrations/20260803140010_create_receipts.sql
npm run dev
Open http://localhost:3000.
npm run build # Production build
npm run start # Serve the production build
npm run lint # ESLint
npm test # Vitest — unit tests, single run
npm run test:watch # Vitest in watch mode
npm run test:e2e # Playwright — browser end-to-end tests
npm run format # Prettier — write
npm run format:check # Prettier — check only
npm run db:migrate <f> # Apply a single SQL migration
npm test covers the pure logic — period bucketing and comparison, budget pace, the 12-month
analytics, receipt parsing, date-range resolution, batch queueing, invitation expiry and email
canonicalisation — 243 tests, no database needed. CI runs it on every pull request alongside
format, lint, types and a full build.
Five end-to-end scripts (113 checks) exercise the database rules against a real Supabase project, driven through the Data API as real signed-in users — so a policy mistake surfaces here rather than in production. All create and delete their own temporary users, so they're safe to re-run:
npx dotenv -e .env.local -- node scripts/test-household-flow.mjs # sharing, invitations + RLS
npx dotenv -e .env.local -- node scripts/test-household-spending-flow.mjs # pooled totals per member
npx dotenv -e .env.local -- node scripts/test-recurring-flow.mjs # recurring date maths + generation
npx dotenv -e .env.local -- node scripts/test-budget-flow.mjs # budget scoping, RLS + unique indexes
npx dotenv -e .env.local -- node scripts/test-analytics-flow.mjs # monthly_spending RPC: sums, bounds, RLS
[!IMPORTANT] These stay out of CI on purpose: they need the service-role key and write to a live database, and on a public repository that key would have to live in Actions secrets. Run them by hand before merging anything that touches
supabase/migrations/.
npm run test:e2e drives the real app in a real browser with Playwright
— starting logged out, signing in, and clicking through the app the way a user would. Real Google
sign-in can't be scripted (no password gets typed on its behalf), so login is seeded instead: the
test asks Supabase's admin API for a magic-link token for a dedicated e2e-playwright@ account and
redeems it at app/api/test/login, which 404s outside next dev and only ever exists locally. That
route calls the same auth.verifyOtp a clicked email link would, through the app's own cookie
plumbing, so the resulting session is real — everything downstream of login runs unmodified. The
test account is wiped of its own data after every run and never touches a real user's.
Same live-database caveat as the RLS scripts above — needs SUPABASE_SERVICE_ROLE_KEY, stays out
of CI, run by hand:
npm run test:e2e
Production is snapbudget.space, on Vercel and
connected to this repository. Every pull request gets a preview deployment; merges to main deploy
to production. The apex redirects to www, which is the canonical host.
Environment variables must be configured in the Vercel project settings (Production, Preview, and Development) to match .env.example.
[!IMPORTANT] Preview deployments share the production database — they are not an isolated environment. Anything written while testing a pull request is written for real.
[!NOTE] The host appears outside this repo as well, and nothing here checks that the copies agree: Supabase Auth's Site URL and redirect allow-list,
NEXT_PUBLIC_SITE_URL, and the Resend sending domain. Changing the domain without updating all of them fails quietly — an unlisted redirect sends sign-ins back to the old host, and a missing site URL creates invitations that send no email.
redirectTo is not allow-listed, which sends sign-ins to the wrong host and reads as a
button that did nothing.pg_cron extension and schedule the nightly snapbudget-generate-recurring sweep that mints due recurring expenses.main is protected — changes land via pull request. Every PR gets a preview deployment and a
full CI run; merging to main deploys to production.