Moneymind documentation
Run it, point it at your own accounts, ship it under your own brand. Written against version 1.1.0 of the download you get on CodeCanyon.
- Version v1.1.0
- Updated Aug 2026
- Platform Flutter
- Stack Flutter · Firebase
On this page
Introduction
AI-powered personal finance app — Flutter + Firebase.
Moneymind is an AI-powered personal finance app shipped as production-grade source code: one Flutter mobile app (apps/mobile) plus a Firebase Cloud Functions backend (apps/functions). It is single-user personal finance — one account tracks their own money. It is not multi-user, family-sharing, or a marketplace.
The differentiator is that the AI is actually functional, not a UI mockup. Receipt scanning, a finance coach, forecasting, smart categorization, anomaly detection, a health score, and budget/savings generators all run for real — through a dual OpenAI + Gemini backend, switchable at runtime via Remote Config. Every model call is server-side, so no AI API keys ever ship in the APK/IPA.
Architecture is offline-first: the on-device Drift/SQLCipher database is the source of truth, and Firestore is used for reconciliation/sync — so the core app works with no network, and AI entry points degrade gracefully when offline.
What’s included
- Full Flutter source (
apps/mobile) + Firebase Functions source (apps/functions) - Drift (SQLite + SQLCipher) schema & migrations, with the mirrored Firestore schema, security rules, and the one declared composite index
- Warm Minimalist design system — Material 3
ColorScheme, Sora type scale, shape + spacing tokens; light, dark, and system modes - 18 Cloud Functions — 9 AI callables, 2 subscription callables, FX rate, RevenueCat webhook + entitlement refresh, 3 schedulers, 1 auth trigger
- Next.js admin panel (
apps/admin) — an optional operations console: users, AI spend, a validated Remote Config editor, billing, system health, audit log, and access control (see Admin panel) - Server-side AI quota + rate limiting with a premium bypass
- RevenueCat subscription stack + AdMob (banner + interstitial), both Remote-Config-gated
- App lock (PIN + biometric + auto-lock) and local notifications
- Google + Apple sign-in (Apple is iOS-only)
- This documentation + the RevenueCat setup guide (
docs/REVENUECAT_SETUP.md) - 6 months email support, lifetime updates
Features
Money management
- Multi-wallet (cash / bank / card), each with its own currency
- Transactions (income / expense / transfer) kept atomic with the wallet balance in a single DB transaction
- Multi-currency with a per-transaction FX rate — live via the
getFxRatecallable, with a manual-entry fallback offline - Categories with custom icons/colors; budgets with progress + threshold colour buckets; recurring rules auto-posted by a daily scheduler
- Debts (lent / borrowed, payments, settle) and savings goals (contributions, completion); reports via
fl_chart
AI features (server-side, functional)
- Receipt scanner —
scanReceipt(GPT-4o Vision) extracts merchant, amount, date, line items from a photo - Smart categorization —
suggestCategory - Finance coach chat —
aiCoachChat(RAG over the user’s own data) - Forecast, anomaly detection, health score —
forecastSpending,detectAnomalies,computeHealthScore - Weekly recap, AI budget generator, savings plan —
generateWeeklyRecap,generateBudgetPlan(bulk-creates budgets),generateSavingsPlan - Subscription detector —
detectSubscriptions+confirmSubscription(90-day pattern match, no LLM)
Monetization
- RevenueCat subscriptions — paywall, restore, entitlement sync via the webhook (authoritative) + a client fast-path
- AdMob banner (Home) + interstitial (every Nth transaction add)
- Free-tier ceilings — wallets & monthly transactions enforced on the client, AI enforced on the server; premium bypasses all of them
Operations (admin panel)
- Optional Next.js console (
apps/admin) — user growth, AI spend, billing, system health, audit log (see Admin panel) - Validated Remote Config editor — typed values, drift detection against what the code actually reads, and an ETag-guarded publish so a concurrent console edit cannot be clobbered
- Gated by a Firebase Auth custom claim; all reads are server-side, so your security rules stay unchanged
- Demo mode makes it read-only for screenshots or show-and-tell
Security and polish
- App lock — salted SHA-256 PIN in secure storage, biometric via
local_auth, configurable auto-lock - Local notifications — budget / recurring / debt / goal thresholds + a weekly recap
- Settings — profile, JSON backup/export, reset data, about; full light/dark theming; Google + Apple sign-in
Backend (Firebase Functions, asia-southeast1)
- Every AI callable follows auth → rate-limit → quota → provider → JSON-validate
- Prompts centralized in
src/prompts/; dual provider behind one interface insrc/providers/ - Schedulers:
processRecurringRules(daily),resetMonthlyAIQuotas(monthly GC); the RevenueCat webhook is the authoritative entitlement writer
Architecture
money-mind/
apps/
mobile/ Flutter app (Riverpod 3 + GoRouter + Drift) ← v1 deliverable
functions/ Firebase Cloud Functions (Node 22 + TypeScript)
admin/ Next.js admin ops console (optional — see docs/ADMIN_SETUP.md)
docs/ REVENUECAT_SETUP.md, codecanyon/
firebase.json firestore.rules firestore.indexes.json storage.rules
pnpm-workspace.yamlFeature-first folders (lib/features/<area>/{presentation,providers}). The Drift schema is mirrored to Firestore across 9 synced tables. JS/TS uses pnpm workspaces (pnpm -F functions …); the Flutter app is driven directly by the Flutter/Dart CLI from apps/mobile.
Firebase project setup
This page is the full reference — the sections from here through Production checklist walk through Firebase, secrets, Remote Config, monetization, and release in order. Monetization specifics also live in docs/REVENUECAT_SETUP.md.
flutterfire configure against your own Firebase project before the app will build.This is the buyer’s biggest task. The repo ships only *.example templates for the Firebase config — the real files are gitignored, so you wire your own project.
- 01Create a Firebase project. Enable Authentication (Email/Password + Google/Apple — see Social sign-in), Firestore, Storage, Remote Config, and Functions (requires the Blaze plan).
- 02
Register an Android app (
com.devsnack.moneymindor your own id) and an iOS app, then fromapps/mobilerun:terminaldart pub global activate flutterfire_cli flutterfire configureThis generates
lib/firebase_options.dartand dropsandroid/app/google-services.json+ios/Runner/GoogleService-Info.plistfor your project. - 03
Deploy rules + indexes + storage rules:
terminalfirebase deploy --only firestore:rules,firestore:indexes,storage - 04Set the Function secrets (see Cloud Functions and secrets) and deploy Functions.
AIza… key, app id, project id) is safe to embed in client apps — protection comes from your Firestore/Storage rules + App Check, not from hiding the key. But each buyer must use their own project, so these files are gitignored and only *.example templates ship.Cloud Functions and secrets
Set the five secrets, then deploy. Functions are pinned to asia-southeast1; first deploys take a few minutes.
firebase functions:secrets:set OPENAI_API_KEY
firebase functions:secrets:set GEMINI_API_KEY
firebase functions:secrets:set EXCHANGERATE_API_KEY # free key: exchangerate-api.com
firebase functions:secrets:set REVENUECAT_WEBHOOK_SECRET # self-generated
firebase functions:secrets:set REVENUECAT_API_KEY # RevenueCat REST key
firebase deploy --only functionsusers/{uid} doc on sign-uprecurringRules docadminStatsMonthly, then garbage-collects old aiUsage docsadminStatsDaily snapshot the admin panel readsRemote Config keys
Publish these in the Firebase console. The client registers in-app defaults in remote_config_init.dart, but the console values win.
openai|gemini)revenuecat_api_key_* → the paywall shows “not configured”; empty AdMob ids → ads stay hidden. Publish the RevenueCat keys before testing purchases.AI provider setup
Moneymind supports OpenAI and Gemini behind one interface. Set whichever key(s) you have:
firebase functions:secrets:set OPENAI_API_KEY
firebase functions:secrets:set GEMINI_API_KEY- Per-feature default: vision (receipt scan) uses
gpt-4o; every other AI feature defaults togemini-2.5-flash-lite(cheap/fast) withgpt-4o-minias the OpenAI fallback. - Global override: Remote Config
ai_provider="openai"or"gemini". - Quota: monthly counters at
users/{uid}/aiUsage/{yyyy-MM}, one shared ceiling fromfree_tier_ai_quota(default 5). Premium users bypass the cap entirely.
Monetization (RevenueCat and AdMob)
Full RevenueCat walkthrough: docs/REVENUECAT_SETUP.md. In short, RevenueCat needs three credentials:
- Per-platform public SDK keys → Remote Config
revenuecat_api_key_ios/_android. - Secret REST key → Functions secret
REVENUECAT_API_KEY. - Self-generated webhook secret →
REVENUECAT_WEBHOOK_SECRET, with the webhook URLhttps://asia-southeast1-<project>.cloudfunctions.net/handleRevenueCatWebhook.
The entitlement identifier must be exactly premium, and your products must be attached to it. Use RevenueCat’s Test Store to validate purchases without store setup.
AdMob: create an AdMob app, put your banner/interstitial unit ids into Remote Config (Google’s test ids are fine for dev), and ads_enabled is the global kill switch. Both surfaces hide for premium users.
Admin panel (optional)
apps/admin is a Next.js 16 operations console for your deployment. It is entirely optional — the mobile app works without it — but it is where you see how many users signed up, what your AI features are costing, whether RevenueCat is reaching you, and why a given user is hitting a limit. It also gives you a validated editor for the Remote Config keys (see Remote Config keys), which beats hand-editing strings in the Firebase console.
firestore.rules stays strictly owner-scoped exactly as it ships. Running the panel does not widen what a mobile client can reach, and the browser never gets a Firestore handle.How access works
Access is a Firebase Auth custom claim (admin: true) — not a password and not an email list. An ordinary Moneymind user has a valid Firebase account and still cannot get in. Email/password is the only sign-in method, and there is deliberately no sign-up screen, so the first admin is created from a terminal.
Setup
Requires the Blaze plan (already needed for Cloud Functions) and firebase-tools 14.4+.
- 01Register a Web app in Firebase console → Project settings → Your apps, then copy its config into
apps/admin/.env.local(start from.env.example). - 02Enable Email/Password under Authentication → Sign-in method. It is the panel’s only sign-in method.
- 03
Create your admin account and grant the claim:
terminalpnpm install gcloud auth application-default login pnpm -F admin grant-admin you@example.com --create
That creates the Firebase Auth user, sets the claim, and prints a generated password once. Pass --password='…' to choose your own. Then run it locally:
pnpm -F admin dev # http://localhost:3000Deploy
The panel is a standard Next.js app. Vercel is the documented target — it resolves pnpm workspaces natively. Import your repo, then set, under Settings → Build & Deployment:
apps/adminSet the four NEXT_PUBLIC_FIREBASE_* values plus FIREBASE_SERVICE_ACCOUNT_JSON under Settings → Environment Variables. Nothing configuration-related is committed, which is why none of it lives in a file. Pick the function region nearest your Firestore data — asia-southeast1 pairs with Vercel’s Singapore region.
FIREBASE_SERVICE_ACCOUNT_JSON to the whole key JSON (Firebase console → Project settings → Service accounts → Generate new private key) and mark it Sensitive. That key has full administrative access and bypasses your security rules — never commit it, never put it in a NEXT_PUBLIC_* variable, and rotate it if it is ever exposed.Deploy the rules and indexes too, if you have not since adding the panel:
firebase deploy --only firestore:rules,firestore:indexespnpm-lock.yaml at the workspace root — so it fails with fah/missing-lock-file (firebase-tools#7478). Use Vercel, or the container path below.Any container host also works: output: 'standalone' is enabled everywhere except Vercel, so gcloud run deploy moneymind-admin --source . from the repo root builds and runs it, as does apps/admin/Dockerfile. On a Google runtime the runtime service account supplies credentials — grant it roles/datastore.user, roles/firebaseauth.admin, and roles/firebaseremoteconfig.admin, and never bake a service-account key into an image.
Demo mode
Set NEXT_PUBLIC_DEMO_MODE=true to make the panel read-only: every page still renders, but Remote Config publishing, admin grant/revoke, and entitlement refresh are all refused. It is enforced server-side in each endpoint, not merely hidden in the UI. Useful for screenshots or showing the console to someone without handing them a live deployment. It is inlined at build time, so changing it needs a redeploy.
docs/ADMIN_SETUP.md covers IAM, troubleshooting, and the alternative hosting path in detail. apps/admin/README.md is the developer quick reference.Branding and customization
App name + bundle id
Edit apps/mobile/pubspec.yaml, android/app/build.gradle.kts (applicationId), android/app/src/main/AndroidManifest.xml (android:label), and ios/Runner/Info.plist (CFBundleDisplayName) + the Xcode bundle id. Default id: com.devsnack.moneymind.
Theme
All colors, type, shape, and spacing live in apps/mobile/lib/core/theme/. Edit colors.dart once and the whole app reskins — there are no hex literals in widgets (a hard guardrail). Font is Sora via google_fonts; swap the family in typography.dart.
Strings / i18n
User-facing strings are ARB-driven (lib/l10n/app_en.arb). English ships in v1; the structure is i18n-ready.
Build and release
# from apps/mobile
flutter build appbundle --release # Android → Play Console
flutter build ipa --release # iOS → App Store Connect
# after any Drift/model change, regenerate code first:
dart run build_runner build --delete-conflicting-outputs
# backend
firebase deploy --only functionsca-app-pub-3940256099942544~…) in ios/Runner/Info.plist and the Android manifest with your own before a production release.App signing
Release builds must be signed with your own keys. The Android build is already wired to read your keystore from android/key.properties (gitignored) — you just need to supply the file and the .jks. Release builds also have R8 minification + resource shrinking on by default; keep rules live in android/app/proguard-rules.pro.
Android — keystore
- 01
Generate an upload keystore (keep this file private — never commit it):
terminalkeytool -genkey -v -keystore ~/upload-keystore.jks \ -keyalg RSA -keysize 2048 -validity 10000 -alias upload - 02
Copy the shipped template and fill it in:
terminalcp android/key.properties.example android/key.propertiesThen edit
android/key.properties:android/key.propertiesstorePassword=******** keyPassword=******** keyAlias=upload storeFile=/absolute/path/to/upload-keystore.jksBoth
key.propertiesand any.jks/.keystoreunderandroid/are already in.gitignore. - 03That’s it for wiring —
android/app/build.gradle.ktsalready loadskey.properties, defines areleasesigningConfig, enablesisMinifyEnabled+isShrinkResources, and pointsbuildTypes.releaseat both. No edits required. - 04
Build the release bundle and upload it to Play Console:
terminalflutter build appbundle --releasePlay App Signing is recommended — you keep the upload key and Google manages the app-signing key.
-keep rule to android/app/proguard-rules.pro and rebuild. Common offenders (Firebase, Drift/SQLCipher, RevenueCat, flutter_local_notifications, Play Core) are already covered.cd android && ./gradlew signingReport, or copy them from the Play Console “App signing” page. Without the right SHA, Google sign-in fails on release builds (see Social sign-in).iOS — code signing
- 01Join the Apple Developer Program and create the app record in App Store Connect with your bundle id (
com.devsnack.moneymindor your own). - 02In Xcode →
Runner→ Signing & Capabilities: select your Team, set the bundle id, and leave Automatically manage signing enabled (Xcode creates the certificate + provisioning profile). Add the Sign in with Apple capability here (see Social sign-in). - 03
Build, then upload via Xcode Organizer or Transporter:
terminalflutter build ipa --release
key.properties, *.jks / *.keystore, and .p12 / provisioning profiles out of git — add them to .gitignore if they aren’t already. Losing or leaking the keystore means you can’t ship updates under the same app.Production checklist
- 01Branding: app name + bundle id across the four native files; launcher icon.
- 02Theme: edit
lib/core/theme/colors.dart— light + dark. - 03Firebase: create project,
flutterfire configure, enable Auth / Firestore / Storage / Remote Config / Functions (Blaze). Enable Google + Apple sign-in; add Android SHA-1/256, the iOSREVERSED_CLIENT_IDURL scheme, and the Xcode “Sign in with Apple” capability (see Social sign-in). - 04terminal
firebase deploy --only firestore:rules,firestore:indexes,storage - 05Set the 5 Function secrets, then
firebase deploy --only functions. - 06Publish Remote Config keys (RevenueCat keys, AdMob ids, free-tier ceilings, prices).
- 07RevenueCat: products +
premiumentitlement + webhook URL/secret (perREVENUECAT_SETUP.md). - 08AdMob: real ad unit ids into Remote Config; replace
GADApplicationIdentifierinInfo.plist+ the Android manifest meta-data. - 09iOS capabilities: Face ID usage string (present); Sign in with Apple (see Social sign-in). Push not required — notifications are local-only in v1.
- 10Build releases + upload to Play Console / App Store Connect.
git archive --format=zip -o moneymind-1.0.0.zip HEAD.Changelog
Every version published so far. Updates are free for the life of the item.
Version 1.0.0
Initial release. Full personal-finance feature set (wallets, transactions, multi-currency, budgets, recurring, debts, goals, reports), functional AI suite (receipt scan, coach, forecast, anomalies, health score, budget/savings generators, subscription detector), RevenueCat + AdMob monetization, app lock, local notifications, Google + Apple sign-in.
Support and licensing
Six months of support are bundled per the CodeCanyon standard, covering setup help and bug fixes.
What support covers
- Six months of support from purchase, extendable at checkout
- Support covers bugs in the template and questions about how it is put together
- It does not cover custom feature work, third-party API changes or store review outcomes
- The Regular licence covers one free end product. Charging users for the app itself needs the Extended licence
Social sign-in (Google and Apple)
The Dart side is already built:
AuthService.signInWithGoogle/signInWithApple(apps/mobile/lib/data/remote/auth_service.dart), shown bySocialSignInButtonson the sign-in and sign-up screens. The Apple button is iOS-only (!kIsWeb && Platform.isIOS); Google appears on both platforms. You wire your own providers:flutterfire configure(or re-downloadgoogle-services.json). Debug and release keystores have different SHA-1s — register both (cd android && ./gradlew signingReport).REVERSED_CLIENT_IDfromGoogleService-Info.plistas a URL scheme (Xcode → Runner → Info → URL Types, orCFBundleURLTypesinInfo.plist).google_sign_in7.x needs it for the auth callback.lib/features/auth/widgets/social_sign_in_buttons.dart. Apple is intentionally hidden on Android — Sign in with Apple there needs a web-redirect relay (out of scope for v1).