Midasoo
Which card should I tap? A Singapore app that worked out the miles, points or cashback each of your cards would earn at a shop, and tracked your spending against every card's bonus caps.
Started as a school project by four co-founders, then kept going for two years. The code is private, so this covers what it did and how.
The problem
Singapore has dozens of miles and cashback cards, and each one pays differently depending on where you spend. A card might earn 4 miles a dollar on dining but nothing on groceries, only for contactless payments, only up to a monthly cap, and only once you pass a minimum spend in that statement cycle. People who care about rewards end up doing this arithmetic at the till, or keeping a spreadsheet.
What we built
Five pieces:
- Mobile app (React Native, Expo). Shops near you on a map, trending merchants, search, and a page per shop that ranks your own cards by what they would earn there. A wallet for your cards (with statement date and whether it’s linked to Amaze), a spending tab with history and trends, and favourites. Sign-in with Firebase.
- API (Go, Echo, PostgreSQL) on Google App Engine. Card recommendations for a shop, transaction logging with the rewards each one earned, progress against each card’s caps, promotion search, and user-submitted corrections.
- Promotion scrapers. Nightly jobs that read the card promotions published by Citi, Amex and KrisPlus, so a shop’s page could show the deals running there.
- Merchant data pipeline (Python). Builds the shop list from Singapore’s business register on data.gov.sg: keeps live companies in consumer-facing industry codes, splits them by size, and looks up store locations with the Google Places API.
- Admin panel (React, Ant Design), started late on. Browse cards, merchants, stores and promotions from the live API; the review queue for user corrections was still on mock data.
How it fits together
Decisions worth explaining
Card rules are data, not code
Every card is a row: base and bonus rates, a formula for each (for example round(floor(spend_amount*base_rpd))), the merchant categories it includes or excludes, the bonus cap and minimum spend, whether bonuses count by calendar or statement month, which payment methods qualify, and the points-to-miles rate. The API evaluates the formula with the purchase amount and the user’s spend so far this cycle. Adding a card, or fixing one when a bank changes its terms, is a data edit instead of a release. It replaced a first version that had one fixed calculation for cashback cards and one for miles cards, which could not express caps, minimum spends or payment-method rules per card.
Unknown shops get a category from OpenAI
Rewards depend on a shop’s merchant category code, which nobody publishes. When a shop without one is opened, the API asks OpenAI using the shop’s name and its Google Maps place types and saves the answer; a one-off job did the same in bulk for the existing list. Users could send corrections from the app, stored for review with who approved them and why, rather than going live.
Rewards are checked again after they post
Banks credit bonus points days or weeks after a purchase. Each logged transaction stores the date its rewards should be final, and a nightly job reconciles everything due that day. Every scheduled run is recorded in the database and posted to a Discord channel, success or failure, so a broken scraper was noticed the next morning.
Promotions are de-duplicated by content
Each scraped promotion’s primary key is a hash of its issuer, merchant, offer, category, link and image, so re-running a scraper updates nothing that hasn’t changed and never creates duplicates.
Where it stopped
Everything above depends on one number: a shop’s merchant category code (MCC), the code a card network uses to decide which spending category a purchase falls into, and therefore which rate a card pays. Banks don’t publish it, the same brand can be coded differently at different branches, and the only sure way to learn it is to make a purchase there. The OpenAI guess was right often enough to look convincing and wrong often enough that a recommendation could not be trusted. An app whose whole promise is “tap this card and you’ll earn the most” can’t be built on a guess, so we stopped there.
Around 460 commits on the API and 370 on the app between 2023 and 2025, most of them mine.