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
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,53 @@
|
|
|
1
|
+
### v3.21.0 (2026-02-11)
|
|
2
|
+
* * *
|
|
3
|
+
|
|
4
|
+
### 🚀 New Feature
|
|
5
|
+
|
|
6
|
+
* Added webhook handler support. Refer to the [README.md](https://github.com/chargebee/chargebee-node/blob/master/README.md#handle-webhooks) for more details.
|
|
7
|
+
|
|
8
|
+
### v3.20.0 (2026-02-06)
|
|
9
|
+
* * *
|
|
10
|
+
### New Attributes:
|
|
11
|
+
- [`subscription_id`](https://apidocs.chargebee.com/docs/api/entitlement_overrides/entitlement-override-object#subscription_id) has been added to [`EntitlementOverride`](https://apidocs.chargebee.com/docs/api/entitlement_overrides).
|
|
12
|
+
- [`is_enabled`](https://apidocs.chargebee.com/docs/api/entitlement_overrides/entitlement-override-object#is_enabled) has been added to [`EntitlementOverride`](https://apidocs.chargebee.com/docs/api/entitlement_overrides).
|
|
13
|
+
- [`decommissioned`](https://apidocs.chargebee.com/docs/api/subscriptions/subscription-object#decommissioned) has been added to [`Subscription`](https://apidocs.chargebee.com/docs/api/subscriptions).
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
### New Parameters:
|
|
17
|
+
- [`omnichannel_subscription_item`](https://apidocs.chargebee.com/docs/api/omnichannel_subscriptions/list-omnichannel-subscriptions#omnichannel_subscription_item) has been added as query parameter to [`list_omnichannel_subscriptions`](https://apidocs.chargebee.com/docs/api/omnichannel_subscriptions/list-omnichannel-subscriptions) in [`OmnichannelSubscription`](https://apidocs.chargebee.com/docs/api/omnichannel_subscriptions).
|
|
18
|
+
- [`entitlement_overrides.entity_id`](https://apidocs.chargebee.com/docs/api/entitlement_overrides/upsert-or-remove-entitlement-overrides-for-a-subscription#entitlement_overrides_entity_id) has been added as request body parameter to [`upsert_or_remove_entitlement_overrides_for_a_subscription`](https://apidocs.chargebee.com/docs/api/entitlement_overrides/upsert-or-remove-entitlement-overrides-for-a-subscription) in [`EntitlementOverride`](https://apidocs.chargebee.com/docs/api/entitlement_overrides).
|
|
19
|
+
- [`entitlement_overrides.entity_type`](https://apidocs.chargebee.com/docs/api/entitlement_overrides/upsert-or-remove-entitlement-overrides-for-a-subscription#entitlement_overrides_entity_type) has been added as request body parameter to [`upsert_or_remove_entitlement_overrides_for_a_subscription`](https://apidocs.chargebee.com/docs/api/entitlement_overrides/upsert-or-remove-entitlement-overrides-for-a-subscription) in [`EntitlementOverride`](https://apidocs.chargebee.com/docs/api/entitlement_overrides).
|
|
20
|
+
- [`entitlement_overrides.is_enabled`](https://apidocs.chargebee.com/docs/api/entitlement_overrides/upsert-or-remove-entitlement-overrides-for-a-subscription#entitlement_overrides_is_enabled) has been added as request body parameter to [`upsert_or_remove_entitlement_overrides_for_a_subscription`](https://apidocs.chargebee.com/docs/api/entitlement_overrides/upsert-or-remove-entitlement-overrides-for-a-subscription) in [`EntitlementOverride`](https://apidocs.chargebee.com/docs/api/entitlement_overrides).
|
|
21
|
+
- [`payment_method_save_policy`](https://apidocs.chargebee.com/docs/api/hosted_pages/collect-now#payment_method_save_policy) has been added as request body parameter to [`collect_now`](https://apidocs.chargebee.com/docs/api/hosted_pages/collect-now) in [`HostedPage`](https://apidocs.chargebee.com/docs/api/hosted_pages).
|
|
22
|
+
- [`decommissioned`](https://apidocs.chargebee.com/docs/api/subscriptions/cancel-subscription-for-items#decommissioned) has been added as request body parameter to [`cancel_subscription_for_items`](https://apidocs.chargebee.com/docs/api/subscriptions/cancel-subscription-for-items) in [`Subscription`](https://apidocs.chargebee.com/docs/api/subscriptions).
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
### Parameter Updates:
|
|
26
|
+
- [`pricing_page`](https://apidocs.chargebee.com/docs/api/pricing_page_sessions/create-pricing-page-for-existing-subscription#pricing_page) has been changed from required to optional in [`create_pricing_page_for_existing_subscription`](https://apidocs.chargebee.com/docs/api/pricing_page_sessions/create-pricing-page-for-existing-subscription) of [`PricingPageSession`](https://apidocs.chargebee.com/docs/api/pricing_page_sessions).
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
### New Events:
|
|
30
|
+
- [`payment_due_reminder`](https://apidocs.chargebee.com/docs/api/events/webhook/payment_due_reminder) has been added.
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
### New Enums:
|
|
34
|
+
- `charge` has been added as a new value enum `EntityType`.
|
|
35
|
+
- `payment_due_reminder` has been added as a new value enum `EventType`.
|
|
36
|
+
- `tempus` has been added as a new value enum `Gateway`.
|
|
37
|
+
- `kakao_pay`, `naver_pay`, `revolut_pay`, and `cash_app_pay` have been added as new values enum `PaymentMethod`.
|
|
38
|
+
- `always`, `ask`, and `never` have been added as new values enum `PaymentMethodSavePolicy`.
|
|
39
|
+
- `kakao_pay`, `naver_pay`, `revolut_pay`, and `cash_app_pay` have been added as new values enum `PaymentMethodType`.
|
|
40
|
+
- `kakao_pay`, `naver_pay`, `revolut_pay`, and `cash_app_pay` have been added as new values enum `Type`.
|
|
41
|
+
- `accepted`, `rejected`, `message_acknowledgement`, `in_process`, `under_query`, `conditionally_accepted`, and `paid` have been added as new values to enum attribute [`einvoice.status`](https://apidocs.chargebee.com/docs/api/credit_notes/credit-note-object#einvoice_status) in [`CreditNote`](https://apidocs.chargebee.com/docs/api/credit_notes).
|
|
42
|
+
- `accepted`, `rejected`, `message_acknowledgement`, `in_process`, `under_query`, `conditionally_accepted`, and `paid` have been added as new values to enum attribute [`status`](https://apidocs.chargebee.com/docs/api/einvoices/einvoice-object#status) in [`Einvoice`](https://apidocs.chargebee.com/docs/api/einvoices).
|
|
43
|
+
- `accepted`, `rejected`, `message_acknowledgement`, `in_process`, `under_query`, `conditionally_accepted`, and `paid` have been added as new values to enum attribute [`einvoice.status`](https://apidocs.chargebee.com/docs/api/invoices/invoice-object#einvoice_status) in [`Invoice`](https://apidocs.chargebee.com/docs/api/invoices).
|
|
44
|
+
- `kakao_pay`, `naver_pay`, `revolut_pay`, `cash_app_pay`, `wechat_pay`, and `alipay` have been added as new values to enum attribute [`payment_method_type`](https://apidocs.chargebee.com/docs/api/payment_intents/payment-intent-object#payment_method_type) in [`PaymentIntent`](https://apidocs.chargebee.com/docs/api/payment_intents).
|
|
45
|
+
- `kakao_pay`, `naver_pay`, `revolut_pay`, `cash_app_pay`, `wechat_pay`, and `alipay` have been added as new values to enum attribute [`active_payment_attempt.payment_method_type`](https://apidocs.chargebee.com/docs/api/payment_intents/payment-intent-object#active_payment_attempt_payment_method_type) in [`PaymentIntent`](https://apidocs.chargebee.com/docs/api/payment_intents).
|
|
46
|
+
- `kakao_pay`, `naver_pay`, `revolut_pay`, `cash_app_pay`, `wechat_pay`, and `alipay` have been added as new values to enum request body parameter `payment_method_type` in [`update_a_payment_intent`](https://apidocs.chargebee.com/docs/api/payment_intents/update-a-payment-intent) of [`PaymentIntent`](https://apidocs.chargebee.com/docs/api/payment_intents).
|
|
47
|
+
- `kakao_pay`, `naver_pay`, `revolut_pay`, `cash_app_pay`, `wechat_pay`, and `alipay` have been added as new values to enum request body parameter `payment_method_type` in [`create_a_payment_intent`](https://apidocs.chargebee.com/docs/api/payment_intents/create-a-payment-intent) of [`PaymentIntent`](https://apidocs.chargebee.com/docs/api/payment_intents).
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
|
|
1
51
|
### v3.19.0 (2026-01-16)
|
|
2
52
|
* * *
|
|
3
53
|
|
package/README.md
CHANGED
|
@@ -148,6 +148,253 @@ const chargebeeSiteEU = new Chargebee({
|
|
|
148
148
|
});
|
|
149
149
|
```
|
|
150
150
|
|
|
151
|
+
### Handle webhooks
|
|
152
|
+
|
|
153
|
+
Use the webhook handlers to parse and route webhook payloads from Chargebee with full TypeScript support.
|
|
154
|
+
|
|
155
|
+
#### Quick Start: Using the instance `webhooks` handler
|
|
156
|
+
|
|
157
|
+
The simplest way to handle webhooks is using the `webhooks` property on your initialized Chargebee client:
|
|
158
|
+
|
|
159
|
+
```typescript
|
|
160
|
+
import express from 'express';
|
|
161
|
+
import Chargebee, {
|
|
162
|
+
AuthenticationError,
|
|
163
|
+
PayloadValidationError,
|
|
164
|
+
PayloadParseError,
|
|
165
|
+
} from 'chargebee';
|
|
166
|
+
|
|
167
|
+
const chargebee = new Chargebee({
|
|
168
|
+
site: '{{site}}',
|
|
169
|
+
apiKey: '{{api-key}}',
|
|
170
|
+
});
|
|
171
|
+
|
|
172
|
+
const app = express();
|
|
173
|
+
app.use(express.json());
|
|
174
|
+
|
|
175
|
+
// ⚠️ Register listeners once at startup, not inside request handlers
|
|
176
|
+
chargebee.webhooks.on('subscription_created', async ({ event, response }) => {
|
|
177
|
+
console.log(`Subscription created: ${event.id}`);
|
|
178
|
+
const subscription = event.content.subscription;
|
|
179
|
+
console.log(`Customer: ${subscription.customer_id}`);
|
|
180
|
+
response?.status(200).send('OK');
|
|
181
|
+
});
|
|
182
|
+
|
|
183
|
+
chargebee.webhooks.on('error', (error, { response }) => {
|
|
184
|
+
if (error instanceof AuthenticationError) {
|
|
185
|
+
response?.status(401).send('Unauthorized');
|
|
186
|
+
} else if (error instanceof PayloadValidationError || error instanceof PayloadParseError) {
|
|
187
|
+
response?.status(400).send('Bad Request');
|
|
188
|
+
} else {
|
|
189
|
+
console.error('Webhook error:', error.message);
|
|
190
|
+
response?.status(500).send('Internal Server Error');
|
|
191
|
+
}
|
|
192
|
+
});
|
|
193
|
+
|
|
194
|
+
app.post('/chargebee/webhooks', async (req, res) => {
|
|
195
|
+
await chargebee.webhooks.handle({
|
|
196
|
+
body: req.body,
|
|
197
|
+
headers: req.headers,
|
|
198
|
+
request: req,
|
|
199
|
+
response: res,
|
|
200
|
+
});
|
|
201
|
+
});
|
|
202
|
+
|
|
203
|
+
app.listen(8080);
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
**Auto-configured Basic Auth:** The `webhooks` handler automatically configures Basic Auth validation if the following environment variables are set:
|
|
207
|
+
|
|
208
|
+
- `CHARGEBEE_WEBHOOK_USERNAME` - The expected username
|
|
209
|
+
- `CHARGEBEE_WEBHOOK_PASSWORD` - The expected password
|
|
210
|
+
|
|
211
|
+
When both are present, incoming webhook requests will be validated against these credentials.
|
|
212
|
+
|
|
213
|
+
#### Creating typed webhook handlers
|
|
214
|
+
|
|
215
|
+
For more control or multiple webhook endpoints, use `chargebee.webhooks.createHandler()`:
|
|
216
|
+
|
|
217
|
+
```typescript
|
|
218
|
+
import express, { Request, Response } from 'express';
|
|
219
|
+
import Chargebee, {
|
|
220
|
+
basicAuthValidator,
|
|
221
|
+
AuthenticationError,
|
|
222
|
+
PayloadValidationError,
|
|
223
|
+
PayloadParseError,
|
|
224
|
+
} from 'chargebee';
|
|
225
|
+
|
|
226
|
+
const chargebee = new Chargebee({
|
|
227
|
+
site: '{{site}}',
|
|
228
|
+
apiKey: '{{api-key}}',
|
|
229
|
+
});
|
|
230
|
+
|
|
231
|
+
const app = express();
|
|
232
|
+
app.use(express.json());
|
|
233
|
+
|
|
234
|
+
// Create a typed handler for Express
|
|
235
|
+
const handler = chargebee.webhooks.createHandler<Request, Response>();
|
|
236
|
+
|
|
237
|
+
// Optional: Add request validator (e.g., Basic Auth)
|
|
238
|
+
handler.requestValidator = basicAuthValidator((username, password) => {
|
|
239
|
+
return username === 'admin' && password === 'secret';
|
|
240
|
+
});
|
|
241
|
+
|
|
242
|
+
// ⚠️ Register event listeners once at startup, not inside request handlers
|
|
243
|
+
handler.on('subscription_created', async ({ event, response }) => {
|
|
244
|
+
console.log(`Subscription created: ${event.id}`);
|
|
245
|
+
const subscription = event.content.subscription;
|
|
246
|
+
console.log(`Customer: ${subscription.customer_id}`);
|
|
247
|
+
console.log(`Plan: ${subscription.plan_id}`);
|
|
248
|
+
response?.status(200).send('OK');
|
|
249
|
+
});
|
|
250
|
+
|
|
251
|
+
handler.on('payment_succeeded', async ({ event, response }) => {
|
|
252
|
+
console.log(`Payment succeeded: ${event.id}`);
|
|
253
|
+
const transaction = event.content.transaction;
|
|
254
|
+
const customer = event.content.customer;
|
|
255
|
+
console.log(`Amount: ${transaction.amount}, Customer: ${customer.email}`);
|
|
256
|
+
response?.status(200).send('OK');
|
|
257
|
+
});
|
|
258
|
+
|
|
259
|
+
handler.on('error', (error, { response }) => {
|
|
260
|
+
if (error instanceof AuthenticationError) {
|
|
261
|
+
response?.status(401).send('Unauthorized');
|
|
262
|
+
} else if (error instanceof PayloadValidationError || error instanceof PayloadParseError) {
|
|
263
|
+
response?.status(400).send('Bad Request');
|
|
264
|
+
} else {
|
|
265
|
+
console.error('Webhook error:', error.message);
|
|
266
|
+
response?.status(500).send('Internal Server Error');
|
|
267
|
+
}
|
|
268
|
+
});
|
|
269
|
+
|
|
270
|
+
app.post('/chargebee/webhooks', async (req, res) => {
|
|
271
|
+
await handler.handle({
|
|
272
|
+
body: req.body,
|
|
273
|
+
headers: req.headers,
|
|
274
|
+
request: req,
|
|
275
|
+
response: res,
|
|
276
|
+
});
|
|
277
|
+
});
|
|
278
|
+
|
|
279
|
+
app.listen(8080);
|
|
280
|
+
```
|
|
281
|
+
|
|
282
|
+
#### Low-level: Parse and handle events manually
|
|
283
|
+
|
|
284
|
+
For more control, you can parse webhook events manually:
|
|
285
|
+
|
|
286
|
+
```typescript
|
|
287
|
+
import express from 'express';
|
|
288
|
+
import Chargebee, { type WebhookEvent } from 'chargebee';
|
|
289
|
+
|
|
290
|
+
const app = express();
|
|
291
|
+
app.use(express.json());
|
|
292
|
+
|
|
293
|
+
app.post('/chargebee/webhooks', async (req, res) => {
|
|
294
|
+
try {
|
|
295
|
+
const event = req.body as WebhookEvent;
|
|
296
|
+
|
|
297
|
+
switch (event.event_type) {
|
|
298
|
+
case 'subscription_created':
|
|
299
|
+
// Access event content with proper typing
|
|
300
|
+
const subscription = event.content.subscription;
|
|
301
|
+
console.log('Subscription created:', subscription.id);
|
|
302
|
+
break;
|
|
303
|
+
|
|
304
|
+
case 'payment_succeeded':
|
|
305
|
+
const transaction = event.content.transaction;
|
|
306
|
+
console.log('Payment succeeded:', transaction.amount);
|
|
307
|
+
break;
|
|
308
|
+
|
|
309
|
+
default:
|
|
310
|
+
console.log('Unhandled event type:', event.event_type);
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
res.status(200).send('OK');
|
|
314
|
+
} catch (err) {
|
|
315
|
+
console.error('Error processing webhook:', err);
|
|
316
|
+
res.status(500).send('Error processing webhook');
|
|
317
|
+
}
|
|
318
|
+
});
|
|
319
|
+
|
|
320
|
+
app.listen(8080);
|
|
321
|
+
```
|
|
322
|
+
|
|
323
|
+
#### Responding to Webhooks
|
|
324
|
+
|
|
325
|
+
> ⚠️ **Important:** Always send an HTTP response from your webhook handlers. If you don't respond with a 2xx status, Chargebee will retry the webhook, potentially causing duplicate processing.
|
|
326
|
+
|
|
327
|
+
**Respond with 200** to acknowledge receipt:
|
|
328
|
+
|
|
329
|
+
```typescript
|
|
330
|
+
handler.on('subscription_created', async ({ event, response }) => {
|
|
331
|
+
await provisionAccess(event.content.subscription);
|
|
332
|
+
response?.status(200).json({ received: true });
|
|
333
|
+
});
|
|
334
|
+
```
|
|
335
|
+
|
|
336
|
+
**Respond with 5xx** so Chargebee retries on failure:
|
|
337
|
+
|
|
338
|
+
```typescript
|
|
339
|
+
handler.on('payment_succeeded', async ({ event, response }) => {
|
|
340
|
+
try {
|
|
341
|
+
await recordPayment(event.content.transaction);
|
|
342
|
+
response?.status(200).send('OK');
|
|
343
|
+
} catch (err) {
|
|
344
|
+
response?.status(500).json({ error: 'Processing failed' });
|
|
345
|
+
}
|
|
346
|
+
});
|
|
347
|
+
```
|
|
348
|
+
|
|
349
|
+
**Access request context** (headers, middleware data):
|
|
350
|
+
|
|
351
|
+
```typescript
|
|
352
|
+
handler.on('customer_created', async ({ event, request, response }) => {
|
|
353
|
+
const tenantId = (request as any)?.tenant?.id;
|
|
354
|
+
await createCustomerForTenant(tenantId, event.content.customer);
|
|
355
|
+
response?.status(200).send('OK');
|
|
356
|
+
});
|
|
357
|
+
```
|
|
358
|
+
|
|
359
|
+
#### Handling Unhandled Events and Errors
|
|
360
|
+
|
|
361
|
+
The webhook handler provides specific error classes for different failure scenarios:
|
|
362
|
+
|
|
363
|
+
- **`AuthenticationError`** - Authentication failed (missing/invalid credentials)
|
|
364
|
+
- **`PayloadValidationError`** - Invalid webhook payload structure (missing required fields)
|
|
365
|
+
- **`PayloadParseError`** - Failed to parse JSON body
|
|
366
|
+
|
|
367
|
+
```typescript
|
|
368
|
+
import {
|
|
369
|
+
AuthenticationError,
|
|
370
|
+
PayloadValidationError,
|
|
371
|
+
PayloadParseError,
|
|
372
|
+
} from 'chargebee';
|
|
373
|
+
|
|
374
|
+
// Handle events without registered listeners
|
|
375
|
+
handler.on('unhandled_event', async ({ event, response }) => {
|
|
376
|
+
console.log(`Unhandled: ${event.event_type}`);
|
|
377
|
+
response?.status(200).send('OK');
|
|
378
|
+
});
|
|
379
|
+
|
|
380
|
+
// Handle errors with appropriate HTTP status codes
|
|
381
|
+
handler.on('error', (error, { response }) => {
|
|
382
|
+
if (error instanceof AuthenticationError) {
|
|
383
|
+
console.error('Authentication failed:', error.message);
|
|
384
|
+
response?.status(401).send('Unauthorized');
|
|
385
|
+
} else if (error instanceof PayloadValidationError) {
|
|
386
|
+
console.error('Invalid payload:', error.message);
|
|
387
|
+
response?.status(400).send('Bad Request');
|
|
388
|
+
} else if (error instanceof PayloadParseError) {
|
|
389
|
+
console.error('Failed to parse JSON:', error.message);
|
|
390
|
+
response?.status(400).send('Bad Request');
|
|
391
|
+
} else {
|
|
392
|
+
console.error('Unknown error:', error.message);
|
|
393
|
+
response?.status(500).send('Internal Server Error');
|
|
394
|
+
}
|
|
395
|
+
});
|
|
396
|
+
```
|
|
397
|
+
|
|
151
398
|
### Processing Webhooks - API Version Check
|
|
152
399
|
|
|
153
400
|
An attribute `api_version` is added to the [Event](https://apidocs.chargebee.com/docs/api/events) resource, which indicates the API version based on which the event content is structured. In your webhook servers, ensure this `api_version` is the same as the [API version](https://apidocs.chargebee.com/docs/api#versions) used by your webhook server's client library.
|
|
@@ -227,35 +474,15 @@ To improve type safety and gain better autocompletion when working with webhooks
|
|
|
227
474
|
import Chargebee, { WebhookEventType, WebhookEvent } from "chargebee";
|
|
228
475
|
|
|
229
476
|
const result = await chargebeeInstance.event.retrieve("{event-id}");
|
|
230
|
-
const subscriptionActivatedEvent: WebhookEvent<
|
|
477
|
+
const subscriptionActivatedEvent: WebhookEvent<WebhookEventType.SubscriptionActivated> = result.event;
|
|
231
478
|
const subscription = subscriptionActivatedEvent.content.subscription;
|
|
232
479
|
```
|
|
233
480
|
|
|
234
|
-
You can also use `WebhookEventType` in switch statements for runtime event handling:
|
|
235
|
-
|
|
236
|
-
```ts
|
|
237
|
-
import { WebhookEventType, WebhookEvent } from "chargebee";
|
|
238
|
-
|
|
239
|
-
function handleWebhook(event: WebhookEvent) {
|
|
240
|
-
switch (event.event_type) {
|
|
241
|
-
case WebhookEventType.SubscriptionCreated:
|
|
242
|
-
console.log("Subscription created:", event.content.subscription?.id);
|
|
243
|
-
break;
|
|
244
|
-
case WebhookEventType.PaymentSucceeded:
|
|
245
|
-
console.log("Payment succeeded:", event.content.transaction?.id);
|
|
246
|
-
break;
|
|
247
|
-
default:
|
|
248
|
-
console.log("Unhandled event:", event.event_type);
|
|
249
|
-
}
|
|
250
|
-
}
|
|
251
|
-
```
|
|
252
|
-
|
|
253
481
|
#### Notes
|
|
254
482
|
|
|
255
483
|
* `WebhookEvent<T>` provides type hinting for the event payload, making it easier to work with specific event structures.
|
|
256
|
-
* Use `WebhookEventType` to specify the exact event type (e.g., `SubscriptionCreated`, `InvoiceGenerated`, etc.).
|
|
257
|
-
*
|
|
258
|
-
* `WebhookContentType` is deprecated but still available for backward compatibility.
|
|
484
|
+
* Use the `WebhookEventType` to specify the exact event type (e.g., `SubscriptionCreated`, `InvoiceGenerated`, etc.).
|
|
485
|
+
* This approach ensures you get proper IntelliSense and compile-time checks when accessing event fields.
|
|
259
486
|
|
|
260
487
|
### Custom HTTP Client
|
|
261
488
|
|
package/cjs/chargebee.cjs.js
CHANGED
|
@@ -2,12 +2,19 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
const createChargebee_js_1 = require("./createChargebee.js");
|
|
4
4
|
const FetchClient_js_1 = require("./net/FetchClient.js");
|
|
5
|
-
const
|
|
5
|
+
const handler_js_1 = require("./resources/webhook/handler.js");
|
|
6
|
+
const auth_js_1 = require("./resources/webhook/auth.js");
|
|
6
7
|
const httpClient = new FetchClient_js_1.FetchHttpClient();
|
|
7
8
|
const Chargebee = (0, createChargebee_js_1.CreateChargebee)(httpClient);
|
|
8
9
|
module.exports = Chargebee;
|
|
9
10
|
module.exports.Chargebee = Chargebee;
|
|
10
11
|
module.exports.default = Chargebee;
|
|
11
|
-
// Export webhook
|
|
12
|
-
module.exports.WebhookEventType =
|
|
13
|
-
module.exports.WebhookContentType =
|
|
12
|
+
// Export webhook utilities
|
|
13
|
+
module.exports.WebhookEventType = handler_js_1.WebhookEventType;
|
|
14
|
+
module.exports.WebhookContentType = handler_js_1.WebhookContentType;
|
|
15
|
+
module.exports.basicAuthValidator = auth_js_1.basicAuthValidator;
|
|
16
|
+
// Export webhook error classes
|
|
17
|
+
module.exports.WebhookError = handler_js_1.WebhookError;
|
|
18
|
+
module.exports.WebhookAuthenticationError = handler_js_1.WebhookAuthenticationError;
|
|
19
|
+
module.exports.WebhookPayloadValidationError = handler_js_1.WebhookPayloadValidationError;
|
|
20
|
+
module.exports.WebhookPayloadParseError = handler_js_1.WebhookPayloadParseError;
|
package/cjs/createChargebee.js
CHANGED
|
@@ -6,6 +6,7 @@ const environment_js_1 = require("./environment.js");
|
|
|
6
6
|
const api_endpoints_js_1 = require("./resources/api_endpoints.js");
|
|
7
7
|
const util_js_1 = require("./util.js");
|
|
8
8
|
const asyncApiSupport_js_1 = require("./asyncApiSupport.js");
|
|
9
|
+
const handler_js_1 = require("./resources/webhook/handler.js");
|
|
9
10
|
const CreateChargebee = (httpClient) => {
|
|
10
11
|
const Chargebee = function (conf) {
|
|
11
12
|
this._env = Object.assign({}, environment_js_1.Environment);
|
|
@@ -15,6 +16,14 @@ const CreateChargebee = (httpClient) => {
|
|
|
15
16
|
conf.httpClient != null ? conf.httpClient : httpClient;
|
|
16
17
|
this._buildResources();
|
|
17
18
|
this._endpoints = api_endpoints_js_1.Endpoints;
|
|
19
|
+
// Initialize webhooks handler with auto-configured Basic Auth (if env vars are set)
|
|
20
|
+
const handler = (0, handler_js_1.createDefaultHandler)();
|
|
21
|
+
// Create webhooks namespace with handler methods + createHandler factory
|
|
22
|
+
this.webhooks = Object.assign(handler, {
|
|
23
|
+
createHandler(options) {
|
|
24
|
+
return new handler_js_1.WebhookHandler(options);
|
|
25
|
+
},
|
|
26
|
+
});
|
|
18
27
|
};
|
|
19
28
|
Chargebee.prototype = {
|
|
20
29
|
_createApiFunc(apiCall, env) {
|
package/cjs/environment.js
CHANGED
|
@@ -11,7 +11,7 @@ exports.Environment = {
|
|
|
11
11
|
hostSuffix: '.chargebee.com',
|
|
12
12
|
apiPath: '/api/v2',
|
|
13
13
|
timeout: DEFAULT_TIME_OUT,
|
|
14
|
-
clientVersion: 'v3.
|
|
14
|
+
clientVersion: 'v3.21.0',
|
|
15
15
|
port: DEFAULT_PORT,
|
|
16
16
|
timemachineWaitInMillis: DEFAULT_TIME_MACHINE_WAIT,
|
|
17
17
|
exportWaitInMillis: DEFAULT_EXPORT_WAIT,
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.basicAuthValidator = void 0;
|
|
4
|
+
const errors_js_1 = require("./errors.js");
|
|
5
|
+
/**
|
|
6
|
+
* Creates a Basic Auth validator for webhook requests.
|
|
7
|
+
* Parses the Authorization header and validates credentials.
|
|
8
|
+
*
|
|
9
|
+
* @param validateCredentials - Function to validate username/password.
|
|
10
|
+
* Can be sync or async (e.g., for database lookups).
|
|
11
|
+
* @returns A request validator function for use with WebhookHandler
|
|
12
|
+
*
|
|
13
|
+
* @throws {WebhookAuthenticationError} When authentication fails
|
|
14
|
+
*
|
|
15
|
+
* @example
|
|
16
|
+
* // Simple sync validation
|
|
17
|
+
* const validator = basicAuthValidator((u, p) => u === 'admin' && p === 'secret');
|
|
18
|
+
*
|
|
19
|
+
* @example
|
|
20
|
+
* // Async validation (e.g., database lookup)
|
|
21
|
+
* const validator = basicAuthValidator(async (u, p) => {
|
|
22
|
+
* const user = await db.findUser(u);
|
|
23
|
+
* return user && await bcrypt.compare(p, user.passwordHash);
|
|
24
|
+
* });
|
|
25
|
+
*/
|
|
26
|
+
const basicAuthValidator = (validateCredentials) => {
|
|
27
|
+
return async (headers) => {
|
|
28
|
+
const authHeader = headers['authorization'] || headers['Authorization'];
|
|
29
|
+
if (!authHeader) {
|
|
30
|
+
throw new errors_js_1.WebhookAuthenticationError('Missing authorization header');
|
|
31
|
+
}
|
|
32
|
+
const authStr = Array.isArray(authHeader) ? authHeader[0] : authHeader;
|
|
33
|
+
if (!authStr) {
|
|
34
|
+
throw new errors_js_1.WebhookAuthenticationError('Invalid authorization header');
|
|
35
|
+
}
|
|
36
|
+
const parts = authStr.split(' ');
|
|
37
|
+
if (parts.length !== 2 || parts[0] !== 'Basic') {
|
|
38
|
+
throw new errors_js_1.WebhookAuthenticationError('Invalid authorization header format');
|
|
39
|
+
}
|
|
40
|
+
const decoded = Buffer.from(parts[1], 'base64').toString();
|
|
41
|
+
const separatorIndex = decoded.indexOf(':');
|
|
42
|
+
if (separatorIndex === -1) {
|
|
43
|
+
throw new errors_js_1.WebhookAuthenticationError('Invalid credentials format');
|
|
44
|
+
}
|
|
45
|
+
const username = decoded.substring(0, separatorIndex);
|
|
46
|
+
const password = decoded.substring(separatorIndex + 1);
|
|
47
|
+
const isValid = await validateCredentials(username, password);
|
|
48
|
+
if (!isValid) {
|
|
49
|
+
throw new errors_js_1.WebhookAuthenticationError('Invalid credentials');
|
|
50
|
+
}
|
|
51
|
+
};
|
|
52
|
+
};
|
|
53
|
+
exports.basicAuthValidator = basicAuthValidator;
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.WebhookPayloadParseError = exports.WebhookPayloadValidationError = exports.WebhookAuthenticationError = exports.WebhookError = void 0;
|
|
4
|
+
/**
|
|
5
|
+
* Base class for all webhook-related errors.
|
|
6
|
+
* Extends the standard Error class with proper stack trace support.
|
|
7
|
+
*/
|
|
8
|
+
class WebhookError extends Error {
|
|
9
|
+
constructor(message) {
|
|
10
|
+
var _a;
|
|
11
|
+
super(message);
|
|
12
|
+
this.name = 'WebhookError';
|
|
13
|
+
// Maintains proper stack trace for where error was thrown (V8 only)
|
|
14
|
+
(_a = Error.captureStackTrace) === null || _a === void 0 ? void 0 : _a.call(Error, this, this.constructor);
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
exports.WebhookError = WebhookError;
|
|
18
|
+
/**
|
|
19
|
+
* Authentication error thrown when webhook request authentication fails.
|
|
20
|
+
*
|
|
21
|
+
* Common scenarios:
|
|
22
|
+
* - Missing authorization header
|
|
23
|
+
* - Invalid authorization header format
|
|
24
|
+
* - Invalid credentials
|
|
25
|
+
*
|
|
26
|
+
* Typically maps to HTTP 401 Unauthorized.
|
|
27
|
+
*
|
|
28
|
+
* @example
|
|
29
|
+
* ```typescript
|
|
30
|
+
* handler.on('error', (error, { response }) => {
|
|
31
|
+
* if (error instanceof WebhookAuthenticationError) {
|
|
32
|
+
* response?.status(401).json({ error: 'Unauthorized' });
|
|
33
|
+
* }
|
|
34
|
+
* });
|
|
35
|
+
* ```
|
|
36
|
+
*/
|
|
37
|
+
class WebhookAuthenticationError extends WebhookError {
|
|
38
|
+
constructor(message) {
|
|
39
|
+
super(message);
|
|
40
|
+
this.name = 'WebhookAuthenticationError';
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
exports.WebhookAuthenticationError = WebhookAuthenticationError;
|
|
44
|
+
/**
|
|
45
|
+
* Payload validation error thrown when the webhook payload structure is invalid.
|
|
46
|
+
*
|
|
47
|
+
* Common scenarios:
|
|
48
|
+
* - Missing required fields (event_type, id)
|
|
49
|
+
* - Invalid field types
|
|
50
|
+
* - Malformed payload structure
|
|
51
|
+
*
|
|
52
|
+
* Typically maps to HTTP 400 Bad Request.
|
|
53
|
+
*
|
|
54
|
+
* @example
|
|
55
|
+
* ```typescript
|
|
56
|
+
* handler.on('error', (error, { response }) => {
|
|
57
|
+
* if (error instanceof WebhookPayloadValidationError) {
|
|
58
|
+
* response?.status(400).json({ error: 'Bad Request', message: error.message });
|
|
59
|
+
* }
|
|
60
|
+
* });
|
|
61
|
+
* ```
|
|
62
|
+
*/
|
|
63
|
+
class WebhookPayloadValidationError extends WebhookError {
|
|
64
|
+
constructor(message) {
|
|
65
|
+
super(message);
|
|
66
|
+
this.name = 'WebhookPayloadValidationError';
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
exports.WebhookPayloadValidationError = WebhookPayloadValidationError;
|
|
70
|
+
/**
|
|
71
|
+
* JSON parsing error thrown when the webhook body cannot be parsed as JSON.
|
|
72
|
+
*
|
|
73
|
+
* Includes the raw body that failed to parse (if available) for debugging.
|
|
74
|
+
*
|
|
75
|
+
* Typically maps to HTTP 400 Bad Request.
|
|
76
|
+
*
|
|
77
|
+
* @example
|
|
78
|
+
* ```typescript
|
|
79
|
+
* handler.on('error', (error, { response }) => {
|
|
80
|
+
* if (error instanceof WebhookPayloadParseError) {
|
|
81
|
+
* console.error('Failed to parse:', error.rawBody);
|
|
82
|
+
* response?.status(400).json({ error: 'Invalid JSON' });
|
|
83
|
+
* }
|
|
84
|
+
* });
|
|
85
|
+
* ```
|
|
86
|
+
*/
|
|
87
|
+
class WebhookPayloadParseError extends WebhookError {
|
|
88
|
+
constructor(message, rawBody) {
|
|
89
|
+
super(message);
|
|
90
|
+
this.rawBody = rawBody;
|
|
91
|
+
this.name = 'WebhookPayloadParseError';
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
exports.WebhookPayloadParseError = WebhookPayloadParseError;
|
|
@@ -125,6 +125,7 @@ var WebhookEventType;
|
|
|
125
125
|
WebhookEventType["OrderResent"] = "order_resent";
|
|
126
126
|
WebhookEventType["OrderReturned"] = "order_returned";
|
|
127
127
|
WebhookEventType["OrderUpdated"] = "order_updated";
|
|
128
|
+
WebhookEventType["PaymentDueReminder"] = "payment_due_reminder";
|
|
128
129
|
WebhookEventType["PaymentFailed"] = "payment_failed";
|
|
129
130
|
WebhookEventType["PaymentInitiated"] = "payment_initiated";
|
|
130
131
|
WebhookEventType["PaymentIntentCreated"] = "payment_intent_created";
|
|
@@ -233,6 +234,16 @@ var WebhookEventType;
|
|
|
233
234
|
WebhookEventType["VoucherExpired"] = "voucher_expired";
|
|
234
235
|
})(WebhookEventType || (exports.WebhookEventType = WebhookEventType = {}));
|
|
235
236
|
/**
|
|
236
|
-
* @deprecated Use WebhookEventType instead.
|
|
237
|
+
* @deprecated Renamed to `WebhookEventType` for clarity. Use `WebhookEventType` instead.
|
|
238
|
+
* This alias will be removed in the next major version.
|
|
239
|
+
*
|
|
240
|
+
* @example
|
|
241
|
+
* // Before (deprecated)
|
|
242
|
+
* import { WebhookContentType } from 'chargebee';
|
|
243
|
+
* if (event.event_type === WebhookContentType.SubscriptionCreated) { ... }
|
|
244
|
+
*
|
|
245
|
+
* // After (recommended)
|
|
246
|
+
* import { WebhookEventType } from 'chargebee';
|
|
247
|
+
* if (event.event_type === WebhookEventType.SubscriptionCreated) { ... }
|
|
237
248
|
*/
|
|
238
249
|
exports.WebhookContentType = WebhookEventType;
|