Fix: Subscription Billing Now Correctly Delivers payment_intent.failed Webhooks
Fix: Subscription Billing Now Correctly Delivers payment_intent.failed Webhooks
Release: v1.0.82
Type: Bug fix
Severity: High
Background
Calmony Pay fires webhook events to your registered endpoints whenever the state of a PaymentIntent changes. Two of the most critical event types in that lifecycle are:
| Event type | Meaning |
|---|---|
payment_intent.succeeded | A charge was captured successfully |
payment_intent.failed | A charge attempt failed |
These event types are defined in the Calmony Pay REST API Contract and are the canonical names you register when creating a webhook endpoint.
The Problem
Subscription billing — the internal function that attempts recurring charges against active subscriptions — was emitting a non-standard event type on failure:
// Before v1.0.82 (incorrect)
payment_intent.payment_failed
// Calmony Pay API Contract (correct)
payment_intent.failed
Because the emitted name did not match the name registered in KNOWN_EVENT_TYPES, the webhook delivery pipeline found no matching endpoints and delivered nothing. Customers who had configured endpoints to listen for payment_intent.failed would never receive a notification when a subscription charge failed.
The payment_intent.succeeded path was unaffected.
The Fix
The emitted event type in src/inngest/functions/subscription-billing.ts has been corrected to payment_intent.failed, bringing it into alignment with the API Contract and the KNOWN_EVENT_TYPES registry in src/app/api/v1/webhook_endpoints/route.ts.
What You Need to Do
Most integrations require no changes. If your webhook endpoint was already registered for payment_intent.failed, it will automatically begin receiving events from subscription billing failures after this release.
If you built a workaround — for example, polling the PaymentIntents list API to detect failures — you can now rely on the webhook event instead.
Verify your webhook endpoint registration
Confirm your endpoint is subscribed to the correct event type:
GET /v1/webhook_endpoints
Authorization: Bearer sk_...
The response should list payment_intent.failed under enabled_events for any endpoint that needs to handle subscription charge failures:
{
"object": "list",
"data": [
{
"id": "we_...",
"url": "https://your-app.example.com/webhooks/calmony",
"enabled_events": [
"payment_intent.succeeded",
"payment_intent.failed"
],
"status": "enabled"
}
]
}
If your endpoint is registered for payment_intent.payment_failed (the incorrect legacy name), update it to payment_intent.failed.
Handling payment_intent.failed in Your Application
A typical handler for a failed subscription charge:
app.post('/webhooks/calmony', (req, res) => {
const event = req.body;
if (event.type === 'payment_intent.failed') {
const paymentIntent = event.data.object;
const customerId = paymentIntent.customer;
const failureMessage = paymentIntent.last_payment_error?.message;
// Trigger your dunning / retry flow
await handleFailedSubscriptionCharge(customerId, failureMessage);
}
res.sendStatus(200);
});
Affected Files
src/inngest/functions/subscription-billing.tssrc/app/api/v1/webhook_endpoints/route.ts