Inspiration

Split Pay started mid-trip. My group was using a popular expense splitter, and right when we needed to settle up, we hit a paywall. Frustrated, we tried to merge everything ourselves — screenshots in a group chat, a shared note, mental math — and it was every settlement argument I wanted this app to prevent, happening in real time.

So I stopped complaining and shipped. I have a cloud AI agent that can plan, implement, and deploy an idea from a single prompt — I described the app I wished existed and had a working MVP before the trip was over: trips behind unguessable share links, cent-exact splits, automatic settlement.

Later this September came another trip — multi-city this time — and it turned out to be the best test lab I could ask for: real expenses in real currencies, real friends as users, feedback gathered at dinner tables and fixes shipped from hotel Wi-Fi. I kept iterating on the go, and somewhere between the two trips it hit me: I never found time for the Shipathon — I was doing it the whole time.

One link — whoever has it, is in.

What it does

Split Pay keeps shared trip costs in one calm place. Create a trip, share one unguessable link — no accounts, no sign-ups; identity is just "which participant are you", stored on your device. Expenses split equally, by exact items off the receipt, by percentages, or weighted shares — tips included. 30+ currencies convert automatically with daily FX rates. The Balances tab collapses everything into the fewest payments, and settlement groups let a couple settle as one wallet. It's offline-first: log expenses without signal; they queue and replay when you're back.

Exact items off the receipt — tip folded in, cent-perfect.

How we built it

One decision shaped everything: a shared-expense app must never demand an account before showing you what you owe. So Split Pay is a link — open it, claim your name, start logging. That constraint drove the architecture:

React + Vite + TypeScript client, Node/Express + SQLite server, wrapped in Capacitor for Android — App Links mean a shared trip opens straight in the app, not a browser tab.

All money math lives in one pure, unit-tested module (money.ts): integer minor units only, largest-remainder allocation. Split €100 three ways and you get 33.34 / 33.33 / 33.33 — never the classic 33.33 × 3 = 99.99.

Settlement is a graph problem. Each person's net is simply paid − owed; greedily matching the biggest creditor with the biggest debtor settles any trip in at most n−1 transfers. No solver, no iteration — just a sort and a walk.

One toggle — direct debts become the fewest payments.

Offline is not an error state. Expenses go into an IndexedDB outbox with ordered replay, so the UI fully works at zero network; a small sync banner shows what's still queued.

FX rates come from frankfurter.app with a per-trip cache and a manual fallback — a dead API never blocks logging a taxi in another currency.

Monetization is an honest tip jar. Three one-time tiers (€1.99 / €4.99 / €9.99), every feature free forever — because any paywall inside the share-link loop blocks the whole group. Purchases run through RevenueCat (Purchases for Capacitor) → Google Play Billing, with RevenueCat as the source of truth for entitlements, regional pricing across 49 regions, and EEA digital-content classification.

Every feature free, forever — tips optional.

Challenges we ran into

  • Cent-exact allocation in every split mode. Equal, exact-items, percentage, and weighted splits all funnel through one pure function over integer minor units — one test suite proves no mode can ever produce a 99.99 total.
  • Offline replay that can't double-count. The IndexedDB outbox had to be ordered and idempotent, so a retried sync after a flaky hotel connection never duplicates an expense.
  • Google Play as a solo developer. Identity verification, an honest data-safety form, EEA digital-content classification for the tip tiers, and app-access instructions built around a demo trip link — all before a single download.
  • One codebase, native feel. The web app is the product; Capacitor and App Links make share links open straight in the app instead of a browser tab.

Accomplishments that we're proud of

I'm proud to say this app exists because a paywall annoyed the right person at the right moment, and delighted to let everyone use the result for free — every feature, forever, tips optional.

  • The core loop needs nothing from anyone: no accounts, and no network — the app stays fully usable offline and syncs when a connection returns.
  • The money layer is one pure, unit-tested module, so every split strategy — equal, exact items, percentages, weighted, tips, multi-currency — shares the same cent-exact foundation.
  • A solo developer shipped a real Android app to Google Play with production billing: RevenueCat entitlements, regional pricing in 49 regions, EEA classification — the unglamorous parts done properly.

What we learned

  • In a share-link product, the link is the growth loop: every participant is a potential new user, so any friction — an account wall, a paywall — taxes virality itself. That's what pushed monetization to a tip jar.
  • RevenueCat made billing genuinely boring (the good kind): receipts, entitlements, and regional pricing I would never have built myself.
  • When the money layer is pure and unit-tested, every weird product rule becomes cheap to add.

What's next for Split Pay

  • iOS. The Capacitor shell is platform-agnostic, and RevenueCat means the same products, entitlements, and tip tiers carry over to the App Store with almost no new billing code.
  • Recording repayments. v1 deliberately treats settle-up as the final word; next up is logging partial repayments against balances so long trips can settle as they go.
  • Trip lifecycle. Archiving finished trips instead of leaving them on the home screen forever.

Built With

Share this project:

Updates

Submission history