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.
- package/CHANGELOG.md +50 -0
- package/README.md +250 -23
- package/cjs/chargebee.cjs.js +11 -4
- package/cjs/createChargebee.js +9 -0
- package/cjs/environment.js +1 -1
- package/cjs/resources/webhook/auth.js +53 -0
- package/cjs/resources/webhook/content.js +4 -0
- package/cjs/resources/webhook/errors.js +94 -0
- package/cjs/resources/webhook/eventType.js +12 -1
- package/cjs/resources/webhook/handler.js +483 -0
- package/esm/chargebee.esm.js +4 -2
- package/esm/createChargebee.js +9 -0
- package/esm/environment.js +1 -1
- package/esm/resources/webhook/auth.js +49 -0
- package/esm/resources/webhook/content.js +2 -0
- package/esm/resources/webhook/errors.js +87 -0
- package/esm/resources/webhook/eventType.js +12 -1
- package/esm/resources/webhook/handler.js +472 -0
- package/package.json +11 -6
- package/types/core.d.ts +19 -3
- package/types/index.d.ts +190 -7
- package/types/resources/Content.d.ts +0 -5
- package/types/resources/CreditNote.d.ts +8 -1
- package/types/resources/Customer.d.ts +14 -2
- package/types/resources/Einvoice.d.ts +8 -1
- package/types/resources/EntitlementOverride.d.ts +5 -0
- package/types/resources/Estimate.d.ts +14 -2
- package/types/resources/Event.d.ts +1 -0
- package/types/resources/Gift.d.ts +14 -2
- package/types/resources/HostedPage.d.ts +1 -0
- package/types/resources/Invoice.d.ts +22 -3
- package/types/resources/OmnichannelSubscription.d.ts +5 -0
- package/types/resources/PaymentIntent.d.ts +35 -5
- package/types/resources/PaymentSource.d.ts +7 -1
- package/types/resources/PricingPageSession.d.ts +1 -1
- package/types/resources/Purchase.d.ts +7 -1
- package/types/resources/Subscription.d.ts +51 -7
- package/types/resources/WebhookEvent.d.ts +11 -14
- package/types/resources/BusinessEntityChange.d.ts +0 -16
- package/types/resources/Product.d.ts +0 -135
- package/types/resources/SalesOrder.d.ts +0 -153
- package/types/resources/UsageReminderInfo.d.ts +0 -9
- package/types/resources/Variant.d.ts +0 -116
|
@@ -122,6 +122,7 @@ export var WebhookEventType;
|
|
|
122
122
|
WebhookEventType["OrderResent"] = "order_resent";
|
|
123
123
|
WebhookEventType["OrderReturned"] = "order_returned";
|
|
124
124
|
WebhookEventType["OrderUpdated"] = "order_updated";
|
|
125
|
+
WebhookEventType["PaymentDueReminder"] = "payment_due_reminder";
|
|
125
126
|
WebhookEventType["PaymentFailed"] = "payment_failed";
|
|
126
127
|
WebhookEventType["PaymentInitiated"] = "payment_initiated";
|
|
127
128
|
WebhookEventType["PaymentIntentCreated"] = "payment_intent_created";
|
|
@@ -230,6 +231,16 @@ export var WebhookEventType;
|
|
|
230
231
|
WebhookEventType["VoucherExpired"] = "voucher_expired";
|
|
231
232
|
})(WebhookEventType || (WebhookEventType = {}));
|
|
232
233
|
/**
|
|
233
|
-
* @deprecated Use WebhookEventType instead.
|
|
234
|
+
* @deprecated Renamed to `WebhookEventType` for clarity. Use `WebhookEventType` instead.
|
|
235
|
+
* This alias will be removed in the next major version.
|
|
236
|
+
*
|
|
237
|
+
* @example
|
|
238
|
+
* // Before (deprecated)
|
|
239
|
+
* import { WebhookContentType } from 'chargebee';
|
|
240
|
+
* if (event.event_type === WebhookContentType.SubscriptionCreated) { ... }
|
|
241
|
+
*
|
|
242
|
+
* // After (recommended)
|
|
243
|
+
* import { WebhookEventType } from 'chargebee';
|
|
244
|
+
* if (event.event_type === WebhookEventType.SubscriptionCreated) { ... }
|
|
234
245
|
*/
|
|
235
246
|
export const WebhookContentType = WebhookEventType;
|
|
@@ -0,0 +1,472 @@
|
|
|
1
|
+
import { EventEmitter } from 'node:events';
|
|
2
|
+
import { basicAuthValidator } from './auth.js';
|
|
3
|
+
import { WebhookEventType, WebhookContentType } from './eventType.js';
|
|
4
|
+
import { WebhookError, WebhookAuthenticationError, WebhookPayloadValidationError, WebhookPayloadParseError, } from './errors.js';
|
|
5
|
+
export { WebhookEventType, WebhookContentType };
|
|
6
|
+
export { WebhookError, WebhookAuthenticationError, WebhookPayloadValidationError, WebhookPayloadParseError, };
|
|
7
|
+
/**
|
|
8
|
+
* Webhook handler for processing Chargebee webhook events.
|
|
9
|
+
*
|
|
10
|
+
* Extends Node.js `EventEmitter` to provide a familiar, event-driven API for
|
|
11
|
+
* handling webhooks. Supports type-safe event listeners with full TypeScript
|
|
12
|
+
* autocomplete for all Chargebee event types.
|
|
13
|
+
*
|
|
14
|
+
* @typeParam ReqT - Framework-specific request type (e.g., `express.Request`)
|
|
15
|
+
* @typeParam ResT - Framework-specific response type (e.g., `express.Response`)
|
|
16
|
+
*
|
|
17
|
+
* @remarks
|
|
18
|
+
* **Lifecycle Warning:** Event listeners persist for the lifetime of the handler
|
|
19
|
+
* instance. Register handlers once at application startup, not per-request.
|
|
20
|
+
*
|
|
21
|
+
* @example Basic Usage with Express
|
|
22
|
+
* ```typescript
|
|
23
|
+
* import express from 'express';
|
|
24
|
+
* import { createHandler, basicAuthValidator } from 'chargebee/webhook';
|
|
25
|
+
*
|
|
26
|
+
* const app = express();
|
|
27
|
+
* app.use(express.json());
|
|
28
|
+
*
|
|
29
|
+
* // Create handler with Basic Auth
|
|
30
|
+
* const webhookHandler = createHandler({
|
|
31
|
+
* requestValidator: basicAuthValidator((u, p) => u === 'admin' && p === 'secret'),
|
|
32
|
+
* });
|
|
33
|
+
*
|
|
34
|
+
* // Register event listeners ONCE at startup (not per-request!)
|
|
35
|
+
* webhookHandler.on('subscription_created', async ({ event, response }) => {
|
|
36
|
+
* console.log('New subscription:', event.content.subscription.id);
|
|
37
|
+
* response?.status(200).send('OK');
|
|
38
|
+
* });
|
|
39
|
+
*
|
|
40
|
+
* webhookHandler.on('error', (error, { response }) => {
|
|
41
|
+
* if (error instanceof WebhookAuthenticationError) {
|
|
42
|
+
* response?.status(401).send('Unauthorized');
|
|
43
|
+
* } else if (error instanceof WebhookPayloadValidationError || error instanceof WebhookPayloadParseError) {
|
|
44
|
+
* response?.status(400).send('Bad Request');
|
|
45
|
+
* } else {
|
|
46
|
+
* response?.status(500).send('Internal Server Error');
|
|
47
|
+
* }
|
|
48
|
+
* });
|
|
49
|
+
*
|
|
50
|
+
* // Route handler
|
|
51
|
+
* app.post('/webhooks', async (req, res) => {
|
|
52
|
+
* await webhookHandler.handle({
|
|
53
|
+
* body: req.body,
|
|
54
|
+
* headers: req.headers,
|
|
55
|
+
* request: req,
|
|
56
|
+
* response: res,
|
|
57
|
+
* });
|
|
58
|
+
* });
|
|
59
|
+
* ```
|
|
60
|
+
*
|
|
61
|
+
* @example Available Event Types
|
|
62
|
+
* ```typescript
|
|
63
|
+
* // Subscription events
|
|
64
|
+
* handler.on('subscription_created', ({ event }) => { ... });
|
|
65
|
+
* handler.on('subscription_changed', ({ event }) => { ... });
|
|
66
|
+
* handler.on('subscription_cancelled', ({ event }) => { ... });
|
|
67
|
+
*
|
|
68
|
+
* // Customer events
|
|
69
|
+
* handler.on('customer_created', ({ event }) => { ... });
|
|
70
|
+
* handler.on('customer_changed', ({ event }) => { ... });
|
|
71
|
+
*
|
|
72
|
+
* // Payment events
|
|
73
|
+
* handler.on('payment_succeeded', ({ event }) => { ... });
|
|
74
|
+
* handler.on('payment_failed', ({ event }) => { ... });
|
|
75
|
+
*
|
|
76
|
+
* // Special events
|
|
77
|
+
* handler.on('unhandled_event', ({ event }) => {
|
|
78
|
+
* console.log('Unhandled event type:', event.event_type);
|
|
79
|
+
* });
|
|
80
|
+
*
|
|
81
|
+
* handler.on('error', (error, { response }) => {
|
|
82
|
+
* if (error instanceof WebhookAuthenticationError) {
|
|
83
|
+
* response?.status(401).send('Unauthorized');
|
|
84
|
+
* } else if (error instanceof WebhookPayloadValidationError || error instanceof WebhookPayloadParseError) {
|
|
85
|
+
* response?.status(400).send('Bad Request');
|
|
86
|
+
* } else {
|
|
87
|
+
* response?.status(500).send('Internal Server Error');
|
|
88
|
+
* }
|
|
89
|
+
* });
|
|
90
|
+
* ```
|
|
91
|
+
*
|
|
92
|
+
* @example Async Handlers
|
|
93
|
+
* ```typescript
|
|
94
|
+
* // Handlers can be async - errors are captured and emitted to 'error' event
|
|
95
|
+
* handler.on('subscription_created', async ({ event, response }) => {
|
|
96
|
+
* await saveToDatabase(event.content.subscription);
|
|
97
|
+
* await sendWelcomeEmail(event.content.customer);
|
|
98
|
+
* response?.status(200).send('OK');
|
|
99
|
+
* });
|
|
100
|
+
* ```
|
|
101
|
+
*/
|
|
102
|
+
export class WebhookHandler extends EventEmitter {
|
|
103
|
+
/**
|
|
104
|
+
* Creates a new WebhookHandler instance.
|
|
105
|
+
*
|
|
106
|
+
* @param options - Optional configuration options
|
|
107
|
+
*
|
|
108
|
+
* @example
|
|
109
|
+
* ```typescript
|
|
110
|
+
* // Without authentication (not recommended for production)
|
|
111
|
+
* const handler = new WebhookHandler();
|
|
112
|
+
*
|
|
113
|
+
* // With Basic Auth
|
|
114
|
+
* const handler = new WebhookHandler({
|
|
115
|
+
* requestValidator: basicAuthValidator((u, p) => u === 'user' && p === 'pass'),
|
|
116
|
+
* });
|
|
117
|
+
* ```
|
|
118
|
+
*/
|
|
119
|
+
constructor(options) {
|
|
120
|
+
super({ captureRejections: true });
|
|
121
|
+
this._noAuthWarningShown = false;
|
|
122
|
+
this._requestValidator = options === null || options === void 0 ? void 0 : options.requestValidator;
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* Gets the current request validator function.
|
|
126
|
+
*
|
|
127
|
+
* @returns The configured validator or `undefined` if no authentication is set
|
|
128
|
+
*
|
|
129
|
+
* @example
|
|
130
|
+
* ```typescript
|
|
131
|
+
* if (handler.requestValidator) {
|
|
132
|
+
* console.log('Authentication is configured');
|
|
133
|
+
* }
|
|
134
|
+
* ```
|
|
135
|
+
*/
|
|
136
|
+
get requestValidator() {
|
|
137
|
+
return this._requestValidator;
|
|
138
|
+
}
|
|
139
|
+
/**
|
|
140
|
+
* Sets or updates the request validator function.
|
|
141
|
+
*
|
|
142
|
+
* Use this to configure authentication after handler creation,
|
|
143
|
+
* or to change validators at runtime.
|
|
144
|
+
*
|
|
145
|
+
* @param validator - The validator function, or `undefined` to disable authentication
|
|
146
|
+
*
|
|
147
|
+
* @example
|
|
148
|
+
* ```typescript
|
|
149
|
+
* // Set up Basic Auth after creation
|
|
150
|
+
* handler.requestValidator = basicAuthValidator((u, p) => u === 'admin' && p === 'secret');
|
|
151
|
+
*
|
|
152
|
+
* // Custom header validation
|
|
153
|
+
* handler.requestValidator = (headers) => {
|
|
154
|
+
* if (headers['x-webhook-secret'] !== process.env.WEBHOOK_SECRET) {
|
|
155
|
+
* throw new Error('Invalid webhook secret');
|
|
156
|
+
* }
|
|
157
|
+
* };
|
|
158
|
+
*
|
|
159
|
+
* // Disable authentication (not recommended)
|
|
160
|
+
* handler.requestValidator = undefined;
|
|
161
|
+
* ```
|
|
162
|
+
*/
|
|
163
|
+
set requestValidator(validator) {
|
|
164
|
+
this._requestValidator = validator;
|
|
165
|
+
}
|
|
166
|
+
/**
|
|
167
|
+
* Registers an event listener for a specific webhook event type.
|
|
168
|
+
*
|
|
169
|
+
* This method is inherited from Node.js `EventEmitter` but is documented here
|
|
170
|
+
* for clarity on available Chargebee webhook events.
|
|
171
|
+
*
|
|
172
|
+
* @param eventName - The Chargebee event type to listen for (e.g., `'subscription_created'`)
|
|
173
|
+
* @param listener - Callback function invoked when the event occurs
|
|
174
|
+
* @returns This handler instance for method chaining
|
|
175
|
+
*
|
|
176
|
+
* @remarks
|
|
177
|
+
* **Memory Leak Warning:** Listeners persist for the handler's lifetime.
|
|
178
|
+
* Always register listeners once at application startup, never inside
|
|
179
|
+
* request handlers or loops.
|
|
180
|
+
*
|
|
181
|
+
* @example Available Events
|
|
182
|
+
* ```typescript
|
|
183
|
+
* // Chargebee business events
|
|
184
|
+
* handler.on('subscription_created', ({ event, response }) => { ... });
|
|
185
|
+
* handler.on('subscription_changed', ({ event, response }) => { ... });
|
|
186
|
+
* handler.on('subscription_cancelled', ({ event, response }) => { ... });
|
|
187
|
+
* handler.on('customer_created', ({ event, response }) => { ... });
|
|
188
|
+
* handler.on('payment_succeeded', ({ event, response }) => { ... });
|
|
189
|
+
* handler.on('invoice_generated', ({ event, response }) => { ... });
|
|
190
|
+
* // ... and many more - see WebhookEventType enum for full list
|
|
191
|
+
*
|
|
192
|
+
* // Special events
|
|
193
|
+
* handler.on('unhandled_event', ({ event }) => {
|
|
194
|
+
* // Called when no listener exists for the event type
|
|
195
|
+
* console.log('Unhandled:', event.event_type);
|
|
196
|
+
* });
|
|
197
|
+
*
|
|
198
|
+
* handler.on('error', (error, { response }) => {
|
|
199
|
+
* // Called on validation errors, parse errors, or handler errors
|
|
200
|
+
* if (error instanceof WebhookAuthenticationError) {
|
|
201
|
+
* response?.status(401).send('Unauthorized');
|
|
202
|
+
* } else if (error instanceof WebhookPayloadValidationError || error instanceof WebhookPayloadParseError) {
|
|
203
|
+
* response?.status(400).send('Bad Request');
|
|
204
|
+
* } else {
|
|
205
|
+
* response?.status(500).send('Internal Server Error');
|
|
206
|
+
* }
|
|
207
|
+
* });
|
|
208
|
+
* ```
|
|
209
|
+
*
|
|
210
|
+
* @example Correct Usage
|
|
211
|
+
* ```typescript
|
|
212
|
+
* // ✅ GOOD: Register once at startup
|
|
213
|
+
* const handler = createHandler();
|
|
214
|
+
* handler.on('subscription_created', handleSubscription);
|
|
215
|
+
* handler.on('error', handleError);
|
|
216
|
+
*
|
|
217
|
+
* app.post('/webhooks', async (req, res) => {
|
|
218
|
+
* await handler.handle({ body: req.body, headers: req.headers, response: res });
|
|
219
|
+
* });
|
|
220
|
+
* ```
|
|
221
|
+
*
|
|
222
|
+
* @example Incorrect Usage
|
|
223
|
+
* ```typescript
|
|
224
|
+
* // ❌ BAD: Don't register inside request handlers - causes memory leak!
|
|
225
|
+
* app.post('/webhooks', async (req, res) => {
|
|
226
|
+
* handler.on('subscription_created', async () => { ... }); // Memory leak!
|
|
227
|
+
* await handler.handle({ ... });
|
|
228
|
+
* });
|
|
229
|
+
* ```
|
|
230
|
+
*/
|
|
231
|
+
// Note: on() is inherited from EventEmitter with proper typing via WebhookEventMap
|
|
232
|
+
/**
|
|
233
|
+
* Handles an incoming webhook request from Chargebee.
|
|
234
|
+
*
|
|
235
|
+
* This method:
|
|
236
|
+
* 1. Validates the request using the configured `requestValidator` (if any)
|
|
237
|
+
* 2. Parses the request body (if it's a string)
|
|
238
|
+
* 3. Validates required fields (`event_type`, `id`)
|
|
239
|
+
* 4. Emits the appropriate event to registered listeners
|
|
240
|
+
*
|
|
241
|
+
* @param options - The webhook request options
|
|
242
|
+
* @returns A promise that resolves when the event has been emitted
|
|
243
|
+
*
|
|
244
|
+
* @throws Error if no `error` listener is registered and an error occurs
|
|
245
|
+
*
|
|
246
|
+
* @remarks
|
|
247
|
+
* **Async Behavior:** This method emits events but does not wait for async
|
|
248
|
+
* listeners to complete. Errors in async listeners are captured via
|
|
249
|
+
* `captureRejections` and emitted to the `error` event.
|
|
250
|
+
*
|
|
251
|
+
* **Response Handling:** The handler does NOT automatically send HTTP responses.
|
|
252
|
+
* Your event listeners must call `response.status(200).send('OK')` or similar.
|
|
253
|
+
*
|
|
254
|
+
* @example Express Integration
|
|
255
|
+
* ```typescript
|
|
256
|
+
* app.post('/webhooks', async (req, res) => {
|
|
257
|
+
* try {
|
|
258
|
+
* await handler.handle({
|
|
259
|
+
* body: req.body,
|
|
260
|
+
* headers: req.headers,
|
|
261
|
+
* request: req,
|
|
262
|
+
* response: res,
|
|
263
|
+
* });
|
|
264
|
+
* } catch (error) {
|
|
265
|
+
* // Only reached if no 'error' listener is registered
|
|
266
|
+
* res.status(500).send('Internal error');
|
|
267
|
+
* }
|
|
268
|
+
* });
|
|
269
|
+
* ```
|
|
270
|
+
*
|
|
271
|
+
* @example Fastify Integration
|
|
272
|
+
* ```typescript
|
|
273
|
+
* fastify.post('/webhooks', async (request, reply) => {
|
|
274
|
+
* await handler.handle({
|
|
275
|
+
* body: request.body,
|
|
276
|
+
* headers: request.headers,
|
|
277
|
+
* request,
|
|
278
|
+
* response: reply,
|
|
279
|
+
* });
|
|
280
|
+
* });
|
|
281
|
+
* ```
|
|
282
|
+
*
|
|
283
|
+
* @example Raw Node.js HTTP
|
|
284
|
+
* ```typescript
|
|
285
|
+
* http.createServer(async (req, res) => {
|
|
286
|
+
* if (req.method === 'POST' && req.url === '/webhooks') {
|
|
287
|
+
* const body = await getBody(req);
|
|
288
|
+
* await handler.handle({ body, headers: req.headers, response: res });
|
|
289
|
+
* }
|
|
290
|
+
* });
|
|
291
|
+
* ```
|
|
292
|
+
*/
|
|
293
|
+
async handle(options) {
|
|
294
|
+
const { body, headers, request, response } = options;
|
|
295
|
+
try {
|
|
296
|
+
if (this._requestValidator) {
|
|
297
|
+
if (!headers) {
|
|
298
|
+
console.warn('[chargebee] Warning: Request validator is configured but no headers were passed. ' +
|
|
299
|
+
'Authentication check skipped. If this is intentional (no-auth webhook), ' +
|
|
300
|
+
'you can remove the requestValidator or ignore this warning.');
|
|
301
|
+
}
|
|
302
|
+
else {
|
|
303
|
+
await this._requestValidator(headers);
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
else if (!this._noAuthWarningShown) {
|
|
307
|
+
this._noAuthWarningShown = true;
|
|
308
|
+
console.warn('[chargebee] Warning: No webhook authentication configured. ' +
|
|
309
|
+
'Consider using basicAuthValidator() or a custom requestValidator for production. ' +
|
|
310
|
+
'See: https://www.chargebee.com/docs/billing/2.0/site-configuration/webhook_settings#basic-authentication');
|
|
311
|
+
}
|
|
312
|
+
let event;
|
|
313
|
+
try {
|
|
314
|
+
event =
|
|
315
|
+
typeof body === 'string' ? JSON.parse(body) : body;
|
|
316
|
+
}
|
|
317
|
+
catch (parseErr) {
|
|
318
|
+
const parseError = parseErr instanceof Error ? parseErr : new Error(String(parseErr));
|
|
319
|
+
throw new WebhookPayloadParseError(`Failed to parse webhook body: ${parseError.message}`, typeof body === 'string' ? body : undefined);
|
|
320
|
+
}
|
|
321
|
+
// Validate required fields
|
|
322
|
+
if (!event || typeof event !== 'object' || Array.isArray(event)) {
|
|
323
|
+
throw new WebhookPayloadValidationError('Invalid webhook payload: body must be a JSON object');
|
|
324
|
+
}
|
|
325
|
+
if (!event.event_type || typeof event.event_type !== 'string') {
|
|
326
|
+
throw new WebhookPayloadValidationError('Invalid webhook payload: missing or invalid event_type');
|
|
327
|
+
}
|
|
328
|
+
if (!event.id) {
|
|
329
|
+
throw new WebhookPayloadValidationError('Invalid webhook payload: missing event id');
|
|
330
|
+
}
|
|
331
|
+
const context = {
|
|
332
|
+
event,
|
|
333
|
+
request,
|
|
334
|
+
response,
|
|
335
|
+
};
|
|
336
|
+
const eventType = event.event_type;
|
|
337
|
+
if (this.listenerCount(eventType) > 0) {
|
|
338
|
+
this.emit(eventType, context);
|
|
339
|
+
}
|
|
340
|
+
else {
|
|
341
|
+
this.emit('unhandled_event', context);
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
catch (err) {
|
|
345
|
+
const error = err instanceof Error ? err : new Error(String(err));
|
|
346
|
+
if (this.listenerCount('error') === 0) {
|
|
347
|
+
console.warn('[chargebee] Webhook error with no handler:', error.message);
|
|
348
|
+
throw error;
|
|
349
|
+
}
|
|
350
|
+
this.emit('error', error, { request, response });
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
/**
|
|
355
|
+
* Creates a new WebhookHandler with custom configuration.
|
|
356
|
+
*
|
|
357
|
+
* This is the recommended factory function for creating webhook handlers.
|
|
358
|
+
* Use this when you need explicit control over authentication configuration.
|
|
359
|
+
*
|
|
360
|
+
* @typeParam ReqT - Framework-specific request type (e.g., `express.Request`)
|
|
361
|
+
* @typeParam ResT - Framework-specific response type (e.g., `express.Response`)
|
|
362
|
+
*
|
|
363
|
+
* @param options - Optional configuration for the handler
|
|
364
|
+
* @returns A new WebhookHandler instance
|
|
365
|
+
*
|
|
366
|
+
* @remarks
|
|
367
|
+
* For multi-route or multi-tenant scenarios, create separate handler instances
|
|
368
|
+
* to maintain isolation and avoid event listener conflicts.
|
|
369
|
+
*
|
|
370
|
+
* @example Basic Auth Configuration
|
|
371
|
+
* ```typescript
|
|
372
|
+
* import { createHandler, basicAuthValidator } from 'chargebee/webhook';
|
|
373
|
+
*
|
|
374
|
+
* const handler = createHandler({
|
|
375
|
+
* requestValidator: basicAuthValidator((username, password) => {
|
|
376
|
+
* return username === 'admin' && password === 'secret';
|
|
377
|
+
* }),
|
|
378
|
+
* });
|
|
379
|
+
* ```
|
|
380
|
+
*
|
|
381
|
+
* @example Custom Authentication
|
|
382
|
+
* ```typescript
|
|
383
|
+
* const handler = createHandler({
|
|
384
|
+
* requestValidator: async (headers) => {
|
|
385
|
+
* const signature = headers['x-chargebee-signature'];
|
|
386
|
+
* const isValid = await verifySignature(signature);
|
|
387
|
+
* if (!isValid) throw new Error('Invalid signature');
|
|
388
|
+
* },
|
|
389
|
+
* });
|
|
390
|
+
* ```
|
|
391
|
+
*
|
|
392
|
+
* @example Multi-Route Setup
|
|
393
|
+
* ```typescript
|
|
394
|
+
* // Separate handlers for different webhook endpoints
|
|
395
|
+
* const billingHandler = createHandler({ ... });
|
|
396
|
+
* const notificationHandler = createHandler({ ... });
|
|
397
|
+
*
|
|
398
|
+
* billingHandler.on('subscription_created', handleBillingEvent);
|
|
399
|
+
* notificationHandler.on('subscription_created', sendNotification);
|
|
400
|
+
*
|
|
401
|
+
* app.post('/webhooks/billing', (req, res) => billingHandler.handle({ ... }));
|
|
402
|
+
* app.post('/webhooks/notifications', (req, res) => notificationHandler.handle({ ... }));
|
|
403
|
+
* ```
|
|
404
|
+
*
|
|
405
|
+
* @example Without Authentication (Development Only)
|
|
406
|
+
* ```typescript
|
|
407
|
+
* // ⚠️ Not recommended for production
|
|
408
|
+
* const handler = createHandler();
|
|
409
|
+
* ```
|
|
410
|
+
*/
|
|
411
|
+
export function createHandler(options) {
|
|
412
|
+
return new WebhookHandler(options);
|
|
413
|
+
}
|
|
414
|
+
/**
|
|
415
|
+
* Creates a WebhookHandler with auto-configured Basic Auth from environment variables.
|
|
416
|
+
*
|
|
417
|
+
* This is a convenience function that automatically configures Basic Auth
|
|
418
|
+
* when the following environment variables are set:
|
|
419
|
+
* - `CHARGEBEE_WEBHOOK_USERNAME`
|
|
420
|
+
* - `CHARGEBEE_WEBHOOK_PASSWORD`
|
|
421
|
+
*
|
|
422
|
+
* If these environment variables are not set, the handler is created without
|
|
423
|
+
* authentication (a warning will be logged on first webhook handling).
|
|
424
|
+
*
|
|
425
|
+
* @typeParam ReqT - Framework-specific request type (e.g., `express.Request`)
|
|
426
|
+
* @typeParam ResT - Framework-specific response type (e.g., `express.Response`)
|
|
427
|
+
*
|
|
428
|
+
* @returns A new WebhookHandler instance with optional auto-configured auth
|
|
429
|
+
*
|
|
430
|
+
* @remarks
|
|
431
|
+
* This function is used internally by `chargebee.webhooks` to provide a
|
|
432
|
+
* pre-configured handler on the Chargebee instance.
|
|
433
|
+
*
|
|
434
|
+
* @example Environment-Based Setup
|
|
435
|
+
* ```bash
|
|
436
|
+
* # .env file
|
|
437
|
+
* CHARGEBEE_WEBHOOK_USERNAME=webhook_user
|
|
438
|
+
* CHARGEBEE_WEBHOOK_PASSWORD=webhook_secret
|
|
439
|
+
* ```
|
|
440
|
+
*
|
|
441
|
+
* ```typescript
|
|
442
|
+
* import { createDefaultHandler } from 'chargebee/webhook';
|
|
443
|
+
*
|
|
444
|
+
* // Auth is automatically configured from env vars
|
|
445
|
+
* const handler = createDefaultHandler();
|
|
446
|
+
*
|
|
447
|
+
* handler.on('subscription_created', ({ event, response }) => {
|
|
448
|
+
* console.log('Subscription:', event.content.subscription.id);
|
|
449
|
+
* response?.status(200).send('OK');
|
|
450
|
+
* });
|
|
451
|
+
* ```
|
|
452
|
+
*
|
|
453
|
+
* @example Overriding Auto-Configured Auth
|
|
454
|
+
* ```typescript
|
|
455
|
+
* const handler = createDefaultHandler();
|
|
456
|
+
*
|
|
457
|
+
* // Override with custom validator if needed
|
|
458
|
+
* handler.requestValidator = myCustomValidator;
|
|
459
|
+
* ```
|
|
460
|
+
*
|
|
461
|
+
* @see {@link createHandler} for explicit configuration without environment variables
|
|
462
|
+
*/
|
|
463
|
+
export function createDefaultHandler() {
|
|
464
|
+
const handler = new WebhookHandler();
|
|
465
|
+
const username = process.env.CHARGEBEE_WEBHOOK_USERNAME;
|
|
466
|
+
const password = process.env.CHARGEBEE_WEBHOOK_PASSWORD;
|
|
467
|
+
if (username && password) {
|
|
468
|
+
handler.requestValidator = basicAuthValidator((u, p) => u === username && p === password);
|
|
469
|
+
}
|
|
470
|
+
return handler;
|
|
471
|
+
}
|
|
472
|
+
export { basicAuthValidator } from './auth.js';
|
package/package.json
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "chargebee",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.21.0",
|
|
4
4
|
"description": "A library for integrating with Chargebee.",
|
|
5
5
|
"scripts": {
|
|
6
6
|
"prepack": "npm install && npm run build",
|
|
7
|
+
"test": "mocha -r ts-node/register 'test/**/*.test.ts'",
|
|
7
8
|
"build": "npm run build-esm && npm run build-cjs",
|
|
8
9
|
"build-esm": "rm -rf esm && mkdir -p esm && tsc -p tsconfig.esm.json && echo '{\"type\":\"module\"}' > esm/package.json",
|
|
9
10
|
"build-cjs": "rm -rf cjs && mkdir -p cjs && tsc -p tsconfig.cjs.json && echo '{\"type\":\"commonjs\"}' > cjs/package.json",
|
|
@@ -32,8 +33,6 @@
|
|
|
32
33
|
"url": "http://github.com/chargebee/chargebee-node/blob/master/LICENSE"
|
|
33
34
|
}
|
|
34
35
|
],
|
|
35
|
-
"dependencies": {
|
|
36
|
-
},
|
|
37
36
|
"exports": {
|
|
38
37
|
"types": "./types/index.d.ts",
|
|
39
38
|
"browser": {
|
|
@@ -62,13 +61,19 @@
|
|
|
62
61
|
}
|
|
63
62
|
},
|
|
64
63
|
"devDependencies": {
|
|
65
|
-
"@types/
|
|
64
|
+
"@types/chai": "^4.3.5",
|
|
65
|
+
"@types/mocha": "^10.0.10",
|
|
66
|
+
"@types/node": "20.12.0",
|
|
67
|
+
"chai": "^4.3.7",
|
|
68
|
+
"mocha": "^10.2.0",
|
|
66
69
|
"prettier": "^3.3.3",
|
|
67
|
-
"
|
|
70
|
+
"ts-node": "^10.9.1",
|
|
71
|
+
"typescript": "^5.5.4",
|
|
72
|
+
"undici-types": "^7.16.0"
|
|
68
73
|
},
|
|
69
74
|
"prettier": {
|
|
70
75
|
"semi": true,
|
|
71
76
|
"singleQuote": true,
|
|
72
77
|
"parser": "typescript"
|
|
73
78
|
}
|
|
74
|
-
}
|
|
79
|
+
}
|
package/types/core.d.ts
CHANGED
|
@@ -151,7 +151,8 @@ declare module 'chargebee' {
|
|
|
151
151
|
| 'omnichannel_one_time_order_item'
|
|
152
152
|
| 'usage_file'
|
|
153
153
|
| 'business_rule'
|
|
154
|
-
| 'ruleset'
|
|
154
|
+
| 'ruleset'
|
|
155
|
+
| 'charge';
|
|
155
156
|
type EventNameEnum = 'cancellation_page_loaded';
|
|
156
157
|
type EventTypeEnum =
|
|
157
158
|
| 'coupon_created'
|
|
@@ -218,6 +219,7 @@ declare module 'chargebee' {
|
|
|
218
219
|
| 'payment_schedule_scheme_deleted'
|
|
219
220
|
| 'subscription_renewal_reminder'
|
|
220
221
|
| 'add_usages_reminder'
|
|
222
|
+
| 'payment_due_reminder'
|
|
221
223
|
| 'transaction_created'
|
|
222
224
|
| 'transaction_updated'
|
|
223
225
|
| 'transaction_deleted'
|
|
@@ -430,6 +432,7 @@ declare module 'chargebee' {
|
|
|
430
432
|
| 'deutsche_bank'
|
|
431
433
|
| 'ezidebit'
|
|
432
434
|
| 'twikey'
|
|
435
|
+
| 'tempus'
|
|
433
436
|
| 'gocardless'
|
|
434
437
|
| 'not_applicable';
|
|
435
438
|
type HierarchyOperationTypeEnum =
|
|
@@ -509,7 +512,12 @@ declare module 'chargebee' {
|
|
|
509
512
|
| 'kbc_payment_button'
|
|
510
513
|
| 'pay_by_bank'
|
|
511
514
|
| 'trustly'
|
|
512
|
-
| 'stablecoin'
|
|
515
|
+
| 'stablecoin'
|
|
516
|
+
| 'kakao_pay'
|
|
517
|
+
| 'naver_pay'
|
|
518
|
+
| 'revolut_pay'
|
|
519
|
+
| 'cash_app_pay';
|
|
520
|
+
type PaymentMethodSavePolicyEnum = 'always' | 'ask' | 'never';
|
|
513
521
|
type PaymentMethodTypeEnum =
|
|
514
522
|
| 'card'
|
|
515
523
|
| 'paypal_express_checkout'
|
|
@@ -540,7 +548,11 @@ declare module 'chargebee' {
|
|
|
540
548
|
| 'kbc_payment_button'
|
|
541
549
|
| 'pay_by_bank'
|
|
542
550
|
| 'trustly'
|
|
543
|
-
| 'stablecoin'
|
|
551
|
+
| 'stablecoin'
|
|
552
|
+
| 'kakao_pay'
|
|
553
|
+
| 'naver_pay'
|
|
554
|
+
| 'revolut_pay'
|
|
555
|
+
| 'cash_app_pay';
|
|
544
556
|
type PaymentVoucherTypeEnum = 'boleto';
|
|
545
557
|
type PeriodUnitEnum = 'day' | 'week' | 'month' | 'year';
|
|
546
558
|
type PriceTypeEnum = 'tax_exclusive' | 'tax_inclusive';
|
|
@@ -639,6 +651,10 @@ declare module 'chargebee' {
|
|
|
639
651
|
| 'pay_by_bank'
|
|
640
652
|
| 'trustly'
|
|
641
653
|
| 'stablecoin'
|
|
654
|
+
| 'kakao_pay'
|
|
655
|
+
| 'naver_pay'
|
|
656
|
+
| 'revolut_pay'
|
|
657
|
+
| 'cash_app_pay'
|
|
642
658
|
| 'free_trial'
|
|
643
659
|
| 'pay_up_front'
|
|
644
660
|
| 'pay_as_you_go';
|