Skip to content

Conversation

@cb-karthikp
Copy link
Collaborator

@cb-karthikp cb-karthikp commented Jan 22, 2026

What

Adds chargebee.webhooks namespace for handling Chargebee webhook events with TypeScript support.

Features

  • Event-driven API using on()/once()/off() patterns
  • 180+ typed webhook events with strongly-typed content
  • Built-in Basic Auth (auto-configures from env vars)
  • Framework-agnostic (Express, Fastify, raw Node.js)
  • createHandler() factory for isolated handlers
  • Payload validation and robust error handling

Usage

Basic (Global Handler)

chargebee.webhooks.on('subscription_created', async ({ event, response }) => {
  console.log('Subscription:', event.content.Subscription.id);
  response?.status(200).send('OK');
});

app.post('/webhooks', async (req, res) => {
  await chargebee.webhooks.handle({ body: req.body, headers: req.headers, response: res });
});

Isolated Handlers (Multi-route / Multi-tenant)

import { basicAuthValidator } from 'chargebee';

// Create separate handlers for different endpoints
const billingHandler = chargebee.webhooks.createHandler({
  requestValidator: basicAuthValidator((u, p) => u === 'billing' && p === 'secret1'),
});

const notificationHandler = chargebee.webhooks.createHandler({
  requestValidator: basicAuthValidator((u, p) => u === 'notify' && p === 'secret2'),
});

billingHandler.on('payment_succeeded', async ({ event, response }) => {
  await recordPayment(event.content.Transaction);
  response?.status(200).send('OK');
});

notificationHandler.on('payment_succeeded', async ({ event, response }) => {
  await sendPaymentEmail(event.content.Customer);
  response?.status(200).send('OK');
});

app.post('/webhooks/billing', async (req, res) => {
  await billingHandler.handle({ body: req.body, headers: req.headers, response: res });
});

app.post('/webhooks/notifications', async (req, res) => {
  await notificationHandler.handle({ body: req.body, headers: req.headers, response: res });
});

Error Handling

// Always register an error handler to catch validation errors, auth failures, etc.
chargebee.webhooks.on('error', (err) => {
  console.error('Webhook error:', err.message);
  // Log to your monitoring service
});

// Handle events that don't have a registered listener
chargebee.webhooks.on('unhandled_event', ({ event, response }) => {
  console.log('Unhandled event type:', event.event_type);
  response?.status(200).send('OK'); // Still acknowledge to prevent retries
});

Note: If no error listener is registered, errors are logged and re-thrown. This ensures webhook failures are never silently ignored.

@snyk-io
Copy link

snyk-io bot commented Jan 22, 2026

Snyk checks have passed. No issues have been found so far.

Status Scanner Critical High Medium Low Total (0)
Open Source Security 0 0 0 0 0 issues
Licenses 0 0 0 0 0 issues

💻 Catch issues earlier using the plugins for VS Code, JetBrains IDEs, Visual Studio, and Eclipse.

@cb-karthikp cb-karthikp changed the title Webhook event handler Webhook Handling for chargebee-node Feb 4, 2026
Comment on lines 4 to 9
Subscription: import('chargebee').Subscription;

Customer: import('chargebee').Customer;

UsageReminderInfo: import('chargebee').UsageReminderInfo;
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can't we just do a top level import * as Chargebee from 'chargebee' and then refer the objects using chargebee.Customer? Will reduce the verbosity of having imports everywhere.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can't use import * as Chargebee from 'chargebee' here because content.ts lives inside the chargebee package itself, and the tsconfig uses moduleResolution: "node" which doesn't support self-referencing imports.

});

// Catch processing errors (invalid JSON, validator failure, etc.)
handler.on('error', (err) => {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Errors here will be swallowed silently. We should pass the request and response objects here as well so it can be handled appropriately?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, that's right, I have included it now.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants