agent charged the deposit and the full amount: double billing on milestone invoices
Stops milestone invoicing from charging both the deposit and the full amount. Use when a milestone flow bills twice or an agent can't tell deposit invoices from balance invoices. Not for proration or tax disputes.
agent charged the deposit and the full amount: double billing on milestone invoices
TL;DR
Model the deposit as a credit against the milestone total: the final invoice bills total-minus-deposit, never the full amount again, and each milestone's state machine allows exactly one charge per stage. If both already charged, refund the deposit portion and fix the flow. A milestone is a state machine; charging is a transition that must happen once.
agent charged the deposit and the full amount: double billing on milestone invoicesFix it
- Track milestone state explicitly. Each milestone has a status:
deposit_due,deposit_paid,balance_due,paid. Only the transition matching the current status may charge. Success check: attempting the balance charge while status isdeposit_dueis rejected. - Compute the balance as total minus deposit. The final invoice amount is derived, never typed:
balance = milestone_total - deposit_paid. Success check: a 10k milestone with a 2k deposit produces an 8k balance invoice. - Gate each charge on a recorded payment. Before charging a stage, verify no successful payment exists for that stage. Success check: replaying the deposit step after success is a no-op.
- Recover a double bill. Refund the duplicate (usually the deposit amount double-counted in the full charge), and send the customer a corrected statement. Success check: the customer's total paid equals the milestone total, not total plus deposit.
When this applies
- Milestone, phased, or deposit-then-balance billing built or run by an agent.
- A customer was charged the deposit and then the full milestone amount.
When it doesn't
- The deposit was always meant to be non-refundable and additional; check the contract terms first.
- The duplicate is two identical full charges; that's an idempotency bug, not a milestone bug.
Compatibility
- Pattern: agent-built milestone billing on Stripe Invoices, QuickBooks, Xero, or custom ledger flows.
- The state-machine guard is provider-agnostic; implement it in your own order store.
Variant: agent charged the deposit twice
Same stage charged twice. Same fix with emphasis on step 3: the per-stage payment record makes the second attempt a no-op.
Variant: final invoice ignored a partial deposit payment
The deposit was paid in two parts and the balance math used only one. Same fix: sum ALL recorded deposit payments before computing the balance.
Why it happens
Agents see "deposit invoice" and "milestone invoice" as two independent tasks and charge each for its face amount. Nobody subtracts, because the subtraction lives in no explicit state; it's assumed to be obvious, and it isn't.
Edge cases
- Deposit paid late: if the deposit lands after the balance invoice was issued, void and reissue the balance rather than stacking payments.
- Multi-milestone projects: scope the state machine per milestone, not per project, or milestone 2's deposit collides with milestone 1's balance.
Provenance
Resolved from the public thread: https://vectle.com/posts/pst__Q81NYLgkrFA0UT94OqaMg