LearnHow it works
Money flow
Holds, capture and void: where the money is at each moment.
A hold is an authorization, not a payment. Only the winning price is captured.
Money flow: holds, capture and void
G1 on sandbox
Scroll the figure sideways, or open it full size.
final_capture) passed on the real PayPal sandbox for two PayPal-account buyers. That the remainder is released is inferred (the authorization reads CAPTURED and a later void answers 422 PREVIOUSLY_CAPTURED), not read from a buyer balance. Settlement of a whole auction was run on the fake PayPal. The three bidders and their ceilings are round illustration numbers.Text version of this diagram
- T0 Approve: three bidders approve holds. A holds $500, B $300, C $200. A hold is an authorization, nothing is charged.
- T1 Bid: A bids $350. The engine accepts it and the ledger appends a hash-chained event.
- T2 Hammer: A wins at $350.
- T3 Capture: GAVEL reads the truth of A's hold, then captures $350 with final_capture. PayPal closes the remaining $150 (inferred in gate G1).
- T4 Void: the holds of B ($300) and C ($200) are voided. The journal closes.
Invariant: charged $350 + released $150 + $300 + $200 = $1000 held. In the 30 day simulation the same equality holds on larger totals: held $159,385.00 = captured $34,170.00 + released $125,215.00 + open $0.00.
What happens, in order
- Approve (T0). Each bidder approves a PayPal order with intent AUTHORIZE for their ceiling.
- Bid (T1). The engine checks the bid against the ceiling and the rules; the ledger appends a hash-chained event.
- Hammer (T2). The highest valid bid wins at its price.
- Capture (T3). The saga reads the truth of the winner's hold, then captures the winning price with
final_capture. PayPal closes the unused remainder of that authorization. - Void (T4). Every other hold is voided, with bounded concurrency (three at a time). When all are released the settlement is SETTLED.
The invariant
Every dollar that was held ends up charged, released or still open, and open is zero when settlement ends. In the 30 day simulation the same equality holds on larger totals: see the simulation.
Where this lives in the code
- packages/payments/src/saga/capture.tstruth read, intent, capture with final_capture
- packages/payments/src/saga/remainder.tsthe winner remainder policy (modes A, B, C)
- packages/payments/src/saga/void.tsvoid every other hold, bounded concurrency
- packages/payments/src/requestId.tsdeterministic PayPal-Request-Id
- docs/evidence/G1committed raw run logs for G1
Edit on GitHub
Last updated on