SlipIQ — Know Your Salary, Know Your Taxes
Inspiration
The idea came from a moment every salaried person in India has felt: opening a payslip and staring at a wall of numbers you're too embarrassed to ask HR about. What is "Special Allowance"? Why does HRA reduce my tax bill? Why did my PF deduction jump this month? Most people just glance at the net pay figure and move on, quietly hoping the rest is correct.
As an independent Flutter developer, I wanted to build something that solved a real, recurring pain point rather than another to-do list app. Salary slips and Form 16 documents are things every working Indian receives monthly or annually, yet almost nobody outside of finance actually understands them. That gap — between "I have this document" and "I understand what it means for my money" — felt like fertile ground for AI to genuinely help rather than just impress.
The deeper motivation was building something with organic, word-of-mouth potential. If SlipIQ genuinely helps someone understand their salary for the first time, they tell their colleagues. That kind of utility-driven virality doesn't need a marketing budget — it needs the product to actually work, and work well, on real, messy, inconsistent Indian payroll formats.
What it does
SlipIQ lets a user snap a photo or upload a PDF of their salary slip or Form 16, and within seconds returns:
- A full earnings and deductions breakdown, each line item tappable for a plain-English, personalized explanation (not generic — it reads the user's actual numbers and flags things like "your Special Allowance is unusually high, which often means your employer is minimizing PF liability")
- Tax Insights — Old vs New regime comparison, HRA exemption calculation, 80C headroom remaining, and TDS variance, all computed using the tax rules that were actually in effect for that specific payslip's financial year
- A local, private history of every analyzed payslip with a salary trend chart and year-to-date TDS tracking
- A branded, shareable PDF report for sending to a CA
Everything runs with a strict privacy guarantee: documents are sent to Gemini for parsing and then discarded — only the extracted numbers are ever saved, and only on the user's own device.
How I built it
The project was built in seven incremental phases, each ending in a testable, runnable build before the next began — scaffold and navigation, local database, document input pipeline, AI integration, the tax engine and result dashboard, monetization, and finally export/analytics/polish. This discipline mattered enormously; skipping it would have meant debugging five interacting systems at once instead of one at a time.
Architecture. Flutter with Clean Architecture — data/domain/presentation layers per feature, flutter_bloc for state, get_it + injectable for dependency injection, go_router for navigation with a StatefulShellRoute for tab persistence.
The AI layer. Gemini (accessed via Firebase AI Logic's Google AI backend) reads the uploaded document and returns structured JSON. For text-based PDFs, I extract the raw text first with Syncfusion and send text instead of an image — roughly ten times cheaper per call, since most salary slips are generated PDFs, not scans. A custom system prompt encodes Indian payroll vocabulary (PF, PT, TDS, HRA, LOP) so the model reliably maps varied company formats onto one consistent schema.
The tax engine. This is where the real domain complexity lives, and it's the part I'm most proud of. The HRA exemption calculation, for instance, follows the actual Income Tax Act rule — the exempt amount is the minimum of three conditions:
$$ \text{HRA Exemption} = \min\Big(\text{HRA}{\text{received}},\;\; \text{Rent}{\text{paid}} - 0.10 \times \text{Basic},\;\; p \times \text{Basic}\Big) $$
where $p = 0.50$ for metro cities and $p = 0.40$ otherwise, all figures annualized.
The Old vs New regime comparison runs both slab structures against the same taxable income and picks the cheaper one:
$$ \text{Tax}{\text{slab}} = \sum{i} \max\big(0,\; \min(\text{Income}, \text{max}_i) - \text{min}_i\big) \times \text{rate}_i $$
with surcharge and a flat 4% health and education cess applied on top, and Section 87A rebate zeroing out tax entirely below the regime's threshold. Because tax slabs change with each Union Budget, the engine doesn't hardcode one year's rules — it selects the correct slab set based on the payslip's own date, not today's date, so a January 2025 payslip is always taxed under FY 2024-25 rules even if analyzed a year later.
Monetization. RevenueCat handles subscription (₹99/month) and one-time (₹49 per document) purchases on top of Google Play Billing, with entitlement checks re-validated on every app foreground so cancellations and payment failures are reflected without the user needing to reinstall.
What I learned
Tax law is genuinely intricate, and users notice inaccuracy immediately. Encoding the HRA and regime-comparison rules correctly took real research, not just intuition — and a subtle bug (treating Form 16's annual figures as if they were monthly and multiplying by twelve) inflated every affected user's income twelvefold before I caught it. That taught me to always ask what unit of time does this number represent before doing arithmetic on it.
AI output needs defensive parsing, always. Even with a strict "return only JSON" instruction, Gemini occasionally returned markdown bullet lists or truncated JSON when the response hit a token ceiling. I ended up writing a fallback parser that could reconstruct valid JSON from a bullet-list format as a last resort, and switched to the API's native JSON response mode once I learned it existed — a good reminder to check for structured-output modes before hand-rolling prompt-based enforcement.
Payment/entitlement systems are full of subtle traps. The most instructive bug of the whole build: I'd attached both the subscription and the one-time consumable product to the same RevenueCat entitlement. Since non-subscription purchases grant a permanent, non-expiring entitlement in RevenueCat's model, a single ₹49 purchase quietly gave a user unlimited Pro access forever — and kept showing Pro even after their actual subscription expired in Google Play. Fixing it meant rethinking the model entirely: the subscription should be the only thing that grants global access, while a one-time purchase should unlock only the specific document it was bought for, tracked locally by ID rather than through the entitlement system at all.
Platform policy is a moving target. Between starting and finishing this build, Google tightened its Photo and Video Permissions policy and added a dedicated Advertising ID declaration separate from the general Data Safety form — both required manifest-level fixes (tools:node="remove") that had nothing to do with application logic but everything to do with shipping successfully.
Challenges I faced
- Package churn mid-build.
google_generative_aiwas deprecated in favor offirebase_aipartway through, and afreezed/freezed_annotationversion mismatch silently produced broken generated code — a reminder that Flutter's fast-moving ecosystem means "what worked last month" isn't a safe assumption. - PDF rendering quirks. The default core font in Dart's
pdfpackage has no glyph for ₹ or the em dash, so currency values were rendering as blank or malformed text until I loaded a proper Unicode font. A semi-transparent gradient overlay I'd used for visual polish also turned out to render invisibly in several PDF viewers — solid colors, it turns out, are simply safer for cross-viewer compatibility. - Firebase App Check and Play Integrity. Getting attestation working reliably on a real device pulled from Google Play's internal testing track took several iterations — Play Integrity has known reliability gaps for apps that haven't yet had a full production release, which pushed me toward a debug-token-based approach as a pragmatic interim solution.
- Getting the double-navigation bug right. A purchase flow that emitted state twice for one user action caused the app to pop two screens instead of one — a bug that only became obvious once I stopped tying navigation to reactive state listeners and instead drove it directly from the awaited result of the purchase call itself.
Building SlipIQ end-to-end — from an idea about a confusing payslip to a shipped app handling real tax law, real payments, and real AI parsing — was as much a lesson in disciplined incremental engineering as it was in Flutter or tax code. Every phase surfaced a class of bug I hadn't anticipated, and fixing each one honestly, rather than papering over it, is what the app is now built on.
Log in or sign up for Devpost to join the conversation.