Skip to content

Extracting Tip and Gratuity Amounts From a Restaurant Receipt

8 min read · updated August 11, 2026

A restaurant card receipt is the one document in this cluster whose printed total is reliably not the amount that was paid. Everything about extracting a tip follows from that.

There are two receipts and they disagree

When a card is presented at a table, the terminal prints two slips. The merchant copy has a blank tip line and a blank total line for the customer to write on and sign; the customer copy is usually identical apart from the wording at the foot. Both print a total, and on both, that printed total is the pre-authorisation amount — food plus tax, before anything is written.

So the field labelled TOTAL on the paper is a real field with a real meaning, and it is not what the diner paid. An extractor that maps the printed TOTAL onto a total field and stops has not made a reading error; it has made a modelling error, and the error is silent because the number is perfectly legible and internally consistent. It will foot against the subtotal and tax exactly. Only a comparison with the card settlement reveals it.

The consequence for your schema is that a restaurant receipt needs at least three amount fields, not one: the printed pre-tip total, the tip, and the final total. Collapsing them loses the ability to say which one you actually have.

Which printed line is which

Below the subtotal, a card slip typically prints some subset of these, in an order that varies by terminal vendor:

  • Subtotal — food and drink before tax.
  • Tax — sometimes split into state and local lines, sometimes a single figure, occasionally itemised per tax rate when a basket mixes rates.
  • Service charge or gratuity — a printed, already-included amount. Discussed below; it is not a tip.
  • Total — the authorised amount.
  • Tip / Gratuity — a blank ruled line.
  • Total again — a second blank ruled line. Two fields with the same caption on one document, which is why keying on the caption alone fails; the blank one is below the tip line.
  • Suggested tip amounts — often three printed figures with percentages beside them.

Those suggested amounts are the most common false positive. They are printed, they are currency, they sit next to the word “tip”, and a model asked “what is the tip” will happily return one of them. Two structural signals distinguish them: there are usually three of them where there is only ever one real tip, and each is exactly a round percentage of the subtotal. Compute 15 %, 18 % and 20 % of the subtotal and discard anything that matches to the cent.

The pen-added tip and total

The tip is handwriting, on a slip that has already been folded, sat in a pocket, and photographed at an angle. Everything from the blurry-photo page applies to it and then some, because handwritten digits carry regional conventions a model may not weight correctly: a European seven with a crossbar, a one with a full upstroke that reads as a seven, a zero struck through, an amount written as 8.- or 8 00 or with the cents raised and underlined.

There is also the matter of where the pen went. People write over the ruled line, into the box below it, or place the tip on the total line and the total on the tip line. Ask the model to return the two values with their positions and the printed caption nearest each, rather than asking for “the tip”; a swapped pair is then obvious because the smaller number will be on the larger line.

An auto-gratuity is not a tip

Restaurants commonly add an automatic service charge for large parties, printed on the slip as GRATUITY, SERVICE CHARGE or AUTO GRAT with the party size beside it. Two things follow. It is already inside the printed total, so adding it to a handwritten tip double-counts. And in US payroll treatment a mandatory service charge is not a tip at all — it is revenue to the restaurant and wages to the server — which is why the two are printed as separate lines rather than combined.

For an expense workflow the practical rule is that the service charge is part of the meal cost and the handwritten tip is added on top. For anything touching payroll or tip reporting they are separate categories, and if you cannot tell which you have, that is a field to flag rather than guess.

Derive the tip instead of reading it

The strongest move available is to stop treating the tip as something to read. The card statement or the processor’s settlement record carries the amount that actually moved, to the cent, in machine-readable form. The receipt carries the pre-tip total, printed by a thermal head rather than a pen. The tip is the difference:

settled_amount   = 47.30    # from the card statement line
printed_total    = 39.75    # from the receipt, machine-printed
derived_tip      = 7.55
derived_tip / (settled_amount - printed_total - tax) -> sanity check

# guard rails
assert derived_tip >= 0
assert derived_tip <= printed_total          # a tip over 100% is possible
                                             # but should go to review

This inverts the difficulty. Instead of reading the least legible field on the document, you read the most legible one and subtract. The handwritten value becomes a cross-check on the derived value rather than the source of truth, and when the two disagree you have a genuine discrepancy worth a human’s attention — a tip adjusted after the fact, or a receipt matched to the wrong transaction.

There is a timing consequence worth designing for. The pre-tip amount is what the terminal authorised at the table; the tipped amount is submitted when the merchant closes its batch, typically at the end of the day, and the settled figure replaces the pending one a day or two later. So for a short window the card feed shows the printed total and not the paid total, and a reconciliation run too eagerly will derive a tip of zero and record it as fact. Wait for a settled transaction rather than a pending one, and treat a receipt matched only to a pending authorisation as unreconciled rather than as a zero tip.

Matching the receipt to the statement line is its own small problem: the amounts differ by the tip, so you match on merchant, date within a couple of days, and the pre-tip amount as a lower bound rather than on equality. The descriptor-normalisation techniques in categorising bank transactions are what make the merchant side of that match work, because the name on the slip and the descriptor on the statement are rarely the same string. Once matched, both documents keep their own amounts and the reconciliation is recorded as a link, not by overwriting one with the other.