Summary
For a B2C checkout funded partly (or fully) by referral credit, the recorded payment legs violate the invariant that the legs sum to the Payment.amount. The CARD leg is stored post-credit, and the REFERRAL_CREDIT leg is recorded positive, so sum(legs) !== Payment.amount. This breaks downstream money reconciliation, refund math, and any auditor that sums legs.
Where it lives
lib/payments/operations/checkout.ts:3346-3399 — leg construction for the credit + card split; the CARD leg is post-credit.
lib/payments/operations/checkout.ts:2953 — the amount/legs assembly point.
Concretely: for an invoice of ₹X paid as (Credit=C, Card=X−C), the CARD leg is ~X−C (post-credit) while the REFERRAL_CREDIT leg is +C (positive), so the two legs sum to X (not a mismatch in magnitude) — but the semantics are inverted: a credit-leg that is positive rather than a negative offset, with the card leg already reduced, means a naive sum(legs) === amount check or a refund that re-adds credit can double-count. The invariant is documented as sum(legs) === Payment.amount and the current encoding makes it unverifiable/unstable.
Why it is real
Money-integrity: refund-on-cancellation and the ledger post either over- or under-attribute the credit, and SonarQube/static checks cannot validate the invariant because the encoding is internally inconsistent.
Suggested fix direction
- Decide the canonical encoding (recommend: CARD leg = full pre-credit amount, REFERRAL_CREDIT leg = negative offset, so
CARD + CREDIT = amount), and make checkout emit it.
- Add a unit test asserting
sum(legs) === Payment.amount for credit-funded, fully-credit, and cash bookings.
- Check the refund/reversal engine (
refund.ts, reversal-engine.ts) against the chosen encoding.
Terms
payments · credit · referral · checkout · ledger · money invariant · refund
Summary
For a B2C checkout funded partly (or fully) by referral credit, the recorded payment legs violate the invariant that the legs sum to the
Payment.amount. The CARD leg is stored post-credit, and the REFERRAL_CREDIT leg is recorded positive, sosum(legs) !== Payment.amount. This breaks downstream money reconciliation, refund math, and any auditor that sums legs.Where it lives
lib/payments/operations/checkout.ts:3346-3399— leg construction for the credit + card split; the CARD leg is post-credit.lib/payments/operations/checkout.ts:2953— the amount/legs assembly point.Concretely: for an invoice of ₹X paid as (Credit=C, Card=X−C), the CARD leg is ~X−C (post-credit) while the REFERRAL_CREDIT leg is +C (positive), so the two legs sum to X (not a mismatch in magnitude) — but the semantics are inverted: a credit-leg that is positive rather than a negative offset, with the card leg already reduced, means a naive
sum(legs) === amountcheck or a refund that re-adds credit can double-count. The invariant is documented assum(legs) === Payment.amountand the current encoding makes it unverifiable/unstable.Why it is real
Money-integrity: refund-on-cancellation and the ledger post either over- or under-attribute the credit, and SonarQube/static checks cannot validate the invariant because the encoding is internally inconsistent.
Suggested fix direction
CARD + CREDIT = amount), and make checkout emit it.sum(legs) === Payment.amountfor credit-funded, fully-credit, and cash bookings.refund.ts,reversal-engine.ts) against the chosen encoding.Terms
payments · credit · referral · checkout · ledger · money invariant · refund