@chatarmin/os 1.7.0 → 1.8.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/README.md +386 -8
- package/dist/billing/client.d.ts +8 -0
- package/dist/billing/client.d.ts.map +1 -0
- package/dist/billing/client.js +46 -0
- package/dist/billing/index.d.ts +3 -0
- package/dist/billing/index.d.ts.map +1 -0
- package/dist/billing/index.js +1 -0
- package/dist/billing/types.d.ts +201 -0
- package/dist/billing/types.d.ts.map +1 -0
- package/dist/billing/types.js +1 -0
- package/dist/billing/usage-breakdown-types.d.ts +132 -0
- package/dist/billing/usage-breakdown-types.d.ts.map +1 -0
- package/dist/billing/usage-breakdown-types.js +1 -0
- package/dist/companies/client.d.ts +2 -2
- package/dist/companies/client.d.ts.map +1 -1
- package/dist/companies/index.d.ts +2 -2
- package/dist/companies/index.d.ts.map +1 -1
- package/dist/companies/index.js +1 -1
- package/dist/features/client.d.ts +8 -0
- package/dist/features/client.d.ts.map +1 -0
- package/dist/features/client.js +18 -0
- package/dist/features/index.d.ts +3 -0
- package/dist/features/index.d.ts.map +1 -0
- package/dist/features/index.js +1 -0
- package/dist/features/types.d.ts +184 -0
- package/dist/features/types.d.ts.map +1 -0
- package/dist/features/types.js +1 -0
- package/dist/index.d.ts +29 -766
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +8 -364
- package/dist/index.js.map +1 -0
- package/dist/webhooks/index.d.ts +17 -0
- package/dist/webhooks/index.d.ts.map +1 -1
- package/dist/webhooks/index.js +17 -0
- package/dist/webhooks/index.js.map +1 -0
- package/package.json +2 -3
package/README.md
CHANGED
|
@@ -194,6 +194,8 @@ const [primary] = await os.contacts.list("comp_xxx", true)
|
|
|
194
194
|
|
|
195
195
|
Check and manage feature access for companies.
|
|
196
196
|
|
|
197
|
+
**Identifiers:** pass **`featureCode`** on input (e.g. `"ai_credit"`). Responses include `featureCode` and `featureAccessId`. Codes are set at feature create and are not renamed via the API — change display with `featureName`, or create a new feature and migrate for a real rename. See [`product-billing-api.md`](../../docs/engineering/product-billing-api.md#feature-identifiers-featurecode) for details.
|
|
198
|
+
|
|
197
199
|
#### `features.check(input)`
|
|
198
200
|
|
|
199
201
|
Check if a company has access to a specific feature.
|
|
@@ -294,6 +296,8 @@ if (access.canUse) {
|
|
|
294
296
|
|
|
295
297
|
Track usage, claim subscriptions, and manage billing.
|
|
296
298
|
|
|
299
|
+
> **API reference:** [`docs/engineering/product-billing-api.md`](../../docs/engineering/product-billing-api.md) — full request/response shapes for every `os.billing.*` method.
|
|
300
|
+
|
|
297
301
|
#### `billing.trackUsage(input)`
|
|
298
302
|
|
|
299
303
|
Track usage for a metered feature.
|
|
@@ -354,25 +358,398 @@ console.log({
|
|
|
354
358
|
})
|
|
355
359
|
```
|
|
356
360
|
|
|
357
|
-
#### `billing.getStatus(
|
|
361
|
+
#### `billing.getStatus(companyIdOrInput)`
|
|
362
|
+
|
|
363
|
+
Quick Stripe subscription status for a company — `activeSubscriptions` (`active` / `trialing` / `past_due`), resolved billing profile, and enabled feature count. Scoped to the product link's billing profile when `externalOrgId` is provided.
|
|
358
364
|
|
|
359
|
-
|
|
365
|
+
See [Product-scoped billing profile — `getStatus` example](../../docs/engineering/product-billing-api.md#example-getstatus-for-subscription-health) for the full walkthrough + sample response.
|
|
360
366
|
|
|
361
367
|
```typescript
|
|
362
|
-
const status = await os.billing.getStatus(
|
|
368
|
+
const status = await os.billing.getStatus('ee0f4a19-2b69-4969-8807-d0013f1670ee')
|
|
369
|
+
|
|
370
|
+
const statusByOrg = await os.billing.getStatus({ externalOrgId: 'org_kKZHiUHBSjdu' })
|
|
371
|
+
|
|
372
|
+
if (status.activeSubscriptions[0]?.status === 'past_due') {
|
|
373
|
+
// gate access for this external org
|
|
374
|
+
}
|
|
363
375
|
|
|
364
376
|
console.log({
|
|
365
|
-
|
|
366
|
-
|
|
377
|
+
stripeCustomer: status.billingProfile?.stripeCustomerId,
|
|
378
|
+
subscriptionStatus: status.activeSubscriptions[0]?.status,
|
|
367
379
|
features: status.enabledFeaturesCount,
|
|
368
380
|
})
|
|
381
|
+
```
|
|
382
|
+
|
|
383
|
+
#### `billing.getOverview(input)`
|
|
384
|
+
|
|
385
|
+
Combined billing page payload: company, billing profile, primary subscription, and recent invoices.
|
|
386
|
+
|
|
387
|
+
```typescript
|
|
388
|
+
const overview = await os.billing.getOverview({
|
|
389
|
+
companyId: "comp_xxx",
|
|
390
|
+
invoiceLimit: 12,
|
|
391
|
+
includeUsage: true, // optional: embed usage rows
|
|
392
|
+
})
|
|
393
|
+
|
|
394
|
+
console.log({
|
|
395
|
+
plan: overview.subscription?.tier?.name,
|
|
396
|
+
status: overview.subscription?.status,
|
|
397
|
+
paymentMethod: overview.billingProfile?.primaryPaymentMethod,
|
|
398
|
+
invoices: overview.invoices.length,
|
|
399
|
+
usageRows: overview.usage?.length,
|
|
400
|
+
})
|
|
401
|
+
```
|
|
402
|
+
|
|
403
|
+
#### `billing.getUsage(companyIdOrInput, options?)`
|
|
404
|
+
|
|
405
|
+
Metronome-style usage aggregates. Returns `{ data: UsageRecord[] }` — feature-first nesting with per-feature billing cycles.
|
|
406
|
+
|
|
407
|
+
**Company scope:** pass **`companyId`** as the first string argument, or pass an input object with **`companyId`** or **`externalOrgId`** (one required). Prefer `externalOrgId` when CX only knows the org slug — usage is scoped to that product link.
|
|
408
|
+
|
|
409
|
+
```typescript
|
|
410
|
+
// CX-native (single product link)
|
|
411
|
+
const { data } = await os.billing.getUsage({
|
|
412
|
+
externalOrgId: 'org_FVCNOtEdpeeP',
|
|
413
|
+
windowSize: 'none',
|
|
414
|
+
})
|
|
415
|
+
|
|
416
|
+
// Or companyId as first arg
|
|
417
|
+
const byCompany = await os.billing.getUsage('comp_xxx', { windowSize: 'none' })
|
|
418
|
+
```
|
|
419
|
+
|
|
420
|
+
- **`data[].featureCode`** — stable feature slug
|
|
421
|
+
- **`data[].cycles[]`** — billing cycles for this feature (`periodOffset`, bounds, `usage[]`)
|
|
422
|
+
|
|
423
|
+
`windowSize` controls whether `usage` points are period totals or time buckets. Filter with **`featureCode`** (stable slug), not `featureAccessId` — see [Feature identifiers](../../docs/engineering/product-billing-api.md#feature-identifiers-featurecode).
|
|
424
|
+
|
|
425
|
+
**`periodOffsetRange`** (time scope for whole billing cycles):
|
|
426
|
+
|
|
427
|
+
| Intent | `periodOffsetRange` |
|
|
428
|
+
|--------|---------------------|
|
|
429
|
+
| Current cycle (default) | omit or `{ from: 0, to: 0 }` |
|
|
430
|
+
| Previous only | `{ from: -1, to: -1 }` |
|
|
431
|
+
| Current + previous | `{ from: -1, to: 0 }` |
|
|
432
|
+
| 12-cycle picker | `{ from: -11, to: 0 }` |
|
|
433
|
+
|
|
434
|
+
Multi-cycle range requires `windowSize: 'none'` (max 12 cycles). Cannot combine with `startingOn`/`endingBefore`.
|
|
435
|
+
|
|
436
|
+
Every `cycles[]` bucket includes `subscriptionItem` and `summary` when it has usage rows. `usage[]` stays thin (`value` + dates).
|
|
437
|
+
|
|
438
|
+
**Response shape:**
|
|
439
|
+
|
|
440
|
+
```typescript
|
|
441
|
+
interface UsageQueryResult {
|
|
442
|
+
data: UsageRecord[]
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
interface UsageRecord {
|
|
446
|
+
featureCode: string
|
|
447
|
+
featureName: string
|
|
448
|
+
cycles: UsageCycleBucket[]
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
interface UsageCycleBucket {
|
|
452
|
+
periodOffset: number
|
|
453
|
+
startingOn: Date
|
|
454
|
+
endingBefore: Date
|
|
455
|
+
label: string
|
|
456
|
+
subscriptionItem?: UsageSubscriptionItem // present when cycle has usage rows
|
|
457
|
+
summary?: UsageCycleSummary // totals + projection; present when cycle has usage rows
|
|
458
|
+
usage: UsagePoint[] // thin: value + dates (+ optional identity/grouping)
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
interface UsagePoint {
|
|
462
|
+
value: number
|
|
463
|
+
startingOn: Date
|
|
464
|
+
endingBefore: Date
|
|
465
|
+
featureAccessId?: string
|
|
466
|
+
meterCode?: string
|
|
467
|
+
groupKey?: string | null // with groupBy: 'productLink'
|
|
468
|
+
groupValue?: string | null
|
|
469
|
+
productLinkId?: string | null // groupBy: 'productLink', or bucketed multi-link companyId
|
|
470
|
+
productLinkLabel?: string | null
|
|
471
|
+
externalOrgId?: string | null
|
|
472
|
+
}
|
|
473
|
+
```
|
|
474
|
+
|
|
475
|
+
Read billing from `cycle.summary` and `cycle.subscriptionItem` — not from `usage[]` points.
|
|
476
|
+
|
|
477
|
+
`productLinkId` (+ label, `externalOrgId`) appears on `usage[]` points when `groupBy: 'productLink'`, or on bucketed `companyId` queries when multiple product links share the company (without `groupKey`).
|
|
478
|
+
|
|
479
|
+
| `windowSize` | `cycles[].usage` | Billing context |
|
|
480
|
+
| ------------ | ---------------- | --------------- |
|
|
481
|
+
| `'none'` | 1 point | `subscriptionItem` + `summary` (always) |
|
|
482
|
+
| `'day'` / `'week'` / `'month'` | N thin points | Same — billing once per cycle, not per bucket |
|
|
483
|
+
|
|
484
|
+
#### Examples by `windowSize`
|
|
485
|
+
|
|
486
|
+
**`windowSize: 'none'`** — period totals:
|
|
487
|
+
|
|
488
|
+
```json
|
|
489
|
+
{
|
|
490
|
+
"data": [{
|
|
491
|
+
"featureCode": "messages",
|
|
492
|
+
"featureName": "Messages",
|
|
493
|
+
"cycles": [{
|
|
494
|
+
"periodOffset": 0,
|
|
495
|
+
"startingOn": "2026-07-01T00:00:00.000Z",
|
|
496
|
+
"endingBefore": "2026-08-01T00:00:00.000Z",
|
|
497
|
+
"label": "Jul 1, 2026 – Aug 1, 2026",
|
|
498
|
+
"subscriptionItem": {
|
|
499
|
+
"includedQuantity": 100,
|
|
500
|
+
"unitAmountCents": 500,
|
|
501
|
+
"currency": "eur"
|
|
502
|
+
},
|
|
503
|
+
"summary": {
|
|
504
|
+
"totalUsage": 120,
|
|
505
|
+
"billableQuantity": 20,
|
|
506
|
+
"estimatedOverageCents": 10000,
|
|
507
|
+
"projectedBillableQuantity": 240,
|
|
508
|
+
"projectedOverageCents": 70000
|
|
509
|
+
},
|
|
510
|
+
"usage": [{
|
|
511
|
+
"value": 120,
|
|
512
|
+
"startingOn": "2026-07-01T00:00:00.000Z",
|
|
513
|
+
"endingBefore": "2026-08-01T00:00:00.000Z"
|
|
514
|
+
}]
|
|
515
|
+
}]
|
|
516
|
+
}]
|
|
517
|
+
}
|
|
518
|
+
```
|
|
369
519
|
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
520
|
+
**`windowSize: 'day'`** — daily chart buckets:
|
|
521
|
+
|
|
522
|
+
```json
|
|
523
|
+
{
|
|
524
|
+
"data": [{
|
|
525
|
+
"featureCode": "messages",
|
|
526
|
+
"featureName": "Messages",
|
|
527
|
+
"cycles": [{
|
|
528
|
+
"periodOffset": 0,
|
|
529
|
+
"subscriptionItem": { "includedQuantity": 100 },
|
|
530
|
+
"summary": { "totalUsage": 20, "billableQuantity": 0 },
|
|
531
|
+
"usage": [
|
|
532
|
+
{ "value": 8, "startingOn": "2026-07-01T00:00:00.000Z", "endingBefore": "2026-07-02T00:00:00.000Z" },
|
|
533
|
+
{ "value": 12, "startingOn": "2026-07-02T00:00:00.000Z", "endingBefore": "2026-07-03T00:00:00.000Z" }
|
|
534
|
+
]
|
|
535
|
+
}]
|
|
536
|
+
}]
|
|
373
537
|
}
|
|
374
538
|
```
|
|
375
539
|
|
|
540
|
+
**Multi-cycle** (`periodOffsetRange: { from: -1, to: 0 }`):
|
|
541
|
+
|
|
542
|
+
```json
|
|
543
|
+
{
|
|
544
|
+
"data": [{
|
|
545
|
+
"featureCode": "messages",
|
|
546
|
+
"featureName": "Messages",
|
|
547
|
+
"cycles": [
|
|
548
|
+
{ "periodOffset": 0, "label": "Jul 1 – Aug 1", "summary": { "totalUsage": 120, "billableQuantity": 20 }, "usage": [{ "value": 120 }] },
|
|
549
|
+
{ "periodOffset": -1, "label": "Jun 1 – Jul 1", "summary": { "totalUsage": 98, "billableQuantity": 0 }, "usage": [{ "value": 98 }] }
|
|
550
|
+
]
|
|
551
|
+
}]
|
|
552
|
+
}
|
|
553
|
+
```
|
|
554
|
+
|
|
555
|
+
Full examples: [`docs/engineering/product-billing-api.md#billinggetusage`](../../docs/engineering/product-billing-api.md#billinggetusage)
|
|
556
|
+
|
|
557
|
+
```typescript
|
|
558
|
+
const { data } = await os.billing.getUsage('comp_xxx', { windowSize: 'none' })
|
|
559
|
+
const messages = data.find((r) => r.featureCode === 'messages')
|
|
560
|
+
const cycle = messages?.cycles[0]
|
|
561
|
+
const currentTotal = cycle?.usage[0]?.value
|
|
562
|
+
const overage = cycle?.summary?.billableQuantity
|
|
563
|
+
const cost = cycle?.summary?.estimatedOverageCents
|
|
564
|
+
|
|
565
|
+
const daily = await os.billing.getUsage('comp_xxx', {
|
|
566
|
+
windowSize: 'day',
|
|
567
|
+
featureCode: 'messages',
|
|
568
|
+
})
|
|
569
|
+
const dailySeries = (daily.data[0]?.cycles[0]?.usage ?? []).map((point) => ({
|
|
570
|
+
day: point.startingOn.toISOString().slice(0, 10),
|
|
571
|
+
usage: point.value,
|
|
572
|
+
}))
|
|
573
|
+
|
|
574
|
+
// Previous billing period totals
|
|
575
|
+
const lastMonth = await os.billing.getUsage('comp_xxx', {
|
|
576
|
+
windowSize: 'none',
|
|
577
|
+
periodOffsetRange: { from: -1, to: -1 },
|
|
578
|
+
})
|
|
579
|
+
|
|
580
|
+
// Last 12 billing cycles (cycle picker)
|
|
581
|
+
const history = await os.billing.getUsage('comp_xxx', {
|
|
582
|
+
windowSize: 'none',
|
|
583
|
+
periodOffsetRange: { from: -11, to: 0 },
|
|
584
|
+
})
|
|
585
|
+
// history.data[0].cycles → one bucket per period; cycle.usage[0] → period total
|
|
586
|
+
|
|
587
|
+
// Custom sub-range within the current billing period (both dates required)
|
|
588
|
+
// usage[] points cover the sub-range; summary totals match it; projections use the full cycle
|
|
589
|
+
const week = await os.billing.getUsage('comp_xxx', {
|
|
590
|
+
windowSize: 'day',
|
|
591
|
+
featureCode: 'messages',
|
|
592
|
+
startingOn: new Date('2026-07-10T00:00:00Z'),
|
|
593
|
+
endingBefore: new Date('2026-07-17T00:00:00Z'),
|
|
594
|
+
})
|
|
595
|
+
```
|
|
596
|
+
|
|
597
|
+
| `windowSize` | Shape |
|
|
598
|
+
| ------------ | ----- |
|
|
599
|
+
| `'none'` | One point per feature (collapsed total when multiple product links share a company; use `groupBy: 'productLink'` for per-link rows) |
|
|
600
|
+
| `'day'` / `'week'` / `'month'` | One point per feature per bucket; one per link per bucket when multiple product links share the company (`productLinkId` on each point) |
|
|
601
|
+
|
|
602
|
+
| Time scope | When to use |
|
|
603
|
+
| ---------- | ----------- |
|
|
604
|
+
| omit | Current billing period (default) |
|
|
605
|
+
| `periodOffsetRange: { from: -1, to: -1 }` | Whole previous billing period |
|
|
606
|
+
| `periodOffsetRange: { from: -11, to: 0 }` | Last 12 billing cycles |
|
|
607
|
+
| `startingOn` + `endingBefore` | Sub-range inside the **current** period (clamped; totals follow sub-range; projections use full cycle) |
|
|
608
|
+
|
|
609
|
+
#### `billing.exportUsage(companyIdOrInput, options?)`
|
|
610
|
+
|
|
611
|
+
Export usage aggregates as CSV. Accepts the same company scope and query options as `getUsage` (plus implicit `format: 'csv'`). CSV includes a `periodOffset` column (first column) for multi-cycle exports. Filename uses exported period bounds: `usage_{windowSize}_{companyId}_{periodStart}_{periodEnd}.csv`.
|
|
612
|
+
|
|
613
|
+
```typescript
|
|
614
|
+
const csv = await os.billing.exportUsage({
|
|
615
|
+
externalOrgId: 'org_FVCNOtEdpeeP',
|
|
616
|
+
windowSize: 'none',
|
|
617
|
+
periodOffsetRange: { from: -11, to: 0 },
|
|
618
|
+
})
|
|
619
|
+
// csv.filename → "usage_none_<companyId>_2026-06-01_2026-08-01.csv"
|
|
620
|
+
// csv.content → first line:
|
|
621
|
+
// periodOffset,featureCode,featureName,value,startingOn,endingBefore,groupKey,groupValue,productLinkId,productLinkLabel,externalOrgId,includedQuantity,billableQuantity,quantityLimit,remaining,percentUsed,unitAmountCents,currency,estimatedOverageCents,projectedBillableQuantity,projectedOverageCents
|
|
622
|
+
```
|
|
623
|
+
|
|
624
|
+
#### `billing.listInvoices(companyIdOrInput, options?)`
|
|
625
|
+
|
|
626
|
+
List invoices for a company (excludes draft/void).
|
|
627
|
+
|
|
628
|
+
```typescript
|
|
629
|
+
const { invoices } = await os.billing.listInvoices("comp_xxx", { limit: 24 })
|
|
630
|
+
```
|
|
631
|
+
|
|
632
|
+
#### `billing.getPaymentMethod(companyIdOrInput)`
|
|
633
|
+
|
|
634
|
+
Payment method summary for the resolved billing profile.
|
|
635
|
+
|
|
636
|
+
```typescript
|
|
637
|
+
const { billingProfile } = await os.billing.getPaymentMethod("comp_xxx")
|
|
638
|
+
```
|
|
639
|
+
|
|
640
|
+
#### `billing.createPortalSession(input)`
|
|
641
|
+
|
|
642
|
+
Open Stripe Customer Portal for manage-billing flows. `returnUrl` must be on an allowed product host (`products.metadata.billing.returnUrlHosts`).
|
|
643
|
+
|
|
644
|
+
```typescript
|
|
645
|
+
const { url } = await os.billing.createPortalSession({
|
|
646
|
+
companyId: "comp_xxx",
|
|
647
|
+
returnUrl: "https://app.example.com/settings/billing",
|
|
648
|
+
})
|
|
649
|
+
|
|
650
|
+
// Redirect the user to url
|
|
651
|
+
```
|
|
652
|
+
|
|
653
|
+
#### `billing.resolveInvoicePaymentUrl(input)`
|
|
654
|
+
|
|
655
|
+
Resolve a hosted invoice payment URL for a specific invoice.
|
|
656
|
+
|
|
657
|
+
```typescript
|
|
658
|
+
const { hostedInvoiceUrl } = await os.billing.resolveInvoicePaymentUrl({
|
|
659
|
+
companyId: "comp_xxx",
|
|
660
|
+
invoiceId: "inv_xxx",
|
|
661
|
+
})
|
|
662
|
+
```
|
|
663
|
+
|
|
664
|
+
#### `billing.listPastDuePaymentUrls(companyIdOrInput)`
|
|
665
|
+
|
|
666
|
+
List overdue invoices with payment URLs.
|
|
667
|
+
|
|
668
|
+
```typescript
|
|
669
|
+
const { items } = await os.billing.listPastDuePaymentUrls("comp_xxx")
|
|
670
|
+
```
|
|
671
|
+
|
|
672
|
+
#### `billing.sendPastDueInvoice(input)`
|
|
673
|
+
|
|
674
|
+
Email a past-due invoice via Stripe (when collection method is `send_invoice`).
|
|
675
|
+
|
|
676
|
+
```typescript
|
|
677
|
+
await os.billing.sendPastDueInvoice({
|
|
678
|
+
companyId: "comp_xxx",
|
|
679
|
+
invoiceId: "inv_xxx",
|
|
680
|
+
})
|
|
681
|
+
```
|
|
682
|
+
|
|
683
|
+
#### `billing.syncCompanyFromStripe(input)`
|
|
684
|
+
|
|
685
|
+
Pull billing profile, subscriptions, and feature links from Stripe.
|
|
686
|
+
|
|
687
|
+
```typescript
|
|
688
|
+
const result = await os.billing.syncCompanyFromStripe({
|
|
689
|
+
companyId: "comp_xxx",
|
|
690
|
+
stripeCustomerId: "cus_xxx",
|
|
691
|
+
})
|
|
692
|
+
|
|
693
|
+
console.log({
|
|
694
|
+
subscriptionsSynced: result.subscriptionsSynced,
|
|
695
|
+
featuresLinked: result.featuresLinked,
|
|
696
|
+
})
|
|
697
|
+
```
|
|
698
|
+
|
|
699
|
+
#### `billing.relinkBillingProfile(input)`
|
|
700
|
+
|
|
701
|
+
Replace the company's billing profile with a new Stripe customer (destructive).
|
|
702
|
+
|
|
703
|
+
```typescript
|
|
704
|
+
await os.billing.relinkBillingProfile({
|
|
705
|
+
companyId: "comp_xxx",
|
|
706
|
+
stripeCustomerId: "cus_new_xxx",
|
|
707
|
+
})
|
|
708
|
+
```
|
|
709
|
+
|
|
710
|
+
#### `billing.ensureFeatureSubscription(input)`
|
|
711
|
+
|
|
712
|
+
Ensure a metered feature is linked to a Stripe subscription item.
|
|
713
|
+
|
|
714
|
+
```typescript
|
|
715
|
+
const result = await os.billing.ensureFeatureSubscription({
|
|
716
|
+
companyId: "comp_xxx",
|
|
717
|
+
featureCode: "ai_credit",
|
|
718
|
+
})
|
|
719
|
+
|
|
720
|
+
console.log(result.action) // 'already_linked' | 'linked_existing' | 'created_new' | 'failed'
|
|
721
|
+
```
|
|
722
|
+
|
|
723
|
+
#### `billing.setUsage(input)`
|
|
724
|
+
|
|
725
|
+
Set absolute usage (corrections / admin). Requires a `reason` for audit.
|
|
726
|
+
|
|
727
|
+
```typescript
|
|
728
|
+
const result = await os.billing.setUsage({
|
|
729
|
+
companyId: "comp_xxx",
|
|
730
|
+
featureCode: "messages",
|
|
731
|
+
absoluteValue: 420,
|
|
732
|
+
reason: "Reconcile after import",
|
|
733
|
+
})
|
|
734
|
+
|
|
735
|
+
console.log({
|
|
736
|
+
previous: result.previousUsage,
|
|
737
|
+
current: result.currentUsage,
|
|
738
|
+
delta: result.delta,
|
|
739
|
+
})
|
|
740
|
+
```
|
|
741
|
+
|
|
742
|
+
#### `billing.getUsageHistory(companyId, featureCode, options?)`
|
|
743
|
+
|
|
744
|
+
Raw usage events for audit/debug (not aggregated rollups).
|
|
745
|
+
|
|
746
|
+
```typescript
|
|
747
|
+
const events = await os.billing.getUsageHistory("comp_xxx", "messages", {
|
|
748
|
+
limit: 50,
|
|
749
|
+
eventType: "increment",
|
|
750
|
+
})
|
|
751
|
+
```
|
|
752
|
+
|
|
376
753
|
---
|
|
377
754
|
|
|
378
755
|
### Subscriptions & Tiers
|
|
@@ -813,6 +1190,7 @@ try {
|
|
|
813
1190
|
|
|
814
1191
|
## Related Documentation
|
|
815
1192
|
|
|
1193
|
+
- **[Product billing API](../../docs/engineering/product-billing-api.md)** — Billing read APIs, `externalOrgId` scoping, and preview test tenants
|
|
816
1194
|
- **[QUICKSTART.md](./QUICKSTART.md)** — Quick publishing & installation guide
|
|
817
1195
|
- **[PUBLISHING.md](./PUBLISHING.md)** — Detailed publishing instructions
|
|
818
1196
|
- **[test-sdk.ts](./test-sdk.ts)** — Complete usage examples
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { AppRouter } from '@chatarmin/api/trpc-types';
|
|
2
|
+
import type { createTRPCClient } from '@trpc/client';
|
|
3
|
+
import type { BillingClient } from './types.js';
|
|
4
|
+
type SdkClient = ReturnType<typeof createTRPCClient<AppRouter>>;
|
|
5
|
+
type SdkCall = <T>(operation: string, action: () => Promise<T>) => Promise<T>;
|
|
6
|
+
export declare function createBillingClient(client: SdkClient, call: SdkCall): BillingClient;
|
|
7
|
+
export {};
|
|
8
|
+
//# sourceMappingURL=client.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../src/billing/client.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,2BAA2B,CAAA;AAC1D,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAA;AACpD,OAAO,KAAK,EACV,aAAa,EAyBd,MAAM,YAAY,CAAA;AAsCnB,KAAK,SAAS,GAAG,UAAU,CAAC,OAAO,gBAAgB,CAAC,SAAS,CAAC,CAAC,CAAA;AAC/D,KAAK,OAAO,GAAG,CAAC,CAAC,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,KAAK,OAAO,CAAC,CAAC,CAAC,CAAA;AAE7E,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,SAAS,EAAE,IAAI,EAAE,OAAO,GAAG,aAAa,CAiHnF"}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
function mergeBillingInput(companyIdOrInput, maybeOptions) {
|
|
2
|
+
if (typeof companyIdOrInput === 'string') {
|
|
3
|
+
return { companyId: companyIdOrInput, ...(maybeOptions ?? {}) };
|
|
4
|
+
}
|
|
5
|
+
return { ...companyIdOrInput, ...(maybeOptions ?? {}) };
|
|
6
|
+
}
|
|
7
|
+
function resolveBillingCompanyScopeInput(companyIdOrInput, maybeOptions) {
|
|
8
|
+
return mergeBillingInput(companyIdOrInput, maybeOptions);
|
|
9
|
+
}
|
|
10
|
+
function resolveBillingUsageInput(companyIdOrInput, maybeOptions) {
|
|
11
|
+
return mergeBillingInput(companyIdOrInput, maybeOptions);
|
|
12
|
+
}
|
|
13
|
+
function resolveBillingExportInput(companyIdOrInput, maybeOptions) {
|
|
14
|
+
return {
|
|
15
|
+
...mergeBillingInput(companyIdOrInput, maybeOptions),
|
|
16
|
+
format: 'csv',
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
export function createBillingClient(client, call) {
|
|
20
|
+
return {
|
|
21
|
+
setUsage: (input) => call('billing.setUsage', () => client.billing.setUsage.mutate({
|
|
22
|
+
...input,
|
|
23
|
+
source: 'api',
|
|
24
|
+
})),
|
|
25
|
+
getUsageHistory: (companyId, featureCode, options) => call('billing.getUsageHistory', () => client.billing.getUsageHistory.query({
|
|
26
|
+
companyId,
|
|
27
|
+
featureCode,
|
|
28
|
+
...options,
|
|
29
|
+
})),
|
|
30
|
+
claimCheckout: (input) => call('billing.claimCheckout', () => client.billing.claimCheckout.mutate(input)),
|
|
31
|
+
linkSubscription: (input) => call('billing.linkSubscription', () => client.billing.linkSubscription.mutate(input)),
|
|
32
|
+
getStatus: (companyIdOrInput) => call('billing.getStatus', () => client.billing.getStatus.query(resolveBillingCompanyScopeInput(companyIdOrInput))),
|
|
33
|
+
getOverview: (input) => call('billing.getOverview', () => client.billing.getOverview.query(input)),
|
|
34
|
+
getUsage: (companyIdOrInput, maybeOptions) => call('billing.getUsage', () => client.billing.getUsage.query(resolveBillingUsageInput(companyIdOrInput, maybeOptions))),
|
|
35
|
+
exportUsage: (companyIdOrInput, maybeOptions) => call('billing.exportUsage', () => client.billing.exportUsage.query(resolveBillingExportInput(companyIdOrInput, maybeOptions))),
|
|
36
|
+
listInvoices: (companyIdOrInput, options) => call('billing.listInvoices', () => client.billing.listInvoices.query(resolveBillingCompanyScopeInput(companyIdOrInput, options))),
|
|
37
|
+
getPaymentMethod: (companyIdOrInput) => call('billing.getPaymentMethod', () => client.billing.getPaymentMethod.query(resolveBillingCompanyScopeInput(companyIdOrInput))),
|
|
38
|
+
createPortalSession: (input) => call('billing.createPortalSession', () => client.billing.createPortalSession.mutate(input)),
|
|
39
|
+
resolveInvoicePaymentUrl: (input) => call('billing.resolveProductInvoicePaymentUrl', () => client.billing.resolveProductInvoicePaymentUrl.mutate(input)),
|
|
40
|
+
listPastDuePaymentUrls: (companyIdOrInput) => call('billing.listProductPastDuePaymentUrls', () => client.billing.listProductPastDuePaymentUrls.query(resolveBillingCompanyScopeInput(companyIdOrInput))),
|
|
41
|
+
sendPastDueInvoice: (input) => call('billing.sendProductPastDueInvoice', () => client.billing.sendProductPastDueInvoice.mutate(input)),
|
|
42
|
+
syncCompanyFromStripe: (input) => call('billing.syncCompanyFromStripe', () => client.billing.syncCompanyFromStripe.mutate(input)),
|
|
43
|
+
relinkBillingProfile: (input) => call('billing.relinkBillingProfile', () => client.billing.relinkBillingProfile.mutate(input)),
|
|
44
|
+
ensureFeatureSubscription: (input) => call('billing.ensureFeatureSubscription', () => client.billing.ensureFeatureSubscription.mutate(input)),
|
|
45
|
+
};
|
|
46
|
+
}
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
export { createBillingClient } from './client.js';
|
|
2
|
+
export type { BillingClient, BillingInvoiceSummary, BillingOverview, BillingProfileSummary, BillingStatus, BillingStatusSubscription, BillingSubscriptionSummary, BillingUsageItem, ClaimCheckoutInput, ClaimCheckoutResult, CreateBillingPortalSessionInput, EnsureFeatureSubscriptionInput, EnsureFeatureSubscriptionResult, ExportUsageInput, ExportUsageResult, GetBillingOverviewInput, GetUsageInput, LinkSubscriptionInput, LinkSubscriptionResult, ListBillingInvoicesOptions, ProductBillingCompanyScopeInput, PeriodOffsetRange, RelinkBillingProfileInput, ResolveInvoicePaymentUrlInput, SendPastDueInvoiceInput, SendPastDueInvoiceResult, SetUsageInput, SyncCompanyFromStripeInput, SyncCompanyFromStripeResult, UsageAggregate, UsageCycleBucket, UsageCycleSummary, UsageGroupBy, UsagePoint, UsageQueryResult, UsageRecord, UsageSubscriptionItem, UsageResult, UsageWindowSize, } from './types.js';
|
|
3
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/billing/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAA;AACjD,YAAY,EACV,aAAa,EACb,qBAAqB,EACrB,eAAe,EACf,qBAAqB,EACrB,aAAa,EACb,yBAAyB,EACzB,0BAA0B,EAC1B,gBAAgB,EAChB,kBAAkB,EAClB,mBAAmB,EACnB,+BAA+B,EAC/B,8BAA8B,EAC9B,+BAA+B,EAC/B,gBAAgB,EAChB,iBAAiB,EACjB,uBAAuB,EACvB,aAAa,EACb,qBAAqB,EACrB,sBAAsB,EACtB,0BAA0B,EAC1B,+BAA+B,EAC/B,iBAAiB,EACjB,yBAAyB,EACzB,6BAA6B,EAC7B,uBAAuB,EACvB,wBAAwB,EACxB,aAAa,EACb,0BAA0B,EAC1B,2BAA2B,EAC3B,cAAc,EACd,gBAAgB,EAChB,iBAAiB,EACjB,YAAY,EACZ,UAAU,EACV,gBAAgB,EAChB,WAAW,EACX,qBAAqB,EACrB,WAAW,EACX,eAAe,GAChB,MAAM,YAAY,CAAA"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { createBillingClient } from './client.js';
|