@stackbe/sdk 0.4.0 → 0.6.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/index.d.mts +199 -0
- package/dist/index.d.ts +199 -0
- package/dist/index.js +172 -0
- package/dist/index.mjs +172 -0
- package/package.json +1 -1
package/dist/index.d.mts
CHANGED
|
@@ -11,6 +11,7 @@ declare class HttpClient {
|
|
|
11
11
|
private request;
|
|
12
12
|
get<T>(path: string, params?: Record<string, string | number | undefined>): Promise<T>;
|
|
13
13
|
post<T>(path: string, body?: unknown, params?: Record<string, string | number | undefined>): Promise<T>;
|
|
14
|
+
put<T>(path: string, body?: unknown, params?: Record<string, string | number | undefined>): Promise<T>;
|
|
14
15
|
patch<T>(path: string, body?: unknown): Promise<T>;
|
|
15
16
|
delete<T>(path: string): Promise<T>;
|
|
16
17
|
}
|
|
@@ -89,6 +90,69 @@ interface CustomerUsageResponse {
|
|
|
89
90
|
billingPeriod: string;
|
|
90
91
|
metrics: UsageMetric[];
|
|
91
92
|
}
|
|
93
|
+
interface SyncUsageOptions {
|
|
94
|
+
/** Idempotency key to prevent duplicate syncs */
|
|
95
|
+
idempotencyKey?: string;
|
|
96
|
+
}
|
|
97
|
+
interface SyncUsageResponse {
|
|
98
|
+
success: boolean;
|
|
99
|
+
currentUsage: number;
|
|
100
|
+
billingPeriod: string;
|
|
101
|
+
limit: number | null;
|
|
102
|
+
remaining: number | null;
|
|
103
|
+
}
|
|
104
|
+
interface CheckUsageWithAddonsResponse extends CheckUsageResponse {
|
|
105
|
+
/** Limit from plan (before addons) */
|
|
106
|
+
planLimit: number | null;
|
|
107
|
+
/** Total addon credits available */
|
|
108
|
+
addonCredits: number;
|
|
109
|
+
/** Total effective limit (plan + addons) */
|
|
110
|
+
effectiveLimit: number | null;
|
|
111
|
+
}
|
|
112
|
+
interface TrackUsageWithAddonsResponse extends TrackUsageResponse {
|
|
113
|
+
/** Usage deducted from plan allocation */
|
|
114
|
+
planUsage: number;
|
|
115
|
+
/** Usage deducted from addon credits */
|
|
116
|
+
addonUsage: number;
|
|
117
|
+
}
|
|
118
|
+
interface UsageAddon {
|
|
119
|
+
id: string;
|
|
120
|
+
appId: string;
|
|
121
|
+
metricId: string;
|
|
122
|
+
metricName: string;
|
|
123
|
+
name: string;
|
|
124
|
+
description?: string;
|
|
125
|
+
quantity: number;
|
|
126
|
+
priceCents: number;
|
|
127
|
+
currency: string;
|
|
128
|
+
status: 'active' | 'archived';
|
|
129
|
+
stripePriceId?: string;
|
|
130
|
+
createdAt: string;
|
|
131
|
+
}
|
|
132
|
+
interface UsageAddonPurchase {
|
|
133
|
+
id: string;
|
|
134
|
+
addonId: string;
|
|
135
|
+
addonName: string;
|
|
136
|
+
customerId: string;
|
|
137
|
+
metricId: string;
|
|
138
|
+
metricName: string;
|
|
139
|
+
quantity: number;
|
|
140
|
+
usedQuantity: number;
|
|
141
|
+
remainingQuantity: number;
|
|
142
|
+
priceCents: number;
|
|
143
|
+
currency: string;
|
|
144
|
+
billingPeriod: string;
|
|
145
|
+
expiresAt?: string;
|
|
146
|
+
createdAt: string;
|
|
147
|
+
}
|
|
148
|
+
interface CustomerCreditsResponse {
|
|
149
|
+
customerId: string;
|
|
150
|
+
metricName: string;
|
|
151
|
+
totalPurchased: number;
|
|
152
|
+
totalUsed: number;
|
|
153
|
+
remaining: number;
|
|
154
|
+
purchases: UsageAddonPurchase[];
|
|
155
|
+
}
|
|
92
156
|
interface CheckEntitlementResponse {
|
|
93
157
|
/** Whether the customer has access to this feature */
|
|
94
158
|
hasAccess: boolean;
|
|
@@ -120,6 +184,10 @@ interface Subscription {
|
|
|
120
184
|
currentPeriodStart: string;
|
|
121
185
|
currentPeriodEnd: string;
|
|
122
186
|
cancelAtPeriodEnd: boolean;
|
|
187
|
+
/** When the subscription was paused (null if not paused) */
|
|
188
|
+
pausedAt?: string | null;
|
|
189
|
+
/** When the subscription will auto-resume (null = indefinite pause) */
|
|
190
|
+
resumesAt?: string | null;
|
|
123
191
|
createdAt: string;
|
|
124
192
|
}
|
|
125
193
|
interface CustomerWithSubscription extends Customer {
|
|
@@ -314,6 +382,10 @@ interface SubscriptionWebhookPayload {
|
|
|
314
382
|
currentPeriodEnd: string;
|
|
315
383
|
cancelAtPeriodEnd: boolean;
|
|
316
384
|
trialEnd?: string;
|
|
385
|
+
/** When the subscription was paused (null if not paused) */
|
|
386
|
+
pausedAt?: string | null;
|
|
387
|
+
/** When the subscription will auto-resume (null = indefinite pause) */
|
|
388
|
+
resumesAt?: string | null;
|
|
317
389
|
}
|
|
318
390
|
/** Customer webhook payload */
|
|
319
391
|
interface CustomerWebhookPayload {
|
|
@@ -409,6 +481,89 @@ declare class UsageClient {
|
|
|
409
481
|
trackAndCheck(customerId: string, metric: string, options?: TrackUsageOptions): Promise<TrackUsageResponse & {
|
|
410
482
|
allowed: boolean;
|
|
411
483
|
}>;
|
|
484
|
+
/**
|
|
485
|
+
* Sync usage to an absolute value (instead of incrementing).
|
|
486
|
+
* Use this when your app tracks usage internally and reports the final count periodically.
|
|
487
|
+
*
|
|
488
|
+
* @example
|
|
489
|
+
* ```typescript
|
|
490
|
+
* // Your app tracks 847 billable tickets internally
|
|
491
|
+
* await stackbe.usage.sync('cust_123', 'tickets', 847);
|
|
492
|
+
* ```
|
|
493
|
+
*/
|
|
494
|
+
sync(customerId: string, metric: string, value: number, options?: SyncUsageOptions): Promise<SyncUsageResponse>;
|
|
495
|
+
/**
|
|
496
|
+
* Check usage limits including purchased add-on credits.
|
|
497
|
+
*
|
|
498
|
+
* @example
|
|
499
|
+
* ```typescript
|
|
500
|
+
* const result = await stackbe.usage.checkWithAddons('cust_123', 'tickets');
|
|
501
|
+
*
|
|
502
|
+
* console.log(`Plan limit: ${result.planLimit}`);
|
|
503
|
+
* console.log(`Addon credits: ${result.addonCredits}`);
|
|
504
|
+
* console.log(`Effective limit: ${result.effectiveLimit}`);
|
|
505
|
+
*
|
|
506
|
+
* if (!result.allowed) {
|
|
507
|
+
* // Prompt to purchase more credits
|
|
508
|
+
* }
|
|
509
|
+
* ```
|
|
510
|
+
*/
|
|
511
|
+
checkWithAddons(customerId: string, metric: string): Promise<CheckUsageWithAddonsResponse>;
|
|
512
|
+
/**
|
|
513
|
+
* Track usage with automatic add-on credit deduction.
|
|
514
|
+
* When plan limit is exceeded, usage is deducted from purchased add-on credits.
|
|
515
|
+
*
|
|
516
|
+
* @example
|
|
517
|
+
* ```typescript
|
|
518
|
+
* const result = await stackbe.usage.trackWithAddons('cust_123', 'tickets');
|
|
519
|
+
*
|
|
520
|
+
* if (result.addonUsage > 0) {
|
|
521
|
+
* console.log(`Used ${result.addonUsage} from addon credits`);
|
|
522
|
+
* }
|
|
523
|
+
* ```
|
|
524
|
+
*/
|
|
525
|
+
trackWithAddons(customerId: string, metric: string, options?: TrackUsageOptions): Promise<TrackUsageWithAddonsResponse>;
|
|
526
|
+
/**
|
|
527
|
+
* List available add-on packs for purchase.
|
|
528
|
+
*
|
|
529
|
+
* @example
|
|
530
|
+
* ```typescript
|
|
531
|
+
* const addons = await stackbe.usage.listAddons();
|
|
532
|
+
*
|
|
533
|
+
* for (const addon of addons) {
|
|
534
|
+
* console.log(`${addon.name}: ${addon.quantity} ${addon.metricName} for $${addon.priceCents / 100}`);
|
|
535
|
+
* }
|
|
536
|
+
* ```
|
|
537
|
+
*/
|
|
538
|
+
listAddons(): Promise<UsageAddon[]>;
|
|
539
|
+
/**
|
|
540
|
+
* Purchase an add-on pack for a customer.
|
|
541
|
+
* Credits are immediately available after purchase.
|
|
542
|
+
*
|
|
543
|
+
* @example
|
|
544
|
+
* ```typescript
|
|
545
|
+
* // Purchase a 10k ticket pack
|
|
546
|
+
* const purchase = await stackbe.usage.purchaseAddon('cust_123', 'addon_xyz');
|
|
547
|
+
* console.log(`Purchased ${purchase.quantity} credits`);
|
|
548
|
+
* ```
|
|
549
|
+
*/
|
|
550
|
+
purchaseAddon(customerId: string, addonId: string): Promise<UsageAddonPurchase>;
|
|
551
|
+
/**
|
|
552
|
+
* Get customer's purchased add-on credits for a metric.
|
|
553
|
+
*
|
|
554
|
+
* @example
|
|
555
|
+
* ```typescript
|
|
556
|
+
* const credits = await stackbe.usage.getCredits('cust_123', 'tickets');
|
|
557
|
+
*
|
|
558
|
+
* console.log(`Total purchased: ${credits.totalPurchased}`);
|
|
559
|
+
* console.log(`Remaining: ${credits.remaining}`);
|
|
560
|
+
*
|
|
561
|
+
* for (const purchase of credits.purchases) {
|
|
562
|
+
* console.log(`${purchase.addonName}: ${purchase.remainingQuantity} left`);
|
|
563
|
+
* }
|
|
564
|
+
* ```
|
|
565
|
+
*/
|
|
566
|
+
getCredits(customerId: string, metric: string): Promise<CustomerCreditsResponse>;
|
|
412
567
|
}
|
|
413
568
|
|
|
414
569
|
declare class EntitlementsClient {
|
|
@@ -713,6 +868,50 @@ declare class SubscriptionsClient {
|
|
|
713
868
|
* ```
|
|
714
869
|
*/
|
|
715
870
|
isActive(customerId: string): Promise<boolean>;
|
|
871
|
+
/**
|
|
872
|
+
* Pause a subscription.
|
|
873
|
+
*
|
|
874
|
+
* Pauses billing collection for a subscription. The subscription remains
|
|
875
|
+
* active but no invoices are generated while paused. Use cases include:
|
|
876
|
+
* - Customer requests temporary pause (vacation, budget constraints)
|
|
877
|
+
* - Seasonal businesses with predictable downtime
|
|
878
|
+
* - Retention strategy: offer pause instead of cancel
|
|
879
|
+
*
|
|
880
|
+
* @example
|
|
881
|
+
* ```typescript
|
|
882
|
+
* // Pause indefinitely (until manually resumed)
|
|
883
|
+
* await stackbe.subscriptions.pause('sub_123');
|
|
884
|
+
*
|
|
885
|
+
* // Pause for 30 days (auto-resume)
|
|
886
|
+
* const resumeDate = new Date();
|
|
887
|
+
* resumeDate.setDate(resumeDate.getDate() + 30);
|
|
888
|
+
* await stackbe.subscriptions.pause('sub_123', { resumesAt: resumeDate });
|
|
889
|
+
*
|
|
890
|
+
* // Check if paused
|
|
891
|
+
* const sub = await stackbe.subscriptions.get('cust_123');
|
|
892
|
+
* if (sub?.pausedAt) {
|
|
893
|
+
* console.log('Subscription is paused');
|
|
894
|
+
* if (sub.resumesAt) {
|
|
895
|
+
* console.log(`Will resume on ${sub.resumesAt}`);
|
|
896
|
+
* }
|
|
897
|
+
* }
|
|
898
|
+
* ```
|
|
899
|
+
*/
|
|
900
|
+
pause(subscriptionId: string, options?: {
|
|
901
|
+
resumesAt?: Date;
|
|
902
|
+
}): Promise<Subscription>;
|
|
903
|
+
/**
|
|
904
|
+
* Resume a paused subscription.
|
|
905
|
+
*
|
|
906
|
+
* Restarts billing collection for a paused subscription.
|
|
907
|
+
*
|
|
908
|
+
* @example
|
|
909
|
+
* ```typescript
|
|
910
|
+
* // Resume a paused subscription
|
|
911
|
+
* await stackbe.subscriptions.resume('sub_123');
|
|
912
|
+
* ```
|
|
913
|
+
*/
|
|
914
|
+
resume(subscriptionId: string): Promise<Subscription>;
|
|
716
915
|
}
|
|
717
916
|
|
|
718
917
|
interface AuthClientOptions {
|
package/dist/index.d.ts
CHANGED
|
@@ -11,6 +11,7 @@ declare class HttpClient {
|
|
|
11
11
|
private request;
|
|
12
12
|
get<T>(path: string, params?: Record<string, string | number | undefined>): Promise<T>;
|
|
13
13
|
post<T>(path: string, body?: unknown, params?: Record<string, string | number | undefined>): Promise<T>;
|
|
14
|
+
put<T>(path: string, body?: unknown, params?: Record<string, string | number | undefined>): Promise<T>;
|
|
14
15
|
patch<T>(path: string, body?: unknown): Promise<T>;
|
|
15
16
|
delete<T>(path: string): Promise<T>;
|
|
16
17
|
}
|
|
@@ -89,6 +90,69 @@ interface CustomerUsageResponse {
|
|
|
89
90
|
billingPeriod: string;
|
|
90
91
|
metrics: UsageMetric[];
|
|
91
92
|
}
|
|
93
|
+
interface SyncUsageOptions {
|
|
94
|
+
/** Idempotency key to prevent duplicate syncs */
|
|
95
|
+
idempotencyKey?: string;
|
|
96
|
+
}
|
|
97
|
+
interface SyncUsageResponse {
|
|
98
|
+
success: boolean;
|
|
99
|
+
currentUsage: number;
|
|
100
|
+
billingPeriod: string;
|
|
101
|
+
limit: number | null;
|
|
102
|
+
remaining: number | null;
|
|
103
|
+
}
|
|
104
|
+
interface CheckUsageWithAddonsResponse extends CheckUsageResponse {
|
|
105
|
+
/** Limit from plan (before addons) */
|
|
106
|
+
planLimit: number | null;
|
|
107
|
+
/** Total addon credits available */
|
|
108
|
+
addonCredits: number;
|
|
109
|
+
/** Total effective limit (plan + addons) */
|
|
110
|
+
effectiveLimit: number | null;
|
|
111
|
+
}
|
|
112
|
+
interface TrackUsageWithAddonsResponse extends TrackUsageResponse {
|
|
113
|
+
/** Usage deducted from plan allocation */
|
|
114
|
+
planUsage: number;
|
|
115
|
+
/** Usage deducted from addon credits */
|
|
116
|
+
addonUsage: number;
|
|
117
|
+
}
|
|
118
|
+
interface UsageAddon {
|
|
119
|
+
id: string;
|
|
120
|
+
appId: string;
|
|
121
|
+
metricId: string;
|
|
122
|
+
metricName: string;
|
|
123
|
+
name: string;
|
|
124
|
+
description?: string;
|
|
125
|
+
quantity: number;
|
|
126
|
+
priceCents: number;
|
|
127
|
+
currency: string;
|
|
128
|
+
status: 'active' | 'archived';
|
|
129
|
+
stripePriceId?: string;
|
|
130
|
+
createdAt: string;
|
|
131
|
+
}
|
|
132
|
+
interface UsageAddonPurchase {
|
|
133
|
+
id: string;
|
|
134
|
+
addonId: string;
|
|
135
|
+
addonName: string;
|
|
136
|
+
customerId: string;
|
|
137
|
+
metricId: string;
|
|
138
|
+
metricName: string;
|
|
139
|
+
quantity: number;
|
|
140
|
+
usedQuantity: number;
|
|
141
|
+
remainingQuantity: number;
|
|
142
|
+
priceCents: number;
|
|
143
|
+
currency: string;
|
|
144
|
+
billingPeriod: string;
|
|
145
|
+
expiresAt?: string;
|
|
146
|
+
createdAt: string;
|
|
147
|
+
}
|
|
148
|
+
interface CustomerCreditsResponse {
|
|
149
|
+
customerId: string;
|
|
150
|
+
metricName: string;
|
|
151
|
+
totalPurchased: number;
|
|
152
|
+
totalUsed: number;
|
|
153
|
+
remaining: number;
|
|
154
|
+
purchases: UsageAddonPurchase[];
|
|
155
|
+
}
|
|
92
156
|
interface CheckEntitlementResponse {
|
|
93
157
|
/** Whether the customer has access to this feature */
|
|
94
158
|
hasAccess: boolean;
|
|
@@ -120,6 +184,10 @@ interface Subscription {
|
|
|
120
184
|
currentPeriodStart: string;
|
|
121
185
|
currentPeriodEnd: string;
|
|
122
186
|
cancelAtPeriodEnd: boolean;
|
|
187
|
+
/** When the subscription was paused (null if not paused) */
|
|
188
|
+
pausedAt?: string | null;
|
|
189
|
+
/** When the subscription will auto-resume (null = indefinite pause) */
|
|
190
|
+
resumesAt?: string | null;
|
|
123
191
|
createdAt: string;
|
|
124
192
|
}
|
|
125
193
|
interface CustomerWithSubscription extends Customer {
|
|
@@ -314,6 +382,10 @@ interface SubscriptionWebhookPayload {
|
|
|
314
382
|
currentPeriodEnd: string;
|
|
315
383
|
cancelAtPeriodEnd: boolean;
|
|
316
384
|
trialEnd?: string;
|
|
385
|
+
/** When the subscription was paused (null if not paused) */
|
|
386
|
+
pausedAt?: string | null;
|
|
387
|
+
/** When the subscription will auto-resume (null = indefinite pause) */
|
|
388
|
+
resumesAt?: string | null;
|
|
317
389
|
}
|
|
318
390
|
/** Customer webhook payload */
|
|
319
391
|
interface CustomerWebhookPayload {
|
|
@@ -409,6 +481,89 @@ declare class UsageClient {
|
|
|
409
481
|
trackAndCheck(customerId: string, metric: string, options?: TrackUsageOptions): Promise<TrackUsageResponse & {
|
|
410
482
|
allowed: boolean;
|
|
411
483
|
}>;
|
|
484
|
+
/**
|
|
485
|
+
* Sync usage to an absolute value (instead of incrementing).
|
|
486
|
+
* Use this when your app tracks usage internally and reports the final count periodically.
|
|
487
|
+
*
|
|
488
|
+
* @example
|
|
489
|
+
* ```typescript
|
|
490
|
+
* // Your app tracks 847 billable tickets internally
|
|
491
|
+
* await stackbe.usage.sync('cust_123', 'tickets', 847);
|
|
492
|
+
* ```
|
|
493
|
+
*/
|
|
494
|
+
sync(customerId: string, metric: string, value: number, options?: SyncUsageOptions): Promise<SyncUsageResponse>;
|
|
495
|
+
/**
|
|
496
|
+
* Check usage limits including purchased add-on credits.
|
|
497
|
+
*
|
|
498
|
+
* @example
|
|
499
|
+
* ```typescript
|
|
500
|
+
* const result = await stackbe.usage.checkWithAddons('cust_123', 'tickets');
|
|
501
|
+
*
|
|
502
|
+
* console.log(`Plan limit: ${result.planLimit}`);
|
|
503
|
+
* console.log(`Addon credits: ${result.addonCredits}`);
|
|
504
|
+
* console.log(`Effective limit: ${result.effectiveLimit}`);
|
|
505
|
+
*
|
|
506
|
+
* if (!result.allowed) {
|
|
507
|
+
* // Prompt to purchase more credits
|
|
508
|
+
* }
|
|
509
|
+
* ```
|
|
510
|
+
*/
|
|
511
|
+
checkWithAddons(customerId: string, metric: string): Promise<CheckUsageWithAddonsResponse>;
|
|
512
|
+
/**
|
|
513
|
+
* Track usage with automatic add-on credit deduction.
|
|
514
|
+
* When plan limit is exceeded, usage is deducted from purchased add-on credits.
|
|
515
|
+
*
|
|
516
|
+
* @example
|
|
517
|
+
* ```typescript
|
|
518
|
+
* const result = await stackbe.usage.trackWithAddons('cust_123', 'tickets');
|
|
519
|
+
*
|
|
520
|
+
* if (result.addonUsage > 0) {
|
|
521
|
+
* console.log(`Used ${result.addonUsage} from addon credits`);
|
|
522
|
+
* }
|
|
523
|
+
* ```
|
|
524
|
+
*/
|
|
525
|
+
trackWithAddons(customerId: string, metric: string, options?: TrackUsageOptions): Promise<TrackUsageWithAddonsResponse>;
|
|
526
|
+
/**
|
|
527
|
+
* List available add-on packs for purchase.
|
|
528
|
+
*
|
|
529
|
+
* @example
|
|
530
|
+
* ```typescript
|
|
531
|
+
* const addons = await stackbe.usage.listAddons();
|
|
532
|
+
*
|
|
533
|
+
* for (const addon of addons) {
|
|
534
|
+
* console.log(`${addon.name}: ${addon.quantity} ${addon.metricName} for $${addon.priceCents / 100}`);
|
|
535
|
+
* }
|
|
536
|
+
* ```
|
|
537
|
+
*/
|
|
538
|
+
listAddons(): Promise<UsageAddon[]>;
|
|
539
|
+
/**
|
|
540
|
+
* Purchase an add-on pack for a customer.
|
|
541
|
+
* Credits are immediately available after purchase.
|
|
542
|
+
*
|
|
543
|
+
* @example
|
|
544
|
+
* ```typescript
|
|
545
|
+
* // Purchase a 10k ticket pack
|
|
546
|
+
* const purchase = await stackbe.usage.purchaseAddon('cust_123', 'addon_xyz');
|
|
547
|
+
* console.log(`Purchased ${purchase.quantity} credits`);
|
|
548
|
+
* ```
|
|
549
|
+
*/
|
|
550
|
+
purchaseAddon(customerId: string, addonId: string): Promise<UsageAddonPurchase>;
|
|
551
|
+
/**
|
|
552
|
+
* Get customer's purchased add-on credits for a metric.
|
|
553
|
+
*
|
|
554
|
+
* @example
|
|
555
|
+
* ```typescript
|
|
556
|
+
* const credits = await stackbe.usage.getCredits('cust_123', 'tickets');
|
|
557
|
+
*
|
|
558
|
+
* console.log(`Total purchased: ${credits.totalPurchased}`);
|
|
559
|
+
* console.log(`Remaining: ${credits.remaining}`);
|
|
560
|
+
*
|
|
561
|
+
* for (const purchase of credits.purchases) {
|
|
562
|
+
* console.log(`${purchase.addonName}: ${purchase.remainingQuantity} left`);
|
|
563
|
+
* }
|
|
564
|
+
* ```
|
|
565
|
+
*/
|
|
566
|
+
getCredits(customerId: string, metric: string): Promise<CustomerCreditsResponse>;
|
|
412
567
|
}
|
|
413
568
|
|
|
414
569
|
declare class EntitlementsClient {
|
|
@@ -713,6 +868,50 @@ declare class SubscriptionsClient {
|
|
|
713
868
|
* ```
|
|
714
869
|
*/
|
|
715
870
|
isActive(customerId: string): Promise<boolean>;
|
|
871
|
+
/**
|
|
872
|
+
* Pause a subscription.
|
|
873
|
+
*
|
|
874
|
+
* Pauses billing collection for a subscription. The subscription remains
|
|
875
|
+
* active but no invoices are generated while paused. Use cases include:
|
|
876
|
+
* - Customer requests temporary pause (vacation, budget constraints)
|
|
877
|
+
* - Seasonal businesses with predictable downtime
|
|
878
|
+
* - Retention strategy: offer pause instead of cancel
|
|
879
|
+
*
|
|
880
|
+
* @example
|
|
881
|
+
* ```typescript
|
|
882
|
+
* // Pause indefinitely (until manually resumed)
|
|
883
|
+
* await stackbe.subscriptions.pause('sub_123');
|
|
884
|
+
*
|
|
885
|
+
* // Pause for 30 days (auto-resume)
|
|
886
|
+
* const resumeDate = new Date();
|
|
887
|
+
* resumeDate.setDate(resumeDate.getDate() + 30);
|
|
888
|
+
* await stackbe.subscriptions.pause('sub_123', { resumesAt: resumeDate });
|
|
889
|
+
*
|
|
890
|
+
* // Check if paused
|
|
891
|
+
* const sub = await stackbe.subscriptions.get('cust_123');
|
|
892
|
+
* if (sub?.pausedAt) {
|
|
893
|
+
* console.log('Subscription is paused');
|
|
894
|
+
* if (sub.resumesAt) {
|
|
895
|
+
* console.log(`Will resume on ${sub.resumesAt}`);
|
|
896
|
+
* }
|
|
897
|
+
* }
|
|
898
|
+
* ```
|
|
899
|
+
*/
|
|
900
|
+
pause(subscriptionId: string, options?: {
|
|
901
|
+
resumesAt?: Date;
|
|
902
|
+
}): Promise<Subscription>;
|
|
903
|
+
/**
|
|
904
|
+
* Resume a paused subscription.
|
|
905
|
+
*
|
|
906
|
+
* Restarts billing collection for a paused subscription.
|
|
907
|
+
*
|
|
908
|
+
* @example
|
|
909
|
+
* ```typescript
|
|
910
|
+
* // Resume a paused subscription
|
|
911
|
+
* await stackbe.subscriptions.resume('sub_123');
|
|
912
|
+
* ```
|
|
913
|
+
*/
|
|
914
|
+
resume(subscriptionId: string): Promise<Subscription>;
|
|
716
915
|
}
|
|
717
916
|
|
|
718
917
|
interface AuthClientOptions {
|
package/dist/index.js
CHANGED
|
@@ -157,6 +157,9 @@ var HttpClient = class {
|
|
|
157
157
|
async post(path, body, params) {
|
|
158
158
|
return this.request("POST", path, { body, params });
|
|
159
159
|
}
|
|
160
|
+
async put(path, body, params) {
|
|
161
|
+
return this.request("PUT", path, { body, params });
|
|
162
|
+
}
|
|
160
163
|
async patch(path, body) {
|
|
161
164
|
return this.request("PATCH", path, { body });
|
|
162
165
|
}
|
|
@@ -256,6 +259,122 @@ var UsageClient = class {
|
|
|
256
259
|
allowed
|
|
257
260
|
};
|
|
258
261
|
}
|
|
262
|
+
/**
|
|
263
|
+
* Sync usage to an absolute value (instead of incrementing).
|
|
264
|
+
* Use this when your app tracks usage internally and reports the final count periodically.
|
|
265
|
+
*
|
|
266
|
+
* @example
|
|
267
|
+
* ```typescript
|
|
268
|
+
* // Your app tracks 847 billable tickets internally
|
|
269
|
+
* await stackbe.usage.sync('cust_123', 'tickets', 847);
|
|
270
|
+
* ```
|
|
271
|
+
*/
|
|
272
|
+
async sync(customerId, metric, value, options = {}) {
|
|
273
|
+
const headers = {};
|
|
274
|
+
if (options.idempotencyKey) {
|
|
275
|
+
headers["Idempotency-Key"] = options.idempotencyKey;
|
|
276
|
+
}
|
|
277
|
+
return this.http.put("/v1/usage/sync", {
|
|
278
|
+
customerId,
|
|
279
|
+
metric,
|
|
280
|
+
value
|
|
281
|
+
});
|
|
282
|
+
}
|
|
283
|
+
/**
|
|
284
|
+
* Check usage limits including purchased add-on credits.
|
|
285
|
+
*
|
|
286
|
+
* @example
|
|
287
|
+
* ```typescript
|
|
288
|
+
* const result = await stackbe.usage.checkWithAddons('cust_123', 'tickets');
|
|
289
|
+
*
|
|
290
|
+
* console.log(`Plan limit: ${result.planLimit}`);
|
|
291
|
+
* console.log(`Addon credits: ${result.addonCredits}`);
|
|
292
|
+
* console.log(`Effective limit: ${result.effectiveLimit}`);
|
|
293
|
+
*
|
|
294
|
+
* if (!result.allowed) {
|
|
295
|
+
* // Prompt to purchase more credits
|
|
296
|
+
* }
|
|
297
|
+
* ```
|
|
298
|
+
*/
|
|
299
|
+
async checkWithAddons(customerId, metric) {
|
|
300
|
+
return this.http.get(
|
|
301
|
+
`/v1/customers/${customerId}/usage/check-with-addons`,
|
|
302
|
+
{ metric }
|
|
303
|
+
);
|
|
304
|
+
}
|
|
305
|
+
/**
|
|
306
|
+
* Track usage with automatic add-on credit deduction.
|
|
307
|
+
* When plan limit is exceeded, usage is deducted from purchased add-on credits.
|
|
308
|
+
*
|
|
309
|
+
* @example
|
|
310
|
+
* ```typescript
|
|
311
|
+
* const result = await stackbe.usage.trackWithAddons('cust_123', 'tickets');
|
|
312
|
+
*
|
|
313
|
+
* if (result.addonUsage > 0) {
|
|
314
|
+
* console.log(`Used ${result.addonUsage} from addon credits`);
|
|
315
|
+
* }
|
|
316
|
+
* ```
|
|
317
|
+
*/
|
|
318
|
+
async trackWithAddons(customerId, metric, options = {}) {
|
|
319
|
+
return this.http.post("/v1/usage/addons/track", {
|
|
320
|
+
customerId,
|
|
321
|
+
metric,
|
|
322
|
+
quantity: options.quantity ?? 1
|
|
323
|
+
});
|
|
324
|
+
}
|
|
325
|
+
/**
|
|
326
|
+
* List available add-on packs for purchase.
|
|
327
|
+
*
|
|
328
|
+
* @example
|
|
329
|
+
* ```typescript
|
|
330
|
+
* const addons = await stackbe.usage.listAddons();
|
|
331
|
+
*
|
|
332
|
+
* for (const addon of addons) {
|
|
333
|
+
* console.log(`${addon.name}: ${addon.quantity} ${addon.metricName} for $${addon.priceCents / 100}`);
|
|
334
|
+
* }
|
|
335
|
+
* ```
|
|
336
|
+
*/
|
|
337
|
+
async listAddons() {
|
|
338
|
+
return this.http.get("/v1/usage/addons");
|
|
339
|
+
}
|
|
340
|
+
/**
|
|
341
|
+
* Purchase an add-on pack for a customer.
|
|
342
|
+
* Credits are immediately available after purchase.
|
|
343
|
+
*
|
|
344
|
+
* @example
|
|
345
|
+
* ```typescript
|
|
346
|
+
* // Purchase a 10k ticket pack
|
|
347
|
+
* const purchase = await stackbe.usage.purchaseAddon('cust_123', 'addon_xyz');
|
|
348
|
+
* console.log(`Purchased ${purchase.quantity} credits`);
|
|
349
|
+
* ```
|
|
350
|
+
*/
|
|
351
|
+
async purchaseAddon(customerId, addonId) {
|
|
352
|
+
return this.http.post("/v1/usage/addons/purchase", {
|
|
353
|
+
customerId,
|
|
354
|
+
addonId
|
|
355
|
+
});
|
|
356
|
+
}
|
|
357
|
+
/**
|
|
358
|
+
* Get customer's purchased add-on credits for a metric.
|
|
359
|
+
*
|
|
360
|
+
* @example
|
|
361
|
+
* ```typescript
|
|
362
|
+
* const credits = await stackbe.usage.getCredits('cust_123', 'tickets');
|
|
363
|
+
*
|
|
364
|
+
* console.log(`Total purchased: ${credits.totalPurchased}`);
|
|
365
|
+
* console.log(`Remaining: ${credits.remaining}`);
|
|
366
|
+
*
|
|
367
|
+
* for (const purchase of credits.purchases) {
|
|
368
|
+
* console.log(`${purchase.addonName}: ${purchase.remainingQuantity} left`);
|
|
369
|
+
* }
|
|
370
|
+
* ```
|
|
371
|
+
*/
|
|
372
|
+
async getCredits(customerId, metric) {
|
|
373
|
+
return this.http.get(
|
|
374
|
+
`/v1/customers/${customerId}/usage/credits`,
|
|
375
|
+
{ metric }
|
|
376
|
+
);
|
|
377
|
+
}
|
|
259
378
|
};
|
|
260
379
|
|
|
261
380
|
// src/entitlements.ts
|
|
@@ -683,6 +802,59 @@ var SubscriptionsClient = class {
|
|
|
683
802
|
const subscription = await this.get(customerId);
|
|
684
803
|
return subscription !== null && (subscription.status === "active" || subscription.status === "trialing");
|
|
685
804
|
}
|
|
805
|
+
/**
|
|
806
|
+
* Pause a subscription.
|
|
807
|
+
*
|
|
808
|
+
* Pauses billing collection for a subscription. The subscription remains
|
|
809
|
+
* active but no invoices are generated while paused. Use cases include:
|
|
810
|
+
* - Customer requests temporary pause (vacation, budget constraints)
|
|
811
|
+
* - Seasonal businesses with predictable downtime
|
|
812
|
+
* - Retention strategy: offer pause instead of cancel
|
|
813
|
+
*
|
|
814
|
+
* @example
|
|
815
|
+
* ```typescript
|
|
816
|
+
* // Pause indefinitely (until manually resumed)
|
|
817
|
+
* await stackbe.subscriptions.pause('sub_123');
|
|
818
|
+
*
|
|
819
|
+
* // Pause for 30 days (auto-resume)
|
|
820
|
+
* const resumeDate = new Date();
|
|
821
|
+
* resumeDate.setDate(resumeDate.getDate() + 30);
|
|
822
|
+
* await stackbe.subscriptions.pause('sub_123', { resumesAt: resumeDate });
|
|
823
|
+
*
|
|
824
|
+
* // Check if paused
|
|
825
|
+
* const sub = await stackbe.subscriptions.get('cust_123');
|
|
826
|
+
* if (sub?.pausedAt) {
|
|
827
|
+
* console.log('Subscription is paused');
|
|
828
|
+
* if (sub.resumesAt) {
|
|
829
|
+
* console.log(`Will resume on ${sub.resumesAt}`);
|
|
830
|
+
* }
|
|
831
|
+
* }
|
|
832
|
+
* ```
|
|
833
|
+
*/
|
|
834
|
+
async pause(subscriptionId, options = {}) {
|
|
835
|
+
return this.http.post(
|
|
836
|
+
`/v1/subscriptions/${subscriptionId}/pause`,
|
|
837
|
+
{
|
|
838
|
+
resumesAt: options.resumesAt?.toISOString()
|
|
839
|
+
}
|
|
840
|
+
);
|
|
841
|
+
}
|
|
842
|
+
/**
|
|
843
|
+
* Resume a paused subscription.
|
|
844
|
+
*
|
|
845
|
+
* Restarts billing collection for a paused subscription.
|
|
846
|
+
*
|
|
847
|
+
* @example
|
|
848
|
+
* ```typescript
|
|
849
|
+
* // Resume a paused subscription
|
|
850
|
+
* await stackbe.subscriptions.resume('sub_123');
|
|
851
|
+
* ```
|
|
852
|
+
*/
|
|
853
|
+
async resume(subscriptionId) {
|
|
854
|
+
return this.http.post(
|
|
855
|
+
`/v1/subscriptions/${subscriptionId}/resume`
|
|
856
|
+
);
|
|
857
|
+
}
|
|
686
858
|
};
|
|
687
859
|
|
|
688
860
|
// src/auth.ts
|
package/dist/index.mjs
CHANGED
|
@@ -124,6 +124,9 @@ var HttpClient = class {
|
|
|
124
124
|
async post(path, body, params) {
|
|
125
125
|
return this.request("POST", path, { body, params });
|
|
126
126
|
}
|
|
127
|
+
async put(path, body, params) {
|
|
128
|
+
return this.request("PUT", path, { body, params });
|
|
129
|
+
}
|
|
127
130
|
async patch(path, body) {
|
|
128
131
|
return this.request("PATCH", path, { body });
|
|
129
132
|
}
|
|
@@ -223,6 +226,122 @@ var UsageClient = class {
|
|
|
223
226
|
allowed
|
|
224
227
|
};
|
|
225
228
|
}
|
|
229
|
+
/**
|
|
230
|
+
* Sync usage to an absolute value (instead of incrementing).
|
|
231
|
+
* Use this when your app tracks usage internally and reports the final count periodically.
|
|
232
|
+
*
|
|
233
|
+
* @example
|
|
234
|
+
* ```typescript
|
|
235
|
+
* // Your app tracks 847 billable tickets internally
|
|
236
|
+
* await stackbe.usage.sync('cust_123', 'tickets', 847);
|
|
237
|
+
* ```
|
|
238
|
+
*/
|
|
239
|
+
async sync(customerId, metric, value, options = {}) {
|
|
240
|
+
const headers = {};
|
|
241
|
+
if (options.idempotencyKey) {
|
|
242
|
+
headers["Idempotency-Key"] = options.idempotencyKey;
|
|
243
|
+
}
|
|
244
|
+
return this.http.put("/v1/usage/sync", {
|
|
245
|
+
customerId,
|
|
246
|
+
metric,
|
|
247
|
+
value
|
|
248
|
+
});
|
|
249
|
+
}
|
|
250
|
+
/**
|
|
251
|
+
* Check usage limits including purchased add-on credits.
|
|
252
|
+
*
|
|
253
|
+
* @example
|
|
254
|
+
* ```typescript
|
|
255
|
+
* const result = await stackbe.usage.checkWithAddons('cust_123', 'tickets');
|
|
256
|
+
*
|
|
257
|
+
* console.log(`Plan limit: ${result.planLimit}`);
|
|
258
|
+
* console.log(`Addon credits: ${result.addonCredits}`);
|
|
259
|
+
* console.log(`Effective limit: ${result.effectiveLimit}`);
|
|
260
|
+
*
|
|
261
|
+
* if (!result.allowed) {
|
|
262
|
+
* // Prompt to purchase more credits
|
|
263
|
+
* }
|
|
264
|
+
* ```
|
|
265
|
+
*/
|
|
266
|
+
async checkWithAddons(customerId, metric) {
|
|
267
|
+
return this.http.get(
|
|
268
|
+
`/v1/customers/${customerId}/usage/check-with-addons`,
|
|
269
|
+
{ metric }
|
|
270
|
+
);
|
|
271
|
+
}
|
|
272
|
+
/**
|
|
273
|
+
* Track usage with automatic add-on credit deduction.
|
|
274
|
+
* When plan limit is exceeded, usage is deducted from purchased add-on credits.
|
|
275
|
+
*
|
|
276
|
+
* @example
|
|
277
|
+
* ```typescript
|
|
278
|
+
* const result = await stackbe.usage.trackWithAddons('cust_123', 'tickets');
|
|
279
|
+
*
|
|
280
|
+
* if (result.addonUsage > 0) {
|
|
281
|
+
* console.log(`Used ${result.addonUsage} from addon credits`);
|
|
282
|
+
* }
|
|
283
|
+
* ```
|
|
284
|
+
*/
|
|
285
|
+
async trackWithAddons(customerId, metric, options = {}) {
|
|
286
|
+
return this.http.post("/v1/usage/addons/track", {
|
|
287
|
+
customerId,
|
|
288
|
+
metric,
|
|
289
|
+
quantity: options.quantity ?? 1
|
|
290
|
+
});
|
|
291
|
+
}
|
|
292
|
+
/**
|
|
293
|
+
* List available add-on packs for purchase.
|
|
294
|
+
*
|
|
295
|
+
* @example
|
|
296
|
+
* ```typescript
|
|
297
|
+
* const addons = await stackbe.usage.listAddons();
|
|
298
|
+
*
|
|
299
|
+
* for (const addon of addons) {
|
|
300
|
+
* console.log(`${addon.name}: ${addon.quantity} ${addon.metricName} for $${addon.priceCents / 100}`);
|
|
301
|
+
* }
|
|
302
|
+
* ```
|
|
303
|
+
*/
|
|
304
|
+
async listAddons() {
|
|
305
|
+
return this.http.get("/v1/usage/addons");
|
|
306
|
+
}
|
|
307
|
+
/**
|
|
308
|
+
* Purchase an add-on pack for a customer.
|
|
309
|
+
* Credits are immediately available after purchase.
|
|
310
|
+
*
|
|
311
|
+
* @example
|
|
312
|
+
* ```typescript
|
|
313
|
+
* // Purchase a 10k ticket pack
|
|
314
|
+
* const purchase = await stackbe.usage.purchaseAddon('cust_123', 'addon_xyz');
|
|
315
|
+
* console.log(`Purchased ${purchase.quantity} credits`);
|
|
316
|
+
* ```
|
|
317
|
+
*/
|
|
318
|
+
async purchaseAddon(customerId, addonId) {
|
|
319
|
+
return this.http.post("/v1/usage/addons/purchase", {
|
|
320
|
+
customerId,
|
|
321
|
+
addonId
|
|
322
|
+
});
|
|
323
|
+
}
|
|
324
|
+
/**
|
|
325
|
+
* Get customer's purchased add-on credits for a metric.
|
|
326
|
+
*
|
|
327
|
+
* @example
|
|
328
|
+
* ```typescript
|
|
329
|
+
* const credits = await stackbe.usage.getCredits('cust_123', 'tickets');
|
|
330
|
+
*
|
|
331
|
+
* console.log(`Total purchased: ${credits.totalPurchased}`);
|
|
332
|
+
* console.log(`Remaining: ${credits.remaining}`);
|
|
333
|
+
*
|
|
334
|
+
* for (const purchase of credits.purchases) {
|
|
335
|
+
* console.log(`${purchase.addonName}: ${purchase.remainingQuantity} left`);
|
|
336
|
+
* }
|
|
337
|
+
* ```
|
|
338
|
+
*/
|
|
339
|
+
async getCredits(customerId, metric) {
|
|
340
|
+
return this.http.get(
|
|
341
|
+
`/v1/customers/${customerId}/usage/credits`,
|
|
342
|
+
{ metric }
|
|
343
|
+
);
|
|
344
|
+
}
|
|
226
345
|
};
|
|
227
346
|
|
|
228
347
|
// src/entitlements.ts
|
|
@@ -650,6 +769,59 @@ var SubscriptionsClient = class {
|
|
|
650
769
|
const subscription = await this.get(customerId);
|
|
651
770
|
return subscription !== null && (subscription.status === "active" || subscription.status === "trialing");
|
|
652
771
|
}
|
|
772
|
+
/**
|
|
773
|
+
* Pause a subscription.
|
|
774
|
+
*
|
|
775
|
+
* Pauses billing collection for a subscription. The subscription remains
|
|
776
|
+
* active but no invoices are generated while paused. Use cases include:
|
|
777
|
+
* - Customer requests temporary pause (vacation, budget constraints)
|
|
778
|
+
* - Seasonal businesses with predictable downtime
|
|
779
|
+
* - Retention strategy: offer pause instead of cancel
|
|
780
|
+
*
|
|
781
|
+
* @example
|
|
782
|
+
* ```typescript
|
|
783
|
+
* // Pause indefinitely (until manually resumed)
|
|
784
|
+
* await stackbe.subscriptions.pause('sub_123');
|
|
785
|
+
*
|
|
786
|
+
* // Pause for 30 days (auto-resume)
|
|
787
|
+
* const resumeDate = new Date();
|
|
788
|
+
* resumeDate.setDate(resumeDate.getDate() + 30);
|
|
789
|
+
* await stackbe.subscriptions.pause('sub_123', { resumesAt: resumeDate });
|
|
790
|
+
*
|
|
791
|
+
* // Check if paused
|
|
792
|
+
* const sub = await stackbe.subscriptions.get('cust_123');
|
|
793
|
+
* if (sub?.pausedAt) {
|
|
794
|
+
* console.log('Subscription is paused');
|
|
795
|
+
* if (sub.resumesAt) {
|
|
796
|
+
* console.log(`Will resume on ${sub.resumesAt}`);
|
|
797
|
+
* }
|
|
798
|
+
* }
|
|
799
|
+
* ```
|
|
800
|
+
*/
|
|
801
|
+
async pause(subscriptionId, options = {}) {
|
|
802
|
+
return this.http.post(
|
|
803
|
+
`/v1/subscriptions/${subscriptionId}/pause`,
|
|
804
|
+
{
|
|
805
|
+
resumesAt: options.resumesAt?.toISOString()
|
|
806
|
+
}
|
|
807
|
+
);
|
|
808
|
+
}
|
|
809
|
+
/**
|
|
810
|
+
* Resume a paused subscription.
|
|
811
|
+
*
|
|
812
|
+
* Restarts billing collection for a paused subscription.
|
|
813
|
+
*
|
|
814
|
+
* @example
|
|
815
|
+
* ```typescript
|
|
816
|
+
* // Resume a paused subscription
|
|
817
|
+
* await stackbe.subscriptions.resume('sub_123');
|
|
818
|
+
* ```
|
|
819
|
+
*/
|
|
820
|
+
async resume(subscriptionId) {
|
|
821
|
+
return this.http.post(
|
|
822
|
+
`/v1/subscriptions/${subscriptionId}/resume`
|
|
823
|
+
);
|
|
824
|
+
}
|
|
653
825
|
};
|
|
654
826
|
|
|
655
827
|
// src/auth.ts
|
package/package.json
CHANGED