---
title: "A Payment Receipt Used the Gateway's Transaction Number Instead of Our Invoice Number"
canonical: https://dxdev.com/blog/2026-01-28_invoice-number-that-kept-a-receipt-from/
datePublished: 2026-01-28
---
The invoice number was sitting in a payment form, doing more work than it looked like it was doing.

I had a receipt page that was supposed to find the person who had just paid, then show the right details. The payment company sent several pieces of information back to that page. Two of them looked especially tempting: one was the payment company’s transaction number, and one was the invoice number that had been sent with the payment in the first place.

They were not the same thing. One was the payment company’s receipt number. The other was the label that pointed back to the registration in our own records.

The first version of the code tried to be helpful. It said, in effect, “Use the transaction number, or use the invoice number if that one is empty.” That sounds sensible if you are tidying a kitchen drawer. If one spoon is missing, use another. But these numbers were not two spoons. They were the house key and the library card. Both are small rectangles. Only one opens the front door.

In testing, that fallback looked fine. In production, it hid the real problem. The page chose the payment company’s number when it needed our own invoice number. Every receipt page then failed to find the registrant. The code made both choices look equally reasonable, so it also made the failure hard to see.

The fix was not clever. I stopped treating the two numbers as backups for each other. The receipt page now reads the invoice number to find the registration. The payment company’s transaction number goes into its own place, where it can be kept as the payment company’s reference. One field, one job.

That change mattered because this was not a practice checkout screen. It handled real credit card registrations. A receipt that cannot find the person who paid is the kind of small break that turns into an anxious phone call, a delayed answer, or someone wondering whether their money disappeared.

The work lasted from 08:06 to 20:52 that day and went out in seven tagged releases. Seven releases for one payment problem may sound like too many trips to the hardware store. Here, it made the work easier to follow. Each release carried one small, named change. If a new problem appeared, it pointed back to one change and one earlier version that had worked. That is much kinder than putting every repair into one giant box and hoping nothing rattles.

The wrong number was only the first problem. The receipt page was open to the public because the payment company had to send its result there. It took the invoice number from that incoming form and placed it into a database question. A database is just the organized filing cabinet where the registrations live. The trouble was that anyone could send a form to a public address, not only the payment company.

To an expert, this is an SQL injection risk. In ordinary words, it means a stranger may try to write part of the filing request for you. If the page accepts whatever they typed, it can ask the filing cabinet the wrong question.

Rebuilding the whole filing system during a payment fix would have been risky and slow. The practical repair was smaller. The page turned the incoming invoice number into a number, checked that it was actually a number, and rejected it if it was zero, negative, or nonsense. Only then could it be used to find a registration. It was one guard at the front door.

There was another bit of unnecessary trouble on the receipt. To show the merchant’s name, the page made a fresh trip to the filing cabinet to ask who the account belonged to. That question sometimes failed, and the failure was hidden by a catchall escape hatch. The receipt simply showed a raw username or a blank space instead.

I removed the extra trip. The page already carried the account name for that request. It was like walking back to the garage to check the color of a bicycle that was already leaning against the kitchen table. Using the information already in hand made the page simpler and removed another chance to ask the wrong question.

None of this required a shiny new system before the next payment could be handled safely. It required paying attention to what each little label meant, checking an outside number before trusting it, and making one change at a time when money was involved.

On Monday, pick one form, spreadsheet, receipt, or intake page in your work and ask: **Which number here points to our record, and which number belongs to somebody else?** If nobody can answer without guessing, write it down before the fallback becomes the bug.
