chargebee 3.19.0 → 3.21.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (43) hide show
  1. package/CHANGELOG.md +50 -0
  2. package/README.md +250 -23
  3. package/cjs/chargebee.cjs.js +11 -4
  4. package/cjs/createChargebee.js +9 -0
  5. package/cjs/environment.js +1 -1
  6. package/cjs/resources/webhook/auth.js +53 -0
  7. package/cjs/resources/webhook/content.js +4 -0
  8. package/cjs/resources/webhook/errors.js +94 -0
  9. package/cjs/resources/webhook/eventType.js +12 -1
  10. package/cjs/resources/webhook/handler.js +483 -0
  11. package/esm/chargebee.esm.js +4 -2
  12. package/esm/createChargebee.js +9 -0
  13. package/esm/environment.js +1 -1
  14. package/esm/resources/webhook/auth.js +49 -0
  15. package/esm/resources/webhook/content.js +2 -0
  16. package/esm/resources/webhook/errors.js +87 -0
  17. package/esm/resources/webhook/eventType.js +12 -1
  18. package/esm/resources/webhook/handler.js +472 -0
  19. package/package.json +11 -6
  20. package/types/core.d.ts +19 -3
  21. package/types/index.d.ts +190 -7
  22. package/types/resources/Content.d.ts +0 -5
  23. package/types/resources/CreditNote.d.ts +8 -1
  24. package/types/resources/Customer.d.ts +14 -2
  25. package/types/resources/Einvoice.d.ts +8 -1
  26. package/types/resources/EntitlementOverride.d.ts +5 -0
  27. package/types/resources/Estimate.d.ts +14 -2
  28. package/types/resources/Event.d.ts +1 -0
  29. package/types/resources/Gift.d.ts +14 -2
  30. package/types/resources/HostedPage.d.ts +1 -0
  31. package/types/resources/Invoice.d.ts +22 -3
  32. package/types/resources/OmnichannelSubscription.d.ts +5 -0
  33. package/types/resources/PaymentIntent.d.ts +35 -5
  34. package/types/resources/PaymentSource.d.ts +7 -1
  35. package/types/resources/PricingPageSession.d.ts +1 -1
  36. package/types/resources/Purchase.d.ts +7 -1
  37. package/types/resources/Subscription.d.ts +51 -7
  38. package/types/resources/WebhookEvent.d.ts +11 -14
  39. package/types/resources/BusinessEntityChange.d.ts +0 -16
  40. package/types/resources/Product.d.ts +0 -135
  41. package/types/resources/SalesOrder.d.ts +0 -153
  42. package/types/resources/UsageReminderInfo.d.ts +0 -9
  43. package/types/resources/Variant.d.ts +0 -116
