Build1 publisher3 min readPublished
Two records of one capture make an order look overpaid by its own value
A dev.to post sorts order-versus-payment disagreements into four classes. All four surface as a total that does not match, the money is already right in three of them, and only the fourth is a broken integration.
The Engineer · Build desk

What happened
- A dev.to post opens on the numbers operations keeps finding: an order recorded at 120.00 euro against a capture of 119.40, an order with no payment at all, and a refund larger than anything ever collected.
- It sorts those disagreements into four classes that look identical from a distance: two versions of one basket, split captures, one capture recorded by two sources, and a record that is simply absent.
- The post describes the usual week: operations blames the payment system, engineering blames the order system, both get exported into a spreadsheet, and the spreadsheet becomes the thing everyone argues about.
- Three of the four are legitimate, and adjusting a figure fixes none of them. That leaves the absent record as the only class the post treats as a real problem.
Compiled by The EngineerSomething wrong?How this is made
Why it matters
- exposure The double-counted capture hands an operator a plausible number and a plausible action, and issuing that refund sends money out of the business on an order that was collected once.
- decision Someone has to name an authoritative source for each kind of record and publish it, because nothing in the data resolves which of two systems to believe.
- constraint A totals-based report will fail on legitimate split captures every time it runs, and the post's observation is that this is how teams learn to ignore the report.
- capability Versioned basket identifiers in the export make the most common class of disagreement disappear without anyone touching a payment.
Classification runs on identifiers. Two versions of one basket show up as a single order identifier carrying two totals, or as a total that moved between one export and the next [8]. Split captures reconcile one by one: a 150 euro order captured as 100 and then 50 [9], which is 150 exactly [22]. Double counting shows up as two records pointing at the same operation identifier from two different sources [13]. The fourth class has nothing to point at, because the record is simply absent [16].
The size of the gap tells you nothing about which one you have. The post's opening example is an order at 120.00 euro against a capture of 119.40 [2], a shortfall of 60 cents [23]. Its basket example is 30 euro apart, and both exports are telling the truth [7]. A spreadsheet compares totals, and all four classes produce a total that does not match [19].
The dangerous one is the double count. The provider reports a capture, the ledger reports the same capture, and adding them makes the order look overpaid by exactly its own value, so somebody starts preparing a refund [11]. "This one is dangerous because it doesn't look like a duplicate, it looks like a finding," the author wrote [12].
The repair for that class is a decision. Decide once which source is authoritative for each kind of record, and write it down somewhere people can find it [13]. No comparison can settle it, because the data cannot say which system to believe [14].
For the absent record, the first step is finding out why it is absent [16]. The causes the post lists are a webhook that failed and was never retried, a nightly export that ran while a transaction was still in flight, and a refund issued from the provider's dashboard that the order system never heard about [15]. "A missing record isn't a discrepancy to reconcile, it's a hole in how two systems talk to each other," the author wrote [17].
What the post asks of a comparison tool is refusal. Two basket versions and no rule for choosing between them: say so, do not pick the bigger one. If two sources claim one payment, report that both are claiming it, do not add them up. And when a record is missing, say it is missing, do not treat it as a zero [20]. A green result then means one narrow thing, that the records supplied are consistent with each other; it says nothing about whether the money arrived or whether the provider's history is complete [21].
The taxonomy is one practitioner's account across banking, healthcare, gaming and public administration, where every team was sure its situation was unusual [5]. The post does not count incidents beyond the twice-a-month figure [1]. Whether the four classes appear in your ledger in anything like these proportions depends on preconditions you can check: class two needs a provider that does partial captures and retries after a decline [9], class three needs provider events and your own writes landing in the same comparison [11], and class one needs an export that snapshots a mutable basket without saying which version it is [8]. A shop that captures once per order, and lets one system write payment records, should expect the fourth class and little of the other three [25].
What to watch
- Whether the author publishes the comparison tool, so the refusal behaviour can be tested against real provider exports.
- A cart or provider export that ships a basket version identifier by default would remove class one before any comparison runs.
- Whether teams actually write down an authoritative source per record type, the only one of the four repairs that code cannot do.