fix(billing): sync Stripe invoice email and stop Bento dunning - #3059
Conversation
Portal email edits never updated orgs.management_email, so cancel and payment events kept going to the old address. Co-authored-by: Cursor <cursoragent@cursor.com>
|
Warning Review limit reachedYou’ve reached a temporary PR review limit under our Fair Usage Limits Policy. Next review available in: 49 minutes Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available. How can I continue?After more reviews become available, a review can be triggered using the To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews. How do review limits work?CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability. For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window. Please refer docs for additional details. Review details⚙️ Run configurationConfiguration used: Organization UI Review profile: ASSERTIVE Plan: Pro Run ID: 📒 Files selected for processing (6)
Comment |
Merging this PR will not alter performance
Comparing Footnotes
|
Co-authored-by: Cursor <cursoragent@cursor.com>
There was a problem hiding this comment.
All reported issues were addressed
Reply with feedback, questions, or to request a fix.
Re-trigger cubic
Read the live customer email so stale webhooks cannot roll back management_email, and fail the webhook with 500 so Stripe retries. Co-authored-by: Cursor <cursoragent@cursor.com>
There was a problem hiding this comment.
All reported issues were addressed across 3 files (changes from recent commits).
Tip: Review your code locally with the cubic CLI to iterate faster.
Re-trigger cubic
The Capgo org email form updated Stripe and the DB but left Bento until a later webhook, so trial/plan tags lagged on the new address. Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
A retrieve error plus webhook fallback could write a stale invoice address. Return 500 so Stripe retries instead. Co-authored-by: Cursor <cursoragent@cursor.com>
Bento exits dunning on Stripe $ChargeSucceeded, which only hits the customer email. Send org:charge_succeeded to every org billing contact so the sequence actually stops after they pay. Co-authored-by: Cursor <cursoragent@cursor.com>
There was a problem hiding this comment.
All reported issues were addressed across 6 files
Reply with feedback, questions, or to request a fix.
Re-trigger cubic
Org admins were getting failed-payment events. Missing charge.customer became "null" and skipped the 400 path. Co-authored-by: Cursor <cursoragent@cursor.com>
There was a problem hiding this comment.
2 issues found across 6 files
Confidence score: 3/5
- In
supabase/functions/_backend/triggers/stripe_event.ts, thecustomer.subscription.updated/replacement-subscription path can emitorg:charge_succeededwithout evidence of an actual successful payment, which could prematurely stop Bento dunning for still-unpaid accounts—gate this event on a verified recovery signal (e.g., a confirmed successful charge/invoice payment) before sending. - In
supabase/functions/_backend/triggers/stripe_event.ts, recovery flows can currently sendorg:charge_succeededtwice (from bothcharge.succeededandcreatedOrUpdatedlogic), which risks duplicate customer comms and noisy automation—deduplicate by enforcing a single source of truth or idempotency check for the stop-dunning event.
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="supabase/functions/_backend/triggers/stripe_event.ts">
<violation number="1" location="supabase/functions/_backend/triggers/stripe_event.ts:173">
P2: When a failed-payment customer receives an unrelated `customer.subscription.updated` or replacement subscription event, this check emits `org:charge_succeeded` without proving payment recovery, so Bento can stop dunning prematurely. Gate the stop event on an actual successful-charge or otherwise explicit recovery signal rather than only the previous `failed` status.</violation>
<violation number="2" location="supabase/functions/_backend/triggers/stripe_event.ts:1194">
P2: On every failed/past-due recovery, `org:charge_succeeded` is sent to the same billing contacts twice: once from the new `charge.succeeded` webhook handler and again from the `createdOrUpdated` `shouldSendDunningStopEvent` block. Duplicate Bento tracking events and redundant live-Stripe/Bento calls per recovery. Keep one source as the canonical dunning exit (the charge.succeeded handler) unless charge.succeeded may be disabled, in which case gate the createdOrUpdated no-op so it does not re-send when the standalone handler already fired.</violation>
</file>
Reply with feedback, questions, or to request a fix.
Re-trigger cubic
Subscription updates were a second, unproven stop path and doubled org:charge_succeeded with charge.succeeded. Co-authored-by: Cursor <cursoragent@cursor.com>
There was a problem hiding this comment.
All reported issues were addressed across 6 files
Reply with feedback, questions, or to request a fix.
Re-trigger cubic
Soft-fail tag sync and fail-closed org writes used the same retrieve path with duplicated catch blocks. Co-authored-by: Cursor <cursoragent@cursor.com>
|



Summary (AI generated)
customer.updated/customer.created), copy it ontoorgs.management_email, then sync billing Bento tagsorg:failed_paymentto billing contacts (management email, billing members, creator, live Stripe customer), not onlymanagement_emailcharge.succeeded, send custom Bento eventorg:charge_succeededto those same contacts so Dunning can exitMotivation (AI generated)
Customers can change the invoice email in Stripe, but Capgo kept tagging and emailing the old
management_email. Separately, Bento Dunning listened to Stripe's$ChargeSucceeded, which only lands on the Stripe customer subscriber. People paid and kept getting dunning emails because the stop never reached the other billing addresses.Business Impact (AI generated)
Invoice email changes stay in sync with Bento. Failed-payment sequences start and stop for every billing contact, so paying customers stop receiving dunning mail.
Test Plan (AI generated)
previous_attributes.emailgatingorg:charge_succeededevent name andcharge.succeededextraction (including missing/expanded customer)org:charge_succeeded(not Stripe$ChargeSucceeded)charge.succeededon the Stripe webhook endpoint if it is not already thereorgs.management_emailupdatesGenerated with AI