tfpdev
3.2Part 3 · Matching

Which Invoice?

Two clients owe the same amount and one of them pays. If every payment lands in one place, you guess who it was from the amount, and you guess wrong. Give every bill its own address and there's nothing to guess.

01Try it

Two clients each owe the same amount. Make one of them pay, then see which bill each side marks as paid. An address is where a crypto payment is sent, like an account number.

Client ANW-2026-120 · 5,800 USDC
Its own address: —
Client BNW-2026-121 · 5,800 USDC
Its own address: —
Each click is one real test payment, sent to that bill's own address. The careless side reads the same payments as if they all landed in one place.15 of 15 payments left this hour
  1. No payments yet
BreaksOne address for everyone
All payments land in one place. It guesses the bill from the amount.
  • Client ANot paid
  • Client BNot paid
HoldsEvery bill has its own address
Each bill has its own address, so the address says whose payment it is.
  • Client ANot paid
  • Client BNot paid

Two clients each owe 5,800 USDC. Have Client B pay first, then see which bill each side marks as paid.

15 payments an hour each. Every payment is real and uses up the lab's test money.

02Step 2

Why every bill gets its own address

Same price, same number

Lots of clients owe exactly the same amount. If all payments land in one place, two of them paying the same week is enough for a mix-up.

Who sent it isn't a clue

Many clients pay from an exchange, where thousands of people share one sending address. The sender tells you nothing.

Addresses cost nothing

One secret key can make a new address for every bill, for free. Only the studio can move money out of any of them.

03Step 3

The code

What runs behind the buttons, in one sentence. The code itself is one click away.

The bad version sends every client to one address and guesses the bill from the amount. The good version gives each bill its own address, so a payment's address says which bill it is.

Show the code anyway
BreaksBreaks
The version that loses money. Read the grey notes under it, not the code.
shared-address.tsts
// every invoice says: pay to STUDIO_ADDRESSfor (const d of depositsTo(STUDIO_ADDRESS)) {  const inv = openInvoices.find((i) => i.amount === d.amount);  if (inv) await markPaid(inv, d);   // first match, whoever sent it}
Everything lands on one address, so the amount is the only clue. Two invoices for the same amount and the first one in the list wins.
HoldsHolds
The version running behind the buttons above.
per-invoice.tsts
// creating an invoiceconst index = await nextAddressIndex();const address = hdKeyToAccount(root, { addressIndex: index }).address;await saveInvoice({ amount, address, index });// a deposit arrivesconst inv = await invoiceByAddress(d.to);if (inv) await addLine(inv, d);      // amounts: see Wrong Amount
Each invoice gets the next child address of one HD key. The address is the reference, so the amount only has to be checked, not guessed from.
04Step 4

Still curious?

A few more questions people ask. Open what you need.

Does the careless side see different payments?

No, the same real payments. The careless side just ignores where each one landed, as if every bill shared one address.

What's the catch?

The money ends up spread over many addresses, and gathering it costs a small network fee for each. You do it in batches, when fees are low.

Why not a reference number, like at the bank?

A crypto payment has no box for a reference. Even where there is one, people forget to fill it in. A bill's own address can't be forgotten.

Not sure this could happen to you?

That's what the call is for. Tell me how your product works, and I'll tell you what applies to you and what I'd check first.