@@ -0,0 +1,483 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.basicAuthValidator = exports.WebhookHandler = exports.WebhookPayloadParseError = exports.WebhookPayloadValidationError = exports.WebhookAuthenticationError = exports.WebhookError = exports.WebhookContentType = exports.WebhookEventType = void 0;
4
+ exports.createHandler = createHandler;
5
+ exports.createDefaultHandler = createDefaultHandler;
6
+ const node_events_1 = require("node:events");
7
+ const auth_js_1 = require("./auth.js");
8
+ const eventType_js_1 = require("./eventType.js");
9
+ Object.defineProperty(exports, "WebhookEventType", { enumerable: true, get: function () { return eventType_js_1.WebhookEventType; } });
10
+ Object.defineProperty(exports, "WebhookContentType", { enumerable: true, get: function () { return eventType_js_1.WebhookContentType; } });
11
+ const errors_js_1 = require("./errors.js");
12
+ Object.defineProperty(exports, "WebhookError", { enumerable: true, get: function () { return errors_js_1.WebhookError; } });
13
+ Object.defineProperty(exports, "WebhookAuthenticationError", { enumerable: true, get: function () { return errors_js_1.WebhookAuthenticationError; } });
14
+ Object.defineProperty(exports, "WebhookPayloadValidationError", { enumerable: true, get: function () { return errors_js_1.WebhookPayloadValidationError; } });
15
+ Object.defineProperty(exports, "WebhookPayloadParseError", { enumerable: true, get: function () { return errors_js_1.WebhookPayloadParseError; } });
16
+ /**
17
+ * Webhook handler for processing Chargebee webhook events.
18
+ *
19
+ * Extends Node.js `EventEmitter` to provide a familiar, event-driven API for
20
+ * handling webhooks. Supports type-safe event listeners with full TypeScript
21
+ * autocomplete for all Chargebee event types.
22
+ *
23
+ * @typeParam ReqT - Framework-specific request type (e.g., `express.Request`)
24
+ * @typeParam ResT - Framework-specific response type (e.g., `express.Response`)
25
+ *
26
+ * @remarks
27
+ * **Lifecycle Warning:** Event listeners persist for the lifetime of the handler
28
+ * instance. Register handlers once at application startup, not per-request.
29
+ *
30
+ * @example Basic Usage with Express
31
+ * ```typescript
32
+ * import express from 'express';
33
+ * import { createHandler, basicAuthValidator } from 'chargebee/webhook';
34
+ *
35
+ * const app = express();
36
+ * app.use(express.json());
37
+ *
38
+ * // Create handler with Basic Auth
39
+ * const webhookHandler = createHandler({
40
+ * requestValidator: basicAuthValidator((u, p) => u === 'admin' && p === 'secret'),
41
+ * });
42
+ *
43
+ * // Register event listeners ONCE at startup (not per-request!)
44
+ * webhookHandler.on('subscription_created', async ({ event, response }) => {
45
+ * console.log('New subscription:', event.content.subscription.id);
46
+ * response?.status(200).send('OK');
47
+ * });
48
+ *
49
+ * webhookHandler.on('error', (error, { response }) => {
50
+ * if (error instanceof WebhookAuthenticationError) {
51
+ * response?.status(401).send('Unauthorized');
52
+ * } else if (error instanceof WebhookPayloadValidationError || error instanceof WebhookPayloadParseError) {
53
+ * response?.status(400).send('Bad Request');
54
+ * } else {
55
+ * response?.status(500).send('Internal Server Error');
56
+ * }
57
+ * });
58
+ *
59
+ * // Route handler
60
+ * app.post('/webhooks', async (req, res) => {
61
+ * await webhookHandler.handle({
62
+ * body: req.body,
63
+ * headers: req.headers,
64
+ * request: req,
65
+ * response: res,
66
+ * });
67
+ * });
68
+ * ```
69
+ *
70
+ * @example Available Event Types
71
+ * ```typescript
72
+ * // Subscription events
73
+ * handler.on('subscription_created', ({ event }) => { ... });
74
+ * handler.on('subscription_changed', ({ event }) => { ... });
75
+ * handler.on('subscription_cancelled', ({ event }) => { ... });
76
+ *
77
+ * // Customer events
78
+ * handler.on('customer_created', ({ event }) => { ... });
79
+ * handler.on('customer_changed', ({ event }) => { ... });
80
+ *
81
+ * // Payment events
82
+ * handler.on('payment_succeeded', ({ event }) => { ... });
83
+ * handler.on('payment_failed', ({ event }) => { ... });
84
+ *
85
+ * // Special events
86
+ * handler.on('unhandled_event', ({ event }) => {
87
+ * console.log('Unhandled event type:', event.event_type);
88
+ * });
89
+ *
90
+ * handler.on('error', (error, { response }) => {
91
+ * if (error instanceof WebhookAuthenticationError) {
92
+ * response?.status(401).send('Unauthorized');
93
+ * } else if (error instanceof WebhookPayloadValidationError || error instanceof WebhookPayloadParseError) {
94
+ * response?.status(400).send('Bad Request');
95
+ * } else {
96
+ * response?.status(500).send('Internal Server Error');
97
+ * }
98
+ * });
99
+ * ```
100
+ *
101
+ * @example Async Handlers
102
+ * ```typescript
103
+ * // Handlers can be async - errors are captured and emitted to 'error' event
104
+ * handler.on('subscription_created', async ({ event, response }) => {
105
+ * await saveToDatabase(event.content.subscription);
106
+ * await sendWelcomeEmail(event.content.customer);
107
+ * response?.status(200).send('OK');
108
+ * });
109
+ * ```
110
+ */
111
+ class WebhookHandler extends node_events_1.EventEmitter {
112
+ /**
113
+ * Creates a new WebhookHandler instance.
114
+ *
115
+ * @param options - Optional configuration options
116
+ *
117
+ * @example
118
+ * ```typescript
119
+ * // Without authentication (not recommended for production)
120
+ * const handler = new WebhookHandler();
121
+ *
122
+ * // With Basic Auth
123
+ * const handler = new WebhookHandler({
124
+ * requestValidator: basicAuthValidator((u, p) => u === 'user' && p === 'pass'),
125
+ * });
126
+ * ```
127
+ */
128
+ constructor(options) {
129
+ super({ captureRejections: true });
130
+ this._noAuthWarningShown = false;
131
+ this._requestValidator = options === null || options === void 0 ? void 0 : options.requestValidator;
132
+ }
133
+ /**
134
+ * Gets the current request validator function.
135
+ *
136
+ * @returns The configured validator or `undefined` if no authentication is set
137
+ *
138
+ * @example
139
+ * ```typescript
140
+ * if (handler.requestValidator) {
141
+ * console.log('Authentication is configured');
142
+ * }
143
+ * ```
144
+ */
145
+ get requestValidator() {
146
+ return this._requestValidator;
147
+ }
148
+ /**
149
+ * Sets or updates the request validator function.
150
+ *
151
+ * Use this to configure authentication after handler creation,
152
+ * or to change validators at runtime.
153
+ *
154
+ * @param validator - The validator function, or `undefined` to disable authentication
155
+ *
156
+ * @example
157
+ * ```typescript
158
+ * // Set up Basic Auth after creation
159
+ * handler.requestValidator = basicAuthValidator((u, p) => u === 'admin' && p === 'secret');
160
+ *
161
+ * // Custom header validation
162
+ * handler.requestValidator = (headers) => {
163
+ * if (headers['x-webhook-secret'] !== process.env.WEBHOOK_SECRET) {
164
+ * throw new Error('Invalid webhook secret');
165
+ * }
166
+ * };
167
+ *
168
+ * // Disable authentication (not recommended)
169
+ * handler.requestValidator = undefined;
170
+ * ```
171
+ */
172
+ set requestValidator(validator) {
173
+ this._requestValidator = validator;
174
+ }
175
+ /**
176
+ * Registers an event listener for a specific webhook event type.
177
+ *
178
+ * This method is inherited from Node.js `EventEmitter` but is documented here
179
+ * for clarity on available Chargebee webhook events.
180
+ *
181
+ * @param eventName - The Chargebee event type to listen for (e.g., `'subscription_created'`)
182
+ * @param listener - Callback function invoked when the event occurs
183
+ * @returns This handler instance for method chaining
184
+ *
185
+ * @remarks
186
+ * **Memory Leak Warning:** Listeners persist for the handler's lifetime.
187
+ * Always register listeners once at application startup, never inside
188
+ * request handlers or loops.
189
+ *
190
+ * @example Available Events
191
+ * ```typescript
192
+ * // Chargebee business events
193
+ * handler.on('subscription_created', ({ event, response }) => { ... });
194
+ * handler.on('subscription_changed', ({ event, response }) => { ... });
195
+ * handler.on('subscription_cancelled', ({ event, response }) => { ... });
196
+ * handler.on('customer_created', ({ event, response }) => { ... });
197
+ * handler.on('payment_succeeded', ({ event, response }) => { ... });
198
+ * handler.on('invoice_generated', ({ event, response }) => { ... });
199
+ * // ... and many more - see WebhookEventType enum for full list
200
+ *
201
+ * // Special events
202
+ * handler.on('unhandled_event', ({ event }) => {
203
+ * // Called when no listener exists for the event type
204
+ * console.log('Unhandled:', event.event_type);
205
+ * });
206
+ *
207
+ * handler.on('error', (error, { response }) => {
208
+ * // Called on validation errors, parse errors, or handler errors
209
+ * if (error instanceof WebhookAuthenticationError) {
210
+ * response?.status(401).send('Unauthorized');
211
+ * } else if (error instanceof WebhookPayloadValidationError || error instanceof WebhookPayloadParseError) {
212
+ * response?.status(400).send('Bad Request');
213
+ * } else {
214
+ * response?.status(500).send('Internal Server Error');
215
+ * }
216
+ * });
217
+ * ```
218
+ *
219
+ * @example Correct Usage
220
+ * ```typescript
221
+ * // ✅ GOOD: Register once at startup
222
+ * const handler = createHandler();
223
+ * handler.on('subscription_created', handleSubscription);
224
+ * handler.on('error', handleError);
225
+ *
226
+ * app.post('/webhooks', async (req, res) => {
227
+ * await handler.handle({ body: req.body, headers: req.headers, response: res });
228
+ * });
229
+ * ```
230
+ *
231
+ * @example Incorrect Usage
232
+ * ```typescript
233
+ * // ❌ BAD: Don't register inside request handlers - causes memory leak!
234
+ * app.post('/webhooks', async (req, res) => {
235
+ * handler.on('subscription_created', async () => { ... }); // Memory leak!
236
+ * await handler.handle({ ... });
237
+ * });
238
+ * ```
239
+ */
240
+ // Note: on() is inherited from EventEmitter with proper typing via WebhookEventMap
241
+ /**
242
+ * Handles an incoming webhook request from Chargebee.
243
+ *
244
+ * This method:
245
+ * 1. Validates the request using the configured `requestValidator` (if any)
246
+ * 2. Parses the request body (if it's a string)
247
+ * 3. Validates required fields (`event_type`, `id`)
248
+ * 4. Emits the appropriate event to registered listeners
249
+ *
250
+ * @param options - The webhook request options
251
+ * @returns A promise that resolves when the event has been emitted
252
+ *
253
+ * @throws Error if no `error` listener is registered and an error occurs
254
+ *
255
+ * @remarks
256
+ * **Async Behavior:** This method emits events but does not wait for async
257
+ * listeners to complete. Errors in async listeners are captured via
258
+ * `captureRejections` and emitted to the `error` event.
259
+ *
260
+ * **Response Handling:** The handler does NOT automatically send HTTP responses.
261
+ * Your event listeners must call `response.status(200).send('OK')` or similar.
262
+ *
263
+ * @example Express Integration
264
+ * ```typescript
265
+ * app.post('/webhooks', async (req, res) => {
266
+ * try {
267
+ * await handler.handle({
268
+ * body: req.body,
269
+ * headers: req.headers,
270
+ * request: req,
271
+ * response: res,
272
+ * });
273
+ * } catch (error) {
274
+ * // Only reached if no 'error' listener is registered
275
+ * res.status(500).send('Internal error');
276
+ * }
277
+ * });
278
+ * ```
279
+ *
280
+ * @example Fastify Integration
281
+ * ```typescript
282
+ * fastify.post('/webhooks', async (request, reply) => {
283
+ * await handler.handle({
284
+ * body: request.body,
285
+ * headers: request.headers,
286
+ * request,
287
+ * response: reply,
288
+ * });
289
+ * });
290
+ * ```
291
+ *
292
+ * @example Raw Node.js HTTP
293
+ * ```typescript
294
+ * http.createServer(async (req, res) => {
295
+ * if (req.method === 'POST' && req.url === '/webhooks') {
296
+ * const body = await getBody(req);
297
+ * await handler.handle({ body, headers: req.headers, response: res });
298
+ * }
299
+ * });
300
+ * ```
301
+ */
302
+ async handle(options) {
303
+ const { body, headers, request, response } = options;
304
+ try {
305
+ if (this._requestValidator) {
306
+ if (!headers) {
307
+ console.warn('[chargebee] Warning: Request validator is configured but no headers were passed. ' +
308
+ 'Authentication check skipped. If this is intentional (no-auth webhook), ' +
309
+ 'you can remove the requestValidator or ignore this warning.');
310
+ }
311
+ else {
312
+ await this._requestValidator(headers);
313
+ }
314
+ }
315
+ else if (!this._noAuthWarningShown) {
316
+ this._noAuthWarningShown = true;
317
+ console.warn('[chargebee] Warning: No webhook authentication configured. ' +
318
+ 'Consider using basicAuthValidator() or a custom requestValidator for production. ' +
319
+ 'See: https://www.chargebee.com/docs/billing/2.0/site-configuration/webhook_settings#basic-authentication');
320
+ }
321
+ let event;
322
+ try {
323
+ event =
324
+ typeof body === 'string' ? JSON.parse(body) : body;
325
+ }
326
+ catch (parseErr) {
327
+ const parseError = parseErr instanceof Error ? parseErr : new Error(String(parseErr));
328
+ throw new errors_js_1.WebhookPayloadParseError(`Failed to parse webhook body: ${parseError.message}`, typeof body === 'string' ? body : undefined);
329
+ }
330
+ // Validate required fields
331
+ if (!event || typeof event !== 'object' || Array.isArray(event)) {
332
+ throw new errors_js_1.WebhookPayloadValidationError('Invalid webhook payload: body must be a JSON object');
333
+ }
334
+ if (!event.event_type || typeof event.event_type !== 'string') {
335
+ throw new errors_js_1.WebhookPayloadValidationError('Invalid webhook payload: missing or invalid event_type');
336
+ }
337
+ if (!event.id) {
338
+ throw new errors_js_1.WebhookPayloadValidationError('Invalid webhook payload: missing event id');
339
+ }
340
+ const context = {
341
+ event,
342
+ request,
343
+ response,
344
+ };
345
+ const eventType = event.event_type;
346
+ if (this.listenerCount(eventType) > 0) {
347
+ this.emit(eventType, context);
348
+ }
349
+ else {
350
+ this.emit('unhandled_event', context);
351
+ }
352
+ }
353
+ catch (err) {
354
+ const error = err instanceof Error ? err : new Error(String(err));
355
+ if (this.listenerCount('error') === 0) {
356
+ console.warn('[chargebee] Webhook error with no handler:', error.message);
357
+ throw error;
358
+ }
359
+ this.emit('error', error, { request, response });
360
+ }
361
+ }
362
+ }
363
+ exports.WebhookHandler = WebhookHandler;
364
+ /**
365
+ * Creates a new WebhookHandler with custom configuration.
366
+ *
367
+ * This is the recommended factory function for creating webhook handlers.
368
+ * Use this when you need explicit control over authentication configuration.
369
+ *
370
+ * @typeParam ReqT - Framework-specific request type (e.g., `express.Request`)
371
+ * @typeParam ResT - Framework-specific response type (e.g., `express.Response`)
372
+ *
373
+ * @param options - Optional configuration for the handler
374
+ * @returns A new WebhookHandler instance
375
+ *
376
+ * @remarks
377
+ * For multi-route or multi-tenant scenarios, create separate handler instances
378
+ * to maintain isolation and avoid event listener conflicts.
379
+ *
380
+ * @example Basic Auth Configuration
381
+ * ```typescript
382
+ * import { createHandler, basicAuthValidator } from 'chargebee/webhook';
383
+ *
384
+ * const handler = createHandler({
385
+ * requestValidator: basicAuthValidator((username, password) => {
386
+ * return username === 'admin' && password === 'secret';
387
+ * }),
388
+ * });
389
+ * ```
390
+ *
391
+ * @example Custom Authentication
392
+ * ```typescript
393
+ * const handler = createHandler({
394
+ * requestValidator: async (headers) => {
395
+ * const signature = headers['x-chargebee-signature'];
396
+ * const isValid = await verifySignature(signature);
397
+ * if (!isValid) throw new Error('Invalid signature');
398
+ * },
399
+ * });
400
+ * ```
401
+ *
402
+ * @example Multi-Route Setup
403
+ * ```typescript
404
+ * // Separate handlers for different webhook endpoints
405
+ * const billingHandler = createHandler({ ... });
406
+ * const notificationHandler = createHandler({ ... });
407
+ *
408
+ * billingHandler.on('subscription_created', handleBillingEvent);
409
+ * notificationHandler.on('subscription_created', sendNotification);
410
+ *
411
+ * app.post('/webhooks/billing', (req, res) => billingHandler.handle({ ... }));
412
+ * app.post('/webhooks/notifications', (req, res) => notificationHandler.handle({ ... }));
413
+ * ```
414
+ *
415
+ * @example Without Authentication (Development Only)
416
+ * ```typescript
417
+ * // ⚠️ Not recommended for production
418
+ * const handler = createHandler();
419
+ * ```
420
+ */
421
+ function createHandler(options) {
422
+ return new WebhookHandler(options);
423
+ }
424
+ /**
425
+ * Creates a WebhookHandler with auto-configured Basic Auth from environment variables.
426
+ *
427
+ * This is a convenience function that automatically configures Basic Auth
428
+ * when the following environment variables are set:
429
+ * - `CHARGEBEE_WEBHOOK_USERNAME`
430
+ * - `CHARGEBEE_WEBHOOK_PASSWORD`
431
+ *
432
+ * If these environment variables are not set, the handler is created without
433
+ * authentication (a warning will be logged on first webhook handling).
434
+ *
435
+ * @typeParam ReqT - Framework-specific request type (e.g., `express.Request`)
436
+ * @typeParam ResT - Framework-specific response type (e.g., `express.Response`)
437
+ *
438
+ * @returns A new WebhookHandler instance with optional auto-configured auth
439
+ *
440
+ * @remarks
441
+ * This function is used internally by `chargebee.webhooks` to provide a
442
+ * pre-configured handler on the Chargebee instance.
443
+ *
444
+ * @example Environment-Based Setup
445
+ * ```bash
446
+ * # .env file
447
+ * CHARGEBEE_WEBHOOK_USERNAME=webhook_user
448
+ * CHARGEBEE_WEBHOOK_PASSWORD=webhook_secret
449
+ * ```
450
+ *
451
+ * ```typescript
452
+ * import { createDefaultHandler } from 'chargebee/webhook';
453
+ *
454
+ * // Auth is automatically configured from env vars
455
+ * const handler = createDefaultHandler();
456
+ *
457
+ * handler.on('subscription_created', ({ event, response }) => {
458
+ * console.log('Subscription:', event.content.subscription.id);
459
+ * response?.status(200).send('OK');
460
+ * });
461
+ * ```
462
+ *
463
+ * @example Overriding Auto-Configured Auth
464
+ * ```typescript
465
+ * const handler = createDefaultHandler();
466
+ *
467
+ * // Override with custom validator if needed
468
+ * handler.requestValidator = myCustomValidator;
469
+ * ```
470
+ *
471
+ * @see {@link createHandler} for explicit configuration without environment variables
472
+ */
473
+ function createDefaultHandler() {
474
+ const handler = new WebhookHandler();
475
+ const username = process.env.CHARGEBEE_WEBHOOK_USERNAME;
476
+ const password = process.env.CHARGEBEE_WEBHOOK_PASSWORD;
477
+ if (username && password) {
478
+ handler.requestValidator = (0, auth_js_1.basicAuthValidator)((u, p) => u === username && p === password);
479
+ }
480
+ return handler;
481
+ }
482
+ var auth_js_2 = require("./auth.js");
483
+ Object.defineProperty(exports, "basicAuthValidator", { enumerable: true, get: function () { return auth_js_2.basicAuthValidator; } });
@@ -3,5 +3,7 @@ import { FetchHttpClient } from './net/FetchClient.js';
3
3
  const httpClient = new FetchHttpClient();
