← changelog

swiftswipe

killed

credit card rewards optimizer. flutter + nestjs + rag. told you which card to use before you tap.

jan 2025 – may 2025solo: product, design, full-stack, data modeling
flutternestjspostgresqlpgvectoropenairedisdocker

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

  1. 1you pick a merchant and enter the amount
  2. 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
  3. 3every card in your wallet is scored: cashback percentage, reward point multipliers, category caps, promo windows, exclusions, all factored in
  4. 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

jan 20, 2025

pilot

jan 23, 2025

card explorer and my cards ui

feb 20, 2025

search ui with animations

mar 10, 2025

login flow with whatsapp otp, api integration started

mar 24, 2025

card management: add, remove, explore

mar 29, 2025

cards module, merchant search

apr 1, 2025

best card recommendation engine

apr 18, 2025

rewards integration, session management, empty states

may 18, 2025

card detail page, firebase, final polish

may 19, 2025

last commit

merchant categorization accuracy wasn't reliable enough. good problem, wrong approach.