@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 +15 -0
- package/dist/index.d.mts +253 -1
- package/dist/index.d.ts +253 -1
- package/dist/index.js +258 -1
- package/dist/index.mjs +257 -1
- package/package.json +1 -1
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