DEV Community

Arthur031221
Arthur031221

Posted on

Splitting a dinner bill by who ate what, with the whole bill in the URL

Dollopify splitting a three person dinner, then opening the same bill in a phone sized window

A table of three splits a $96 dinner equally. One of them skipped the cocktails and still pays $32. I wanted a way to split one dinner by who ate what, shared plates included, and to send the result to friends who will not install anything. The result is Dollopify, a single HTML file.

Money is whole units

Amounts are integers in minor units, so a float never touches a price. A dish is split evenly among the people on it. When it does not divide evenly, the spare cents go to its owners one each, round robin, so nobody always pays them. Tax, tip and service charge are shared in proportion to what each person ordered, using the largest remainder method with BigInt for exact products.

import { fromWire } from './src/core/codec.js'
import { allocate } from './src/core/split.js'
const bill = fromWire({ v: 1, c: 'USD', p: ['Ava', 'Ben'], i: [['Pizza', 2000, 3], ['Beer', 800, 2]], x: [0, 400, 0] })
console.log(allocate(bill).people.map((p) => [p.name, p.total]))
// [ [ 'Ava', 1143 ], [ 'Ben', 2057 ] ]
Enter fullscreen mode Exit fullscreen mode

A randomized test builds 3,000 bills and checks that every share adds up to the dishes plus the extras to the unit.

The bill is the link

There is no backend. The page keeps the bill in the URL fragment as minified JSON, encoded as UTF-8 and then as unpadded base64url. Each dish is a name, a price and a bitmask of who shares it. A 20 dish bill for 8 people is 710 characters after the #, or 752 with the hosted address. Browsers do not send the fragment to a server, and the build adds a Content Security Policy that blocks every network request, which a browser test checks.

A link is untrusted input

Anyone can edit a link, so the decoder validates every field: the schema version, the currency, name length, owner masks, amounts and the 8192 character limit. Names reach the page only as text, and a test feeds it an image tag and a script tag to confirm nothing runs.

What is rough

You type the amounts, because there is no receipt scanner. I ran the browser tests in Chromium and Firefox, with phone profiles for the link check, and I have not tried Safari or a physical phone. Anyone who has a link can read the bill.

The code is MIT licensed. If you have a bill where the rounding feels unfair, I would like to see it.

Top comments (0)