@stackbe/sdk 0.6.4 → 0.7.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.mjs CHANGED
@@ -584,7 +584,7 @@ var CheckoutClient = class {
584
584
  *
585
585
  * @example
586
586
  * ```typescript
587
- * // With new customer (will be created)
587
+ * // With customer email (looks up or creates customer)
588
588
  * const { url } = await stackbe.checkout.createSession({
589
589
  * customer: { email: 'user@example.com', name: 'John' },
590
590
  * planId: 'plan_pro_monthly',
@@ -592,6 +592,18 @@ var CheckoutClient = class {
592
592
  * trialDays: 14,
593
593
  * });
594
594
  * ```
595
+ *
596
+ * @example
597
+ * ```typescript
598
+ * // Organization-level subscription (B2B)
599
+ * const { url } = await stackbe.checkout.createSession({
600
+ * customer: 'cust_123',
601
+ * organizationId: 'org_456', // Customer must be owner/admin
602
+ * planId: 'plan_team',
603
+ * successUrl: 'https://myapp.com/success',
604
+ * cancelUrl: 'https://myapp.com/pricing',
605
+ * });
606
+ * ```
595
607
  */
596
608
  async createSession(options) {
597
609
  const body = {
@@ -599,15 +611,25 @@ var CheckoutClient = class {
599
611
  planId: options.planId,
600
612
  successUrl: options.successUrl,
601
613
  cancelUrl: options.cancelUrl,
614
+ organizationId: options.organizationId,
602
615
  allowPromotionCodes: options.allowPromotionCodes,
603
616
  trialDays: options.trialDays,
604
617
  metadata: options.metadata
605
618
  };
619
+ if (!options.customer) {
620
+ throw new Error(
621
+ 'customer is required. Use customer: "cust_xxx" for existing customer ID, or customer: { email: "user@example.com" } for email-based lookup.'
622
+ );
623
+ }
606
624
  if (typeof options.customer === "string") {
607
625
  body.customerId = options.customer;
608
- } else {
626
+ } else if (options.customer.email) {
609
627
  body.customerEmail = options.customer.email;
610
628
  body.customerName = options.customer.name;
629
+ } else {
630
+ throw new Error(
631
+ 'customer.email is required when using object form. Use customer: { email: "user@example.com" }'
632
+ );
611
633
  }
612
634
  return this.http.post("/v1/checkout/session", body);
613
635
  }
@@ -1299,6 +1321,297 @@ var OrganizationsClient = class {
1299
1321
  }
1300
1322
  };
1301
1323
 
1324
+ // src/plans.ts
1325
+ var PlansClient = class {
1326
+ constructor(http) {
1327
+ this.http = http;
1328
+ }
1329
+ /**
1330
+ * List all plans for the app.
1331
+ *
1332
+ * @example
1333
+ * ```typescript
1334
+ * // List all plans
1335
+ * const plans = await stackbe.plans.list();
1336
+ *
1337
+ * // Filter by product
1338
+ * const plans = await stackbe.plans.list({ productId: 'prod_123' });
1339
+ *
1340
+ * // Display in pricing page
1341
+ * plans.forEach(plan => {
1342
+ * console.log(`${plan.name}: $${plan.priceCents / 100}/${plan.interval}`);
1343
+ * });
1344
+ * ```
1345
+ */
1346
+ async list(options) {
1347
+ const params = new URLSearchParams();
1348
+ if (options?.productId) {
1349
+ params.set("productId", options.productId);
1350
+ }
1351
+ const query = params.toString();
1352
+ return this.http.get(`/v1/plans${query ? `?${query}` : ""}`);
1353
+ }
1354
+ /**
1355
+ * Get a plan by ID.
1356
+ *
1357
+ * @example
1358
+ * ```typescript
1359
+ * const plan = await stackbe.plans.get('plan_123');
1360
+ * console.log(plan.name, plan.priceCents, plan.entitlements);
1361
+ * ```
1362
+ */
1363
+ async get(planId) {
1364
+ return this.http.get(`/v1/plans/${planId}`);
1365
+ }
1366
+ /**
1367
+ * Get active plans only (excludes archived plans).
1368
+ *
1369
+ * @example
1370
+ * ```typescript
1371
+ * const activePlans = await stackbe.plans.getActive();
1372
+ * ```
1373
+ */
1374
+ async getActive(options) {
1375
+ const plans = await this.list(options);
1376
+ return plans.filter((p) => p.status === "active");
1377
+ }
1378
+ /**
1379
+ * Get plans sorted by price (ascending).
1380
+ *
1381
+ * @example
1382
+ * ```typescript
1383
+ * const plans = await stackbe.plans.listByPrice();
1384
+ * // [Free, Starter, Pro, Enterprise]
1385
+ * ```
1386
+ */
1387
+ async listByPrice(options) {
1388
+ const plans = await this.getActive(options);
1389
+ return plans.sort((a, b) => a.priceCents - b.priceCents);
1390
+ }
1391
+ };
1392
+ var ProductsClient = class {
1393
+ constructor(http, appId) {
1394
+ this.http = http;
1395
+ this.appId = appId;
1396
+ }
1397
+ /**
1398
+ * List all products for the app.
1399
+ *
1400
+ * @example
1401
+ * ```typescript
1402
+ * const products = await stackbe.products.list();
1403
+ * products.forEach(product => {
1404
+ * console.log(product.name, product.plans?.length, 'plans');
1405
+ * });
1406
+ * ```
1407
+ */
1408
+ async list(options) {
1409
+ const params = new URLSearchParams();
1410
+ params.set("appId", options?.appId ?? this.appId);
1411
+ return this.http.get(`/v1/products?${params.toString()}`);
1412
+ }
1413
+ /**
1414
+ * Get a product by ID.
1415
+ *
1416
+ * @example
1417
+ * ```typescript
1418
+ * const product = await stackbe.products.get('prod_123');
1419
+ * console.log(product.name, product.description);
1420
+ * ```
1421
+ */
1422
+ async get(productId) {
1423
+ return this.http.get(`/v1/products/${productId}`);
1424
+ }
1425
+ };
1426
+
1427
+ // src/feature-requests.ts
1428
+ var FeatureRequestsClient = class {
1429
+ constructor(http, appId) {
1430
+ this.http = http;
1431
+ this.appId = appId;
1432
+ }
1433
+ /**
1434
+ * List feature requests for the app.
1435
+ *
1436
+ * @example
1437
+ * ```typescript
1438
+ * // Get all feature requests
1439
+ * const { data, total } = await stackbe.featureRequests.list();
1440
+ *
1441
+ * // Filter by status
1442
+ * const planned = await stackbe.featureRequests.list({ status: 'planned' });
1443
+ *
1444
+ * // Sort by votes
1445
+ * const popular = await stackbe.featureRequests.list({ sortBy: 'votes' });
1446
+ * ```
1447
+ */
1448
+ async list(options = {}) {
1449
+ const params = {};
1450
+ if (options.status) params.status = options.status;
1451
+ if (options.category) params.category = options.category;
1452
+ if (options.sortBy) params.sortBy = options.sortBy;
1453
+ if (options.limit) params.limit = options.limit;
1454
+ if (options.offset) params.offset = options.offset;
1455
+ return this.http.get(
1456
+ `/v1/apps/${this.appId}/feature-requests`,
1457
+ params
1458
+ );
1459
+ }
1460
+ /**
1461
+ * Submit a new feature request.
1462
+ * Requires customer session token.
1463
+ *
1464
+ * @example
1465
+ * ```typescript
1466
+ * const request = await stackbe.featureRequests.create({
1467
+ * title: 'Dark mode support',
1468
+ * description: 'Would love to have a dark theme option',
1469
+ * category: 'UI',
1470
+ * });
1471
+ * ```
1472
+ */
1473
+ async create(options) {
1474
+ return this.http.post(
1475
+ `/v1/apps/${this.appId}/feature-requests`,
1476
+ options
1477
+ );
1478
+ }
1479
+ /**
1480
+ * Get a specific feature request by ID.
1481
+ *
1482
+ * @example
1483
+ * ```typescript
1484
+ * const request = await stackbe.featureRequests.get('req_123');
1485
+ * console.log(`${request.title} has ${request.voteCount} votes`);
1486
+ * ```
1487
+ */
1488
+ async get(requestId) {
1489
+ return this.http.get(
1490
+ `/v1/apps/${this.appId}/feature-requests/${requestId}`
1491
+ );
1492
+ }
1493
+ /**
1494
+ * Upvote a feature request.
1495
+ * Requires customer session token. Idempotent.
1496
+ *
1497
+ * @example
1498
+ * ```typescript
1499
+ * await stackbe.featureRequests.vote('req_123');
1500
+ * ```
1501
+ */
1502
+ async vote(requestId) {
1503
+ return this.http.post(
1504
+ `/v1/apps/${this.appId}/feature-requests/${requestId}/vote`
1505
+ );
1506
+ }
1507
+ /**
1508
+ * Remove your vote from a feature request.
1509
+ * Requires customer session token.
1510
+ *
1511
+ * @example
1512
+ * ```typescript
1513
+ * await stackbe.featureRequests.removeVote('req_123');
1514
+ * ```
1515
+ */
1516
+ async removeVote(requestId) {
1517
+ return this.http.delete(
1518
+ `/v1/apps/${this.appId}/feature-requests/${requestId}/vote`
1519
+ );
1520
+ }
1521
+ /**
1522
+ * Add a comment to a feature request.
1523
+ * Requires customer session token.
1524
+ *
1525
+ * @example
1526
+ * ```typescript
1527
+ * await stackbe.featureRequests.addComment('req_123', {
1528
+ * content: 'This would be really helpful for my workflow!',
1529
+ * });
1530
+ * ```
1531
+ */
1532
+ async addComment(requestId, options) {
1533
+ return this.http.post(
1534
+ `/v1/apps/${this.appId}/feature-requests/${requestId}/comments`,
1535
+ options
1536
+ );
1537
+ }
1538
+ // ==================== Admin Methods ====================
1539
+ /**
1540
+ * Update a feature request status or category.
1541
+ * Requires API key (admin).
1542
+ *
1543
+ * @example
1544
+ * ```typescript
1545
+ * await stackbe.featureRequests.update('req_123', {
1546
+ * status: 'planned',
1547
+ * category: 'Core Features',
1548
+ * });
1549
+ * ```
1550
+ */
1551
+ async update(requestId, options) {
1552
+ return this.http.patch(
1553
+ `/v1/apps/${this.appId}/feature-requests/${requestId}`,
1554
+ options
1555
+ );
1556
+ }
1557
+ /**
1558
+ * Delete a feature request.
1559
+ * Requires API key (admin).
1560
+ *
1561
+ * @example
1562
+ * ```typescript
1563
+ * await stackbe.featureRequests.delete('req_123');
1564
+ * ```
1565
+ */
1566
+ async delete(requestId) {
1567
+ return this.http.delete(
1568
+ `/v1/apps/${this.appId}/feature-requests/${requestId}`
1569
+ );
1570
+ }
1571
+ /**
1572
+ * Get all customers interested in a feature request (author + voters).
1573
+ * Useful for user research or notifying users when the feature ships.
1574
+ * Requires API key (admin).
1575
+ *
1576
+ * @example
1577
+ * ```typescript
1578
+ * const { author, voters, totalInterested } = await stackbe.featureRequests.getInterestedCustomers('req_123');
1579
+ *
1580
+ * // Notify all interested users
1581
+ * const emails = [author.email, ...voters.map(v => v.email)];
1582
+ * await sendNotification(emails, 'Your requested feature is now live!');
1583
+ * ```
1584
+ */
1585
+ async getInterestedCustomers(requestId) {
1586
+ return this.http.get(
1587
+ `/v1/apps/${this.appId}/feature-requests/${requestId}/interested`
1588
+ );
1589
+ }
1590
+ /**
1591
+ * Get a customer's feature request activity (requests submitted + votes).
1592
+ * Useful for displaying on customer profile.
1593
+ * Requires API key (admin).
1594
+ *
1595
+ * @example
1596
+ * ```typescript
1597
+ * const activity = await stackbe.featureRequests.getCustomerActivity('cust_123');
1598
+ *
1599
+ * console.log(`Submitted ${activity.totalSubmitted} requests`);
1600
+ * console.log(`Voted on ${activity.totalVotes} requests`);
1601
+ *
1602
+ * // Show their most wanted features
1603
+ * activity.votedOn.forEach(r => {
1604
+ * console.log(`- ${r.title} (${r.status})`);
1605
+ * });
1606
+ * ```
1607
+ */
1608
+ async getCustomerActivity(customerId) {
1609
+ return this.http.get(
1610
+ `/v1/apps/${this.appId}/feature-requests/customer/${customerId}/activity`
1611
+ );
1612
+ }
1613
+ };
1614
+
1302
1615
  // src/client.ts
1303
1616
  var DEFAULT_BASE_URL = "https://api.stackbe.io";
1304
1617
  var DEFAULT_TIMEOUT = 3e4;
@@ -1359,6 +1672,9 @@ var StackBE = class {
1359
1672
  devCallbackUrl: config.devCallbackUrl
1360
1673
  });
1361
1674
  this.organizations = new OrganizationsClient(this.http, config.appId);
1675
+ this.plans = new PlansClient(this.http);
1676
+ this.products = new ProductsClient(this.http, config.appId);
1677
+ this.featureRequests = new FeatureRequestsClient(this.http, config.appId);
1362
1678
  }
1363
1679
  /**
1364
1680
  * Create a middleware for Express that tracks usage automatically.
@@ -1495,7 +1811,10 @@ export {
1495
1811
  CheckoutClient,
1496
1812
  CustomersClient,
1497
1813
  EntitlementsClient,
1814
+ FeatureRequestsClient,
1498
1815
  OrganizationsClient,
1816
+ PlansClient,
1817
+ ProductsClient,
1499
1818
  StackBE,
1500
1819
  StackBEError,
1501
1820
  SubscriptionsClient,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@stackbe/sdk",
3
- "version": "0.6.4",
3
+ "version": "0.7.1",
4
4
  "description": "Official JavaScript/TypeScript SDK for StackBE - the billing backend for your side project",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.mjs",