-
Notifications
You must be signed in to change notification settings - Fork 3.4k
Description
Bug
The Stripe webhook handler in backend/core/billing/external/stripe/webhooks.py returns HTTP 200 with {'status': 'success'} even when webhook processing fails with an exception (line 107).
# Line 91-107
except Exception as e:
logger.error(f"[WEBHOOK] Error processing webhook: {e}")
# ...error logging...
if event and hasattr(event, 'id'):
await WebhookLock.mark_webhook_failed(event.id, error_message)
return {'status': 'success', 'error': 'processed_with_errors', 'message': 'Webhook logged as failed internally'}The endpoint in backend/core/billing/endpoints/webhooks.py directly returns this dict, so FastAPI sends a 200 response to Stripe.
Impact
- Stripe stops retrying failed webhooks — Stripe interprets any 2xx response as successful delivery and will not attempt redelivery (Stripe docs: retry behavior)
- Billing events are silently lost — failed
checkout.session.completed,customer.subscription.updated,invoice.payment_succeeded, etc. events are never retried - Users may pay but not receive their subscription — if the
CheckoutHandlerorSubscriptionHandlerthrows an exception, the payment went through on Stripe's side but Suna never processes it - The failure IS logged internally and marked via
WebhookLock.mark_webhook_failed(), but there is no automatic recovery path since Stripe won't retry
Expected behavior
When webhook processing fails, the handler should return a non-2xx status code (e.g., 500) so that Stripe retries the event delivery using its exponential backoff schedule (up to 3 days). The handler should still log the failure internally, but the HTTP response must signal failure to Stripe.
Suggested fix
Replace the return on line 107 with an HTTP 500 response:
except Exception as e:
logger.error(f"[WEBHOOK] Error processing webhook: {e}")
# ...existing error logging...
if event and hasattr(event, 'id'):
await WebhookLock.mark_webhook_failed(event.id, error_message)
raise HTTPException(status_code=500, detail="Webhook processing failed")This ensures Stripe retries the event while still preserving the internal failure tracking via WebhookLock.