Playbook
Payment Evidence vs. Payment State (Payment Evidence Vs Payment State)
Evidence is what the PSP told you. State is what you decided. Conflate the two, and recovery leaves you unsure which one to trust.
Distributed Payment Engine
Part 8 of 22
A series on distributed payment architecture — the gap between capture and complete.
Two different questions, two different records
“What did the PSP say?” and “what did we decide?” look alike but are actually two completely different questions. The answer to the first is evidence: the raw webhook from the PSP, the API response, the timestamp — an unchangeable record. The answer to the second is state: the decision you made by combining that evidence with your business rules — is the payment Captured, is the checkout Completed.
Evidence State
The PSP's raw response → Your decision
Immutable, append-only → Mutable, decision table
"What happened" → "What we decided"
This part explains why you should never keep these two in the same record, and why that separation is a lifesaver during recovery.
Concepts, defined where they first appear
📦 Evidence
The raw fact an external system (the PSP) reported to you, stored without interpretation or alteration.
📦 State
The decision you made — as defined in the state machine from parts two and four — by combining evidence with your business rules.
📦 Append-only Log
A storage form where records are only added, never overwritten or deleted.
📦 Source of Truth vs. Derived Truth
The first is the raw, undisputed fact; the second is the interpreted decision derived from that fact.
📦 Reconciliation
The process of re-reading evidence records to verify whether the current state is still consistent with them.
The clearest example of this split: the webhook payload the PSP sends is evidence; you reading that payload and writing Payment.Status = Captured is a state decision. Evidence never changes; state can be updated as new evidence arrives.
Why the two can't live in the same table
Some systems write an incoming PSP webhook directly over the payments table: when a new webhook arrives, the relevant row gets updated, and the old value is gone. In this design, you can no longer answer “what exactly did the PSP say” — only “what was our latest interpretation.”
Wrong model:
payments
id | status | raw_payload
1 | Captured | {...content of the latest webhook, previous ones overwritten...}
This is losing exactly the information you'll need most during an incident: the historical chain of evidence.
The right model: two separate stores
Evidence lives in its own append-only table; every new webhook or API response gets added as a new row, and no row is ever updated or deleted. State lives in a separate table as the current decision derived from that evidence.
payment_evidence (append-only)
id | payment_id | source | received_at | raw_payload
1 | pay_42 | psp | t1 | {...authorize...}
2 | pay_42 | psp | t2 | {...captured...}
3 | pay_42 | psp | t5 | {...captured (duplicate)...}
payments (state)
id | status
pay_42 | Captured ← derived from evidence #2, confirmed again by #3
With this separation, the state row never “forgets” when and why it changed — because the evidence that produced it is still there.
Why recovery reads evidence and doesn't trust state
After an incident (a worker crash, a bad deploy, a suspicious inconsistency), when you need to bring your system back to a “correct” state, relying on the current state is risky — because that state might be exactly what the incident corrupted. Instead, re-reading the evidence log from the start and re-deriving state (a replay) is the more reliable path to recovery.
Recovery process
1. Read every record for pay_42 from payment_evidence, in chronological order
2. Reprocess each piece of evidence through the business rules (through the guards)
3. Compare the resulting derived state against the current value in the payments table
4. If they differ, decide which one is correct based on the evidence — not the state
This process is the foundation of the reconciliation workers we'll cover later in this series: when state is in doubt, evidence is always the referee.
Never store evidence “interpreted”
One last trap: some systems “clean up” evidence even while storing it — dropping fields they think are unnecessary, normalizing the format. This destroys evidence's actual value — knowing exactly, byte for byte, what the PSP said. Evidence should be the raw, unaltered response the PSP gave you; interpretation and normalization belong to the state-derivation step, not to the evidence itself.
The mappings that get confused most often
❌ Evidence and state can share a row, one overwriting the other
✓ Evidence is append-only; state lives in a separate table, derived from evidence
❌ "Cleaning up" the webhook payload before storing it is harmless
✓ Cleaned-up evidence loses the information of exactly what the PSP said
❌ Trusting the current state during an incident is the fastest path forward
✓ State is suspect during an incident; re-deriving it from evidence (a replay) is more reliable
❌ Evidence is just for debugging/logging, not needed for business decisions
✓ Evidence is the only reliable source for reconciliation and recovery
A checklist for auditing your evidence/state split
- Is the raw webhook payload from the PSP stored in a separate, append-only table, or does it overwrite the
paymentstable? - Does a second or third webhook for the same payment overwrite the first record, or does it add a new evidence row?
- Do you have a process that can replay the evidence log to re-derive state whenever a state inconsistency is suspected?
- Does any step “clean up” or normalize evidence before storing it? If so, do you also keep the raw data separately?
- Can you trace, after the fact, which evidence record a given row in your state table was derived from?
If you answer “no” to even one of these five, you may not be able to answer “what did the PSP actually say” during an incident.
What to take away from this part
- Evidence is the raw, unchangeable fact the PSP reported to you; state is the decision you made by combining that fact with your business rules.
- Evidence must be append-only; a new webhook arriving adds a new row, it never overwrites the old one.
- Recovery and reconciliation processes shouldn't trust the current state; they should re-read the evidence log and re-derive state.
- “Cleaning up” evidence while storing it destroys the information of exactly what the PSP said; the raw data must always be preserved.
State is today's interpretation; evidence is the witness that never changes. If you start doubting the witness instead of your own interpretation during an incident, you lose.
FAQ
Frequently asked questions
What is Evidence?
The raw fact an external system (the PSP) reported to you, stored without interpretation or alteration.
What is State?
The decision you made — as defined in the state machine from parts two and four — by combining evidence with your business rules.
Is it true that "Evidence and state can share a row, one overwriting the other"?
Evidence is append-only; state lives in a separate table, derived from evidence
What does this part lock in?
This part explains why you should never keep these two in the same record, and why that separation is a lifesaver during recovery. Evidence is the raw, unchangeable fact the PSP reported to you; state is the decision you made by combining that fact with your business rules. “What did the PSP say?” and “what did we decide?” look alike but are actually two completely different questions. The answer to the first is evidence: the raw webhook from the PSP, the API response, the timestamp — an unchangeable record. The answer to the second is state: the decision you made by combining that evidence with your business rules — is the payment `Captured`, is the checkout `Completed`.
Engineering Principles Learned
- Evidence is the PSP's raw, unchangeable fact; state is the decision you made by combining that fact with business rules — the two are never the same record.
- Evidence must always be append-only; new information is added alongside the old, never written over it.
- Recovery and reconciliation must trust the unchanging evidence, never the state that's currently in doubt.
Continue reading
Continue reading
Next in series
Provider Abstraction Without Leaking SDKs
How the provider gateway owns the PSP SDK while the checkout orchestrator only ever sees a semantic interface — and why card and wallet flows share a…
Next in series
The Outbox/Inbox Pattern in Payment Systems
If a database write and an event publish aren't in the same transaction, one can vanish or duplicate. Outbox publishes; inbox dedups on the consumer.
Same series
Semantic Events Over Raw Provider Payloads
Should the webhook the provider gateway receives reach downstream consumers under the PSP's own event name, or as a semantic event like…