Payments · Money · Reporting

Full refund, part refund, two different card totals

Our card reconciliation took a full refund off the day’s card total twice and a partial one off once. Right on the common case, so it lasted.


Card reconciliation is the cash drawer’s quieter cousin. At the end of the day the terminal prints a total, the till has its own idea of what went through on card, and the screen that compares them leads with the difference. Its whole purpose is that the difference means something. So a screen that manufactures a difference out of nothing is worse than no screen at all.

Two refunds, two treatments

We ran a day with two card sales of £100. One was refunded in full, back onto the card. The other had £30 refunded, also back onto the card. The till’s card figure came out at £100, and its card refunds at £130.

Look at what happened to each sale. The one refunded in full stopped counting as paid, so it left the card total entirely, and then its refund was listed as well. £100 off twice. The one refunded in part stayed paid, so its full £100 remained in the card total, and its £30 came off once. The same kind of money, handled two different ways depending on whether the customer wanted all of it back or some of it.

Why it was invisible

This is the same shape as the drawer bug we wrote about yesterday. Excluding refunded sales gives the right answer for a full refund by coincidence, and full refunds are most refunds. The partial refund is the uncommon case, and the uncommon case is the one nobody puts through the screen on a quiet afternoon to see what happens.

Our reconciliation screen compares the terminal’s net against the till’s card figure with a zero default tolerance. A refunded card sale therefore produced a phantom difference equal to itself, on the one screen whose job is to tell you when something is actually wrong.

One meaning for one word

The fix is to make the card figure mean one thing. Refunded sales are no longer excluded from it. A sale that went through on card is in the card total whatever happened to it afterwards, and refunds live in their own fields. That is a rule we already had written down for this screen: keep the parts apart and do not add them together too early. The code had the rule and had not followed it.

There was a second, smaller gap. Only refunds the till could not place anywhere produced a warning. A refund it knew perfectly well had gone back on a card said nothing. Now it says so, and it says it conditionally, because whether the terminal’s own total already nets refunds off is a fact about the paper in your hand, not something our code can know.

What we have not decided

That last point is the honest limit of this release. Whether the headline difference should net refunds off at all depends on what a real end-of-day receipt from a real terminal says, and we will settle it by reading one, not by reading our own code again. Until then the screen states which basis it is on and leaves the netting alone. No venue had reconciled a card day on the old arithmetic, so nothing was ever signed off wrongly. The test pins that a full refund and a part refund now move the card figure by exactly the same amount, which is none.


Try it on tonight’s service.

Nothing to install, no card. Not better by the weekend? Close the tab.