4
4
  const Chargebee = CreateChargebee(httpClient);
5
5
  export default Chargebee;
6
- // Export webhook event types
7
- export { WebhookEventType, WebhookContentType, } from './resources/webhook/eventType.js';
6
+ // Export webhook utilities
7
+ export { WebhookEventType, WebhookContentType, } from './resources/webhook/handler.js';
8
+ export { basicAuthValidator } from './resources/webhook/auth.js';
9
+ export { WebhookError, WebhookAuthenticationError, WebhookPayloadValidationError, WebhookPayloadParseError, } from './resources/webhook/handler.js';
@@ -3,6 +3,7 @@ import { Environment } from './environment.js';
3
3
  import { Endpoints } from './resources/api_endpoints.js';
4
4
  import { extend, sleep } from './util.js';
5
5
  import { waitForProcessToComplete } from './asyncApiSupport.js';
6
+ import { WebhookHandler, createDefaultHandler, } from './resources/webhook/handler.js';
6
7
  export const CreateChargebee = (httpClient) => {
7
8
  const Chargebee = function (conf) {
8
9
  this._env = Object.assign({}, Environment);
@@ -12,6 +13,14 @@ export const CreateChargebee = (httpClient) => {
12
13
  conf.httpClient != null ? conf.httpClient : httpClient;
13
14
  this._buildResources();
14
15
  this._endpoints = Endpoints;
16
+ // Initialize webhooks handler with auto-configured Basic Auth (if env vars are set)
17
+ const handler = createDefaultHandler();
18
+ // Create webhooks namespace with handler methods + createHandler factory
19
+ this.webhooks = Object.assign(handler, {
20
+ createHandler(options) {
21
+ return new WebhookHandler(options);
22
+ },
23
+ });
15
24
  };
16
25
  Chargebee.prototype = {
17
26
  _createApiFunc(apiCall, env) {
@@ -8,7 +8,7 @@ export const Environment = {
8
8
  hostSuffix: '.chargebee.com',
9
9
  apiPath: '/api/v2',
10
10
  timeout: DEFAULT_TIME_OUT,
11
- clientVersion: 'v3.19.0',
11
+ clientVersion: 'v3.21.0',
12
12
  port: DEFAULT_PORT,
13
13
  timemachineWaitInMillis: DEFAULT_TIME_MACHINE_WAIT,
14
14
  exportWaitInMillis: DEFAULT_EXPORT_WAIT,
@@ -0,0 +1,49 @@
1
+ import { WebhookAuthenticationError } from './errors.js';
2
+ /**
3
+ * Creates a Basic Auth validator for webhook requests.
4
+ * Parses the Authorization header and validates credentials.
5
+ *
6
+ * @param validateCredentials - Function to validate username/password.
7
+ * Can be sync or async (e.g., for database lookups).
8
+ * @returns A request validator function for use with WebhookHandler
9
+ *
10
+ * @throws {WebhookAuthenticationError} When authentication fails
11
+ *
12
+ * @example
13
+ * // Simple sync validation
14
+ * const validator = basicAuthValidator((u, p) => u === 'admin' && p === 'secret');
15
+ *
16
+ * @example
17
+ * // Async validation (e.g., database lookup)
18
+ * const validator = basicAuthValidator(async (u, p) => {
19
+ * const user = await db.findUser(u);
20
+ * return user && await bcrypt.compare(p, user.passwordHash);
21
+ * });
22
+ */
23
+ export const basicAuthValidator = (validateCredentials) => {
24
+ return async (headers) => {
25
+ const authHeader = headers['authorization'] || headers['Authorization'];
26
+ if (!authHeader) {
27
+ throw new WebhookAuthenticationError('Missing authorization header');
28
+ }
29
+ const authStr = Array.isArray(authHeader) ? authHeader[0] : authHeader;
30
+ if (!authStr) {
31
+ throw new WebhookAuthenticationError('Invalid authorization header');
32
+ }
33
+ const parts = authStr.split(' ');
34
+ if (parts.length !== 2 || parts[0] !== 'Basic') {
35
+ throw new WebhookAuthenticationError('Invalid authorization header format');
36
+ }
37
+ const decoded = Buffer.from(parts[1], 'base64').toString();
38
+ const separatorIndex = decoded.indexOf(':');
39
+ if (separatorIndex === -1) {
40
+ throw new WebhookAuthenticationError('Invalid credentials format');
41
+ }
42
+ const username = decoded.substring(0, separatorIndex);
43
+ const password = decoded.substring(separatorIndex + 1);
44
+ const isValid = await validateCredentials(username, password);
45
+ if (!isValid) {
46
+ throw new WebhookAuthenticationError('Invalid credentials');
47
+ }
48
+ };
49
+ };
@@ -0,0 +1,2 @@
1
+ ///<reference path='../../../types/index.d.ts'/>
2
+ import { WebhookEventType } from './eventType.js';
@@ -0,0 +1,87 @@
1
+ /**
2
+ * Base class for all webhook-related errors.
3
+ * Extends the standard Error class with proper stack trace support.
4
+ */
5
+ export class WebhookError extends Error {
6
+ constructor(message) {
7
+ var _a;
8
+ super(message);
9
+ this.name = 'WebhookError';
10
+ // Maintains proper stack trace for where error was thrown (V8 only)
11
+ (_a = Error.captureStackTrace) === null || _a === void 0 ? void 0 : _a.call(Error, this, this.constructor);
12
+ }
13
+ }
14
+ /**
15
+ * Authentication error thrown when webhook request authentication fails.
16
+ *
17
+ * Common scenarios:
18
+ * - Missing authorization header
19
+ * - Invalid authorization header format
20
+ * - Invalid credentials
21
+ *
22
+ * Typically maps to HTTP 401 Unauthorized.
23
+ *
24
+ * @example
25
+ * ```typescript
26
+ * handler.on('error', (error, { response }) => {
27
+ * if (error instanceof WebhookAuthenticationError) {
28
+ * response?.status(401).json({ error: 'Unauthorized' });
29
+ * }
30
+ * });
31
+ * ```
32
+ */
33
+ export class WebhookAuthenticationError extends WebhookError {
34
+ constructor(message) {
35
+ super(message);
36
+ this.name = 'WebhookAuthenticationError';
37
+ }
38
+ }
39
+ /**
40
+ * Payload validation error thrown when the webhook payload structure is invalid.
41
+ *
42
+ * Common scenarios:
43
+ * - Missing required fields (event_type, id)
44
+ * - Invalid field types
45
+ * - Malformed payload structure
46
+ *
47
+ * Typically maps to HTTP 400 Bad Request.
48
+ *
49
+ * @example
50
+ * ```typescript
51
+ * handler.on('error', (error, { response }) => {
52
+ * if (error instanceof WebhookPayloadValidationError) {
53
+ * response?.status(400).json({ error: 'Bad Request', message: error.message });
54
+ * }
55
+ * });
56
+ * ```
57
+ */
58
+ export class WebhookPayloadValidationError extends WebhookError {
59
+ constructor(message) {
60
+ super(message);
61
+ this.name = 'WebhookPayloadValidationError';
62
+ }
63
+ }
64
+ /**
65
+ * JSON parsing error thrown when the webhook body cannot be parsed as JSON.
66
+ *
67
+ * Includes the raw body that failed to parse (if available) for debugging.
68
+ *
69
+ * Typically maps to HTTP 400 Bad Request.
70
+ *
71
+ * @example
72
+ * ```typescript
73
+ * handler.on('error', (error, { response }) => {
74
+ * if (error instanceof WebhookPayloadParseError) {
75
+ * console.error('Failed to parse:', error.rawBody);
76
+ * response?.status(400).json({ error: 'Invalid JSON' });
77
+ * }
78
+ * });
79
+ * ```
80
+ */
81
+ export class WebhookPayloadParseError extends WebhookError {
82
+ constructor(message, rawBody) {
83
+ super(message);
84
+ this.rawBody = rawBody;
85
+ this.name = 'WebhookPayloadParseError';
86
+ }
87
+ }