@stackbe/sdk 0.2.0 → 0.4.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/README.md +311 -77
- package/dist/index.d.mts +203 -19
- package/dist/index.d.ts +203 -19
- package/dist/index.js +218 -26
- package/dist/index.mjs +218 -26
- package/package.json +1 -1
package/dist/index.d.mts
CHANGED
|
@@ -24,6 +24,30 @@ interface StackBEConfig {
|
|
|
24
24
|
baseUrl?: string;
|
|
25
25
|
/** Request timeout in milliseconds (default: 30000) */
|
|
26
26
|
timeout?: number;
|
|
27
|
+
/**
|
|
28
|
+
* Session cache TTL in seconds (default: 0 = disabled).
|
|
29
|
+
* When set, getSession() results are cached to reduce API calls.
|
|
30
|
+
* @example
|
|
31
|
+
* ```typescript
|
|
32
|
+
* new StackBE({ apiKey, appId, sessionCacheTTL: 120 }) // Cache for 2 minutes
|
|
33
|
+
* ```
|
|
34
|
+
*/
|
|
35
|
+
sessionCacheTTL?: number;
|
|
36
|
+
/**
|
|
37
|
+
* Development callback URL for magic link redirects.
|
|
38
|
+
* When set, this URL is used automatically when:
|
|
39
|
+
* - NODE_ENV !== 'production', or
|
|
40
|
+
* - useDev: true is passed to sendMagicLink()
|
|
41
|
+
* @example
|
|
42
|
+
* ```typescript
|
|
43
|
+
* new StackBE({
|
|
44
|
+
* apiKey,
|
|
45
|
+
* appId,
|
|
46
|
+
* devCallbackUrl: 'http://localhost:3000/auth/callback'
|
|
47
|
+
* })
|
|
48
|
+
* ```
|
|
49
|
+
*/
|
|
50
|
+
devCallbackUrl?: string;
|
|
27
51
|
}
|
|
28
52
|
interface TrackUsageOptions {
|
|
29
53
|
/** Quantity to track (default: 1) */
|
|
@@ -121,15 +145,43 @@ interface MagicLinkResponse {
|
|
|
121
145
|
message: string;
|
|
122
146
|
}
|
|
123
147
|
interface VerifyTokenResponse {
|
|
148
|
+
success: boolean;
|
|
124
149
|
valid: boolean;
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
150
|
+
/** Customer ID */
|
|
151
|
+
customerId: string;
|
|
152
|
+
/** Customer email */
|
|
153
|
+
email: string;
|
|
154
|
+
/** Session JWT token - use this for subsequent authenticated requests */
|
|
155
|
+
sessionToken: string;
|
|
156
|
+
/** Tenant ID (your StackBE tenant) */
|
|
157
|
+
tenantId?: string;
|
|
158
|
+
/** Organization ID if in org context */
|
|
159
|
+
organizationId?: string;
|
|
160
|
+
/** Role in the organization */
|
|
161
|
+
orgRole?: 'owner' | 'admin' | 'member';
|
|
162
|
+
/** URL to redirect to after verification */
|
|
163
|
+
redirectUrl?: string;
|
|
128
164
|
}
|
|
129
165
|
interface SessionResponse {
|
|
130
|
-
|
|
166
|
+
valid: boolean;
|
|
167
|
+
/** Customer ID */
|
|
168
|
+
customerId: string;
|
|
169
|
+
/** Customer email */
|
|
170
|
+
email: string;
|
|
171
|
+
/** Session expiration time */
|
|
172
|
+
expiresAt?: string;
|
|
173
|
+
/** Tenant ID (your StackBE tenant) */
|
|
174
|
+
tenantId?: string;
|
|
175
|
+
/** Organization ID if in org context */
|
|
176
|
+
organizationId?: string;
|
|
177
|
+
/** Role in the organization */
|
|
178
|
+
orgRole?: 'owner' | 'admin' | 'member';
|
|
179
|
+
/** Customer details (if included) */
|
|
180
|
+
customer?: Customer;
|
|
181
|
+
/** Current subscription (if any) */
|
|
131
182
|
subscription?: Subscription;
|
|
132
|
-
|
|
183
|
+
/** Feature entitlements */
|
|
184
|
+
entitlements?: Record<string, boolean | number | string>;
|
|
133
185
|
}
|
|
134
186
|
interface CreateCheckoutOptions {
|
|
135
187
|
/** Customer ID or email */
|
|
@@ -189,16 +241,109 @@ interface UpdateSubscriptionOptions {
|
|
|
189
241
|
/** Proration behavior */
|
|
190
242
|
prorate?: boolean;
|
|
191
243
|
}
|
|
244
|
+
/**
|
|
245
|
+
* Error codes returned by StackBE API.
|
|
246
|
+
* Use these to handle specific error cases in your application.
|
|
247
|
+
*
|
|
248
|
+
* @example
|
|
249
|
+
* ```typescript
|
|
250
|
+
* try {
|
|
251
|
+
* await stackbe.auth.verifyToken(token);
|
|
252
|
+
* } catch (error) {
|
|
253
|
+
* if (error instanceof StackBEError) {
|
|
254
|
+
* switch (error.code) {
|
|
255
|
+
* case 'TOKEN_EXPIRED':
|
|
256
|
+
* return res.redirect('/login?error=expired');
|
|
257
|
+
* case 'TOKEN_ALREADY_USED':
|
|
258
|
+
* return res.redirect('/login?error=used');
|
|
259
|
+
* case 'SESSION_EXPIRED':
|
|
260
|
+
* return res.redirect('/login');
|
|
261
|
+
* default:
|
|
262
|
+
* return res.status(500).json({ error: 'Authentication failed' });
|
|
263
|
+
* }
|
|
264
|
+
* }
|
|
265
|
+
* }
|
|
266
|
+
* ```
|
|
267
|
+
*/
|
|
268
|
+
type StackBEErrorCode = 'TOKEN_EXPIRED' | 'TOKEN_ALREADY_USED' | 'TOKEN_INVALID' | 'SESSION_EXPIRED' | 'SESSION_INVALID' | 'UNAUTHORIZED' | 'NOT_FOUND' | 'CUSTOMER_NOT_FOUND' | 'SUBSCRIPTION_NOT_FOUND' | 'PLAN_NOT_FOUND' | 'APP_NOT_FOUND' | 'VALIDATION_ERROR' | 'MISSING_REQUIRED_FIELD' | 'INVALID_EMAIL' | 'USAGE_LIMIT_EXCEEDED' | 'METRIC_NOT_FOUND' | 'FEATURE_NOT_AVAILABLE' | 'NO_ACTIVE_SUBSCRIPTION' | 'TIMEOUT' | 'NETWORK_ERROR' | 'UNKNOWN_ERROR';
|
|
192
269
|
interface StackBEErrorResponse {
|
|
193
270
|
statusCode: number;
|
|
194
271
|
message: string;
|
|
195
272
|
error: string;
|
|
273
|
+
code?: StackBEErrorCode;
|
|
196
274
|
}
|
|
197
275
|
declare class StackBEError extends Error {
|
|
198
276
|
readonly statusCode: number;
|
|
199
|
-
readonly code:
|
|
200
|
-
constructor(message: string, statusCode: number, code: string);
|
|
277
|
+
readonly code: StackBEErrorCode;
|
|
278
|
+
constructor(message: string, statusCode: number, code: StackBEErrorCode | string);
|
|
279
|
+
/** Check if this is a specific error type */
|
|
280
|
+
is(code: StackBEErrorCode): boolean;
|
|
281
|
+
/** Check if this is an auth-related error */
|
|
282
|
+
isAuthError(): boolean;
|
|
283
|
+
/** Check if this is a "not found" error */
|
|
284
|
+
isNotFoundError(): boolean;
|
|
285
|
+
}
|
|
286
|
+
/**
|
|
287
|
+
* Webhook event types emitted by StackBE.
|
|
288
|
+
* Subscribe to these events to react to changes in your customers' subscriptions.
|
|
289
|
+
*/
|
|
290
|
+
type WebhookEventType = 'subscription_created' | 'subscription_updated' | 'subscription_cancelled' | 'subscription_renewed' | 'trial_started' | 'trial_ended' | 'payment_succeeded' | 'payment_failed' | 'customer_created' | 'customer_updated';
|
|
291
|
+
/** Base webhook event structure */
|
|
292
|
+
interface WebhookEvent<T extends WebhookEventType = WebhookEventType, P = unknown> {
|
|
293
|
+
/** Event ID */
|
|
294
|
+
id: string;
|
|
295
|
+
/** Event type */
|
|
296
|
+
type: T;
|
|
297
|
+
/** Tenant ID */
|
|
298
|
+
tenantId: string;
|
|
299
|
+
/** App ID */
|
|
300
|
+
appId: string;
|
|
301
|
+
/** Event payload */
|
|
302
|
+
data: P;
|
|
303
|
+
/** When the event was created */
|
|
304
|
+
createdAt: string;
|
|
201
305
|
}
|
|
306
|
+
/** Subscription webhook payload */
|
|
307
|
+
interface SubscriptionWebhookPayload {
|
|
308
|
+
id: string;
|
|
309
|
+
customerId: string;
|
|
310
|
+
planId: string;
|
|
311
|
+
planName: string;
|
|
312
|
+
status: 'active' | 'canceled' | 'past_due' | 'trialing' | 'paused';
|
|
313
|
+
currentPeriodStart: string;
|
|
314
|
+
currentPeriodEnd: string;
|
|
315
|
+
cancelAtPeriodEnd: boolean;
|
|
316
|
+
trialEnd?: string;
|
|
317
|
+
}
|
|
318
|
+
/** Customer webhook payload */
|
|
319
|
+
interface CustomerWebhookPayload {
|
|
320
|
+
id: string;
|
|
321
|
+
email: string;
|
|
322
|
+
name?: string;
|
|
323
|
+
metadata?: Record<string, unknown>;
|
|
324
|
+
}
|
|
325
|
+
/** Payment webhook payload */
|
|
326
|
+
interface PaymentWebhookPayload {
|
|
327
|
+
id: string;
|
|
328
|
+
subscriptionId: string;
|
|
329
|
+
customerId: string;
|
|
330
|
+
amount: number;
|
|
331
|
+
currency: string;
|
|
332
|
+
status: 'succeeded' | 'failed';
|
|
333
|
+
failureReason?: string;
|
|
334
|
+
}
|
|
335
|
+
type SubscriptionCreatedEvent = WebhookEvent<'subscription_created', SubscriptionWebhookPayload>;
|
|
336
|
+
type SubscriptionUpdatedEvent = WebhookEvent<'subscription_updated', SubscriptionWebhookPayload>;
|
|
337
|
+
type SubscriptionCancelledEvent = WebhookEvent<'subscription_cancelled', SubscriptionWebhookPayload>;
|
|
338
|
+
type SubscriptionRenewedEvent = WebhookEvent<'subscription_renewed', SubscriptionWebhookPayload>;
|
|
339
|
+
type TrialStartedEvent = WebhookEvent<'trial_started', SubscriptionWebhookPayload>;
|
|
340
|
+
type TrialEndedEvent = WebhookEvent<'trial_ended', SubscriptionWebhookPayload>;
|
|
341
|
+
type PaymentSucceededEvent = WebhookEvent<'payment_succeeded', PaymentWebhookPayload>;
|
|
342
|
+
type PaymentFailedEvent = WebhookEvent<'payment_failed', PaymentWebhookPayload>;
|
|
343
|
+
type CustomerCreatedEvent = WebhookEvent<'customer_created', CustomerWebhookPayload>;
|
|
344
|
+
type CustomerUpdatedEvent = WebhookEvent<'customer_updated', CustomerWebhookPayload>;
|
|
345
|
+
/** Union of all webhook event types */
|
|
346
|
+
type AnyWebhookEvent = SubscriptionCreatedEvent | SubscriptionUpdatedEvent | SubscriptionCancelledEvent | SubscriptionRenewedEvent | TrialStartedEvent | TrialEndedEvent | PaymentSucceededEvent | PaymentFailedEvent | CustomerCreatedEvent | CustomerUpdatedEvent;
|
|
202
347
|
|
|
203
348
|
declare class UsageClient {
|
|
204
349
|
private http;
|
|
@@ -570,13 +715,37 @@ declare class SubscriptionsClient {
|
|
|
570
715
|
isActive(customerId: string): Promise<boolean>;
|
|
571
716
|
}
|
|
572
717
|
|
|
718
|
+
interface AuthClientOptions {
|
|
719
|
+
/** Session cache TTL in seconds (0 = disabled) */
|
|
720
|
+
sessionCacheTTL?: number;
|
|
721
|
+
/** Development callback URL for magic links */
|
|
722
|
+
devCallbackUrl?: string;
|
|
723
|
+
}
|
|
573
724
|
declare class AuthClient {
|
|
574
725
|
private http;
|
|
575
726
|
private appId;
|
|
576
|
-
|
|
727
|
+
private sessionCache;
|
|
728
|
+
private sessionCacheTTL;
|
|
729
|
+
private devCallbackUrl?;
|
|
730
|
+
constructor(http: HttpClient, appId: string, options?: AuthClientOptions);
|
|
731
|
+
/**
|
|
732
|
+
* Check if we're in a development environment.
|
|
733
|
+
*/
|
|
734
|
+
private isDev;
|
|
735
|
+
/**
|
|
736
|
+
* Clear the session cache (useful for testing or logout).
|
|
737
|
+
*/
|
|
738
|
+
clearCache(): void;
|
|
739
|
+
/**
|
|
740
|
+
* Remove a specific session from the cache.
|
|
741
|
+
*/
|
|
742
|
+
invalidateSession(sessionToken: string): void;
|
|
577
743
|
/**
|
|
578
744
|
* Send a magic link email to a customer for passwordless authentication.
|
|
579
745
|
*
|
|
746
|
+
* If `devCallbackUrl` is configured and we're in a dev environment (NODE_ENV !== 'production'),
|
|
747
|
+
* the dev callback URL will be used automatically.
|
|
748
|
+
*
|
|
580
749
|
* @example
|
|
581
750
|
* ```typescript
|
|
582
751
|
* // Send magic link
|
|
@@ -587,7 +756,7 @@ declare class AuthClient {
|
|
|
587
756
|
* redirectUrl: 'https://myapp.com/dashboard',
|
|
588
757
|
* });
|
|
589
758
|
*
|
|
590
|
-
* // For localhost development
|
|
759
|
+
* // For localhost development (auto-detected if devCallbackUrl is configured)
|
|
591
760
|
* await stackbe.auth.sendMagicLink('user@example.com', {
|
|
592
761
|
* useDev: true,
|
|
593
762
|
* });
|
|
@@ -598,19 +767,27 @@ declare class AuthClient {
|
|
|
598
767
|
* Verify a magic link token and get a session token.
|
|
599
768
|
* Call this when the user clicks the magic link.
|
|
600
769
|
*
|
|
770
|
+
* Returns the session token along with tenant and organization context.
|
|
771
|
+
*
|
|
601
772
|
* @example
|
|
602
773
|
* ```typescript
|
|
603
774
|
* // In your /verify route handler
|
|
604
775
|
* const { token } = req.query;
|
|
605
776
|
*
|
|
606
|
-
*
|
|
777
|
+
* try {
|
|
778
|
+
* const result = await stackbe.auth.verifyToken(token);
|
|
607
779
|
*
|
|
608
|
-
*
|
|
609
|
-
*
|
|
610
|
-
* res.cookie('session', result.token, { httpOnly: true });
|
|
780
|
+
* // result includes: customerId, email, sessionToken, tenantId, organizationId
|
|
781
|
+
* res.cookie('session', result.sessionToken, { httpOnly: true });
|
|
611
782
|
* res.redirect('/dashboard');
|
|
612
|
-
* }
|
|
613
|
-
*
|
|
783
|
+
* } catch (error) {
|
|
784
|
+
* if (error instanceof StackBEError) {
|
|
785
|
+
* if (error.code === 'TOKEN_EXPIRED') {
|
|
786
|
+
* res.redirect('/login?error=expired');
|
|
787
|
+
* } else if (error.code === 'TOKEN_ALREADY_USED') {
|
|
788
|
+
* res.redirect('/login?error=used');
|
|
789
|
+
* }
|
|
790
|
+
* }
|
|
614
791
|
* }
|
|
615
792
|
* ```
|
|
616
793
|
*/
|
|
@@ -619,6 +796,11 @@ declare class AuthClient {
|
|
|
619
796
|
* Get the current session for an authenticated customer.
|
|
620
797
|
* Use this to validate session tokens and get customer data.
|
|
621
798
|
*
|
|
799
|
+
* If `sessionCacheTTL` is configured, results are cached to reduce API calls.
|
|
800
|
+
* Use `invalidateSession()` or `clearCache()` to manually clear cached sessions.
|
|
801
|
+
*
|
|
802
|
+
* Returns session info including tenant and organization context extracted from the JWT.
|
|
803
|
+
*
|
|
622
804
|
* @example
|
|
623
805
|
* ```typescript
|
|
624
806
|
* // Validate session on each request
|
|
@@ -627,9 +809,11 @@ declare class AuthClient {
|
|
|
627
809
|
* const session = await stackbe.auth.getSession(sessionToken);
|
|
628
810
|
*
|
|
629
811
|
* if (session) {
|
|
630
|
-
*
|
|
631
|
-
*
|
|
632
|
-
*
|
|
812
|
+
* console.log(session.customerId);
|
|
813
|
+
* console.log(session.tenantId); // Tenant context
|
|
814
|
+
* console.log(session.organizationId); // Org context (if applicable)
|
|
815
|
+
* console.log(session.subscription);
|
|
816
|
+
* console.log(session.entitlements);
|
|
633
817
|
* }
|
|
634
818
|
* ```
|
|
635
819
|
*/
|
|
@@ -781,4 +965,4 @@ declare class StackBE {
|
|
|
781
965
|
}): (req: any, res: any, next: any) => Promise<any>;
|
|
782
966
|
}
|
|
783
967
|
|
|
784
|
-
export { AuthClient, type CancelSubscriptionOptions, type CancelSubscriptionResponse, type CheckEntitlementResponse, type CheckUsageResponse, CheckoutClient, type CheckoutSessionResponse, type CreateCheckoutOptions, type CreateCustomerOptions, type Customer, type CustomerUsageResponse, type CustomerWithSubscription, CustomersClient, EntitlementsClient, type EntitlementsResponse, type MagicLinkOptions, type MagicLinkResponse, type SessionResponse, StackBE, type StackBEConfig, StackBEError, type StackBEErrorResponse, type Subscription, type SubscriptionWithPlan, SubscriptionsClient, type TrackUsageOptions, type TrackUsageResponse, type UpdateCustomerOptions, type UpdateSubscriptionOptions, UsageClient, type UsageMetric, type VerifyTokenResponse };
|
|
968
|
+
export { type AnyWebhookEvent, AuthClient, type CancelSubscriptionOptions, type CancelSubscriptionResponse, type CheckEntitlementResponse, type CheckUsageResponse, CheckoutClient, type CheckoutSessionResponse, type CreateCheckoutOptions, type CreateCustomerOptions, type Customer, type CustomerCreatedEvent, type CustomerUpdatedEvent, type CustomerUsageResponse, type CustomerWebhookPayload, type CustomerWithSubscription, CustomersClient, EntitlementsClient, type EntitlementsResponse, type MagicLinkOptions, type MagicLinkResponse, type PaymentFailedEvent, type PaymentSucceededEvent, type PaymentWebhookPayload, type SessionResponse, StackBE, type StackBEConfig, StackBEError, type StackBEErrorCode, type StackBEErrorResponse, type Subscription, type SubscriptionCancelledEvent, type SubscriptionCreatedEvent, type SubscriptionRenewedEvent, type SubscriptionUpdatedEvent, type SubscriptionWebhookPayload, type SubscriptionWithPlan, SubscriptionsClient, type TrackUsageOptions, type TrackUsageResponse, type TrialEndedEvent, type TrialStartedEvent, type UpdateCustomerOptions, type UpdateSubscriptionOptions, UsageClient, type UsageMetric, type VerifyTokenResponse, type WebhookEvent, type WebhookEventType };
|
package/dist/index.d.ts
CHANGED
|
@@ -24,6 +24,30 @@ interface StackBEConfig {
|
|
|
24
24
|
baseUrl?: string;
|
|
25
25
|
/** Request timeout in milliseconds (default: 30000) */
|
|
26
26
|
timeout?: number;
|
|
27
|
+
/**
|
|
28
|
+
* Session cache TTL in seconds (default: 0 = disabled).
|
|
29
|
+
* When set, getSession() results are cached to reduce API calls.
|
|
30
|
+
* @example
|
|
31
|
+
* ```typescript
|
|
32
|
+
* new StackBE({ apiKey, appId, sessionCacheTTL: 120 }) // Cache for 2 minutes
|
|
33
|
+
* ```
|
|
34
|
+
*/
|
|
35
|
+
sessionCacheTTL?: number;
|
|
36
|
+
/**
|
|
37
|
+
* Development callback URL for magic link redirects.
|
|
38
|
+
* When set, this URL is used automatically when:
|
|
39
|
+
* - NODE_ENV !== 'production', or
|
|
40
|
+
* - useDev: true is passed to sendMagicLink()
|
|
41
|
+
* @example
|
|
42
|
+
* ```typescript
|
|
43
|
+
* new StackBE({
|
|
44
|
+
* apiKey,
|
|
45
|
+
* appId,
|
|
46
|
+
* devCallbackUrl: 'http://localhost:3000/auth/callback'
|
|
47
|
+
* })
|
|
48
|
+
* ```
|
|
49
|
+
*/
|
|
50
|
+
devCallbackUrl?: string;
|
|
27
51
|
}
|
|
28
52
|
interface TrackUsageOptions {
|
|
29
53
|
/** Quantity to track (default: 1) */
|
|
@@ -121,15 +145,43 @@ interface MagicLinkResponse {
|
|
|
121
145
|
message: string;
|
|
122
146
|
}
|
|
123
147
|
interface VerifyTokenResponse {
|
|
148
|
+
success: boolean;
|
|
124
149
|
valid: boolean;
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
150
|
+
/** Customer ID */
|
|
151
|
+
customerId: string;
|
|
152
|
+
/** Customer email */
|
|
153
|
+
email: string;
|
|
154
|
+
/** Session JWT token - use this for subsequent authenticated requests */
|
|
155
|
+
sessionToken: string;
|
|
156
|
+
/** Tenant ID (your StackBE tenant) */
|
|
157
|
+
tenantId?: string;
|
|
158
|
+
/** Organization ID if in org context */
|
|
159
|
+
organizationId?: string;
|
|
160
|
+
/** Role in the organization */
|
|
161
|
+
orgRole?: 'owner' | 'admin' | 'member';
|
|
162
|
+
/** URL to redirect to after verification */
|
|
163
|
+
redirectUrl?: string;
|
|
128
164
|
}
|
|
129
165
|
interface SessionResponse {
|
|
130
|
-
|
|
166
|
+
valid: boolean;
|
|
167
|
+
/** Customer ID */
|
|
168
|
+
customerId: string;
|
|
169
|
+
/** Customer email */
|
|
170
|
+
email: string;
|
|
171
|
+
/** Session expiration time */
|
|
172
|
+
expiresAt?: string;
|
|
173
|
+
/** Tenant ID (your StackBE tenant) */
|
|
174
|
+
tenantId?: string;
|
|
175
|
+
/** Organization ID if in org context */
|
|
176
|
+
organizationId?: string;
|
|
177
|
+
/** Role in the organization */
|
|
178
|
+
orgRole?: 'owner' | 'admin' | 'member';
|
|
179
|
+
/** Customer details (if included) */
|
|
180
|
+
customer?: Customer;
|
|
181
|
+
/** Current subscription (if any) */
|
|
131
182
|
subscription?: Subscription;
|
|
132
|
-
|
|
183
|
+
/** Feature entitlements */
|
|
184
|
+
entitlements?: Record<string, boolean | number | string>;
|
|
133
185
|
}
|
|
134
186
|
interface CreateCheckoutOptions {
|
|
135
187
|
/** Customer ID or email */
|
|
@@ -189,16 +241,109 @@ interface UpdateSubscriptionOptions {
|
|
|
189
241
|
/** Proration behavior */
|
|
190
242
|
prorate?: boolean;
|
|
191
243
|
}
|
|
244
|
+
/**
|
|
245
|
+
* Error codes returned by StackBE API.
|
|
246
|
+
* Use these to handle specific error cases in your application.
|
|
247
|
+
*
|
|
248
|
+
* @example
|
|
249
|
+
* ```typescript
|
|
250
|
+
* try {
|
|
251
|
+
* await stackbe.auth.verifyToken(token);
|
|
252
|
+
* } catch (error) {
|
|
253
|
+
* if (error instanceof StackBEError) {
|
|
254
|
+
* switch (error.code) {
|
|
255
|
+
* case 'TOKEN_EXPIRED':
|
|
256
|
+
* return res.redirect('/login?error=expired');
|
|
257
|
+
* case 'TOKEN_ALREADY_USED':
|
|
258
|
+
* return res.redirect('/login?error=used');
|
|
259
|
+
* case 'SESSION_EXPIRED':
|
|
260
|
+
* return res.redirect('/login');
|
|
261
|
+
* default:
|
|
262
|
+
* return res.status(500).json({ error: 'Authentication failed' });
|
|
263
|
+
* }
|
|
264
|
+
* }
|
|
265
|
+
* }
|
|
266
|
+
* ```
|
|
267
|
+
*/
|
|
268
|
+
type StackBEErrorCode = 'TOKEN_EXPIRED' | 'TOKEN_ALREADY_USED' | 'TOKEN_INVALID' | 'SESSION_EXPIRED' | 'SESSION_INVALID' | 'UNAUTHORIZED' | 'NOT_FOUND' | 'CUSTOMER_NOT_FOUND' | 'SUBSCRIPTION_NOT_FOUND' | 'PLAN_NOT_FOUND' | 'APP_NOT_FOUND' | 'VALIDATION_ERROR' | 'MISSING_REQUIRED_FIELD' | 'INVALID_EMAIL' | 'USAGE_LIMIT_EXCEEDED' | 'METRIC_NOT_FOUND' | 'FEATURE_NOT_AVAILABLE' | 'NO_ACTIVE_SUBSCRIPTION' | 'TIMEOUT' | 'NETWORK_ERROR' | 'UNKNOWN_ERROR';
|
|
192
269
|
interface StackBEErrorResponse {
|
|
193
270
|
statusCode: number;
|
|
194
271
|
message: string;
|
|
195
272
|
error: string;
|
|
273
|
+
code?: StackBEErrorCode;
|
|
196
274
|
}
|
|
197
275
|
declare class StackBEError extends Error {
|
|
198
276
|
readonly statusCode: number;
|
|
199
|
-
readonly code:
|
|
200
|
-
constructor(message: string, statusCode: number, code: string);
|
|
277
|
+
readonly code: StackBEErrorCode;
|
|
278
|
+
constructor(message: string, statusCode: number, code: StackBEErrorCode | string);
|
|
279
|
+
/** Check if this is a specific error type */
|
|
280
|
+
is(code: StackBEErrorCode): boolean;
|
|
281
|
+
/** Check if this is an auth-related error */
|
|
282
|
+
isAuthError(): boolean;
|
|
283
|
+
/** Check if this is a "not found" error */
|
|
284
|
+
isNotFoundError(): boolean;
|
|
285
|
+
}
|
|
286
|
+
/**
|
|
287
|
+
* Webhook event types emitted by StackBE.
|
|
288
|
+
* Subscribe to these events to react to changes in your customers' subscriptions.
|
|
289
|
+
*/
|
|
290
|
+
type WebhookEventType = 'subscription_created' | 'subscription_updated' | 'subscription_cancelled' | 'subscription_renewed' | 'trial_started' | 'trial_ended' | 'payment_succeeded' | 'payment_failed' | 'customer_created' | 'customer_updated';
|
|
291
|
+
/** Base webhook event structure */
|
|
292
|
+
interface WebhookEvent<T extends WebhookEventType = WebhookEventType, P = unknown> {
|
|
293
|
+
/** Event ID */
|
|
294
|
+
id: string;
|
|
295
|
+
/** Event type */
|
|
296
|
+
type: T;
|
|
297
|
+
/** Tenant ID */
|
|
298
|
+
tenantId: string;
|
|
299
|
+
/** App ID */
|
|
300
|
+
appId: string;
|
|
301
|
+
/** Event payload */
|
|
302
|
+
data: P;
|
|
303
|
+
/** When the event was created */
|
|
304
|
+
createdAt: string;
|
|
201
305
|
}
|
|
306
|
+
/** Subscription webhook payload */
|
|
307
|
+
interface SubscriptionWebhookPayload {
|
|
308
|
+
id: string;
|
|
309
|
+
customerId: string;
|
|
310
|
+
planId: string;
|
|
311
|
+
planName: string;
|
|
312
|
+
status: 'active' | 'canceled' | 'past_due' | 'trialing' | 'paused';
|
|
313
|
+
currentPeriodStart: string;
|
|
314
|
+
currentPeriodEnd: string;
|
|
315
|
+
cancelAtPeriodEnd: boolean;
|
|
316
|
+
trialEnd?: string;
|
|
317
|
+
}
|
|
318
|
+
/** Customer webhook payload */
|
|
319
|
+
interface CustomerWebhookPayload {
|
|
320
|
+
id: string;
|
|
321
|
+
email: string;
|
|
322
|
+
name?: string;
|
|
323
|
+
metadata?: Record<string, unknown>;
|
|
324
|
+
}
|
|
325
|
+
/** Payment webhook payload */
|
|
326
|
+
interface PaymentWebhookPayload {
|
|
327
|
+
id: string;
|
|
328
|
+
subscriptionId: string;
|
|
329
|
+
customerId: string;
|
|
330
|
+
amount: number;
|
|
331
|
+
currency: string;
|
|
332
|
+
status: 'succeeded' | 'failed';
|
|
333
|
+
failureReason?: string;
|
|
334
|
+
}
|
|
335
|
+
type SubscriptionCreatedEvent = WebhookEvent<'subscription_created', SubscriptionWebhookPayload>;
|
|
336
|
+
type SubscriptionUpdatedEvent = WebhookEvent<'subscription_updated', SubscriptionWebhookPayload>;
|
|
337
|
+
type SubscriptionCancelledEvent = WebhookEvent<'subscription_cancelled', SubscriptionWebhookPayload>;
|
|
338
|
+
type SubscriptionRenewedEvent = WebhookEvent<'subscription_renewed', SubscriptionWebhookPayload>;
|
|
339
|
+
type TrialStartedEvent = WebhookEvent<'trial_started', SubscriptionWebhookPayload>;
|
|
340
|
+
type TrialEndedEvent = WebhookEvent<'trial_ended', SubscriptionWebhookPayload>;
|
|
341
|
+
type PaymentSucceededEvent = WebhookEvent<'payment_succeeded', PaymentWebhookPayload>;
|
|
342
|
+
type PaymentFailedEvent = WebhookEvent<'payment_failed', PaymentWebhookPayload>;
|
|
343
|
+
type CustomerCreatedEvent = WebhookEvent<'customer_created', CustomerWebhookPayload>;
|
|
344
|
+
type CustomerUpdatedEvent = WebhookEvent<'customer_updated', CustomerWebhookPayload>;
|
|
345
|
+
/** Union of all webhook event types */
|
|
346
|
+
type AnyWebhookEvent = SubscriptionCreatedEvent | SubscriptionUpdatedEvent | SubscriptionCancelledEvent | SubscriptionRenewedEvent | TrialStartedEvent | TrialEndedEvent | PaymentSucceededEvent | PaymentFailedEvent | CustomerCreatedEvent | CustomerUpdatedEvent;
|
|
202
347
|
|
|
203
348
|
declare class UsageClient {
|
|
204
349
|
private http;
|
|
@@ -570,13 +715,37 @@ declare class SubscriptionsClient {
|
|
|
570
715
|
isActive(customerId: string): Promise<boolean>;
|
|
571
716
|
}
|
|
572
717
|
|
|
718
|
+
interface AuthClientOptions {
|
|
719
|
+
/** Session cache TTL in seconds (0 = disabled) */
|
|
720
|
+
sessionCacheTTL?: number;
|
|
721
|
+
/** Development callback URL for magic links */
|
|
722
|
+
devCallbackUrl?: string;
|
|
723
|
+
}
|
|
573
724
|
declare class AuthClient {
|
|
574
725
|
private http;
|
|
575
726
|
private appId;
|
|
576
|
-
|
|
727
|
+
private sessionCache;
|
|
728
|
+
private sessionCacheTTL;
|
|
729
|
+
private devCallbackUrl?;
|
|
730
|
+
constructor(http: HttpClient, appId: string, options?: AuthClientOptions);
|
|
731
|
+
/**
|
|
732
|
+
* Check if we're in a development environment.
|
|
733
|
+
*/
|
|
734
|
+
private isDev;
|
|
735
|
+
/**
|
|
736
|
+
* Clear the session cache (useful for testing or logout).
|
|
737
|
+
*/
|
|
738
|
+
clearCache(): void;
|
|
739
|
+
/**
|
|
740
|
+
* Remove a specific session from the cache.
|
|
741
|
+
*/
|
|
742
|
+
invalidateSession(sessionToken: string): void;
|
|
577
743
|
/**
|
|
578
744
|
* Send a magic link email to a customer for passwordless authentication.
|
|
579
745
|
*
|
|
746
|
+
* If `devCallbackUrl` is configured and we're in a dev environment (NODE_ENV !== 'production'),
|
|
747
|
+
* the dev callback URL will be used automatically.
|
|
748
|
+
*
|
|
580
749
|
* @example
|
|
581
750
|
* ```typescript
|
|
582
751
|
* // Send magic link
|
|
@@ -587,7 +756,7 @@ declare class AuthClient {
|
|
|
587
756
|
* redirectUrl: 'https://myapp.com/dashboard',
|
|
588
757
|
* });
|
|
589
758
|
*
|
|
590
|
-
* // For localhost development
|
|
759
|
+
* // For localhost development (auto-detected if devCallbackUrl is configured)
|
|
591
760
|
* await stackbe.auth.sendMagicLink('user@example.com', {
|
|
592
761
|
* useDev: true,
|
|
593
762
|
* });
|
|
@@ -598,19 +767,27 @@ declare class AuthClient {
|
|
|
598
767
|
* Verify a magic link token and get a session token.
|
|
599
768
|
* Call this when the user clicks the magic link.
|
|
600
769
|
*
|
|
770
|
+
* Returns the session token along with tenant and organization context.
|
|
771
|
+
*
|
|
601
772
|
* @example
|
|
602
773
|
* ```typescript
|
|
603
774
|
* // In your /verify route handler
|
|
604
775
|
* const { token } = req.query;
|
|
605
776
|
*
|
|
606
|
-
*
|
|
777
|
+
* try {
|
|
778
|
+
* const result = await stackbe.auth.verifyToken(token);
|
|
607
779
|
*
|
|
608
|
-
*
|
|
609
|
-
*
|
|
610
|
-
* res.cookie('session', result.token, { httpOnly: true });
|
|
780
|
+
* // result includes: customerId, email, sessionToken, tenantId, organizationId
|
|
781
|
+
* res.cookie('session', result.sessionToken, { httpOnly: true });
|
|
611
782
|
* res.redirect('/dashboard');
|
|
612
|
-
* }
|
|
613
|
-
*
|
|
783
|
+
* } catch (error) {
|
|
784
|
+
* if (error instanceof StackBEError) {
|
|
785
|
+
* if (error.code === 'TOKEN_EXPIRED') {
|
|
786
|
+
* res.redirect('/login?error=expired');
|
|
787
|
+
* } else if (error.code === 'TOKEN_ALREADY_USED') {
|
|
788
|
+
* res.redirect('/login?error=used');
|
|
789
|
+
* }
|
|
790
|
+
* }
|
|
614
791
|
* }
|
|
615
792
|
* ```
|
|
616
793
|
*/
|
|
@@ -619,6 +796,11 @@ declare class AuthClient {
|
|
|
619
796
|
* Get the current session for an authenticated customer.
|
|
620
797
|
* Use this to validate session tokens and get customer data.
|
|
621
798
|
*
|
|
799
|
+
* If `sessionCacheTTL` is configured, results are cached to reduce API calls.
|
|
800
|
+
* Use `invalidateSession()` or `clearCache()` to manually clear cached sessions.
|
|
801
|
+
*
|
|
802
|
+
* Returns session info including tenant and organization context extracted from the JWT.
|
|
803
|
+
*
|
|
622
804
|
* @example
|
|
623
805
|
* ```typescript
|
|
624
806
|
* // Validate session on each request
|
|
@@ -627,9 +809,11 @@ declare class AuthClient {
|
|
|
627
809
|
* const session = await stackbe.auth.getSession(sessionToken);
|
|
628
810
|
*
|
|
629
811
|
* if (session) {
|
|
630
|
-
*
|
|
631
|
-
*
|
|
632
|
-
*
|
|
812
|
+
* console.log(session.customerId);
|
|
813
|
+
* console.log(session.tenantId); // Tenant context
|
|
814
|
+
* console.log(session.organizationId); // Org context (if applicable)
|
|
815
|
+
* console.log(session.subscription);
|
|
816
|
+
* console.log(session.entitlements);
|
|
633
817
|
* }
|
|
634
818
|
* ```
|
|
635
819
|
*/
|
|
@@ -781,4 +965,4 @@ declare class StackBE {
|
|
|
781
965
|
}): (req: any, res: any, next: any) => Promise<any>;
|
|
782
966
|
}
|
|
783
967
|
|
|
784
|
-
export { AuthClient, type CancelSubscriptionOptions, type CancelSubscriptionResponse, type CheckEntitlementResponse, type CheckUsageResponse, CheckoutClient, type CheckoutSessionResponse, type CreateCheckoutOptions, type CreateCustomerOptions, type Customer, type CustomerUsageResponse, type CustomerWithSubscription, CustomersClient, EntitlementsClient, type EntitlementsResponse, type MagicLinkOptions, type MagicLinkResponse, type SessionResponse, StackBE, type StackBEConfig, StackBEError, type StackBEErrorResponse, type Subscription, type SubscriptionWithPlan, SubscriptionsClient, type TrackUsageOptions, type TrackUsageResponse, type UpdateCustomerOptions, type UpdateSubscriptionOptions, UsageClient, type UsageMetric, type VerifyTokenResponse };
|
|
968
|
+
export { type AnyWebhookEvent, AuthClient, type CancelSubscriptionOptions, type CancelSubscriptionResponse, type CheckEntitlementResponse, type CheckUsageResponse, CheckoutClient, type CheckoutSessionResponse, type CreateCheckoutOptions, type CreateCustomerOptions, type Customer, type CustomerCreatedEvent, type CustomerUpdatedEvent, type CustomerUsageResponse, type CustomerWebhookPayload, type CustomerWithSubscription, CustomersClient, EntitlementsClient, type EntitlementsResponse, type MagicLinkOptions, type MagicLinkResponse, type PaymentFailedEvent, type PaymentSucceededEvent, type PaymentWebhookPayload, type SessionResponse, StackBE, type StackBEConfig, StackBEError, type StackBEErrorCode, type StackBEErrorResponse, type Subscription, type SubscriptionCancelledEvent, type SubscriptionCreatedEvent, type SubscriptionRenewedEvent, type SubscriptionUpdatedEvent, type SubscriptionWebhookPayload, type SubscriptionWithPlan, SubscriptionsClient, type TrackUsageOptions, type TrackUsageResponse, type TrialEndedEvent, type TrialStartedEvent, type UpdateCustomerOptions, type UpdateSubscriptionOptions, UsageClient, type UsageMetric, type VerifyTokenResponse, type WebhookEvent, type WebhookEventType };
|