@stackbe/sdk 0.5.0 → 0.6.1

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 CHANGED
@@ -184,6 +184,10 @@ interface Subscription {
184
184
  currentPeriodStart: string;
185
185
  currentPeriodEnd: string;
186
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;
187
191
  createdAt: string;
188
192
  }
189
193
  interface CustomerWithSubscription extends Customer {
@@ -332,7 +336,7 @@ interface UpdateSubscriptionOptions {
332
336
  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';
333
337
  interface StackBEErrorResponse {
334
338
  statusCode: number;
335
- message: string;
339
+ message: string | string[];
336
340
  error: string;
337
341
  code?: StackBEErrorCode;
338
342
  }
@@ -378,6 +382,10 @@ interface SubscriptionWebhookPayload {
378
382
  currentPeriodEnd: string;
379
383
  cancelAtPeriodEnd: boolean;
380
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;
381
389
  }
382
390
  /** Customer webhook payload */
383
391
  interface CustomerWebhookPayload {
@@ -860,6 +868,50 @@ declare class SubscriptionsClient {
860
868
  * ```
861
869
  */
862
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>;
863
915
  }
864
916
 
865
917
  interface AuthClientOptions {
package/dist/index.d.ts CHANGED
@@ -184,6 +184,10 @@ interface Subscription {
184
184
  currentPeriodStart: string;
185
185
  currentPeriodEnd: string;
186
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;
187
191
  createdAt: string;
188
192
  }
189
193
  interface CustomerWithSubscription extends Customer {
@@ -332,7 +336,7 @@ interface UpdateSubscriptionOptions {
332
336
  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';
333
337
  interface StackBEErrorResponse {
334
338
  statusCode: number;
335
- message: string;
339
+ message: string | string[];
336
340
  error: string;
337
341
  code?: StackBEErrorCode;
338
342
  }
@@ -378,6 +382,10 @@ interface SubscriptionWebhookPayload {
378
382
  currentPeriodEnd: string;
379
383
  cancelAtPeriodEnd: boolean;
380
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;
381
389
  }
382
390
  /** Customer webhook payload */
383
391
  interface CustomerWebhookPayload {
@@ -860,6 +868,50 @@ declare class SubscriptionsClient {
860
868
  * ```
861
869
  */
862
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>;
863
915
  }
864
916
 
865
917
  interface AuthClientOptions {
package/dist/index.js CHANGED
@@ -73,7 +73,8 @@ function mapErrorCode(status, message, errorType) {
73
73
  if (codeFromError.includes("NOT_FOUND")) return "NOT_FOUND";
74
74
  if (codeFromError.includes("UNAUTHORIZED")) return "UNAUTHORIZED";
75
75
  }
76
- const lowerMessage = message.toLowerCase();
76
+ const messageStr = Array.isArray(message) ? message.join(" ") : String(message || "");
77
+ const lowerMessage = messageStr.toLowerCase();
77
78
  if (lowerMessage.includes("token") && lowerMessage.includes("expired")) return "TOKEN_EXPIRED";
78
79
  if (lowerMessage.includes("already been used")) return "TOKEN_ALREADY_USED";
79
80
  if (lowerMessage.includes("invalid") && lowerMessage.includes("token")) return "TOKEN_INVALID";
@@ -132,8 +133,9 @@ var HttpClient = class {
132
133
  const data = await response.json();
133
134
  if (!response.ok) {
134
135
  const errorData = data;
135
- const message = errorData.message || "Unknown error";
136
- const code = errorData.code || mapErrorCode(response.status, message, errorData.error || "");
136
+ const rawMessage = errorData.message || "Unknown error";
137
+ const message = Array.isArray(rawMessage) ? rawMessage.join(", ") : String(rawMessage);
138
+ const code = errorData.code || mapErrorCode(response.status, rawMessage, errorData.error || "");
137
139
  throw new StackBEError(message, errorData.statusCode || response.status, code);
138
140
  }
139
141
  return data;
@@ -802,6 +804,59 @@ var SubscriptionsClient = class {
802
804
  const subscription = await this.get(customerId);
803
805
  return subscription !== null && (subscription.status === "active" || subscription.status === "trialing");
804
806
  }
807
+ /**
808
+ * Pause a subscription.
809
+ *
810
+ * Pauses billing collection for a subscription. The subscription remains
811
+ * active but no invoices are generated while paused. Use cases include:
812
+ * - Customer requests temporary pause (vacation, budget constraints)
813
+ * - Seasonal businesses with predictable downtime
814
+ * - Retention strategy: offer pause instead of cancel
815
+ *
816
+ * @example
817
+ * ```typescript
818
+ * // Pause indefinitely (until manually resumed)
819
+ * await stackbe.subscriptions.pause('sub_123');
820
+ *
821
+ * // Pause for 30 days (auto-resume)
822
+ * const resumeDate = new Date();
823
+ * resumeDate.setDate(resumeDate.getDate() + 30);
824
+ * await stackbe.subscriptions.pause('sub_123', { resumesAt: resumeDate });
825
+ *
826
+ * // Check if paused
827
+ * const sub = await stackbe.subscriptions.get('cust_123');
828
+ * if (sub?.pausedAt) {
829
+ * console.log('Subscription is paused');
830
+ * if (sub.resumesAt) {
831
+ * console.log(`Will resume on ${sub.resumesAt}`);
832
+ * }
833
+ * }
834
+ * ```
835
+ */
836
+ async pause(subscriptionId, options = {}) {
837
+ return this.http.post(
838
+ `/v1/subscriptions/${subscriptionId}/pause`,
839
+ {
840
+ resumesAt: options.resumesAt?.toISOString()
841
+ }
842
+ );
843
+ }
844
+ /**
845
+ * Resume a paused subscription.
846
+ *
847
+ * Restarts billing collection for a paused subscription.
848
+ *
849
+ * @example
850
+ * ```typescript
851
+ * // Resume a paused subscription
852
+ * await stackbe.subscriptions.resume('sub_123');
853
+ * ```
854
+ */
855
+ async resume(subscriptionId) {
856
+ return this.http.post(
857
+ `/v1/subscriptions/${subscriptionId}/resume`
858
+ );
859
+ }
805
860
  };
806
861
 
807
862
  // src/auth.ts
package/dist/index.mjs CHANGED
@@ -40,7 +40,8 @@ function mapErrorCode(status, message, errorType) {
40
40
  if (codeFromError.includes("NOT_FOUND")) return "NOT_FOUND";
41
41
  if (codeFromError.includes("UNAUTHORIZED")) return "UNAUTHORIZED";
42
42
  }
43
- const lowerMessage = message.toLowerCase();
43
+ const messageStr = Array.isArray(message) ? message.join(" ") : String(message || "");
44
+ const lowerMessage = messageStr.toLowerCase();
44
45
  if (lowerMessage.includes("token") && lowerMessage.includes("expired")) return "TOKEN_EXPIRED";
45
46
  if (lowerMessage.includes("already been used")) return "TOKEN_ALREADY_USED";
46
47
  if (lowerMessage.includes("invalid") && lowerMessage.includes("token")) return "TOKEN_INVALID";
@@ -99,8 +100,9 @@ var HttpClient = class {
99
100
  const data = await response.json();
100
101
  if (!response.ok) {
101
102
  const errorData = data;
102
- const message = errorData.message || "Unknown error";
103
- const code = errorData.code || mapErrorCode(response.status, message, errorData.error || "");
103
+ const rawMessage = errorData.message || "Unknown error";
104
+ const message = Array.isArray(rawMessage) ? rawMessage.join(", ") : String(rawMessage);
105
+ const code = errorData.code || mapErrorCode(response.status, rawMessage, errorData.error || "");
104
106
  throw new StackBEError(message, errorData.statusCode || response.status, code);
105
107
  }
106
108
  return data;
@@ -769,6 +771,59 @@ var SubscriptionsClient = class {
769
771
  const subscription = await this.get(customerId);
770
772
  return subscription !== null && (subscription.status === "active" || subscription.status === "trialing");
771
773
  }
774
+ /**
775
+ * Pause a subscription.
776
+ *
777
+ * Pauses billing collection for a subscription. The subscription remains
778
+ * active but no invoices are generated while paused. Use cases include:
779
+ * - Customer requests temporary pause (vacation, budget constraints)
780
+ * - Seasonal businesses with predictable downtime
781
+ * - Retention strategy: offer pause instead of cancel
782
+ *
783
+ * @example
784
+ * ```typescript
785
+ * // Pause indefinitely (until manually resumed)
786
+ * await stackbe.subscriptions.pause('sub_123');
787
+ *
788
+ * // Pause for 30 days (auto-resume)
789
+ * const resumeDate = new Date();
790
+ * resumeDate.setDate(resumeDate.getDate() + 30);
791
+ * await stackbe.subscriptions.pause('sub_123', { resumesAt: resumeDate });
792
+ *
793
+ * // Check if paused
794
+ * const sub = await stackbe.subscriptions.get('cust_123');
795
+ * if (sub?.pausedAt) {
796
+ * console.log('Subscription is paused');
797
+ * if (sub.resumesAt) {
798
+ * console.log(`Will resume on ${sub.resumesAt}`);
799
+ * }
800
+ * }
801
+ * ```
802
+ */
803
+ async pause(subscriptionId, options = {}) {
804
+ return this.http.post(
805
+ `/v1/subscriptions/${subscriptionId}/pause`,
806
+ {
807
+ resumesAt: options.resumesAt?.toISOString()
808
+ }
809
+ );
810
+ }
811
+ /**
812
+ * Resume a paused subscription.
813
+ *
814
+ * Restarts billing collection for a paused subscription.
815
+ *
816
+ * @example
817
+ * ```typescript
818
+ * // Resume a paused subscription
819
+ * await stackbe.subscriptions.resume('sub_123');
820
+ * ```
821
+ */
822
+ async resume(subscriptionId) {
823
+ return this.http.post(
824
+ `/v1/subscriptions/${subscriptionId}/resume`
825
+ );
826
+ }
772
827
  };
773
828
 
774
829
  // src/auth.ts
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@stackbe/sdk",
3
- "version": "0.5.0",
3
+ "version": "0.6.1",
4
4
  "description": "Official JavaScript/TypeScript SDK for StackBE - the billing backend for your side project",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.mjs",