Testnet: Monad Testnet, test USDC only, unaudited
Open app
On this page How a table pays
All guide pages

How it works

Explanation

How a table paysLink to this section

This is what happens in the FairPay app when a table at a FairEats restaurant pays, step by step. A bill between friends works the same way, with public payments only. The browser only ever sends the server a transaction hash; every fact the server relies on comes from the chain.

  1. Guests order. The phone sends dish ids, quantities and which guest each dish is for. The server looks up the prices, adds GST and any service charge, and splits the total by what each guest ordered (or evenly), in whole cents that always add up to the total.
  2. The server makes one share per guest (or per person on a bill). Each share gets a random secret and a reference: orderRef = keccak256(orderId, shareId, secret). Only the reference ever goes on-chain. It reveals nothing about the order.
  3. Each guest pays.
    • Public: the browser approves the exact amount and calls FairPay.directPay. The guest's wallet is the payer.
    • Private: the browser builds one zero-knowledge proof per 5 USDC note and posts it to the relayer on FairPay's server. The relayer calls FairPool.pay, which pays through FairPay. The pool is the payer.
  4. The browser sends the transaction hash. That is all it sends.
  5. The server verifies on-chain. verifyPaymentTx checks that the transaction succeeded, that exactly one PaymentReceived event from the FairPay contract carries this reference and merchant, that the amount matches, and that the receipt's block is the canonical block at that height. It stores the payment under a unique (chain id, tx hash, log index), so one payment can never pay two shares.
  6. The kitchen gets the order once every share is verified. Staff move it through Start cooking, Ready and Served on the kitchen board, and the GST invoice is issued. On a bill, the share simply turns Paid.

If a payment was sent but is not confirmed yet, the app remembers it and checks it again before it lets you pay a second time. This stops double payments after a reload or a dropped connection.

PhoneFairPay serverMonad Testnetdishes per guestpublic: approve + directPayprivate: proofrelayer: pool.paytx hashPaymentReceived (read)prices, shares, orderRefsFairPayFairPool → FairPayverifyPaymentTxall shares verified → kitchen

Back to top

Private payments, explained simplyLink to this section

A private payment proves "one of these notes is mine and unspent" without saying which one. The app calls it a Confidential payment.

An everyday versionLink to this section

Think of a jar of identical tokens at the front desk. Earlier, you bought a token and dropped a sealed slip into the jar that only you can match. At the table, you show the waiter a proof that one of the slips in the jar is yours and has not been used, without pulling it out. The waiter knows the bill is paid. They do not know which slip, so they cannot tell when you bought it or from which wallet.

In FairPay termsLink to this section

  • Adding notes is public. You deposit 5 USDC into the pool for each note. The chain records your wallet and the deposit. Your browser keeps the note's secret.
  • Paying is private. Your browser builds a proof that your note is in the pool and has not been spent. The proof is tied to this restaurant and this share, so nobody can redirect it.
  • The relayer sends it. The relayer on FairPay's server pays the gas and submits the proof. Your wallet never touches this transaction.
  • The pool spends the note once. Each note has a one-time marker (a nullifier). Using it twice fails.
  • Only verified businesses. The pool pays only allowlisted payout wallets, such as a FairEats restaurant's, so bills between friends are paid publicly. If a share isn't a whole number of notes, the last note pays a little more and the extra goes to the restaurant as a tip; the app says so before you pay.

Privacy: how private a payment is depends on the crowd. You are hidden among the other deposits in the pool when you paid. On a testnet pool that can be a handful of people. Fewer than 10 is a small crowd: timing alone can point to you.

Habits that keep it privateLink to this section

  • Add notes well before you pay. Topping up and paying a minute later makes the two easy to link.
  • Prefer shares of one note when you can. A 15 USDC share is three notes sent back to back, which can be linked to each other.
  • Back up your notes on the Profile page. A note works like cash: whoever has the file can spend it, and losing it loses the deposit.

What private does not hide: the amount, the restaurant, the time, and anything FairPay's server sees about your connection (such as your IP address). The Security and limitations section has the full list.

See it for real: private payment 1 (real private payment 1, opens in a new tab). In each, the sender is the relayer, not the guest.

Back to top