@stackbe/sdk 0.12.1 → 0.14.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 CHANGED
@@ -174,6 +174,12 @@ await stackbe.auth.sendMagicLink('user@example.com', {
174
174
  useDev: true,
175
175
  });
176
176
 
177
+ // Log user directly into a specific organization
178
+ // Customer must already be a member of the org
179
+ await stackbe.auth.sendMagicLink('user@example.com', {
180
+ organizationId: 'org_abc123',
181
+ });
182
+
177
183
  // Verify magic link token (in your /verify route)
178
184
  const { token } = req.query;
179
185
  const result = await stackbe.auth.verifyToken(token);
@@ -198,6 +204,15 @@ if (session) {
198
204
 
199
205
  // Check if authenticated
200
206
  const isAuthenticated = await stackbe.auth.isAuthenticated(sessionToken);
207
+
208
+ // Switch to a different organization (for multi-org users)
209
+ const orgs = await stackbe.organizations.listByCustomer(customerId);
210
+ if (orgs.length > 1) {
211
+ // User belongs to multiple orgs - show org picker
212
+ const { sessionToken: newToken, organizationId, orgRole } =
213
+ await stackbe.auth.switchOrganization(sessionToken, targetOrgId);
214
+ // Store newToken - it's scoped to the target org
215
+ }
201
216
  ```
202
217
 
203
218
  ### Plans & Products
package/dist/index.d.mts CHANGED
@@ -255,6 +255,12 @@ interface MagicLinkOptions {
255
255
  redirectUrl?: string;
256
256
  /** Use development callback URL (for localhost testing) */
257
257
  useDev?: boolean;
258
+ /**
259
+ * Organization ID to scope the session to after verification.
260
+ * Customer must already be a member of this organization.
261
+ * Use this to log existing members directly into a specific org.
262
+ */
263
+ organizationId?: string;
258
264
  }
259
265
  interface MagicLinkResponse {
260
266
  success: boolean;
@@ -278,6 +284,14 @@ interface VerifyTokenResponse {
278
284
  /** URL to redirect to after verification */
279
285
  redirectUrl?: string;
280
286
  }
287
+ interface SwitchOrganizationResponse {
288
+ /** New session token scoped to the target organization */
289
+ sessionToken: string;
290
+ /** Organization ID the session is now scoped to */
291
+ organizationId: string;
292
+ /** Customer's role in the organization */
293
+ orgRole: 'owner' | 'admin' | 'member';
294
+ }
281
295
  interface SessionResponse {
282
296
  valid: boolean;
283
297
  /** Customer ID */
@@ -551,6 +565,56 @@ interface PaymentWebhookPayload {
551
565
  status: 'succeeded' | 'failed';
552
566
  failureReason?: string;
553
567
  }
568
+ /** Payment method details */
569
+ interface PaymentMethod {
570
+ id: string;
571
+ customerId: string;
572
+ gateway: 'stripe' | 'paypal' | 'authorize_net';
573
+ type: string;
574
+ last4?: string;
575
+ brand?: string;
576
+ expiryMonth?: number;
577
+ expiryYear?: number;
578
+ isDefault: boolean;
579
+ status: 'active' | 'expired' | 'failed';
580
+ createdAt: string;
581
+ }
582
+ /** Create setup session options */
583
+ interface CreateSetupSessionOptions {
584
+ successUrl: string;
585
+ cancelUrl: string;
586
+ }
587
+ /** Setup session response */
588
+ interface SetupSessionResponse {
589
+ sessionId: string;
590
+ url: string;
591
+ clientSecret?: string;
592
+ }
593
+ /** Attach payment method options */
594
+ interface AttachPaymentMethodOptions {
595
+ gatewayPaymentMethodId: string;
596
+ setAsDefault?: boolean;
597
+ }
598
+ /** Charge subscription result */
599
+ interface ChargeResult {
600
+ chargeId: string;
601
+ success: boolean;
602
+ status: 'succeeded' | 'failed' | 'pending' | 'requires_action';
603
+ gatewayChargeId?: string;
604
+ errorCode?: string;
605
+ errorMessage?: string;
606
+ requiresAction?: boolean;
607
+ actionUrl?: string;
608
+ actionExpiresAt?: string;
609
+ }
610
+ /** Migration to StackBE-managed result */
611
+ interface MigrateToStackbeManagedResult {
612
+ subscriptionId: string;
613
+ billingMode: 'stackbe_managed';
614
+ paymentMethodId: string;
615
+ nextBillingDate: string;
616
+ message: string;
617
+ }
554
618
  type SubscriptionCreatedEvent = WebhookEvent<'subscription_created', SubscriptionWebhookPayload>;
555
619
  type SubscriptionUpdatedEvent = WebhookEvent<'subscription_updated', SubscriptionWebhookPayload>;
556
620
  type SubscriptionCancelledEvent = WebhookEvent<'subscription_cancelled', SubscriptionWebhookPayload>;
@@ -1334,6 +1398,160 @@ declare class SubscriptionsClient {
1334
1398
  checkAccess(subscriptionId: string): Promise<{
1335
1399
  hasAccess: boolean;
1336
1400
  }>;
1401
+ /**
1402
+ * Manually charge a StackBE-managed subscription.
1403
+ *
1404
+ * This is useful for:
1405
+ * - Testing billing in development
1406
+ * - Recovering from failed automatic charges
1407
+ * - Charging ahead of schedule
1408
+ *
1409
+ * The subscription must be in `stackbe_managed` billing mode.
1410
+ *
1411
+ * @example
1412
+ * ```typescript
1413
+ * const result = await stackbe.subscriptions.charge('sub_123');
1414
+ *
1415
+ * if (result.success) {
1416
+ * console.log('Charge successful:', result.chargeId);
1417
+ * } else if (result.requiresAction) {
1418
+ * // Redirect customer to 3DS authentication
1419
+ * window.location.href = result.actionUrl;
1420
+ * } else {
1421
+ * console.log('Charge failed:', result.errorMessage);
1422
+ * }
1423
+ * ```
1424
+ */
1425
+ charge(subscriptionId: string): Promise<ChargeResult>;
1426
+ /**
1427
+ * Migrate a subscription from gateway-managed to StackBE-managed billing.
1428
+ *
1429
+ * This will:
1430
+ * 1. Extract the payment method from the gateway subscription
1431
+ * 2. Store it in StackBE's PaymentMethod table
1432
+ * 3. Set the subscription to `stackbe_managed` billing mode
1433
+ * 4. Cancel the gateway subscription at period end
1434
+ * 5. StackBE will handle renewals going forward
1435
+ *
1436
+ * Use cases:
1437
+ * - Migrate existing Stripe subscriptions to unified StackBE billing
1438
+ * - Prepare for multi-gateway support
1439
+ * - Gain more control over billing timing and retry logic
1440
+ *
1441
+ * @example
1442
+ * ```typescript
1443
+ * const result = await stackbe.subscriptions.migrateToStackbeManaged('sub_123');
1444
+ *
1445
+ * console.log('Migration complete');
1446
+ * console.log('Payment method:', result.paymentMethodId);
1447
+ * console.log('Next billing date:', result.nextBillingDate);
1448
+ * ```
1449
+ */
1450
+ migrateToStackbeManaged(subscriptionId: string): Promise<MigrateToStackbeManagedResult>;
1451
+ }
1452
+
1453
+ declare class PaymentMethodsClient {
1454
+ private http;
1455
+ constructor(http: HttpClient);
1456
+ /**
1457
+ * Create a setup session for collecting a payment method.
1458
+ *
1459
+ * Returns a URL to redirect the customer to for secure payment method collection.
1460
+ * This is similar to Stripe Checkout but in setup mode - no charge is made.
1461
+ *
1462
+ * @example
1463
+ * ```typescript
1464
+ * const session = await stackbe.paymentMethods.createSetupSession('cust_123', {
1465
+ * successUrl: 'https://yourapp.com/payment-methods/success',
1466
+ * cancelUrl: 'https://yourapp.com/payment-methods/cancel',
1467
+ * });
1468
+ *
1469
+ * // Redirect customer to payment method collection
1470
+ * window.location.href = session.url;
1471
+ * ```
1472
+ */
1473
+ createSetupSession(customerId: string, options: CreateSetupSessionOptions): Promise<SetupSessionResponse>;
1474
+ /**
1475
+ * Attach a payment method to a customer.
1476
+ *
1477
+ * After the customer completes the setup session, call this to store
1478
+ * the payment method in StackBE for future charges.
1479
+ *
1480
+ * @example
1481
+ * ```typescript
1482
+ * // After redirect from setup session
1483
+ * const paymentMethod = await stackbe.paymentMethods.attach('cust_123', {
1484
+ * gatewayPaymentMethodId: 'pm_1234...', // From setup session completion
1485
+ * setAsDefault: true,
1486
+ * });
1487
+ *
1488
+ * console.log(`Added ${paymentMethod.brand} ending in ${paymentMethod.last4}`);
1489
+ * ```
1490
+ */
1491
+ attach(customerId: string, options: AttachPaymentMethodOptions): Promise<PaymentMethod>;
1492
+ /**
1493
+ * List all payment methods for a customer.
1494
+ *
1495
+ * @example
1496
+ * ```typescript
1497
+ * const paymentMethods = await stackbe.paymentMethods.list('cust_123');
1498
+ *
1499
+ * for (const pm of paymentMethods) {
1500
+ * console.log(`${pm.brand} **** ${pm.last4} ${pm.isDefault ? '(default)' : ''}`);
1501
+ * }
1502
+ * ```
1503
+ */
1504
+ list(customerId: string, options?: {
1505
+ status?: string;
1506
+ }): Promise<PaymentMethod[]>;
1507
+ /**
1508
+ * Get a specific payment method.
1509
+ *
1510
+ * @example
1511
+ * ```typescript
1512
+ * const pm = await stackbe.paymentMethods.get('cust_123', 'pm_123');
1513
+ * console.log(`Expires: ${pm.expiryMonth}/${pm.expiryYear}`);
1514
+ * ```
1515
+ */
1516
+ get(customerId: string, paymentMethodId: string): Promise<PaymentMethod>;
1517
+ /**
1518
+ * Set a payment method as the default for a customer.
1519
+ *
1520
+ * The default payment method is used for subscription renewals.
1521
+ *
1522
+ * @example
1523
+ * ```typescript
1524
+ * await stackbe.paymentMethods.setDefault('cust_123', 'pm_456');
1525
+ * console.log('Default payment method updated');
1526
+ * ```
1527
+ */
1528
+ setDefault(customerId: string, paymentMethodId: string): Promise<PaymentMethod>;
1529
+ /**
1530
+ * Remove a payment method.
1531
+ *
1532
+ * Cannot remove a payment method that is being used by an active subscription.
1533
+ *
1534
+ * @example
1535
+ * ```typescript
1536
+ * await stackbe.paymentMethods.remove('cust_123', 'pm_old');
1537
+ * console.log('Payment method removed');
1538
+ * ```
1539
+ */
1540
+ remove(customerId: string, paymentMethodId: string): Promise<{
1541
+ success: boolean;
1542
+ }>;
1543
+ /**
1544
+ * Get the default payment method for a customer.
1545
+ *
1546
+ * @example
1547
+ * ```typescript
1548
+ * const defaultPM = await stackbe.paymentMethods.getDefault('cust_123');
1549
+ * if (defaultPM) {
1550
+ * console.log(`Default: ${defaultPM.brand} **** ${defaultPM.last4}`);
1551
+ * }
1552
+ * ```
1553
+ */
1554
+ getDefault(customerId: string): Promise<PaymentMethod | null>;
1337
1555
  }
1338
1556
 
1339
1557
  interface AuthClientOptions {
@@ -1381,6 +1599,11 @@ declare class AuthClient {
1381
1599
  * await stackbe.auth.sendMagicLink('user@example.com', {
1382
1600
  * useDev: true,
1383
1601
  * });
1602
+ *
1603
+ * // Log user directly into a specific organization
1604
+ * await stackbe.auth.sendMagicLink('user@example.com', {
1605
+ * organizationId: 'org_abc123',
1606
+ * });
1384
1607
  * ```
1385
1608
  */
1386
1609
  sendMagicLink(email: string, options?: MagicLinkOptions): Promise<MagicLinkResponse>;
@@ -1469,6 +1692,31 @@ declare class AuthClient {
1469
1692
  * ```
1470
1693
  */
1471
1694
  isAuthenticated(sessionToken: string): Promise<boolean>;
1695
+ /**
1696
+ * Switch the current session to a different organization.
1697
+ * The customer must be a member of the target organization.
1698
+ *
1699
+ * Returns a new session token scoped to the target organization.
1700
+ * The original session token remains valid but is scoped to the previous org (or no org).
1701
+ *
1702
+ * @example
1703
+ * ```typescript
1704
+ * // Get available organizations for the customer
1705
+ * const orgs = await stackbe.organizations.listByCustomer(customerId);
1706
+ *
1707
+ * // Switch to a different organization
1708
+ * const { sessionToken: newToken, organizationId, orgRole } =
1709
+ * await stackbe.auth.switchOrganization(currentSessionToken, targetOrgId);
1710
+ *
1711
+ * // Store the new token - it's scoped to the target org
1712
+ * localStorage.setItem('sessionToken', newToken);
1713
+ * ```
1714
+ *
1715
+ * @throws {StackBEError} with code 'NOT_MEMBER' if customer is not a member of the organization
1716
+ * @throws {StackBEError} with code 'ORG_NOT_FOUND' if organization doesn't exist
1717
+ * @throws {StackBEError} with code 'SESSION_INVALID' if session token is invalid
1718
+ */
1719
+ switchOrganization(sessionToken: string, organizationId: string): Promise<SwitchOrganizationResponse>;
1472
1720
  }
1473
1721
 
1474
1722
  declare class OrganizationsClient {
@@ -2325,6 +2573,8 @@ interface CreateCouponOptions {
2325
2573
  interface UpdateCouponOptions {
2326
2574
  name?: string;
2327
2575
  maxRedemptions?: number;
2576
+ /** Reset or adjust the redemption count */
2577
+ timesRedeemed?: number;
2328
2578
  expiresAt?: string;
2329
2579
  validPlanIds?: string[];
2330
2580
  active?: boolean;
@@ -2619,6 +2869,8 @@ declare class StackBE {
2619
2869
  readonly checkout: CheckoutClient;
2620
2870
  /** Subscription management */
2621
2871
  readonly subscriptions: SubscriptionsClient;
2872
+ /** Payment method management (StackBE-managed billing) */
2873
+ readonly paymentMethods: PaymentMethodsClient;
2622
2874
  /** Customer authentication (magic links) */
2623
2875
  readonly auth: AuthClient;
2624
2876
  /** Organization management (B2B multi-user) */
@@ -2737,4 +2989,4 @@ declare class StackBE {
2737
2989
  }): (req: any, res: any, next: any) => Promise<any>;
2738
2990
  }
2739
2991
 
2740
- export { type AddMemberOptions, type AffiliateCommission, type AffiliateCommissionsResponse, type AffiliateEnrollment, type AffiliateInfo, type AffiliateStats, AffiliatesClient, AnalyticsClient, type AnalyticsFilterOptions, type AnyWebhookEvent, AuthClient, type BillingPortalSession, type CancelSubscriptionOptions, type CancelSubscriptionResponse, type CheckEntitlementResponse, type CheckUsageResponse, CheckoutClient, type CheckoutSessionResponse, type Coupon, type CouponDiscountType, type CouponDuration, CouponsClient, type CreateCheckoutOptions, type CreateCouponOptions, type CreateCustomerOptions, type CreateEarlyAccessSignupOptions, type CreateFeatureRequestOptions, type CreateOrganizationOptions, type Customer, type CustomerCreatedEvent, type CustomerFeatureActivity, type CustomerInvoice, type CustomerInvoicesResponse, type CustomerPaymentMethod, type CustomerSubscriptionHistory, type CustomerSubscriptionHistoryResponse, type CustomerUpdatedEvent, type CustomerUsageResponse, type CustomerWebhookPayload, type CustomerWithSubscription, CustomersClient, type DashboardMetrics, type DunningStatus, EarlyAccessClient, type EarlyAccessSignup, type EarlyAccessSignupListResponse, type EnrollOptions, EntitlementsClient, type EntitlementsResponse, type FeatureRequest, type FeatureRequestComment, type FeatureRequestImage, type FeatureRequestListResponse, FeatureRequestsClient, type FeatureUsagePoint, type GetSubscriptionOptions, type GrowthChartPoint, type GrowthMetrics, type InterestedCustomer, type InterestedCustomersResponse, type InviteMemberOptions, type ListEarlyAccessOptions, type ListFeatureRequestsOptions, type ListPlansOptions, type ListProductsOptions, type MRRHistoryPoint, type MagicLinkOptions, type MagicLinkResponse, type Organization, type OrganizationInvite, type OrganizationMember, OrganizationsClient, type PaymentFailedEvent, type PaymentSucceededEvent, type PaymentWebhookPayload, type PerformanceMetrics, type Plan, type PlanMetrics, PlansClient, type PopularEndpoint, type Product, ProductsClient, type RequestCountPoint, type RevenueByPlan, type SessionResponse, StackBE, type StackBEConfig, StackBEError, type StackBEErrorCode, type StackBEErrorResponse, type Subscription, type SubscriptionCancelledEvent, type SubscriptionCreatedEvent, type SubscriptionPlan, type SubscriptionRenewedEvent, type SubscriptionUpdatedEvent, type SubscriptionWebhookPayload, type SubscriptionWithPlan, SubscriptionsClient, type TimeRangeOptions, type TrackReferralResponse, type TrackUsageOptions, type TrackUsageResponse, type TrialEndedEvent, type TrialMetrics, type TrialStartedEvent, type TrialStatus, type UpcomingInvoice, type UpdateCouponOptions, type UpdateCustomerOptions, type UpdateOrganizationOptions, type UpdateSubscriptionOptions, UsageClient, type UsageMetric, type ValidateCouponResult, type VerifyTokenResponse, type WebhookEvent, type WebhookEventType };
2992
+ export { type AddMemberOptions, type AffiliateCommission, type AffiliateCommissionsResponse, type AffiliateEnrollment, type AffiliateInfo, type AffiliateStats, AffiliatesClient, AnalyticsClient, type AnalyticsFilterOptions, type AnyWebhookEvent, type AttachPaymentMethodOptions, AuthClient, type BillingPortalSession, type CancelSubscriptionOptions, type CancelSubscriptionResponse, type ChargeResult, type CheckEntitlementResponse, type CheckUsageResponse, CheckoutClient, type CheckoutSessionResponse, type Coupon, type CouponDiscountType, type CouponDuration, CouponsClient, type CreateCheckoutOptions, type CreateCouponOptions, type CreateCustomerOptions, type CreateEarlyAccessSignupOptions, type CreateFeatureRequestOptions, type CreateOrganizationOptions, type CreateSetupSessionOptions, type Customer, type CustomerCreatedEvent, type CustomerFeatureActivity, type CustomerInvoice, type CustomerInvoicesResponse, type CustomerPaymentMethod, type CustomerSubscriptionHistory, type CustomerSubscriptionHistoryResponse, type CustomerUpdatedEvent, type CustomerUsageResponse, type CustomerWebhookPayload, type CustomerWithSubscription, CustomersClient, type DashboardMetrics, type DunningStatus, EarlyAccessClient, type EarlyAccessSignup, type EarlyAccessSignupListResponse, type EnrollOptions, EntitlementsClient, type EntitlementsResponse, type FeatureRequest, type FeatureRequestComment, type FeatureRequestImage, type FeatureRequestListResponse, FeatureRequestsClient, type FeatureUsagePoint, type GetSubscriptionOptions, type GrowthChartPoint, type GrowthMetrics, type InterestedCustomer, type InterestedCustomersResponse, type InviteMemberOptions, type ListEarlyAccessOptions, type ListFeatureRequestsOptions, type ListPlansOptions, type ListProductsOptions, type MRRHistoryPoint, type MagicLinkOptions, type MagicLinkResponse, type MigrateToStackbeManagedResult, type Organization, type OrganizationInvite, type OrganizationMember, OrganizationsClient, type PaymentFailedEvent, type PaymentMethod, PaymentMethodsClient, type PaymentSucceededEvent, type PaymentWebhookPayload, type PerformanceMetrics, type Plan, type PlanMetrics, PlansClient, type PopularEndpoint, type Product, ProductsClient, type RequestCountPoint, type RevenueByPlan, type SessionResponse, type SetupSessionResponse, StackBE, type StackBEConfig, StackBEError, type StackBEErrorCode, type StackBEErrorResponse, type Subscription, type SubscriptionCancelledEvent, type SubscriptionCreatedEvent, type SubscriptionPlan, type SubscriptionRenewedEvent, type SubscriptionUpdatedEvent, type SubscriptionWebhookPayload, type SubscriptionWithPlan, SubscriptionsClient, type SwitchOrganizationResponse, type TimeRangeOptions, type TrackReferralResponse, type TrackUsageOptions, type TrackUsageResponse, type TrialEndedEvent, type TrialMetrics, type TrialStartedEvent, type TrialStatus, type UpcomingInvoice, type UpdateCouponOptions, type UpdateCustomerOptions, type UpdateOrganizationOptions, type UpdateSubscriptionOptions, UsageClient, type UsageMetric, type ValidateCouponResult, type VerifyTokenResponse, type WebhookEvent, type WebhookEventType };
package/dist/index.d.ts CHANGED
@@ -255,6 +255,12 @@ interface MagicLinkOptions {
255
255
  redirectUrl?: string;
256
256
  /** Use development callback URL (for localhost testing) */
257
257
  useDev?: boolean;
258
+ /**
259
+ * Organization ID to scope the session to after verification.
260
+ * Customer must already be a member of this organization.
261
+ * Use this to log existing members directly into a specific org.
262
+ */
263
+ organizationId?: string;
258
264
  }
259
265
  interface MagicLinkResponse {
260
266
  success: boolean;
@@ -278,6 +284,14 @@ interface VerifyTokenResponse {
278
284
  /** URL to redirect to after verification */
279
285
  redirectUrl?: string;
280
286
  }
287
+ interface SwitchOrganizationResponse {
288
+ /** New session token scoped to the target organization */
289
+ sessionToken: string;
290
+ /** Organization ID the session is now scoped to */
291
+ organizationId: string;
292
+ /** Customer's role in the organization */
293
+ orgRole: 'owner' | 'admin' | 'member';
294
+ }
281
295
  interface SessionResponse {
282
296
  valid: boolean;
283
297
  /** Customer ID */
@@ -551,6 +565,56 @@ interface PaymentWebhookPayload {
551
565
  status: 'succeeded' | 'failed';
552
566
  failureReason?: string;
553
567
  }
568
+ /** Payment method details */
569
+ interface PaymentMethod {
570
+ id: string;
571
+ customerId: string;
572
+ gateway: 'stripe' | 'paypal' | 'authorize_net';
573
+ type: string;
574
+ last4?: string;
575
+ brand?: string;
576
+ expiryMonth?: number;
577
+ expiryYear?: number;
578
+ isDefault: boolean;
579
+ status: 'active' | 'expired' | 'failed';
580
+ createdAt: string;
581
+ }
582
+ /** Create setup session options */
583
+ interface CreateSetupSessionOptions {
584
+ successUrl: string;
585
+ cancelUrl: string;
586
+ }
587
+ /** Setup session response */
588
+ interface SetupSessionResponse {
589
+ sessionId: string;
590
+ url: string;
591
+ clientSecret?: string;
592
+ }
593
+ /** Attach payment method options */
594
+ interface AttachPaymentMethodOptions {
595
+ gatewayPaymentMethodId: string;
596
+ setAsDefault?: boolean;
597
+ }
598
+ /** Charge subscription result */
599
+ interface ChargeResult {
600
+ chargeId: string;
601
+ success: boolean;
602
+ status: 'succeeded' | 'failed' | 'pending' | 'requires_action';
603
+ gatewayChargeId?: string;
604
+ errorCode?: string;
605
+ errorMessage?: string;
606
+ requiresAction?: boolean;
607
+ actionUrl?: string;
608
+ actionExpiresAt?: string;
609
+ }
610
+ /** Migration to StackBE-managed result */
611
+ interface MigrateToStackbeManagedResult {
612
+ subscriptionId: string;
613
+ billingMode: 'stackbe_managed';
614
+ paymentMethodId: string;
615
+ nextBillingDate: string;
616
+ message: string;
617
+ }
554
618
  type SubscriptionCreatedEvent = WebhookEvent<'subscription_created', SubscriptionWebhookPayload>;
555
619
  type SubscriptionUpdatedEvent = WebhookEvent<'subscription_updated', SubscriptionWebhookPayload>;
556
620
  type SubscriptionCancelledEvent = WebhookEvent<'subscription_cancelled', SubscriptionWebhookPayload>;
@@ -1334,6 +1398,160 @@ declare class SubscriptionsClient {
1334
1398
  checkAccess(subscriptionId: string): Promise<{
1335
1399
  hasAccess: boolean;
1336
1400
  }>;
1401
+ /**
1402
+ * Manually charge a StackBE-managed subscription.
1403
+ *
1404
+ * This is useful for:
1405
+ * - Testing billing in development
1406
+ * - Recovering from failed automatic charges
1407
+ * - Charging ahead of schedule
1408
+ *
1409
+ * The subscription must be in `stackbe_managed` billing mode.
1410
+ *
1411
+ * @example
1412
+ * ```typescript
1413
+ * const result = await stackbe.subscriptions.charge('sub_123');
1414
+ *
1415
+ * if (result.success) {
1416
+ * console.log('Charge successful:', result.chargeId);
1417
+ * } else if (result.requiresAction) {
1418
+ * // Redirect customer to 3DS authentication
1419
+ * window.location.href = result.actionUrl;
1420
+ * } else {
1421
+ * console.log('Charge failed:', result.errorMessage);
1422
+ * }
1423
+ * ```
1424
+ */
1425
+ charge(subscriptionId: string): Promise<ChargeResult>;
1426
+ /**
1427
+ * Migrate a subscription from gateway-managed to StackBE-managed billing.
1428
+ *
1429
+ * This will:
1430
+ * 1. Extract the payment method from the gateway subscription
1431
+ * 2. Store it in StackBE's PaymentMethod table
1432
+ * 3. Set the subscription to `stackbe_managed` billing mode
1433
+ * 4. Cancel the gateway subscription at period end
1434
+ * 5. StackBE will handle renewals going forward
1435
+ *
1436
+ * Use cases:
1437
+ * - Migrate existing Stripe subscriptions to unified StackBE billing
1438
+ * - Prepare for multi-gateway support
1439
+ * - Gain more control over billing timing and retry logic
1440
+ *
1441
+ * @example
1442
+ * ```typescript
1443
+ * const result = await stackbe.subscriptions.migrateToStackbeManaged('sub_123');
1444
+ *
1445
+ * console.log('Migration complete');
1446
+ * console.log('Payment method:', result.paymentMethodId);
1447
+ * console.log('Next billing date:', result.nextBillingDate);
1448
+ * ```
1449
+ */
1450
+ migrateToStackbeManaged(subscriptionId: string): Promise<MigrateToStackbeManagedResult>;
1451
+ }
1452
+
1453
+ declare class PaymentMethodsClient {
1454
+ private http;
1455
+ constructor(http: HttpClient);
1456
+ /**
1457
+ * Create a setup session for collecting a payment method.
1458
+ *
1459
+ * Returns a URL to redirect the customer to for secure payment method collection.
1460
+ * This is similar to Stripe Checkout but in setup mode - no charge is made.
1461
+ *
1462
+ * @example
1463
+ * ```typescript
1464
+ * const session = await stackbe.paymentMethods.createSetupSession('cust_123', {
1465
+ * successUrl: 'https://yourapp.com/payment-methods/success',
1466
+ * cancelUrl: 'https://yourapp.com/payment-methods/cancel',
1467
+ * });
1468
+ *
1469
+ * // Redirect customer to payment method collection
1470
+ * window.location.href = session.url;
1471
+ * ```
1472
+ */
1473
+ createSetupSession(customerId: string, options: CreateSetupSessionOptions): Promise<SetupSessionResponse>;
1474
+ /**
1475
+ * Attach a payment method to a customer.
1476
+ *
1477
+ * After the customer completes the setup session, call this to store
1478
+ * the payment method in StackBE for future charges.
1479
+ *
1480
+ * @example
1481
+ * ```typescript
1482
+ * // After redirect from setup session
1483
+ * const paymentMethod = await stackbe.paymentMethods.attach('cust_123', {
1484
+ * gatewayPaymentMethodId: 'pm_1234...', // From setup session completion
1485
+ * setAsDefault: true,
1486
+ * });
1487
+ *
1488
+ * console.log(`Added ${paymentMethod.brand} ending in ${paymentMethod.last4}`);
1489
+ * ```
1490
+ */
1491
+ attach(customerId: string, options: AttachPaymentMethodOptions): Promise<PaymentMethod>;
1492
+ /**
1493
+ * List all payment methods for a customer.
1494
+ *
1495
+ * @example
1496
+ * ```typescript
1497
+ * const paymentMethods = await stackbe.paymentMethods.list('cust_123');
1498
+ *
1499
+ * for (const pm of paymentMethods) {
1500
+ * console.log(`${pm.brand} **** ${pm.last4} ${pm.isDefault ? '(default)' : ''}`);
1501
+ * }
1502
+ * ```
1503
+ */
1504
+ list(customerId: string, options?: {
1505
+ status?: string;
1506
+ }): Promise<PaymentMethod[]>;
1507
+ /**
1508
+ * Get a specific payment method.
1509
+ *
1510
+ * @example
1511
+ * ```typescript
1512
+ * const pm = await stackbe.paymentMethods.get('cust_123', 'pm_123');
1513
+ * console.log(`Expires: ${pm.expiryMonth}/${pm.expiryYear}`);
1514
+ * ```
1515
+ */
1516
+ get(customerId: string, paymentMethodId: string): Promise<PaymentMethod>;
1517
+ /**
1518
+ * Set a payment method as the default for a customer.
1519
+ *
1520
+ * The default payment method is used for subscription renewals.
1521
+ *
1522
+ * @example
1523
+ * ```typescript
1524
+ * await stackbe.paymentMethods.setDefault('cust_123', 'pm_456');
1525
+ * console.log('Default payment method updated');
1526
+ * ```
1527
+ */
1528
+ setDefault(customerId: string, paymentMethodId: string): Promise<PaymentMethod>;
1529
+ /**
1530
+ * Remove a payment method.
1531
+ *
1532
+ * Cannot remove a payment method that is being used by an active subscription.
1533
+ *
1534
+ * @example
1535
+ * ```typescript
1536
+ * await stackbe.paymentMethods.remove('cust_123', 'pm_old');
1537
+ * console.log('Payment method removed');
1538
+ * ```
1539
+ */
1540
+ remove(customerId: string, paymentMethodId: string): Promise<{
1541
+ success: boolean;
1542
+ }>;
1543
+ /**
1544
+ * Get the default payment method for a customer.
1545
+ *
1546
+ * @example
1547
+ * ```typescript
1548
+ * const defaultPM = await stackbe.paymentMethods.getDefault('cust_123');
1549
+ * if (defaultPM) {
1550
+ * console.log(`Default: ${defaultPM.brand} **** ${defaultPM.last4}`);
1551
+ * }
1552
+ * ```
1553
+ */
1554
+ getDefault(customerId: string): Promise<PaymentMethod | null>;
1337
1555
  }
1338
1556
 
1339
1557
  interface AuthClientOptions {
@@ -1381,6 +1599,11 @@ declare class AuthClient {
1381
1599
  * await stackbe.auth.sendMagicLink('user@example.com', {
1382
1600
  * useDev: true,
1383
1601
  * });
1602
+ *
1603
+ * // Log user directly into a specific organization
1604
+ * await stackbe.auth.sendMagicLink('user@example.com', {
1605
+ * organizationId: 'org_abc123',
1606
+ * });
1384
1607
  * ```
1385
1608
  */
1386
1609
  sendMagicLink(email: string, options?: MagicLinkOptions): Promise<MagicLinkResponse>;
@@ -1469,6 +1692,31 @@ declare class AuthClient {
1469
1692
  * ```
1470
1693
  */
1471
1694
  isAuthenticated(sessionToken: string): Promise<boolean>;
1695
+ /**
1696
+ * Switch the current session to a different organization.
1697
+ * The customer must be a member of the target organization.
1698
+ *
1699
+ * Returns a new session token scoped to the target organization.
1700
+ * The original session token remains valid but is scoped to the previous org (or no org).
1701
+ *
1702
+ * @example
1703
+ * ```typescript
1704
+ * // Get available organizations for the customer
1705
+ * const orgs = await stackbe.organizations.listByCustomer(customerId);
1706
+ *
1707
+ * // Switch to a different organization
1708
+ * const { sessionToken: newToken, organizationId, orgRole } =
1709
+ * await stackbe.auth.switchOrganization(currentSessionToken, targetOrgId);
1710
+ *
1711
+ * // Store the new token - it's scoped to the target org
1712
+ * localStorage.setItem('sessionToken', newToken);
1713
+ * ```
1714
+ *
1715
+ * @throws {StackBEError} with code 'NOT_MEMBER' if customer is not a member of the organization
1716
+ * @throws {StackBEError} with code 'ORG_NOT_FOUND' if organization doesn't exist
1717
+ * @throws {StackBEError} with code 'SESSION_INVALID' if session token is invalid
1718
+ */
1719
+ switchOrganization(sessionToken: string, organizationId: string): Promise<SwitchOrganizationResponse>;
1472
1720
  }
1473
1721
 
1474
1722
  declare class OrganizationsClient {
@@ -2325,6 +2573,8 @@ interface CreateCouponOptions {
2325
2573
  interface UpdateCouponOptions {
2326
2574
  name?: string;
2327
2575
  maxRedemptions?: number;
2576
+ /** Reset or adjust the redemption count */
2577
+ timesRedeemed?: number;
2328
2578
  expiresAt?: string;
2329
2579
  validPlanIds?: string[];
2330
2580
  active?: boolean;
@@ -2619,6 +2869,8 @@ declare class StackBE {
2619
2869
  readonly checkout: CheckoutClient;
2620
2870
  /** Subscription management */
2621
2871
  readonly subscriptions: SubscriptionsClient;
2872
+ /** Payment method management (StackBE-managed billing) */
2873
+ readonly paymentMethods: PaymentMethodsClient;
2622
2874
  /** Customer authentication (magic links) */
2623
2875
  readonly auth: AuthClient;
2624
2876
  /** Organization management (B2B multi-user) */
@@ -2737,4 +2989,4 @@ declare class StackBE {
2737
2989
  }): (req: any, res: any, next: any) => Promise<any>;
2738
2990
  }
2739
2991
 
2740
- export { type AddMemberOptions, type AffiliateCommission, type AffiliateCommissionsResponse, type AffiliateEnrollment, type AffiliateInfo, type AffiliateStats, AffiliatesClient, AnalyticsClient, type AnalyticsFilterOptions, type AnyWebhookEvent, AuthClient, type BillingPortalSession, type CancelSubscriptionOptions, type CancelSubscriptionResponse, type CheckEntitlementResponse, type CheckUsageResponse, CheckoutClient, type CheckoutSessionResponse, type Coupon, type CouponDiscountType, type CouponDuration, CouponsClient, type CreateCheckoutOptions, type CreateCouponOptions, type CreateCustomerOptions, type CreateEarlyAccessSignupOptions, type CreateFeatureRequestOptions, type CreateOrganizationOptions, type Customer, type CustomerCreatedEvent, type CustomerFeatureActivity, type CustomerInvoice, type CustomerInvoicesResponse, type CustomerPaymentMethod, type CustomerSubscriptionHistory, type CustomerSubscriptionHistoryResponse, type CustomerUpdatedEvent, type CustomerUsageResponse, type CustomerWebhookPayload, type CustomerWithSubscription, CustomersClient, type DashboardMetrics, type DunningStatus, EarlyAccessClient, type EarlyAccessSignup, type EarlyAccessSignupListResponse, type EnrollOptions, EntitlementsClient, type EntitlementsResponse, type FeatureRequest, type FeatureRequestComment, type FeatureRequestImage, type FeatureRequestListResponse, FeatureRequestsClient, type FeatureUsagePoint, type GetSubscriptionOptions, type GrowthChartPoint, type GrowthMetrics, type InterestedCustomer, type InterestedCustomersResponse, type InviteMemberOptions, type ListEarlyAccessOptions, type ListFeatureRequestsOptions, type ListPlansOptions, type ListProductsOptions, type MRRHistoryPoint, type MagicLinkOptions, type MagicLinkResponse, type Organization, type OrganizationInvite, type OrganizationMember, OrganizationsClient, type PaymentFailedEvent, type PaymentSucceededEvent, type PaymentWebhookPayload, type PerformanceMetrics, type Plan, type PlanMetrics, PlansClient, type PopularEndpoint, type Product, ProductsClient, type RequestCountPoint, type RevenueByPlan, type SessionResponse, StackBE, type StackBEConfig, StackBEError, type StackBEErrorCode, type StackBEErrorResponse, type Subscription, type SubscriptionCancelledEvent, type SubscriptionCreatedEvent, type SubscriptionPlan, type SubscriptionRenewedEvent, type SubscriptionUpdatedEvent, type SubscriptionWebhookPayload, type SubscriptionWithPlan, SubscriptionsClient, type TimeRangeOptions, type TrackReferralResponse, type TrackUsageOptions, type TrackUsageResponse, type TrialEndedEvent, type TrialMetrics, type TrialStartedEvent, type TrialStatus, type UpcomingInvoice, type UpdateCouponOptions, type UpdateCustomerOptions, type UpdateOrganizationOptions, type UpdateSubscriptionOptions, UsageClient, type UsageMetric, type ValidateCouponResult, type VerifyTokenResponse, type WebhookEvent, type WebhookEventType };
2992
+ export { type AddMemberOptions, type AffiliateCommission, type AffiliateCommissionsResponse, type AffiliateEnrollment, type AffiliateInfo, type AffiliateStats, AffiliatesClient, AnalyticsClient, type AnalyticsFilterOptions, type AnyWebhookEvent, type AttachPaymentMethodOptions, AuthClient, type BillingPortalSession, type CancelSubscriptionOptions, type CancelSubscriptionResponse, type ChargeResult, type CheckEntitlementResponse, type CheckUsageResponse, CheckoutClient, type CheckoutSessionResponse, type Coupon, type CouponDiscountType, type CouponDuration, CouponsClient, type CreateCheckoutOptions, type CreateCouponOptions, type CreateCustomerOptions, type CreateEarlyAccessSignupOptions, type CreateFeatureRequestOptions, type CreateOrganizationOptions, type CreateSetupSessionOptions, type Customer, type CustomerCreatedEvent, type CustomerFeatureActivity, type CustomerInvoice, type CustomerInvoicesResponse, type CustomerPaymentMethod, type CustomerSubscriptionHistory, type CustomerSubscriptionHistoryResponse, type CustomerUpdatedEvent, type CustomerUsageResponse, type CustomerWebhookPayload, type CustomerWithSubscription, CustomersClient, type DashboardMetrics, type DunningStatus, EarlyAccessClient, type EarlyAccessSignup, type EarlyAccessSignupListResponse, type EnrollOptions, EntitlementsClient, type EntitlementsResponse, type FeatureRequest, type FeatureRequestComment, type FeatureRequestImage, type FeatureRequestListResponse, FeatureRequestsClient, type FeatureUsagePoint, type GetSubscriptionOptions, type GrowthChartPoint, type GrowthMetrics, type InterestedCustomer, type InterestedCustomersResponse, type InviteMemberOptions, type ListEarlyAccessOptions, type ListFeatureRequestsOptions, type ListPlansOptions, type ListProductsOptions, type MRRHistoryPoint, type MagicLinkOptions, type MagicLinkResponse, type MigrateToStackbeManagedResult, type Organization, type OrganizationInvite, type OrganizationMember, OrganizationsClient, type PaymentFailedEvent, type PaymentMethod, PaymentMethodsClient, type PaymentSucceededEvent, type PaymentWebhookPayload, type PerformanceMetrics, type Plan, type PlanMetrics, PlansClient, type PopularEndpoint, type Product, ProductsClient, type RequestCountPoint, type RevenueByPlan, type SessionResponse, type SetupSessionResponse, StackBE, type StackBEConfig, StackBEError, type StackBEErrorCode, type StackBEErrorResponse, type Subscription, type SubscriptionCancelledEvent, type SubscriptionCreatedEvent, type SubscriptionPlan, type SubscriptionRenewedEvent, type SubscriptionUpdatedEvent, type SubscriptionWebhookPayload, type SubscriptionWithPlan, SubscriptionsClient, type SwitchOrganizationResponse, type TimeRangeOptions, type TrackReferralResponse, type TrackUsageOptions, type TrackUsageResponse, type TrialEndedEvent, type TrialMetrics, type TrialStartedEvent, type TrialStatus, type UpcomingInvoice, type UpdateCouponOptions, type UpdateCustomerOptions, type UpdateOrganizationOptions, type UpdateSubscriptionOptions, UsageClient, type UsageMetric, type ValidateCouponResult, type VerifyTokenResponse, type WebhookEvent, type WebhookEventType };
package/dist/index.js CHANGED
@@ -30,6 +30,7 @@ __export(index_exports, {
30
30
  EntitlementsClient: () => EntitlementsClient,
31
31
  FeatureRequestsClient: () => FeatureRequestsClient,
32
32
  OrganizationsClient: () => OrganizationsClient,
33
+ PaymentMethodsClient: () => PaymentMethodsClient,
33
34
  PlansClient: () => PlansClient,
34
35
  ProductsClient: () => ProductsClient,
35
36
  StackBE: () => StackBE,
@@ -1168,6 +1169,201 @@ var SubscriptionsClient = class {
1168
1169
  `/v1/subscriptions/${subscriptionId}/has-access`
1169
1170
  );
1170
1171
  }
1172
+ // ============================================
1173
+ // StackBE-Managed Billing Methods
1174
+ // ============================================
1175
+ /**
1176
+ * Manually charge a StackBE-managed subscription.
1177
+ *
1178
+ * This is useful for:
1179
+ * - Testing billing in development
1180
+ * - Recovering from failed automatic charges
1181
+ * - Charging ahead of schedule
1182
+ *
1183
+ * The subscription must be in `stackbe_managed` billing mode.
1184
+ *
1185
+ * @example
1186
+ * ```typescript
1187
+ * const result = await stackbe.subscriptions.charge('sub_123');
1188
+ *
1189
+ * if (result.success) {
1190
+ * console.log('Charge successful:', result.chargeId);
1191
+ * } else if (result.requiresAction) {
1192
+ * // Redirect customer to 3DS authentication
1193
+ * window.location.href = result.actionUrl;
1194
+ * } else {
1195
+ * console.log('Charge failed:', result.errorMessage);
1196
+ * }
1197
+ * ```
1198
+ */
1199
+ async charge(subscriptionId) {
1200
+ return this.http.post(
1201
+ `/v1/subscriptions/${subscriptionId}/charge`
1202
+ );
1203
+ }
1204
+ /**
1205
+ * Migrate a subscription from gateway-managed to StackBE-managed billing.
1206
+ *
1207
+ * This will:
1208
+ * 1. Extract the payment method from the gateway subscription
1209
+ * 2. Store it in StackBE's PaymentMethod table
1210
+ * 3. Set the subscription to `stackbe_managed` billing mode
1211
+ * 4. Cancel the gateway subscription at period end
1212
+ * 5. StackBE will handle renewals going forward
1213
+ *
1214
+ * Use cases:
1215
+ * - Migrate existing Stripe subscriptions to unified StackBE billing
1216
+ * - Prepare for multi-gateway support
1217
+ * - Gain more control over billing timing and retry logic
1218
+ *
1219
+ * @example
1220
+ * ```typescript
1221
+ * const result = await stackbe.subscriptions.migrateToStackbeManaged('sub_123');
1222
+ *
1223
+ * console.log('Migration complete');
1224
+ * console.log('Payment method:', result.paymentMethodId);
1225
+ * console.log('Next billing date:', result.nextBillingDate);
1226
+ * ```
1227
+ */
1228
+ async migrateToStackbeManaged(subscriptionId) {
1229
+ return this.http.post(
1230
+ `/v1/subscriptions/${subscriptionId}/migrate-to-stackbe-managed`
1231
+ );
1232
+ }
1233
+ };
1234
+
1235
+ // src/payment-methods.ts
1236
+ var PaymentMethodsClient = class {
1237
+ constructor(http) {
1238
+ this.http = http;
1239
+ }
1240
+ /**
1241
+ * Create a setup session for collecting a payment method.
1242
+ *
1243
+ * Returns a URL to redirect the customer to for secure payment method collection.
1244
+ * This is similar to Stripe Checkout but in setup mode - no charge is made.
1245
+ *
1246
+ * @example
1247
+ * ```typescript
1248
+ * const session = await stackbe.paymentMethods.createSetupSession('cust_123', {
1249
+ * successUrl: 'https://yourapp.com/payment-methods/success',
1250
+ * cancelUrl: 'https://yourapp.com/payment-methods/cancel',
1251
+ * });
1252
+ *
1253
+ * // Redirect customer to payment method collection
1254
+ * window.location.href = session.url;
1255
+ * ```
1256
+ */
1257
+ async createSetupSession(customerId, options) {
1258
+ return this.http.post(
1259
+ `/v1/apps/{appId}/customers/${customerId}/payment-methods/setup`,
1260
+ options
1261
+ );
1262
+ }
1263
+ /**
1264
+ * Attach a payment method to a customer.
1265
+ *
1266
+ * After the customer completes the setup session, call this to store
1267
+ * the payment method in StackBE for future charges.
1268
+ *
1269
+ * @example
1270
+ * ```typescript
1271
+ * // After redirect from setup session
1272
+ * const paymentMethod = await stackbe.paymentMethods.attach('cust_123', {
1273
+ * gatewayPaymentMethodId: 'pm_1234...', // From setup session completion
1274
+ * setAsDefault: true,
1275
+ * });
1276
+ *
1277
+ * console.log(`Added ${paymentMethod.brand} ending in ${paymentMethod.last4}`);
1278
+ * ```
1279
+ */
1280
+ async attach(customerId, options) {
1281
+ return this.http.post(
1282
+ `/v1/apps/{appId}/customers/${customerId}/payment-methods`,
1283
+ options
1284
+ );
1285
+ }
1286
+ /**
1287
+ * List all payment methods for a customer.
1288
+ *
1289
+ * @example
1290
+ * ```typescript
1291
+ * const paymentMethods = await stackbe.paymentMethods.list('cust_123');
1292
+ *
1293
+ * for (const pm of paymentMethods) {
1294
+ * console.log(`${pm.brand} **** ${pm.last4} ${pm.isDefault ? '(default)' : ''}`);
1295
+ * }
1296
+ * ```
1297
+ */
1298
+ async list(customerId, options) {
1299
+ const params = {};
1300
+ if (options?.status) params.status = options.status;
1301
+ return this.http.get(
1302
+ `/v1/apps/{appId}/customers/${customerId}/payment-methods`,
1303
+ params
1304
+ );
1305
+ }
1306
+ /**
1307
+ * Get a specific payment method.
1308
+ *
1309
+ * @example
1310
+ * ```typescript
1311
+ * const pm = await stackbe.paymentMethods.get('cust_123', 'pm_123');
1312
+ * console.log(`Expires: ${pm.expiryMonth}/${pm.expiryYear}`);
1313
+ * ```
1314
+ */
1315
+ async get(customerId, paymentMethodId) {
1316
+ return this.http.get(
1317
+ `/v1/apps/{appId}/customers/${customerId}/payment-methods/${paymentMethodId}`
1318
+ );
1319
+ }
1320
+ /**
1321
+ * Set a payment method as the default for a customer.
1322
+ *
1323
+ * The default payment method is used for subscription renewals.
1324
+ *
1325
+ * @example
1326
+ * ```typescript
1327
+ * await stackbe.paymentMethods.setDefault('cust_123', 'pm_456');
1328
+ * console.log('Default payment method updated');
1329
+ * ```
1330
+ */
1331
+ async setDefault(customerId, paymentMethodId) {
1332
+ return this.http.post(
1333
+ `/v1/apps/{appId}/customers/${customerId}/payment-methods/${paymentMethodId}/default`
1334
+ );
1335
+ }
1336
+ /**
1337
+ * Remove a payment method.
1338
+ *
1339
+ * Cannot remove a payment method that is being used by an active subscription.
1340
+ *
1341
+ * @example
1342
+ * ```typescript
1343
+ * await stackbe.paymentMethods.remove('cust_123', 'pm_old');
1344
+ * console.log('Payment method removed');
1345
+ * ```
1346
+ */
1347
+ async remove(customerId, paymentMethodId) {
1348
+ return this.http.delete(
1349
+ `/v1/apps/{appId}/customers/${customerId}/payment-methods/${paymentMethodId}`
1350
+ );
1351
+ }
1352
+ /**
1353
+ * Get the default payment method for a customer.
1354
+ *
1355
+ * @example
1356
+ * ```typescript
1357
+ * const defaultPM = await stackbe.paymentMethods.getDefault('cust_123');
1358
+ * if (defaultPM) {
1359
+ * console.log(`Default: ${defaultPM.brand} **** ${defaultPM.last4}`);
1360
+ * }
1361
+ * ```
1362
+ */
1363
+ async getDefault(customerId) {
1364
+ const paymentMethods = await this.list(customerId, { status: "active" });
1365
+ return paymentMethods.find((pm) => pm.isDefault) || null;
1366
+ }
1171
1367
  };
1172
1368
 
1173
1369
  // src/auth.ts
@@ -1230,6 +1426,11 @@ var AuthClient = class {
1230
1426
  * await stackbe.auth.sendMagicLink('user@example.com', {
1231
1427
  * useDev: true,
1232
1428
  * });
1429
+ *
1430
+ * // Log user directly into a specific organization
1431
+ * await stackbe.auth.sendMagicLink('user@example.com', {
1432
+ * organizationId: 'org_abc123',
1433
+ * });
1233
1434
  * ```
1234
1435
  */
1235
1436
  async sendMagicLink(email, options = {}) {
@@ -1245,7 +1446,8 @@ var AuthClient = class {
1245
1446
  {
1246
1447
  email,
1247
1448
  redirectUrl,
1248
- useDev
1449
+ useDev,
1450
+ organizationId: options.organizationId
1249
1451
  }
1250
1452
  );
1251
1453
  }
@@ -1460,6 +1662,59 @@ var AuthClient = class {
1460
1662
  const session = await this.getSession(sessionToken);
1461
1663
  return session !== null;
1462
1664
  }
1665
+ /**
1666
+ * Switch the current session to a different organization.
1667
+ * The customer must be a member of the target organization.
1668
+ *
1669
+ * Returns a new session token scoped to the target organization.
1670
+ * The original session token remains valid but is scoped to the previous org (or no org).
1671
+ *
1672
+ * @example
1673
+ * ```typescript
1674
+ * // Get available organizations for the customer
1675
+ * const orgs = await stackbe.organizations.listByCustomer(customerId);
1676
+ *
1677
+ * // Switch to a different organization
1678
+ * const { sessionToken: newToken, organizationId, orgRole } =
1679
+ * await stackbe.auth.switchOrganization(currentSessionToken, targetOrgId);
1680
+ *
1681
+ * // Store the new token - it's scoped to the target org
1682
+ * localStorage.setItem('sessionToken', newToken);
1683
+ * ```
1684
+ *
1685
+ * @throws {StackBEError} with code 'NOT_MEMBER' if customer is not a member of the organization
1686
+ * @throws {StackBEError} with code 'ORG_NOT_FOUND' if organization doesn't exist
1687
+ * @throws {StackBEError} with code 'SESSION_INVALID' if session token is invalid
1688
+ */
1689
+ async switchOrganization(sessionToken, organizationId) {
1690
+ this.sessionCache.delete(sessionToken);
1691
+ const response = await fetch(
1692
+ `${this.http.baseUrl}/v1/apps/${this.appId}/auth/switch-organization`,
1693
+ {
1694
+ method: "POST",
1695
+ headers: {
1696
+ "Authorization": `Bearer ${sessionToken}`,
1697
+ "Content-Type": "application/json"
1698
+ },
1699
+ body: JSON.stringify({ organizationId })
1700
+ }
1701
+ );
1702
+ if (!response.ok) {
1703
+ const errorData = await response.json().catch(() => ({}));
1704
+ const message = errorData.message || errorData.error || "Failed to switch organization";
1705
+ let code = "SWITCH_ORG_FAILED";
1706
+ if (response.status === 401) {
1707
+ code = "SESSION_INVALID";
1708
+ } else if (message.toLowerCase().includes("not a member")) {
1709
+ code = "NOT_MEMBER";
1710
+ } else if (message.toLowerCase().includes("not found")) {
1711
+ code = "ORG_NOT_FOUND";
1712
+ }
1713
+ throw new StackBEError(message, response.status, code);
1714
+ }
1715
+ const data = await response.json();
1716
+ return data;
1717
+ }
1463
1718
  };
1464
1719
 
1465
1720
  // src/organizations.ts
@@ -2507,6 +2762,7 @@ var StackBE = class {
2507
2762
  this.customers = new CustomersClient(this.http, config.appId);
2508
2763
  this.checkout = new CheckoutClient(this.http, config.appId);
2509
2764
  this.subscriptions = new SubscriptionsClient(this.http);
2765
+ this.paymentMethods = new PaymentMethodsClient(this.http);
2510
2766
  this.auth = new AuthClient(this.http, config.appId, {
2511
2767
  sessionCacheTTL: config.sessionCacheTTL,
2512
2768
  devCallbackUrl: config.devCallbackUrl
@@ -2662,6 +2918,7 @@ var StackBE = class {
2662
2918
  EntitlementsClient,
2663
2919
  FeatureRequestsClient,
2664
2920
  OrganizationsClient,
2921
+ PaymentMethodsClient,
2665
2922
  PlansClient,
2666
2923
  ProductsClient,
2667
2924
  StackBE,
package/dist/index.mjs CHANGED
@@ -1127,6 +1127,201 @@ var SubscriptionsClient = class {
1127
1127
  `/v1/subscriptions/${subscriptionId}/has-access`
1128
1128
  );
1129
1129
  }
1130
+ // ============================================
1131
+ // StackBE-Managed Billing Methods
1132
+ // ============================================
1133
+ /**
1134
+ * Manually charge a StackBE-managed subscription.
1135
+ *
1136
+ * This is useful for:
1137
+ * - Testing billing in development
1138
+ * - Recovering from failed automatic charges
1139
+ * - Charging ahead of schedule
1140
+ *
1141
+ * The subscription must be in `stackbe_managed` billing mode.
1142
+ *
1143
+ * @example
1144
+ * ```typescript
1145
+ * const result = await stackbe.subscriptions.charge('sub_123');
1146
+ *
1147
+ * if (result.success) {
1148
+ * console.log('Charge successful:', result.chargeId);
1149
+ * } else if (result.requiresAction) {
1150
+ * // Redirect customer to 3DS authentication
1151
+ * window.location.href = result.actionUrl;
1152
+ * } else {
1153
+ * console.log('Charge failed:', result.errorMessage);
1154
+ * }
1155
+ * ```
1156
+ */
1157
+ async charge(subscriptionId) {
1158
+ return this.http.post(
1159
+ `/v1/subscriptions/${subscriptionId}/charge`
1160
+ );
1161
+ }
1162
+ /**
1163
+ * Migrate a subscription from gateway-managed to StackBE-managed billing.
1164
+ *
1165
+ * This will:
1166
+ * 1. Extract the payment method from the gateway subscription
1167
+ * 2. Store it in StackBE's PaymentMethod table
1168
+ * 3. Set the subscription to `stackbe_managed` billing mode
1169
+ * 4. Cancel the gateway subscription at period end
1170
+ * 5. StackBE will handle renewals going forward
1171
+ *
1172
+ * Use cases:
1173
+ * - Migrate existing Stripe subscriptions to unified StackBE billing
1174
+ * - Prepare for multi-gateway support
1175
+ * - Gain more control over billing timing and retry logic
1176
+ *
1177
+ * @example
1178
+ * ```typescript
1179
+ * const result = await stackbe.subscriptions.migrateToStackbeManaged('sub_123');
1180
+ *
1181
+ * console.log('Migration complete');
1182
+ * console.log('Payment method:', result.paymentMethodId);
1183
+ * console.log('Next billing date:', result.nextBillingDate);
1184
+ * ```
1185
+ */
1186
+ async migrateToStackbeManaged(subscriptionId) {
1187
+ return this.http.post(
1188
+ `/v1/subscriptions/${subscriptionId}/migrate-to-stackbe-managed`
1189
+ );
1190
+ }
1191
+ };
1192
+
1193
+ // src/payment-methods.ts
1194
+ var PaymentMethodsClient = class {
1195
+ constructor(http) {
1196
+ this.http = http;
1197
+ }
1198
+ /**
1199
+ * Create a setup session for collecting a payment method.
1200
+ *
1201
+ * Returns a URL to redirect the customer to for secure payment method collection.
1202
+ * This is similar to Stripe Checkout but in setup mode - no charge is made.
1203
+ *
1204
+ * @example
1205
+ * ```typescript
1206
+ * const session = await stackbe.paymentMethods.createSetupSession('cust_123', {
1207
+ * successUrl: 'https://yourapp.com/payment-methods/success',
1208
+ * cancelUrl: 'https://yourapp.com/payment-methods/cancel',
1209
+ * });
1210
+ *
1211
+ * // Redirect customer to payment method collection
1212
+ * window.location.href = session.url;
1213
+ * ```
1214
+ */
1215
+ async createSetupSession(customerId, options) {
1216
+ return this.http.post(
1217
+ `/v1/apps/{appId}/customers/${customerId}/payment-methods/setup`,
1218
+ options
1219
+ );
1220
+ }
1221
+ /**
1222
+ * Attach a payment method to a customer.
1223
+ *
1224
+ * After the customer completes the setup session, call this to store
1225
+ * the payment method in StackBE for future charges.
1226
+ *
1227
+ * @example
1228
+ * ```typescript
1229
+ * // After redirect from setup session
1230
+ * const paymentMethod = await stackbe.paymentMethods.attach('cust_123', {
1231
+ * gatewayPaymentMethodId: 'pm_1234...', // From setup session completion
1232
+ * setAsDefault: true,
1233
+ * });
1234
+ *
1235
+ * console.log(`Added ${paymentMethod.brand} ending in ${paymentMethod.last4}`);
1236
+ * ```
1237
+ */
1238
+ async attach(customerId, options) {
1239
+ return this.http.post(
1240
+ `/v1/apps/{appId}/customers/${customerId}/payment-methods`,
1241
+ options
1242
+ );
1243
+ }
1244
+ /**
1245
+ * List all payment methods for a customer.
1246
+ *
1247
+ * @example
1248
+ * ```typescript
1249
+ * const paymentMethods = await stackbe.paymentMethods.list('cust_123');
1250
+ *
1251
+ * for (const pm of paymentMethods) {
1252
+ * console.log(`${pm.brand} **** ${pm.last4} ${pm.isDefault ? '(default)' : ''}`);
1253
+ * }
1254
+ * ```
1255
+ */
1256
+ async list(customerId, options) {
1257
+ const params = {};
1258
+ if (options?.status) params.status = options.status;
1259
+ return this.http.get(
1260
+ `/v1/apps/{appId}/customers/${customerId}/payment-methods`,
1261
+ params
1262
+ );
1263
+ }
1264
+ /**
1265
+ * Get a specific payment method.
1266
+ *
1267
+ * @example
1268
+ * ```typescript
1269
+ * const pm = await stackbe.paymentMethods.get('cust_123', 'pm_123');
1270
+ * console.log(`Expires: ${pm.expiryMonth}/${pm.expiryYear}`);
1271
+ * ```
1272
+ */
1273
+ async get(customerId, paymentMethodId) {
1274
+ return this.http.get(
1275
+ `/v1/apps/{appId}/customers/${customerId}/payment-methods/${paymentMethodId}`
1276
+ );
1277
+ }
1278
+ /**
1279
+ * Set a payment method as the default for a customer.
1280
+ *
1281
+ * The default payment method is used for subscription renewals.
1282
+ *
1283
+ * @example
1284
+ * ```typescript
1285
+ * await stackbe.paymentMethods.setDefault('cust_123', 'pm_456');
1286
+ * console.log('Default payment method updated');
1287
+ * ```
1288
+ */
1289
+ async setDefault(customerId, paymentMethodId) {
1290
+ return this.http.post(
1291
+ `/v1/apps/{appId}/customers/${customerId}/payment-methods/${paymentMethodId}/default`
1292
+ );
1293
+ }
1294
+ /**
1295
+ * Remove a payment method.
1296
+ *
1297
+ * Cannot remove a payment method that is being used by an active subscription.
1298
+ *
1299
+ * @example
1300
+ * ```typescript
1301
+ * await stackbe.paymentMethods.remove('cust_123', 'pm_old');
1302
+ * console.log('Payment method removed');
1303
+ * ```
1304
+ */
1305
+ async remove(customerId, paymentMethodId) {
1306
+ return this.http.delete(
1307
+ `/v1/apps/{appId}/customers/${customerId}/payment-methods/${paymentMethodId}`
1308
+ );
1309
+ }
1310
+ /**
1311
+ * Get the default payment method for a customer.
1312
+ *
1313
+ * @example
1314
+ * ```typescript
1315
+ * const defaultPM = await stackbe.paymentMethods.getDefault('cust_123');
1316
+ * if (defaultPM) {
1317
+ * console.log(`Default: ${defaultPM.brand} **** ${defaultPM.last4}`);
1318
+ * }
1319
+ * ```
1320
+ */
1321
+ async getDefault(customerId) {
1322
+ const paymentMethods = await this.list(customerId, { status: "active" });
1323
+ return paymentMethods.find((pm) => pm.isDefault) || null;
1324
+ }
1130
1325
  };
1131
1326
 
1132
1327
  // src/auth.ts
@@ -1189,6 +1384,11 @@ var AuthClient = class {
1189
1384
  * await stackbe.auth.sendMagicLink('user@example.com', {
1190
1385
  * useDev: true,
1191
1386
  * });
1387
+ *
1388
+ * // Log user directly into a specific organization
1389
+ * await stackbe.auth.sendMagicLink('user@example.com', {
1390
+ * organizationId: 'org_abc123',
1391
+ * });
1192
1392
  * ```
1193
1393
  */
1194
1394
  async sendMagicLink(email, options = {}) {
@@ -1204,7 +1404,8 @@ var AuthClient = class {
1204
1404
  {
1205
1405
  email,
1206
1406
  redirectUrl,
1207
- useDev
1407
+ useDev,
1408
+ organizationId: options.organizationId
1208
1409
  }
1209
1410
  );
1210
1411
  }
@@ -1419,6 +1620,59 @@ var AuthClient = class {
1419
1620
  const session = await this.getSession(sessionToken);
1420
1621
  return session !== null;
1421
1622
  }
1623
+ /**
1624
+ * Switch the current session to a different organization.
1625
+ * The customer must be a member of the target organization.
1626
+ *
1627
+ * Returns a new session token scoped to the target organization.
1628
+ * The original session token remains valid but is scoped to the previous org (or no org).
1629
+ *
1630
+ * @example
1631
+ * ```typescript
1632
+ * // Get available organizations for the customer
1633
+ * const orgs = await stackbe.organizations.listByCustomer(customerId);
1634
+ *
1635
+ * // Switch to a different organization
1636
+ * const { sessionToken: newToken, organizationId, orgRole } =
1637
+ * await stackbe.auth.switchOrganization(currentSessionToken, targetOrgId);
1638
+ *
1639
+ * // Store the new token - it's scoped to the target org
1640
+ * localStorage.setItem('sessionToken', newToken);
1641
+ * ```
1642
+ *
1643
+ * @throws {StackBEError} with code 'NOT_MEMBER' if customer is not a member of the organization
1644
+ * @throws {StackBEError} with code 'ORG_NOT_FOUND' if organization doesn't exist
1645
+ * @throws {StackBEError} with code 'SESSION_INVALID' if session token is invalid
1646
+ */
1647
+ async switchOrganization(sessionToken, organizationId) {
1648
+ this.sessionCache.delete(sessionToken);
1649
+ const response = await fetch(
1650
+ `${this.http.baseUrl}/v1/apps/${this.appId}/auth/switch-organization`,
1651
+ {
1652
+ method: "POST",
1653
+ headers: {
1654
+ "Authorization": `Bearer ${sessionToken}`,
1655
+ "Content-Type": "application/json"
1656
+ },
1657
+ body: JSON.stringify({ organizationId })
1658
+ }
1659
+ );
1660
+ if (!response.ok) {
1661
+ const errorData = await response.json().catch(() => ({}));
1662
+ const message = errorData.message || errorData.error || "Failed to switch organization";
1663
+ let code = "SWITCH_ORG_FAILED";
1664
+ if (response.status === 401) {
1665
+ code = "SESSION_INVALID";
1666
+ } else if (message.toLowerCase().includes("not a member")) {
1667
+ code = "NOT_MEMBER";
1668
+ } else if (message.toLowerCase().includes("not found")) {
1669
+ code = "ORG_NOT_FOUND";
1670
+ }
1671
+ throw new StackBEError(message, response.status, code);
1672
+ }
1673
+ const data = await response.json();
1674
+ return data;
1675
+ }
1422
1676
  };
1423
1677
 
1424
1678
  // src/organizations.ts
@@ -2466,6 +2720,7 @@ var StackBE = class {
2466
2720
  this.customers = new CustomersClient(this.http, config.appId);
2467
2721
  this.checkout = new CheckoutClient(this.http, config.appId);
2468
2722
  this.subscriptions = new SubscriptionsClient(this.http);
2723
+ this.paymentMethods = new PaymentMethodsClient(this.http);
2469
2724
  this.auth = new AuthClient(this.http, config.appId, {
2470
2725
  sessionCacheTTL: config.sessionCacheTTL,
2471
2726
  devCallbackUrl: config.devCallbackUrl
@@ -2620,6 +2875,7 @@ export {
2620
2875
  EntitlementsClient,
2621
2876
  FeatureRequestsClient,
2622
2877
  OrganizationsClient,
2878
+ PaymentMethodsClient,
2623
2879
  PlansClient,
2624
2880
  ProductsClient,
2625
2881
  StackBE,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@stackbe/sdk",
3
- "version": "0.12.1",
3
+ "version": "0.14.0",
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",