swiftswipe
killedcredit card rewards optimizer. flutter + nestjs + rag. told you which card to use before you tap.
the problem
most people in india hold 2-4 credit cards and have no idea which one gives the best return for a given purchase. the reward structures are buried in PDFs, vary by merchant category, have caps and promo windows, and change quietly. people either pick a card at random or spend 20 minutes researching, and still get it wrong.
swiftswipe takes the guesswork out. you tell it where you're spending and how much, and it tells you which card to pull out. behind the scenes, it resolves the merchant to a category, scores every card in your wallet, and surfaces the winner.
how it works
- 1you pick a merchant and enter the amount
- 2the merchant name gets matched to a category code. "Swiggy Instamart" and "Swiggy" both resolve correctly, without maintaining a lookup table. this uses embeddings and vector similarity against known MCCs
- 3every card in your wallet is scored: cashback percentage, reward point multipliers, category caps, promo windows, exclusions, all factored in
- 4the best card is returned with a clear breakdown of why it wins
key decisions
rag for merchant categorization
merchants don't map cleanly to category codes. "Croma" could be electronics or retail depending on the bank. string matching breaks constantly. embedding the merchant name and running vector similarity against MCC descriptions handles edge cases that rules never would.
clean architecture in flutter
the reward logic is genuinely complex: category multipliers, caps, promos, exclusions. separating domain logic from the UI means i can test the scoring engine without touching flutter, and swap the API layer without rewriting business rules.
postgresql + pgvector over a dedicated vector db
cards, reward rules, users, and wallets are relational. adding pgvector to the same database avoids syncing data between two systems for a few thousand embeddings.
bullmq for async processing
embedding merchants and computing rewards across a full wallet isn't instant. a job queue keeps the API responsive and lets retries happen cleanly.
what i built
- flutter mobile app with bloc state management and offline-first card portfolio (objectbox)
- nestjs api with jwt auth, whatsapp otp (msg91), and a modular reward engine
- merchant categorization pipeline: openai ada-002 embeddings → pgvector similarity search → mcc resolution
- redis caching for hot card and reward data
- dockerized with nginx reverse proxy
- 20+ indian credit cards catalogued with full reward rules
why it died
worked perfectly on paper, but merchant-to-MCC mapping was too ambiguous. accuracy wasn't reliable enough for fintech. if it's wrong once, nobody trusts it again.
timeline
pilot
card explorer and my cards ui
search ui with animations
login flow with whatsapp otp, api integration started
card management: add, remove, explore
cards module, merchant search
best card recommendation engine
rewards integration, session management, empty states
card detail page, firebase, final polish
last commit
merchant categorization accuracy wasn't reliable enough. good problem, wrong approach.