recur-tw 0.15.0 → 0.16.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/dist/server.cjs +101 -0
- package/dist/server.d.cts +99 -1
- package/dist/server.d.ts +99 -1
- package/dist/server.js +97 -1
- package/package.json +1 -1
package/dist/server.cjs
CHANGED
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
+
var crypto = require('crypto');
|
|
4
|
+
|
|
5
|
+
function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
|
|
6
|
+
|
|
7
|
+
var crypto__default = /*#__PURE__*/_interopDefault(crypto);
|
|
8
|
+
|
|
3
9
|
var __defProp = Object.defineProperty;
|
|
4
10
|
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
5
11
|
var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
|
|
@@ -21,6 +27,12 @@ var RecurAPIError = class extends Error {
|
|
|
21
27
|
this.details = error.details;
|
|
22
28
|
}
|
|
23
29
|
};
|
|
30
|
+
var WebhookSignatureVerificationError = class extends Error {
|
|
31
|
+
constructor(message) {
|
|
32
|
+
super(message);
|
|
33
|
+
this.name = "WebhookSignatureVerificationError";
|
|
34
|
+
}
|
|
35
|
+
};
|
|
24
36
|
|
|
25
37
|
// package.json
|
|
26
38
|
var package_default = {
|
|
@@ -277,6 +289,80 @@ var Entitlements = class {
|
|
|
277
289
|
return { entitlements };
|
|
278
290
|
}
|
|
279
291
|
};
|
|
292
|
+
var Webhooks = class {
|
|
293
|
+
// Accept config for consistency with other resources (future API methods)
|
|
294
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
295
|
+
constructor(_config) {
|
|
296
|
+
}
|
|
297
|
+
/**
|
|
298
|
+
* Verify a webhook signature and return the parsed event
|
|
299
|
+
*
|
|
300
|
+
* @param payload - The raw request body as a string (use `await request.text()`, NOT `JSON.stringify(await request.json())`)
|
|
301
|
+
* @param signature - The value of the `X-Recur-Signature` header
|
|
302
|
+
* @param secret - The webhook signing secret from your dashboard
|
|
303
|
+
* @returns The parsed webhook event
|
|
304
|
+
* @throws {WebhookSignatureVerificationError} If the signature is missing or invalid
|
|
305
|
+
*
|
|
306
|
+
* @example
|
|
307
|
+
* ```typescript
|
|
308
|
+
* import { Recur } from 'recur-tw/server';
|
|
309
|
+
*
|
|
310
|
+
* const recur = new Recur(process.env.RECUR_SECRET_KEY!);
|
|
311
|
+
*
|
|
312
|
+
* export async function POST(request: Request) {
|
|
313
|
+
* const payload = await request.text();
|
|
314
|
+
* const signature = request.headers.get('x-recur-signature');
|
|
315
|
+
*
|
|
316
|
+
* try {
|
|
317
|
+
* const event = recur.webhooks.verify(
|
|
318
|
+
* payload,
|
|
319
|
+
* signature,
|
|
320
|
+
* process.env.RECUR_WEBHOOK_SECRET!,
|
|
321
|
+
* );
|
|
322
|
+
* // Handle event based on event.type
|
|
323
|
+
* switch (event.type) {
|
|
324
|
+
* case 'checkout.completed':
|
|
325
|
+
* break;
|
|
326
|
+
* case 'subscription.cancelled':
|
|
327
|
+
* break;
|
|
328
|
+
* }
|
|
329
|
+
* return new Response('OK', { status: 200 });
|
|
330
|
+
* } catch (err) {
|
|
331
|
+
* return new Response('Invalid signature', { status: 401 });
|
|
332
|
+
* }
|
|
333
|
+
* }
|
|
334
|
+
* ```
|
|
335
|
+
*/
|
|
336
|
+
verify(payload, signature, secret) {
|
|
337
|
+
if (!signature) {
|
|
338
|
+
throw new WebhookSignatureVerificationError(
|
|
339
|
+
"Missing webhook signature. Expected the `X-Recur-Signature` header."
|
|
340
|
+
);
|
|
341
|
+
}
|
|
342
|
+
if (!secret) {
|
|
343
|
+
throw new WebhookSignatureVerificationError(
|
|
344
|
+
"Missing webhook secret. Pass your webhook signing secret as the third argument."
|
|
345
|
+
);
|
|
346
|
+
}
|
|
347
|
+
const expectedSignature = crypto__default.default.createHmac("sha256", secret).update(payload).digest("base64");
|
|
348
|
+
let isValid;
|
|
349
|
+
try {
|
|
350
|
+
isValid = crypto__default.default.timingSafeEqual(
|
|
351
|
+
Buffer.from(signature),
|
|
352
|
+
Buffer.from(expectedSignature)
|
|
353
|
+
);
|
|
354
|
+
} catch {
|
|
355
|
+
isValid = false;
|
|
356
|
+
}
|
|
357
|
+
if (!isValid) {
|
|
358
|
+
throw new WebhookSignatureVerificationError(
|
|
359
|
+
"Webhook signature verification failed. Ensure you are using the raw request body and the correct signing secret."
|
|
360
|
+
);
|
|
361
|
+
}
|
|
362
|
+
const event = JSON.parse(payload);
|
|
363
|
+
return event;
|
|
364
|
+
}
|
|
365
|
+
};
|
|
280
366
|
|
|
281
367
|
// src/server/recur.ts
|
|
282
368
|
var Recur = class {
|
|
@@ -292,6 +378,19 @@ var Recur = class {
|
|
|
292
378
|
* Portal resource for managing customer portal sessions
|
|
293
379
|
*/
|
|
294
380
|
__publicField(this, "portal");
|
|
381
|
+
/**
|
|
382
|
+
* Webhooks resource for verifying webhook signatures
|
|
383
|
+
*
|
|
384
|
+
* @example
|
|
385
|
+
* ```typescript
|
|
386
|
+
* const event = recur.webhooks.verify(
|
|
387
|
+
* payload,
|
|
388
|
+
* signature,
|
|
389
|
+
* process.env.RECUR_WEBHOOK_SECRET!,
|
|
390
|
+
* );
|
|
391
|
+
* ```
|
|
392
|
+
*/
|
|
393
|
+
__publicField(this, "webhooks");
|
|
295
394
|
/**
|
|
296
395
|
* Entitlements resource for checking customer subscription access
|
|
297
396
|
*
|
|
@@ -325,9 +424,11 @@ var Recur = class {
|
|
|
325
424
|
baseUrl: options?.baseUrl || "https://api.recur.tw"
|
|
326
425
|
};
|
|
327
426
|
this.portal = new Portal(this.config);
|
|
427
|
+
this.webhooks = new Webhooks(this.config);
|
|
328
428
|
this.entitlements = new Entitlements(this.config);
|
|
329
429
|
}
|
|
330
430
|
};
|
|
331
431
|
|
|
332
432
|
exports.Recur = Recur;
|
|
333
433
|
exports.RecurAPIError = RecurAPIError;
|
|
434
|
+
exports.WebhookSignatureVerificationError = WebhookSignatureVerificationError;
|
package/dist/server.d.cts
CHANGED
|
@@ -250,6 +250,40 @@ interface EntitlementListResult {
|
|
|
250
250
|
*/
|
|
251
251
|
entitlements: Entitlement[];
|
|
252
252
|
}
|
|
253
|
+
/**
|
|
254
|
+
* Known webhook event types
|
|
255
|
+
*
|
|
256
|
+
* Uses a union with `(string & {})` to allow unknown event types
|
|
257
|
+
* while still providing autocomplete for known ones.
|
|
258
|
+
*/
|
|
259
|
+
type WebhookEventType = 'checkout.completed' | 'subscription.created' | 'subscription.activated' | 'subscription.updated' | 'subscription.cancelled' | 'subscription.expired' | 'subscription.trial_ending' | 'subscription.payment_failed' | 'invoice.created' | 'invoice.paid' | 'invoice.payment_failed' | 'invoice.refunded' | 'customer.created' | 'customer.updated' | 'order.paid' | (string & {});
|
|
260
|
+
/**
|
|
261
|
+
* A webhook event received from Recur
|
|
262
|
+
*/
|
|
263
|
+
interface WebhookEvent {
|
|
264
|
+
/**
|
|
265
|
+
* Unique event identifier
|
|
266
|
+
*/
|
|
267
|
+
id: string;
|
|
268
|
+
/**
|
|
269
|
+
* Event type
|
|
270
|
+
*/
|
|
271
|
+
type: WebhookEventType;
|
|
272
|
+
/**
|
|
273
|
+
* ISO 8601 timestamp of when the event was created
|
|
274
|
+
*/
|
|
275
|
+
timestamp: string;
|
|
276
|
+
/**
|
|
277
|
+
* Event-specific data payload
|
|
278
|
+
*/
|
|
279
|
+
data: Record<string, unknown>;
|
|
280
|
+
}
|
|
281
|
+
/**
|
|
282
|
+
* Error thrown when webhook signature verification fails
|
|
283
|
+
*/
|
|
284
|
+
declare class WebhookSignatureVerificationError extends Error {
|
|
285
|
+
constructor(message: string);
|
|
286
|
+
}
|
|
253
287
|
|
|
254
288
|
/**
|
|
255
289
|
* Portal Sessions Resource
|
|
@@ -415,6 +449,57 @@ declare class Entitlements {
|
|
|
415
449
|
list(options: EntitlementListOptions): Promise<EntitlementListResult>;
|
|
416
450
|
}
|
|
417
451
|
|
|
452
|
+
/**
|
|
453
|
+
* Webhooks Resource
|
|
454
|
+
*
|
|
455
|
+
* Provides webhook signature verification for securing webhook endpoints.
|
|
456
|
+
* Uses HMAC-SHA256 with Base64 encoding, matching the Recur webhook infrastructure.
|
|
457
|
+
*/
|
|
458
|
+
|
|
459
|
+
declare class Webhooks {
|
|
460
|
+
constructor(_config: RecurConfig);
|
|
461
|
+
/**
|
|
462
|
+
* Verify a webhook signature and return the parsed event
|
|
463
|
+
*
|
|
464
|
+
* @param payload - The raw request body as a string (use `await request.text()`, NOT `JSON.stringify(await request.json())`)
|
|
465
|
+
* @param signature - The value of the `X-Recur-Signature` header
|
|
466
|
+
* @param secret - The webhook signing secret from your dashboard
|
|
467
|
+
* @returns The parsed webhook event
|
|
468
|
+
* @throws {WebhookSignatureVerificationError} If the signature is missing or invalid
|
|
469
|
+
*
|
|
470
|
+
* @example
|
|
471
|
+
* ```typescript
|
|
472
|
+
* import { Recur } from 'recur-tw/server';
|
|
473
|
+
*
|
|
474
|
+
* const recur = new Recur(process.env.RECUR_SECRET_KEY!);
|
|
475
|
+
*
|
|
476
|
+
* export async function POST(request: Request) {
|
|
477
|
+
* const payload = await request.text();
|
|
478
|
+
* const signature = request.headers.get('x-recur-signature');
|
|
479
|
+
*
|
|
480
|
+
* try {
|
|
481
|
+
* const event = recur.webhooks.verify(
|
|
482
|
+
* payload,
|
|
483
|
+
* signature,
|
|
484
|
+
* process.env.RECUR_WEBHOOK_SECRET!,
|
|
485
|
+
* );
|
|
486
|
+
* // Handle event based on event.type
|
|
487
|
+
* switch (event.type) {
|
|
488
|
+
* case 'checkout.completed':
|
|
489
|
+
* break;
|
|
490
|
+
* case 'subscription.cancelled':
|
|
491
|
+
* break;
|
|
492
|
+
* }
|
|
493
|
+
* return new Response('OK', { status: 200 });
|
|
494
|
+
* } catch (err) {
|
|
495
|
+
* return new Response('Invalid signature', { status: 401 });
|
|
496
|
+
* }
|
|
497
|
+
* }
|
|
498
|
+
* ```
|
|
499
|
+
*/
|
|
500
|
+
verify(payload: string, signature: string | null, secret: string): WebhookEvent;
|
|
501
|
+
}
|
|
502
|
+
|
|
418
503
|
/**
|
|
419
504
|
* Recur Server SDK
|
|
420
505
|
*
|
|
@@ -441,6 +526,19 @@ declare class Recur {
|
|
|
441
526
|
* Portal resource for managing customer portal sessions
|
|
442
527
|
*/
|
|
443
528
|
readonly portal: Portal;
|
|
529
|
+
/**
|
|
530
|
+
* Webhooks resource for verifying webhook signatures
|
|
531
|
+
*
|
|
532
|
+
* @example
|
|
533
|
+
* ```typescript
|
|
534
|
+
* const event = recur.webhooks.verify(
|
|
535
|
+
* payload,
|
|
536
|
+
* signature,
|
|
537
|
+
* process.env.RECUR_WEBHOOK_SECRET!,
|
|
538
|
+
* );
|
|
539
|
+
* ```
|
|
540
|
+
*/
|
|
541
|
+
readonly webhooks: Webhooks;
|
|
444
542
|
/**
|
|
445
543
|
* Entitlements resource for checking customer subscription access
|
|
446
544
|
*
|
|
@@ -468,4 +566,4 @@ declare class Recur {
|
|
|
468
566
|
constructor(secretKey: string, options?: Omit<RecurConfig, 'secretKey'>);
|
|
469
567
|
}
|
|
470
568
|
|
|
471
|
-
export { type ApiErrorCode, type CustomerIdentifier, type DuplicateSubscriptionDetails, type Entitlement, type EntitlementCheckOptions, type EntitlementCheckResult, type EntitlementListOptions, type EntitlementListResult, type EntitlementStatus, type EntitlementSubscription, type PortalSession, type PortalSessionCreateParams, Recur, RecurAPIError, type RecurConfig, type RecurError };
|
|
569
|
+
export { type ApiErrorCode, type CustomerIdentifier, type DuplicateSubscriptionDetails, type Entitlement, type EntitlementCheckOptions, type EntitlementCheckResult, type EntitlementListOptions, type EntitlementListResult, type EntitlementStatus, type EntitlementSubscription, type PortalSession, type PortalSessionCreateParams, Recur, RecurAPIError, type RecurConfig, type RecurError, type WebhookEvent, type WebhookEventType, WebhookSignatureVerificationError };
|
package/dist/server.d.ts
CHANGED
|
@@ -250,6 +250,40 @@ interface EntitlementListResult {
|
|
|
250
250
|
*/
|
|
251
251
|
entitlements: Entitlement[];
|
|
252
252
|
}
|
|
253
|
+
/**
|
|
254
|
+
* Known webhook event types
|
|
255
|
+
*
|
|
256
|
+
* Uses a union with `(string & {})` to allow unknown event types
|
|
257
|
+
* while still providing autocomplete for known ones.
|
|
258
|
+
*/
|
|
259
|
+
type WebhookEventType = 'checkout.completed' | 'subscription.created' | 'subscription.activated' | 'subscription.updated' | 'subscription.cancelled' | 'subscription.expired' | 'subscription.trial_ending' | 'subscription.payment_failed' | 'invoice.created' | 'invoice.paid' | 'invoice.payment_failed' | 'invoice.refunded' | 'customer.created' | 'customer.updated' | 'order.paid' | (string & {});
|
|
260
|
+
/**
|
|
261
|
+
* A webhook event received from Recur
|
|
262
|
+
*/
|
|
263
|
+
interface WebhookEvent {
|
|
264
|
+
/**
|
|
265
|
+
* Unique event identifier
|
|
266
|
+
*/
|
|
267
|
+
id: string;
|
|
268
|
+
/**
|
|
269
|
+
* Event type
|
|
270
|
+
*/
|
|
271
|
+
type: WebhookEventType;
|
|
272
|
+
/**
|
|
273
|
+
* ISO 8601 timestamp of when the event was created
|
|
274
|
+
*/
|
|
275
|
+
timestamp: string;
|
|
276
|
+
/**
|
|
277
|
+
* Event-specific data payload
|
|
278
|
+
*/
|
|
279
|
+
data: Record<string, unknown>;
|
|
280
|
+
}
|
|
281
|
+
/**
|
|
282
|
+
* Error thrown when webhook signature verification fails
|
|
283
|
+
*/
|
|
284
|
+
declare class WebhookSignatureVerificationError extends Error {
|
|
285
|
+
constructor(message: string);
|
|
286
|
+
}
|
|
253
287
|
|
|
254
288
|
/**
|
|
255
289
|
* Portal Sessions Resource
|
|
@@ -415,6 +449,57 @@ declare class Entitlements {
|
|
|
415
449
|
list(options: EntitlementListOptions): Promise<EntitlementListResult>;
|
|
416
450
|
}
|
|
417
451
|
|
|
452
|
+
/**
|
|
453
|
+
* Webhooks Resource
|
|
454
|
+
*
|
|
455
|
+
* Provides webhook signature verification for securing webhook endpoints.
|
|
456
|
+
* Uses HMAC-SHA256 with Base64 encoding, matching the Recur webhook infrastructure.
|
|
457
|
+
*/
|
|
458
|
+
|
|
459
|
+
declare class Webhooks {
|
|
460
|
+
constructor(_config: RecurConfig);
|
|
461
|
+
/**
|
|
462
|
+
* Verify a webhook signature and return the parsed event
|
|
463
|
+
*
|
|
464
|
+
* @param payload - The raw request body as a string (use `await request.text()`, NOT `JSON.stringify(await request.json())`)
|
|
465
|
+
* @param signature - The value of the `X-Recur-Signature` header
|
|
466
|
+
* @param secret - The webhook signing secret from your dashboard
|
|
467
|
+
* @returns The parsed webhook event
|
|
468
|
+
* @throws {WebhookSignatureVerificationError} If the signature is missing or invalid
|
|
469
|
+
*
|
|
470
|
+
* @example
|
|
471
|
+
* ```typescript
|
|
472
|
+
* import { Recur } from 'recur-tw/server';
|
|
473
|
+
*
|
|
474
|
+
* const recur = new Recur(process.env.RECUR_SECRET_KEY!);
|
|
475
|
+
*
|
|
476
|
+
* export async function POST(request: Request) {
|
|
477
|
+
* const payload = await request.text();
|
|
478
|
+
* const signature = request.headers.get('x-recur-signature');
|
|
479
|
+
*
|
|
480
|
+
* try {
|
|
481
|
+
* const event = recur.webhooks.verify(
|
|
482
|
+
* payload,
|
|
483
|
+
* signature,
|
|
484
|
+
* process.env.RECUR_WEBHOOK_SECRET!,
|
|
485
|
+
* );
|
|
486
|
+
* // Handle event based on event.type
|
|
487
|
+
* switch (event.type) {
|
|
488
|
+
* case 'checkout.completed':
|
|
489
|
+
* break;
|
|
490
|
+
* case 'subscription.cancelled':
|
|
491
|
+
* break;
|
|
492
|
+
* }
|
|
493
|
+
* return new Response('OK', { status: 200 });
|
|
494
|
+
* } catch (err) {
|
|
495
|
+
* return new Response('Invalid signature', { status: 401 });
|
|
496
|
+
* }
|
|
497
|
+
* }
|
|
498
|
+
* ```
|
|
499
|
+
*/
|
|
500
|
+
verify(payload: string, signature: string | null, secret: string): WebhookEvent;
|
|
501
|
+
}
|
|
502
|
+
|
|
418
503
|
/**
|
|
419
504
|
* Recur Server SDK
|
|
420
505
|
*
|
|
@@ -441,6 +526,19 @@ declare class Recur {
|
|
|
441
526
|
* Portal resource for managing customer portal sessions
|
|
442
527
|
*/
|
|
443
528
|
readonly portal: Portal;
|
|
529
|
+
/**
|
|
530
|
+
* Webhooks resource for verifying webhook signatures
|
|
531
|
+
*
|
|
532
|
+
* @example
|
|
533
|
+
* ```typescript
|
|
534
|
+
* const event = recur.webhooks.verify(
|
|
535
|
+
* payload,
|
|
536
|
+
* signature,
|
|
537
|
+
* process.env.RECUR_WEBHOOK_SECRET!,
|
|
538
|
+
* );
|
|
539
|
+
* ```
|
|
540
|
+
*/
|
|
541
|
+
readonly webhooks: Webhooks;
|
|
444
542
|
/**
|
|
445
543
|
* Entitlements resource for checking customer subscription access
|
|
446
544
|
*
|
|
@@ -468,4 +566,4 @@ declare class Recur {
|
|
|
468
566
|
constructor(secretKey: string, options?: Omit<RecurConfig, 'secretKey'>);
|
|
469
567
|
}
|
|
470
568
|
|
|
471
|
-
export { type ApiErrorCode, type CustomerIdentifier, type DuplicateSubscriptionDetails, type Entitlement, type EntitlementCheckOptions, type EntitlementCheckResult, type EntitlementListOptions, type EntitlementListResult, type EntitlementStatus, type EntitlementSubscription, type PortalSession, type PortalSessionCreateParams, Recur, RecurAPIError, type RecurConfig, type RecurError };
|
|
569
|
+
export { type ApiErrorCode, type CustomerIdentifier, type DuplicateSubscriptionDetails, type Entitlement, type EntitlementCheckOptions, type EntitlementCheckResult, type EntitlementListOptions, type EntitlementListResult, type EntitlementStatus, type EntitlementSubscription, type PortalSession, type PortalSessionCreateParams, Recur, RecurAPIError, type RecurConfig, type RecurError, type WebhookEvent, type WebhookEventType, WebhookSignatureVerificationError };
|
package/dist/server.js
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import crypto from 'crypto';
|
|
2
|
+
|
|
1
3
|
var __defProp = Object.defineProperty;
|
|
2
4
|
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
3
5
|
var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
|
|
@@ -19,6 +21,12 @@ var RecurAPIError = class extends Error {
|
|
|
19
21
|
this.details = error.details;
|
|
20
22
|
}
|
|
21
23
|
};
|
|
24
|
+
var WebhookSignatureVerificationError = class extends Error {
|
|
25
|
+
constructor(message) {
|
|
26
|
+
super(message);
|
|
27
|
+
this.name = "WebhookSignatureVerificationError";
|
|
28
|
+
}
|
|
29
|
+
};
|
|
22
30
|
|
|
23
31
|
// package.json
|
|
24
32
|
var package_default = {
|
|
@@ -275,6 +283,80 @@ var Entitlements = class {
|
|
|
275
283
|
return { entitlements };
|
|
276
284
|
}
|
|
277
285
|
};
|
|
286
|
+
var Webhooks = class {
|
|
287
|
+
// Accept config for consistency with other resources (future API methods)
|
|
288
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
289
|
+
constructor(_config) {
|
|
290
|
+
}
|
|
291
|
+
/**
|
|
292
|
+
* Verify a webhook signature and return the parsed event
|
|
293
|
+
*
|
|
294
|
+
* @param payload - The raw request body as a string (use `await request.text()`, NOT `JSON.stringify(await request.json())`)
|
|
295
|
+
* @param signature - The value of the `X-Recur-Signature` header
|
|
296
|
+
* @param secret - The webhook signing secret from your dashboard
|
|
297
|
+
* @returns The parsed webhook event
|
|
298
|
+
* @throws {WebhookSignatureVerificationError} If the signature is missing or invalid
|
|
299
|
+
*
|
|
300
|
+
* @example
|
|
301
|
+
* ```typescript
|
|
302
|
+
* import { Recur } from 'recur-tw/server';
|
|
303
|
+
*
|
|
304
|
+
* const recur = new Recur(process.env.RECUR_SECRET_KEY!);
|
|
305
|
+
*
|
|
306
|
+
* export async function POST(request: Request) {
|
|
307
|
+
* const payload = await request.text();
|
|
308
|
+
* const signature = request.headers.get('x-recur-signature');
|
|
309
|
+
*
|
|
310
|
+
* try {
|
|
311
|
+
* const event = recur.webhooks.verify(
|
|
312
|
+
* payload,
|
|
313
|
+
* signature,
|
|
314
|
+
* process.env.RECUR_WEBHOOK_SECRET!,
|
|
315
|
+
* );
|
|
316
|
+
* // Handle event based on event.type
|
|
317
|
+
* switch (event.type) {
|
|
318
|
+
* case 'checkout.completed':
|
|
319
|
+
* break;
|
|
320
|
+
* case 'subscription.cancelled':
|
|
321
|
+
* break;
|
|
322
|
+
* }
|
|
323
|
+
* return new Response('OK', { status: 200 });
|
|
324
|
+
* } catch (err) {
|
|
325
|
+
* return new Response('Invalid signature', { status: 401 });
|
|
326
|
+
* }
|
|
327
|
+
* }
|
|
328
|
+
* ```
|
|
329
|
+
*/
|
|
330
|
+
verify(payload, signature, secret) {
|
|
331
|
+
if (!signature) {
|
|
332
|
+
throw new WebhookSignatureVerificationError(
|
|
333
|
+
"Missing webhook signature. Expected the `X-Recur-Signature` header."
|
|
334
|
+
);
|
|
335
|
+
}
|
|
336
|
+
if (!secret) {
|
|
337
|
+
throw new WebhookSignatureVerificationError(
|
|
338
|
+
"Missing webhook secret. Pass your webhook signing secret as the third argument."
|
|
339
|
+
);
|
|
340
|
+
}
|
|
341
|
+
const expectedSignature = crypto.createHmac("sha256", secret).update(payload).digest("base64");
|
|
342
|
+
let isValid;
|
|
343
|
+
try {
|
|
344
|
+
isValid = crypto.timingSafeEqual(
|
|
345
|
+
Buffer.from(signature),
|
|
346
|
+
Buffer.from(expectedSignature)
|
|
347
|
+
);
|
|
348
|
+
} catch {
|
|
349
|
+
isValid = false;
|
|
350
|
+
}
|
|
351
|
+
if (!isValid) {
|
|
352
|
+
throw new WebhookSignatureVerificationError(
|
|
353
|
+
"Webhook signature verification failed. Ensure you are using the raw request body and the correct signing secret."
|
|
354
|
+
);
|
|
355
|
+
}
|
|
356
|
+
const event = JSON.parse(payload);
|
|
357
|
+
return event;
|
|
358
|
+
}
|
|
359
|
+
};
|
|
278
360
|
|
|
279
361
|
// src/server/recur.ts
|
|
280
362
|
var Recur = class {
|
|
@@ -290,6 +372,19 @@ var Recur = class {
|
|
|
290
372
|
* Portal resource for managing customer portal sessions
|
|
291
373
|
*/
|
|
292
374
|
__publicField(this, "portal");
|
|
375
|
+
/**
|
|
376
|
+
* Webhooks resource for verifying webhook signatures
|
|
377
|
+
*
|
|
378
|
+
* @example
|
|
379
|
+
* ```typescript
|
|
380
|
+
* const event = recur.webhooks.verify(
|
|
381
|
+
* payload,
|
|
382
|
+
* signature,
|
|
383
|
+
* process.env.RECUR_WEBHOOK_SECRET!,
|
|
384
|
+
* );
|
|
385
|
+
* ```
|
|
386
|
+
*/
|
|
387
|
+
__publicField(this, "webhooks");
|
|
293
388
|
/**
|
|
294
389
|
* Entitlements resource for checking customer subscription access
|
|
295
390
|
*
|
|
@@ -323,8 +418,9 @@ var Recur = class {
|
|
|
323
418
|
baseUrl: options?.baseUrl || "https://api.recur.tw"
|
|
324
419
|
};
|
|
325
420
|
this.portal = new Portal(this.config);
|
|
421
|
+
this.webhooks = new Webhooks(this.config);
|
|
326
422
|
this.entitlements = new Entitlements(this.config);
|
|
327
423
|
}
|
|
328
424
|
};
|
|
329
425
|
|
|
330
|
-
export { Recur, RecurAPIError };
|
|
426
|
+
export { Recur, RecurAPIError, WebhookSignatureVerificationError };
|