What we fixed
A race condition between the trial-extension webhook and the renewal webhook was causing both to attempt a charge against the same invoice. Stripe accepted the second one as a separate transaction.
What we did
- Patched the webhook handler to use Stripe's idempotency keys so duplicates are detected and dropped.
- Ran a reconciliation against the affected window and issued automatic refunds for the duplicate charges. No customer action needed.
- Added a synthetic test that simulates simultaneous trial-extension + renewal events. CI now fails if this regression sneaks back.