All Docs
FeaturesCalmony PayUpdated March 15, 2026

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 typeMeaning
payment_intent.succeededA charge was captured successfully
payment_intent.failedA 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.ts
  • src/app/api/v1/webhook_endpoints/route.ts