Skip to content

fix(refunds): stopgap — only auto-reverse commissions on a full refund - #67

Open
keithfawcett wants to merge 2 commits into
mainfrom
fix/partial-refund-stopgap
Open

fix(refunds): stopgap — only auto-reverse commissions on a full refund#67
keithfawcett wants to merge 2 commits into
mainfrom
fix/partial-refund-stopgap

Conversation

@keithfawcett

Copy link
Copy Markdown
Contributor

Problem

charge.refunded invoked invoice-wide commission reversal on any refund: every accrued/approved commission for the invoice flipped to reversed. Combined with charge.amount_refunded being cumulative, a $1 refund on a $100 order wiped 100% of the partner's commission, and successive partial refunds mis-stated the amounts. (Audit item #9, Codex-confirmed.)

Fix (stopgap)

Only auto-reverse when the refund is full (amount_refunded >= amount). A partial refund is still recorded as a corrective refund event (for the audit trail) and flagged with partialRefundReversalSkipped: true so ops can handle it manually. fullRefund and chargeAmount are added to the event metadata.

This is deliberately a stopgap, not the end state — it strictly removes the partner-harming over-clawback. The real fix is proportional clawback (incremental refund deltas + immutable CommissionAdjustment rows), which is a separate ledger change worth its own design.

Tests

The two existing full-refund tests now carry charge.amount (so they stay full refunds and still reverse — proving the full path is unchanged). A new test drives a $1 refund on a $100 charge and asserts commissions stay accrued, reversedCommissions: 0, and partialRefundReversalSkipped: true. Typecheck clean.

🤖 Generated with Claude Code

keithfawcett and others added 2 commits August 8, 2026 11:58
charge.refunded reversed 100% of an invoice's accrued/approved commissions
on ANY refund, and charge.amount_refunded is cumulative — so a $1 refund on
a $100 order wiped the entire commission, and successive partials mis-stated
the reversal. Codex-recommended safe stopgap: only auto-reverse when the
refund is full (amount_refunded >= amount). Partial refunds are still
recorded as a corrective 'refund' event and flagged with
partialRefundReversalSkipped for manual handling.

This is a stopgap, not the end state: proportional clawback (incremental
refund deltas + immutable CommissionAdjustment rows) is the real fix and a
separate ledger change. The stopgap strictly prevents the current
partner-harming over-clawback.

Tests: existing full-refund cases now carry charge.amount (so they remain
full and still reverse); new case asserts a partial refund leaves
commissions accrued and sets the skip flag.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The stopgap compared amount_refunded to charge.amount (the INTENDED amount),
but Stripe's amount_captured is what was actually collected and can be
smaller after a partial capture. A $100 auth captured for $60 then fully
refunded ($60) has amount_refunded=6000 < amount=10000, so it was wrongly
classified partial → commissions left payable on a fully-refunded sale. Base
the full-refund test on amount_captured (fallback to amount when absent).

Test: a partially-captured ($60 of $100) charge, fully refunded, now reverses
commissions (fullRefund=true).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant