A Malaysian group-settlement app that turns a receipt into a live shared bill — then turns the final balance back into DuitNow.
RECEIPT → PEOPLE → CLAIMS → BALANCES → PAYMENT
Imagine this:
You are six people deep into dinner.
The waiter drops one giant receipt.
Then someone says:
“Okay… who had what?”
Five minutes later, everyone's opening calculator apps.
Split-Bill removes that moment.
THE OLD WAY
Receipt
↓
"Who ate this?"
↓
Mental math
↓
Calculator
↓
"Wait, did you include SST?"
↓
Someone owes RM0.37
↓
😭
THE SPLIT-BILL WAY
📸 Receipt
↓
AI parsing
↓
👥 Claim items
↓
🧮 Exact reconciliation
↓
🇲🇾 DuitNow QR
↓
✅ Done
The existing product screenshots are intentionally kept here — these are the real UI designs from the repository, not mock placeholders.
LIGHT · ENGLISH![]() |
DARK · ENGLISH![]() |
LIGHT · 中文![]() |
DARK · 中文![]() |
The interface supports instant Light/Dark and English/Simplified Chinese switching without a full-page reload.
Split-Bill starts with the physical source document.
┌───────────────────────────────┐
│ RESTAURANT │
│ │
│ Chicken Rice RM 12.00 │
│ Nasi Lemak RM 10.00 │
│ Pizza RM 24.00 │
│ Milo RM 4.50 │
│ │
│ SST RM 2.83 │
│ Service Charge RM 4.05 │
│ ───────────────────────────── │
│ TOTAL RM 57.38 │
└───────────────────────────────┘
│
▼
structured receipt
The receipt parser turns the image into structured line items that the rest of the system can reason about.
That means the user does not have to rebuild the bill manually.
The interaction model is deliberately closer to shopping cart ownership than a traditional “split equally” calculator.
Chicken Rice
│
└────────► Daniel
RM 12.00
Pizza
│
├────► Daniel 1/2
└────► Aidan 1/2
The claim engine supports both modes while preventing an item from becoming simultaneously exclusive and fractional.
What happens when everyone taps Claim at almost the same time?
Daniel ──┐
Aidan ──┤
Sean ──┼──► CLAIM PIZZA
YT ──┤
Guest ──┘
A normal read → modify → write flow can race.
Split-Bill moves the critical operation into atomic Lua execution in the KV layer.
Daniel ──┐
Aidan ──┤
Sean ──┼──► ATOMIC CLAIM
YT ──┤ │
Guest ──┘ ▼
CONSISTENT STATE
The implementation includes dedicated claim scripts for exclusive and shared-item operations, including cross-mode protection.
Not internally, anyway.
RM 10.00
↓
1000 sen
Why?
Because this:
10.00 / 3
is not a clean monetary representation.
But this is:
1000 / 3
333 + 333 + 334 = 1000
The reconciliation engine works in integer sen/cents and systematically distributes the remainder so that the final participant balances still equal the receipt total.
The bill should reconcile to the last sen.
Taxes and service charges also need to follow the bill.
RECEIPT
│
┌──────────┴──────────┐
▼ ▼
LINE ITEMS CHARGES
│ SST / SERVICE
└──────────┬──────────┘
▼
FINAL BALANCES
Changing a parsed item can therefore propagate through its downstream charge allocation rather than leaving stale amounts behind.
The final step uses DuitNow QR rather than inventing another payment ecosystem.
HOST'S QR
│
▼
EMVCo parsing
│
▼
merchant details
│
▼
inject amount
│
▼
rebuilt QR
│
▼
existing bank app
The repository has a dedicated duitnowQR.ts module and tests around QR handling.
The payment flow is therefore:
claim first → calculate exactly → pay through the rail everyone already has.
A restaurant bill is an event, not a permanent social record.
CREATE
↓
SHARE
↓
CLAIM
↓
SETTLE
↓
⌛
EXPIRE
The current design uses short-lived KV storage with a 2-hour TTL for temporary session state.
The project also applies privacy controls around uploaded receipt images, including metadata handling and a 500 KB upload limit.
Dinner ends. The session should end with it.
Trip Mode treats multiple dinners as one eventual settlement.
FRI NIGHT RM 42.80
SAT BRUNCH RM 18.50
SAT DINNER RM 67.30
COFFEE RM 11.20
────────
TOTAL RM139.80
Distinct sessions can be combined into a payment hub so the user does not need to manually reconcile four separate micro-debts.
The interface was built with a custom theme/context system and a dedicated i18n layer.
SPLIT-BILL
│
┌───────┴───────┐
▼ ▼
VISUAL LANGUAGE
│ │
┌────┴────┐ ┌──┴───┐
▼ ▼ ▼ ▼
LIGHT DARK EN 中文
The four existing screenshots above demonstrate all four combinations.
USER
│
▼
┌──────────────┐
│ NEXT.JS UI │
└──────┬───────┘
│
┌────────────────┼────────────────┐
▼ ▼ ▼
RECEIPT CLAIMS PAYMENT
PARSER ENGINE QR
│ │ │
▼ ┌──────┴──────┐ ▼
STRUCTURED │ ATOMIC │ EMVCo
BILL │ LUA / KV │ REBUILD
│ └──────┬──────┘ │
└────────────────┼────────────────┘
▼
MATH / RECONCILE
│
▼
FINAL BALANCE
│
▼
DUITNOW
| Layer | Choice |
|---|---|
| Web framework | Next.js 15 |
| UI | React 19 |
| Language | TypeScript 5 |
| Styling | Tailwind CSS 4 |
| Ephemeral state | Vercel KV |
| QR decoding | jsQR |
| QR generation | qrcode.react |
| Validation | Zod |
| Icons | Lucide React |
| Testing | Vitest |
The dependency manifest in the repository confirms this application stack.
split-bill/
│
├── app/ → routes + application UI
│
├── src/
│ ├── receiptParser.ts → receipt → structured items
│ ├── mathEngine.ts → exact monetary reconciliation
│ ├── duitnowQR.ts → EMVCo QR parsing / rebuilding
│ ├── privacy.ts → privacy helpers
│ ├── rateLimit.ts → request protection
│ ├── ThemeContext.tsx → theme state
│ ├── i18n.ts → localization
│ └── *.test.ts → automated tests
│
├── public/docs/ → product screenshots
├── constraints.md → implementation constraints
└── package.json
git clone https://github.com/DanielKoh2004/split-bill.git
cd split-bill
npm install
npm run devThen open:
http://localhost:3000
Run tests:
npm testProduction:
npm run build
npm startConfigure the required .env.local values for the project's KV and receipt-processing integrations.
This project looks simple from the outside.
Underneath, it combines several surprisingly serious problems:
| Problem | Engineering response |
|---|---|
| Messy receipt image | Structured receipt parser |
| Multiple people claiming simultaneously | Atomic Lua operations |
| Shared-item ownership | Explicit fractional claims |
| Floating-point currency | Integer sen arithmetic |
| Tax / service allocation | Proportional reconciliation |
| Payment interoperability | EMVCo / DuitNow reconstruction |
| Temporary personal data | TTL-based ephemeral state |
| QR manipulation | Strict parsing + validation |
That's what makes the project more than a “split calculator”.
Start from the receipt.
Everyone claims their own items.
Calculate in sen.
Use existing payment rails.
Let the session expire.
╭──────────────────────────────────────╮
│ SPLIT-BILL │
│ │
│ Scan it. │
│ Claim it. │
│ Split it. │
│ Settle it. │
│ │
│ And get back to dinner. │
╰──────────────────────────────────────╯



