@withaevum/sdk 1.3.9 → 1.3.11
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/index.d.ts +2 -2
- package/dist/index.js +1 -1
- package/dist/webhooks.d.ts +158 -2
- package/dist/webhooks.js +106 -3
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -8,7 +8,7 @@ export { CalendarAPI } from './calendar';
|
|
|
8
8
|
export { AnalyticsAPI } from './analytics';
|
|
9
9
|
export { AvailabilityAPI } from './availability';
|
|
10
10
|
export { PaymentsAPI } from './payments';
|
|
11
|
-
export { verifyWebhook, WebhookVerificationError } from './webhooks';
|
|
12
|
-
export type { WebhookEvent } from './webhooks';
|
|
11
|
+
export { verifyWebhook, WebhookVerificationError, extractWebhookSignature, isBookingEvent, isPaymentEvent, isBookingCreatedEvent, isBookingUpdatedEvent, isBookingCancelledEvent, isBookingConfirmedEvent, isPaymentSucceededEvent, isPaymentFailedEvent, isPaymentRefundedEvent, isWebhookTestEvent, } from './webhooks';
|
|
12
|
+
export type { WebhookEvent, WebhookEventType, BookingCreatedEvent, BookingUpdatedEvent, BookingCancelledEvent, BookingConfirmedEvent, PaymentSucceededEvent, PaymentFailedEvent, PaymentRefundedEvent, WebhookTestEvent, } from './webhooks';
|
|
13
13
|
export * from './types';
|
|
14
14
|
export * from './errors';
|
package/dist/index.js
CHANGED
|
@@ -9,6 +9,6 @@ export { CalendarAPI } from './calendar';
|
|
|
9
9
|
export { AnalyticsAPI } from './analytics';
|
|
10
10
|
export { AvailabilityAPI } from './availability';
|
|
11
11
|
export { PaymentsAPI } from './payments';
|
|
12
|
-
export { verifyWebhook, WebhookVerificationError } from './webhooks';
|
|
12
|
+
export { verifyWebhook, WebhookVerificationError, extractWebhookSignature, isBookingEvent, isPaymentEvent, isBookingCreatedEvent, isBookingUpdatedEvent, isBookingCancelledEvent, isBookingConfirmedEvent, isPaymentSucceededEvent, isPaymentFailedEvent, isPaymentRefundedEvent, isWebhookTestEvent, } from './webhooks';
|
|
13
13
|
export * from './types';
|
|
14
14
|
export * from './errors';
|
package/dist/webhooks.d.ts
CHANGED
|
@@ -1,20 +1,176 @@
|
|
|
1
|
+
export type WebhookEventType = 'booking.created' | 'booking.updated' | 'booking.cancelled' | 'booking.confirmed' | 'payment.succeeded' | 'payment.failed' | 'payment.refunded' | 'offering.created' | 'offering.updated' | 'offering.deleted' | 'availability.updated' | 'webhook.test';
|
|
1
2
|
export interface WebhookEvent {
|
|
2
|
-
type:
|
|
3
|
+
type: WebhookEventType;
|
|
3
4
|
version: string;
|
|
4
5
|
id: string;
|
|
5
6
|
delivery_id: string;
|
|
6
7
|
timestamp: string;
|
|
7
8
|
data: Record<string, unknown>;
|
|
8
9
|
}
|
|
10
|
+
export interface BookingCreatedEvent extends WebhookEvent {
|
|
11
|
+
type: 'booking.created';
|
|
12
|
+
data: {
|
|
13
|
+
id: string;
|
|
14
|
+
organization_id: string;
|
|
15
|
+
customer_id: string | null;
|
|
16
|
+
status: string;
|
|
17
|
+
start_time: string;
|
|
18
|
+
end_time: string;
|
|
19
|
+
price_cents: number | null;
|
|
20
|
+
notes: string | null;
|
|
21
|
+
provider_ids: string[];
|
|
22
|
+
offering_ids: string[];
|
|
23
|
+
customer?: {
|
|
24
|
+
id: string;
|
|
25
|
+
name: string | null;
|
|
26
|
+
email: string | null;
|
|
27
|
+
phone: string | null;
|
|
28
|
+
};
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
export interface BookingUpdatedEvent extends WebhookEvent {
|
|
32
|
+
type: 'booking.updated';
|
|
33
|
+
data: {
|
|
34
|
+
id: string;
|
|
35
|
+
organization_id: string;
|
|
36
|
+
customer_id: string | null;
|
|
37
|
+
status: string;
|
|
38
|
+
start_time: string;
|
|
39
|
+
end_time: string;
|
|
40
|
+
price_cents: number | null;
|
|
41
|
+
notes: string | null;
|
|
42
|
+
provider_ids: string[];
|
|
43
|
+
offering_ids: string[];
|
|
44
|
+
previous_status?: string;
|
|
45
|
+
customer?: {
|
|
46
|
+
id: string;
|
|
47
|
+
name: string | null;
|
|
48
|
+
email: string | null;
|
|
49
|
+
phone: string | null;
|
|
50
|
+
};
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
export interface BookingCancelledEvent extends WebhookEvent {
|
|
54
|
+
type: 'booking.cancelled';
|
|
55
|
+
data: {
|
|
56
|
+
id: string;
|
|
57
|
+
organization_id: string;
|
|
58
|
+
cancelled_at: string;
|
|
59
|
+
cancellation_reason?: string;
|
|
60
|
+
[key: string]: unknown;
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
export interface BookingConfirmedEvent extends WebhookEvent {
|
|
64
|
+
type: 'booking.confirmed';
|
|
65
|
+
data: {
|
|
66
|
+
id: string;
|
|
67
|
+
organization_id: string;
|
|
68
|
+
confirmed_at: string;
|
|
69
|
+
[key: string]: unknown;
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
export interface PaymentSucceededEvent extends WebhookEvent {
|
|
73
|
+
type: 'payment.succeeded';
|
|
74
|
+
data: {
|
|
75
|
+
id: string;
|
|
76
|
+
booking_id: string;
|
|
77
|
+
organization_id: string;
|
|
78
|
+
amount_cents: number;
|
|
79
|
+
currency: string;
|
|
80
|
+
payment_intent_id: string;
|
|
81
|
+
charged_at: string;
|
|
82
|
+
customer_id: string | null;
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
export interface PaymentFailedEvent extends WebhookEvent {
|
|
86
|
+
type: 'payment.failed';
|
|
87
|
+
data: {
|
|
88
|
+
id: string;
|
|
89
|
+
booking_id: string;
|
|
90
|
+
organization_id: string;
|
|
91
|
+
amount_cents: number;
|
|
92
|
+
payment_intent_id: string;
|
|
93
|
+
failure_reason?: string;
|
|
94
|
+
customer_id: string | null;
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
export interface PaymentRefundedEvent extends WebhookEvent {
|
|
98
|
+
type: 'payment.refunded';
|
|
99
|
+
data: {
|
|
100
|
+
id: string;
|
|
101
|
+
booking_id: string;
|
|
102
|
+
organization_id: string;
|
|
103
|
+
refund_amount_cents: number;
|
|
104
|
+
refunded_at: string;
|
|
105
|
+
customer_id: string | null;
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
export interface WebhookTestEvent extends WebhookEvent {
|
|
109
|
+
type: 'webhook.test';
|
|
110
|
+
data: {
|
|
111
|
+
message: string;
|
|
112
|
+
test: boolean;
|
|
113
|
+
organization_id: string;
|
|
114
|
+
};
|
|
115
|
+
}
|
|
9
116
|
export declare class WebhookVerificationError extends Error {
|
|
10
117
|
constructor(message: string);
|
|
11
118
|
}
|
|
119
|
+
/**
|
|
120
|
+
* Extract webhook signature from request headers
|
|
121
|
+
* @param headers - Request headers (Headers object or Record<string, string>)
|
|
122
|
+
* @returns Signature string or null if not found
|
|
123
|
+
*/
|
|
124
|
+
export declare function extractWebhookSignature(headers: Headers | Record<string, string | string[] | null | undefined>): string | null;
|
|
12
125
|
/**
|
|
13
126
|
* Verify webhook signature and parse event
|
|
127
|
+
* Supports secret rotation by checking previous secret if provided and not expired
|
|
14
128
|
* @param body - Raw request body as string
|
|
15
129
|
* @param signature - Signature from X-Aevum-Signature header
|
|
16
130
|
* @param secret - Webhook secret
|
|
131
|
+
* @param previousSecret - Previous secret (optional, for secret rotation grace period)
|
|
132
|
+
* @param previousSecretExpiresAt - When previous secret expires (optional, ISO 8601 string or Date)
|
|
17
133
|
* @returns Parsed webhook event
|
|
18
134
|
* @throws WebhookVerificationError if signature verification fails
|
|
19
135
|
*/
|
|
20
|
-
export declare function verifyWebhook(body: string, signature: string, secret: string): WebhookEvent;
|
|
136
|
+
export declare function verifyWebhook(body: string, signature: string, secret: string, previousSecret?: string | null, previousSecretExpiresAt?: string | Date | null): WebhookEvent;
|
|
137
|
+
/**
|
|
138
|
+
* Type guard: Check if event is a booking event
|
|
139
|
+
*/
|
|
140
|
+
export declare function isBookingEvent(event: WebhookEvent): event is BookingCreatedEvent | BookingUpdatedEvent | BookingCancelledEvent | BookingConfirmedEvent;
|
|
141
|
+
/**
|
|
142
|
+
* Type guard: Check if event is a payment event
|
|
143
|
+
*/
|
|
144
|
+
export declare function isPaymentEvent(event: WebhookEvent): event is PaymentSucceededEvent | PaymentFailedEvent | PaymentRefundedEvent;
|
|
145
|
+
/**
|
|
146
|
+
* Type guard: Check if event is booking.created
|
|
147
|
+
*/
|
|
148
|
+
export declare function isBookingCreatedEvent(event: WebhookEvent): event is BookingCreatedEvent;
|
|
149
|
+
/**
|
|
150
|
+
* Type guard: Check if event is booking.updated
|
|
151
|
+
*/
|
|
152
|
+
export declare function isBookingUpdatedEvent(event: WebhookEvent): event is BookingUpdatedEvent;
|
|
153
|
+
/**
|
|
154
|
+
* Type guard: Check if event is booking.cancelled
|
|
155
|
+
*/
|
|
156
|
+
export declare function isBookingCancelledEvent(event: WebhookEvent): event is BookingCancelledEvent;
|
|
157
|
+
/**
|
|
158
|
+
* Type guard: Check if event is booking.confirmed
|
|
159
|
+
*/
|
|
160
|
+
export declare function isBookingConfirmedEvent(event: WebhookEvent): event is BookingConfirmedEvent;
|
|
161
|
+
/**
|
|
162
|
+
* Type guard: Check if event is payment.succeeded
|
|
163
|
+
*/
|
|
164
|
+
export declare function isPaymentSucceededEvent(event: WebhookEvent): event is PaymentSucceededEvent;
|
|
165
|
+
/**
|
|
166
|
+
* Type guard: Check if event is payment.failed
|
|
167
|
+
*/
|
|
168
|
+
export declare function isPaymentFailedEvent(event: WebhookEvent): event is PaymentFailedEvent;
|
|
169
|
+
/**
|
|
170
|
+
* Type guard: Check if event is payment.refunded
|
|
171
|
+
*/
|
|
172
|
+
export declare function isPaymentRefundedEvent(event: WebhookEvent): event is PaymentRefundedEvent;
|
|
173
|
+
/**
|
|
174
|
+
* Type guard: Check if event is webhook.test
|
|
175
|
+
*/
|
|
176
|
+
export declare function isWebhookTestEvent(event: WebhookEvent): event is WebhookTestEvent;
|
package/dist/webhooks.js
CHANGED
|
@@ -7,15 +7,36 @@ export class WebhookVerificationError extends Error {
|
|
|
7
7
|
this.name = 'WebhookVerificationError';
|
|
8
8
|
}
|
|
9
9
|
}
|
|
10
|
+
/**
|
|
11
|
+
* Extract webhook signature from request headers
|
|
12
|
+
* @param headers - Request headers (Headers object or Record<string, string>)
|
|
13
|
+
* @returns Signature string or null if not found
|
|
14
|
+
*/
|
|
15
|
+
export function extractWebhookSignature(headers) {
|
|
16
|
+
if (headers instanceof Headers) {
|
|
17
|
+
return headers.get('x-aevum-signature');
|
|
18
|
+
}
|
|
19
|
+
const signature = headers['x-aevum-signature'];
|
|
20
|
+
if (typeof signature === 'string') {
|
|
21
|
+
return signature;
|
|
22
|
+
}
|
|
23
|
+
if (Array.isArray(signature) && signature.length > 0) {
|
|
24
|
+
return signature[0];
|
|
25
|
+
}
|
|
26
|
+
return null;
|
|
27
|
+
}
|
|
10
28
|
/**
|
|
11
29
|
* Verify webhook signature and parse event
|
|
30
|
+
* Supports secret rotation by checking previous secret if provided and not expired
|
|
12
31
|
* @param body - Raw request body as string
|
|
13
32
|
* @param signature - Signature from X-Aevum-Signature header
|
|
14
33
|
* @param secret - Webhook secret
|
|
34
|
+
* @param previousSecret - Previous secret (optional, for secret rotation grace period)
|
|
35
|
+
* @param previousSecretExpiresAt - When previous secret expires (optional, ISO 8601 string or Date)
|
|
15
36
|
* @returns Parsed webhook event
|
|
16
37
|
* @throws WebhookVerificationError if signature verification fails
|
|
17
38
|
*/
|
|
18
|
-
export function verifyWebhook(body, signature, secret) {
|
|
39
|
+
export function verifyWebhook(body, signature, secret, previousSecret, previousSecretExpiresAt) {
|
|
19
40
|
if (!body || !signature || !secret) {
|
|
20
41
|
throw new WebhookVerificationError('Missing required parameters: body, signature, or secret');
|
|
21
42
|
}
|
|
@@ -25,13 +46,30 @@ export function verifyWebhook(body, signature, secret) {
|
|
|
25
46
|
throw new WebhookVerificationError('Invalid signature format. Expected format: sha256=<hex>');
|
|
26
47
|
}
|
|
27
48
|
const providedSignature = signatureMatch[1];
|
|
28
|
-
// Compute signature with secret
|
|
49
|
+
// Compute signature with current secret
|
|
29
50
|
const computedSignature = crypto
|
|
30
51
|
.createHmac('sha256', secret)
|
|
31
52
|
.update(body)
|
|
32
53
|
.digest('hex');
|
|
33
54
|
// Compare signatures using constant-time comparison
|
|
34
|
-
|
|
55
|
+
let isValid = crypto.timingSafeEqual(Buffer.from(providedSignature, 'hex'), Buffer.from(computedSignature, 'hex'));
|
|
56
|
+
// If current secret doesn't match and previous secret is provided, check it
|
|
57
|
+
if (!isValid &&
|
|
58
|
+
previousSecret &&
|
|
59
|
+
previousSecretExpiresAt) {
|
|
60
|
+
const expiresAt = typeof previousSecretExpiresAt === 'string'
|
|
61
|
+
? new Date(previousSecretExpiresAt)
|
|
62
|
+
: previousSecretExpiresAt;
|
|
63
|
+
// Check if previous secret is still valid (within grace period)
|
|
64
|
+
if (new Date() < expiresAt) {
|
|
65
|
+
const previousComputedSignature = crypto
|
|
66
|
+
.createHmac('sha256', previousSecret)
|
|
67
|
+
.update(body)
|
|
68
|
+
.digest('hex');
|
|
69
|
+
isValid = crypto.timingSafeEqual(Buffer.from(providedSignature, 'hex'), Buffer.from(previousComputedSignature, 'hex'));
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
if (!isValid) {
|
|
35
73
|
throw new WebhookVerificationError('Invalid webhook signature');
|
|
36
74
|
}
|
|
37
75
|
// Parse and validate event
|
|
@@ -48,3 +86,68 @@ export function verifyWebhook(body, signature, secret) {
|
|
|
48
86
|
}
|
|
49
87
|
return event;
|
|
50
88
|
}
|
|
89
|
+
/**
|
|
90
|
+
* Type guard: Check if event is a booking event
|
|
91
|
+
*/
|
|
92
|
+
export function isBookingEvent(event) {
|
|
93
|
+
return (event.type === 'booking.created' ||
|
|
94
|
+
event.type === 'booking.updated' ||
|
|
95
|
+
event.type === 'booking.cancelled' ||
|
|
96
|
+
event.type === 'booking.confirmed');
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* Type guard: Check if event is a payment event
|
|
100
|
+
*/
|
|
101
|
+
export function isPaymentEvent(event) {
|
|
102
|
+
return (event.type === 'payment.succeeded' ||
|
|
103
|
+
event.type === 'payment.failed' ||
|
|
104
|
+
event.type === 'payment.refunded');
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* Type guard: Check if event is booking.created
|
|
108
|
+
*/
|
|
109
|
+
export function isBookingCreatedEvent(event) {
|
|
110
|
+
return event.type === 'booking.created';
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* Type guard: Check if event is booking.updated
|
|
114
|
+
*/
|
|
115
|
+
export function isBookingUpdatedEvent(event) {
|
|
116
|
+
return event.type === 'booking.updated';
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* Type guard: Check if event is booking.cancelled
|
|
120
|
+
*/
|
|
121
|
+
export function isBookingCancelledEvent(event) {
|
|
122
|
+
return event.type === 'booking.cancelled';
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* Type guard: Check if event is booking.confirmed
|
|
126
|
+
*/
|
|
127
|
+
export function isBookingConfirmedEvent(event) {
|
|
128
|
+
return event.type === 'booking.confirmed';
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* Type guard: Check if event is payment.succeeded
|
|
132
|
+
*/
|
|
133
|
+
export function isPaymentSucceededEvent(event) {
|
|
134
|
+
return event.type === 'payment.succeeded';
|
|
135
|
+
}
|
|
136
|
+
/**
|
|
137
|
+
* Type guard: Check if event is payment.failed
|
|
138
|
+
*/
|
|
139
|
+
export function isPaymentFailedEvent(event) {
|
|
140
|
+
return event.type === 'payment.failed';
|
|
141
|
+
}
|
|
142
|
+
/**
|
|
143
|
+
* Type guard: Check if event is payment.refunded
|
|
144
|
+
*/
|
|
145
|
+
export function isPaymentRefundedEvent(event) {
|
|
146
|
+
return event.type === 'payment.refunded';
|
|
147
|
+
}
|
|
148
|
+
/**
|
|
149
|
+
* Type guard: Check if event is webhook.test
|
|
150
|
+
*/
|
|
151
|
+
export function isWebhookTestEvent(event) {
|
|
152
|
+
return event.type === 'webhook.test';
|
|
153
|
+
}
|