@soulcraft/sdk 1.0.0 → 1.1.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/dist/index.d.ts +6 -5
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/modules/billing/firestore-provider.d.ts +60 -0
- package/dist/modules/billing/firestore-provider.d.ts.map +1 -0
- package/dist/modules/billing/firestore-provider.js +314 -0
- package/dist/modules/billing/firestore-provider.js.map +1 -0
- package/dist/modules/billing/index.d.ts +46 -0
- package/dist/modules/billing/index.d.ts.map +1 -0
- package/dist/modules/billing/index.js +149 -0
- package/dist/modules/billing/index.js.map +1 -0
- package/dist/modules/billing/local-provider.d.ts +38 -0
- package/dist/modules/billing/local-provider.d.ts.map +1 -0
- package/dist/modules/billing/local-provider.js +242 -0
- package/dist/modules/billing/local-provider.js.map +1 -0
- package/dist/modules/billing/types.d.ts +322 -3
- package/dist/modules/billing/types.d.ts.map +1 -1
- package/dist/modules/billing/types.js +21 -2
- package/dist/modules/billing/types.js.map +1 -1
- package/dist/modules/billing/usage-buffer.d.ts +72 -0
- package/dist/modules/billing/usage-buffer.d.ts.map +1 -0
- package/dist/modules/billing/usage-buffer.js +141 -0
- package/dist/modules/billing/usage-buffer.js.map +1 -0
- package/dist/modules/formats/types.d.ts +65 -3
- package/dist/modules/formats/types.d.ts.map +1 -1
- package/dist/modules/formats/types.js +40 -3
- package/dist/modules/formats/types.js.map +1 -1
- package/dist/modules/formats/wdoc.d.ts +263 -0
- package/dist/modules/formats/wdoc.d.ts.map +1 -0
- package/dist/modules/formats/wdoc.js +21 -0
- package/dist/modules/formats/wdoc.js.map +1 -0
- package/dist/modules/formats/wquiz.d.ts +122 -0
- package/dist/modules/formats/wquiz.d.ts.map +1 -0
- package/dist/modules/formats/wquiz.js +23 -0
- package/dist/modules/formats/wquiz.js.map +1 -0
- package/dist/modules/formats/wslide.d.ts +130 -0
- package/dist/modules/formats/wslide.d.ts.map +1 -0
- package/dist/modules/formats/wslide.js +23 -0
- package/dist/modules/formats/wslide.js.map +1 -0
- package/dist/modules/formats/wviz.d.ts +114 -0
- package/dist/modules/formats/wviz.d.ts.map +1 -0
- package/dist/modules/formats/wviz.js +21 -0
- package/dist/modules/formats/wviz.js.map +1 -0
- package/dist/modules/kits/index.d.ts +41 -0
- package/dist/modules/kits/index.d.ts.map +1 -0
- package/dist/modules/kits/index.js +85 -0
- package/dist/modules/kits/index.js.map +1 -0
- package/dist/modules/kits/types.d.ts +107 -3
- package/dist/modules/kits/types.d.ts.map +1 -1
- package/dist/modules/kits/types.js +15 -2
- package/dist/modules/kits/types.js.map +1 -1
- package/dist/modules/license/index.d.ts +53 -0
- package/dist/modules/license/index.d.ts.map +1 -0
- package/dist/modules/license/index.js +233 -0
- package/dist/modules/license/index.js.map +1 -0
- package/dist/modules/license/types.d.ts +222 -3
- package/dist/modules/license/types.d.ts.map +1 -1
- package/dist/modules/license/types.js +21 -2
- package/dist/modules/license/types.js.map +1 -1
- package/dist/modules/notifications/index.d.ts +40 -0
- package/dist/modules/notifications/index.d.ts.map +1 -0
- package/dist/modules/notifications/index.js +280 -0
- package/dist/modules/notifications/index.js.map +1 -0
- package/dist/modules/notifications/types.d.ts +152 -3
- package/dist/modules/notifications/types.d.ts.map +1 -1
- package/dist/modules/notifications/types.js +21 -2
- package/dist/modules/notifications/types.js.map +1 -1
- package/dist/server/create-sdk.d.ts +4 -0
- package/dist/server/create-sdk.d.ts.map +1 -1
- package/dist/server/create-sdk.js +19 -26
- package/dist/server/create-sdk.js.map +1 -1
- package/dist/server/index.d.ts +6 -0
- package/dist/server/index.d.ts.map +1 -1
- package/dist/server/index.js +5 -0
- package/dist/server/index.js.map +1 -1
- package/dist/types.d.ts +13 -7
- package/dist/types.d.ts.map +1 -1
- package/package.json +16 -3
package/dist/index.d.ts
CHANGED
|
@@ -25,9 +25,10 @@ export type { AiModule, AiCompleteOptions, AiCompleteResult, AiStreamOptions, Ai
|
|
|
25
25
|
export { AI_MODELS } from './modules/ai/types.js';
|
|
26
26
|
export type { EventsModule, SoulcraftEventMap, SoulcraftEventName, VfsWriteEvent, VfsDeleteEvent, VfsRenameEvent, } from './modules/events/types.js';
|
|
27
27
|
export type { SkillsModule, Skill, SkillListOptions, SkillInstallOptions, } from './modules/skills/types.js';
|
|
28
|
-
export type
|
|
29
|
-
export type
|
|
30
|
-
export type
|
|
31
|
-
export
|
|
32
|
-
export type
|
|
28
|
+
export type { LicenseResult, LicenseModule, LicensePlanTier, LicenseFeatures, AICreditCheckResult, AIUsageRecord, AIProviderConfig, } from './modules/license/types.js';
|
|
29
|
+
export type { KitsModule, SoulcraftKitConfig, } from './modules/kits/types.js';
|
|
30
|
+
export type { WvizDocument, WvizEntity, WvizEdge, WvizSnapshot, WvizViewType, WdocDocument, WdocMeta, WdocBlock, WdocInlineContent, WdocTextNode, WdocMark, WdocParagraphBlock, WdocHeadingBlock, WdocBlockquoteBlock, WdocCodeBlock, WdocBulletListBlock, WdocOrderedListBlock, WdocListItemBlock, WdocTaskListBlock, WdocTaskItemBlock, WdocImageBlock, WdocVideoBlock, WdocIconBlock, WdocSvgEmbedBlock, WdocEmbedBlock, WdocTableBlock, WdocTableRowBlock, WdocTableCellBlock, WdocTableHeaderBlock, WdocHorizontalRuleBlock, WdocCalloutBlock, WslideDocument, WslideSlide, WslideBackground, WslideLayout, WslideTransition, WslideConfig, WquizDocument, WquizQuestion, WquizQuestionType, WquizAnswer, WquizScoringRules, SoulcraftFormat, SoulcraftFormatDocument, } from './modules/formats/types.js';
|
|
31
|
+
export { SOULCRAFT_FORMATS } from './modules/formats/types.js';
|
|
32
|
+
export type { BillingModule, BillingPlanType, SubscriptionStatus, ModelUsage, UsageData, UsageStatus, SubscriptionData, LimitCheckResult, BatchIncrementData, CreateCheckoutSessionOptions, CreatePortalSessionOptions, } from './modules/billing/types.js';
|
|
33
|
+
export type { NotificationsModule, Notification, NotificationResult, NotificationSender, EmailNotification, SmsNotification, } from './modules/notifications/types.js';
|
|
33
34
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,YAAY,EAAE,YAAY,EAAE,UAAU,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAA;AAG9F,YAAY,EAAE,eAAe,EAAE,MAAM,2BAA2B,CAAA;AAChE,YAAY,EACV,MAAM,EACN,QAAQ,EACR,MAAM,EACN,SAAS,EACT,YAAY,EACZ,YAAY,EACZ,UAAU,EACV,YAAY,EACZ,mBAAmB,EACnB,iBAAiB,EACjB,eAAe,EACf,cAAc,EACd,gBAAgB,EAChB,QAAQ,EACR,QAAQ,EACR,iBAAiB,EACjB,kBAAkB,EAClB,oBAAoB,GACrB,MAAM,2BAA2B,CAAA;AAClC,YAAY,EACV,4BAA4B,EAC5B,qBAAqB,GACtB,MAAM,0BAA0B,CAAA;AACjC,OAAO,EACL,qBAAqB,EACrB,qBAAqB,GACtB,MAAM,0BAA0B,CAAA;AACjC,OAAO,EACL,QAAQ,EACR,oBAAoB,EACpB,eAAe,EACf,YAAY,EACZ,iBAAiB,EACjB,WAAW,EACX,sBAAsB,GACvB,MAAM,4BAA4B,CAAA;AAGnC,YAAY,EAAE,SAAS,EAAE,MAAM,wBAAwB,CAAA;AAGvD,YAAY,EAAE,cAAc,EAAE,MAAM,6BAA6B,CAAA;AAGjE,YAAY,EACV,UAAU,EACV,UAAU,EACV,WAAW,EACX,iBAAiB,EACjB,YAAY,EACZ,qBAAqB,EACrB,cAAc,EACd,YAAY,GACb,MAAM,yBAAyB,CAAA;AAGhC,YAAY,EACV,UAAU,EACV,YAAY,EACZ,qBAAqB,EACrB,qBAAqB,EACrB,mBAAmB,EACnB,oBAAoB,EACpB,gBAAgB,EAChB,gBAAgB,EAChB,QAAQ,GACT,MAAM,yBAAyB,CAAA;AAChC,OAAO,EACL,qBAAqB,EACrB,wBAAwB,EACxB,gBAAgB,EAChB,WAAW,EACX,mBAAmB,GACpB,MAAM,0BAA0B,CAAA;AAGjC,YAAY,EACV,QAAQ,EACR,iBAAiB,EACjB,gBAAgB,EAChB,eAAe,EACf,aAAa,EACb,UAAU,EACV,SAAS,EACT,cAAc,EACd,MAAM,EACN,OAAO,GACR,MAAM,uBAAuB,CAAA;AAC9B,OAAO,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAA;AAGjD,YAAY,EACV,YAAY,EACZ,iBAAiB,EACjB,kBAAkB,EAClB,aAAa,EACb,cAAc,EACd,cAAc,GACf,MAAM,2BAA2B,CAAA;AAGlC,YAAY,EACV,YAAY,EACZ,KAAK,EACL,gBAAgB,EAChB,mBAAmB,GACpB,MAAM,2BAA2B,CAAA;AAGlC,mBAAmB,4BAA4B,CAAA;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
package/dist/index.js
CHANGED
|
@@ -14,4 +14,5 @@ export { createCapabilityToken, verifyCapabilityToken, } from './modules/brainy/
|
|
|
14
14
|
export { SDKError, SDKDisconnectedError, SDKTimeoutError, SDKAuthError, SDKForbiddenError, SDKRpcError, SDKMethodNotFoundError, } from './modules/brainy/errors.js';
|
|
15
15
|
export { SOULCRAFT_USER_FIELDS, SOULCRAFT_SESSION_CONFIG, computeEmailHash, getAuthMode, getOIDCClientConfig, } from './modules/auth/config.js';
|
|
16
16
|
export { AI_MODELS } from './modules/ai/types.js';
|
|
17
|
+
export { SOULCRAFT_FORMATS } from './modules/formats/types.js';
|
|
17
18
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AA8BH,OAAO,EACL,qBAAqB,EACrB,qBAAqB,GACtB,MAAM,0BAA0B,CAAA;AACjC,OAAO,EACL,QAAQ,EACR,oBAAoB,EACpB,eAAe,EACf,YAAY,EACZ,iBAAiB,EACjB,WAAW,EACX,sBAAsB,GACvB,MAAM,4BAA4B,CAAA;AAgCnC,OAAO,EACL,qBAAqB,EACrB,wBAAwB,EACxB,gBAAgB,EAChB,WAAW,EACX,mBAAmB,GACpB,MAAM,0BAA0B,CAAA;AAejC,OAAO,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAA"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AA8BH,OAAO,EACL,qBAAqB,EACrB,qBAAqB,GACtB,MAAM,0BAA0B,CAAA;AACjC,OAAO,EACL,QAAQ,EACR,oBAAoB,EACpB,eAAe,EACf,YAAY,EACZ,iBAAiB,EACjB,WAAW,EACX,sBAAsB,GACvB,MAAM,4BAA4B,CAAA;AAgCnC,OAAO,EACL,qBAAqB,EACrB,wBAAwB,EACxB,gBAAgB,EAChB,WAAW,EACX,mBAAmB,GACpB,MAAM,0BAA0B,CAAA;AAejC,OAAO,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAA;AAoFjD,OAAO,EAAE,iBAAiB,EAAE,MAAM,4BAA4B,CAAA"}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module billing/firestore-provider
|
|
3
|
+
* @description Firestore-backed billing provider for production deployments.
|
|
4
|
+
*
|
|
5
|
+
* Reads subscription and license data from the `licenses` Firestore collection
|
|
6
|
+
* managed by Portal (`soulcraft.com`). Workshop, Venue, and Academy all write
|
|
7
|
+
* usage counters to the same document so Portal's Admin can see cross-product usage.
|
|
8
|
+
*
|
|
9
|
+
* This provider is activated when `FIREBASE_SERVICE_ACCOUNT_KEY` is set in the
|
|
10
|
+
* environment. `firebase-admin` is loaded lazily via dynamic import so that the
|
|
11
|
+
* SDK does not require the package when using the local provider.
|
|
12
|
+
*
|
|
13
|
+
* **Firestore document structure:**
|
|
14
|
+
* ```
|
|
15
|
+
* licenses/{docId}
|
|
16
|
+
* email: string (user email — used to look up the doc)
|
|
17
|
+
* product: 'workshop' | 'venue' | 'academy'
|
|
18
|
+
* status: 'active' | 'past_due' | 'canceled' | 'trialing'
|
|
19
|
+
* plan: 'standard' | 'byok'
|
|
20
|
+
* credits: {
|
|
21
|
+
* monthlyIncluded: number
|
|
22
|
+
* monthlyUsed: number (SDK increments this)
|
|
23
|
+
* topUpBalance: number (SDK decrements this)
|
|
24
|
+
* inputTokens: number
|
|
25
|
+
* outputTokens: number
|
|
26
|
+
* byModel: Record<model, { input, output }>
|
|
27
|
+
* periodStart: Timestamp
|
|
28
|
+
* periodEnd: Timestamp
|
|
29
|
+
* lastUpdated: Timestamp
|
|
30
|
+
* }
|
|
31
|
+
* stripeCustomerId: string
|
|
32
|
+
* stripeSubscriptionId: string
|
|
33
|
+
* cancelAtPeriodEnd: boolean
|
|
34
|
+
* ```
|
|
35
|
+
*
|
|
36
|
+
* Note: The `userId` passed to provider methods is the user's email address when
|
|
37
|
+
* using Firestore, since the `licenses` collection is keyed by email. Products
|
|
38
|
+
* must pass `user.email` (not `user.id`) when constructing the SDK in Firestore mode.
|
|
39
|
+
*/
|
|
40
|
+
import type { BillingProvider, UsageData, SubscriptionData, LimitCheckResult, UsageStatus, BatchIncrementData } from './types.js';
|
|
41
|
+
/**
|
|
42
|
+
* Firestore-backed billing provider for production deployments.
|
|
43
|
+
*
|
|
44
|
+
* The `userId` parameter on all methods must be the user's **email address**,
|
|
45
|
+
* since the Firestore `licenses` collection is indexed by email.
|
|
46
|
+
*/
|
|
47
|
+
export declare class FirestoreBillingProvider implements BillingProvider {
|
|
48
|
+
checkLimit(userId: string, hasPersonalKey: boolean): Promise<LimitCheckResult>;
|
|
49
|
+
getUsage(userId: string): Promise<UsageData>;
|
|
50
|
+
getUsageStatus(userId: string, hasPersonalKey: boolean): Promise<UsageStatus>;
|
|
51
|
+
incrementUsage(userId: string, inputTokens: number, outputTokens: number, model: string, useTopUp: boolean): Promise<void>;
|
|
52
|
+
batchIncrementUsage(userId: string, batch: BatchIncrementData): Promise<void>;
|
|
53
|
+
getSubscription(userId: string): Promise<SubscriptionData | undefined>;
|
|
54
|
+
hasActiveSubscription(userId: string): Promise<boolean>;
|
|
55
|
+
getTopUpBalance(userId: string): Promise<number>;
|
|
56
|
+
canAccessPaidFeature(userId: string): Promise<boolean>;
|
|
57
|
+
private _toSubscription;
|
|
58
|
+
private _resetPeriod;
|
|
59
|
+
}
|
|
60
|
+
//# sourceMappingURL=firestore-provider.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"firestore-provider.d.ts","sourceRoot":"","sources":["../../../src/modules/billing/firestore-provider.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAsCG;AAEH,OAAO,KAAK,EACV,eAAe,EACf,SAAS,EACT,gBAAgB,EAChB,gBAAgB,EAChB,WAAW,EACX,kBAAkB,EACnB,MAAM,YAAY,CAAA;AAkJnB;;;;;GAKG;AACH,qBAAa,wBAAyB,YAAW,eAAe;IACxD,UAAU,CAAC,MAAM,EAAE,MAAM,EAAE,cAAc,EAAE,OAAO,GAAG,OAAO,CAAC,gBAAgB,CAAC;IA8B9E,QAAQ,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,SAAS,CAAC;IAiB5C,cAAc,CAAC,MAAM,EAAE,MAAM,EAAE,cAAc,EAAE,OAAO,GAAG,OAAO,CAAC,WAAW,CAAC;IAgC7E,cAAc,CAAC,MAAM,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC;IAmB1H,mBAAmB,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,kBAAkB,GAAG,OAAO,CAAC,IAAI,CAAC;IAqB7E,eAAe,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,gBAAgB,GAAG,SAAS,CAAC;IAMtE,qBAAqB,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAOvD,eAAe,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAOhD,oBAAoB,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAM5D,OAAO,CAAC,eAAe;YA2BT,YAAY;CAe3B"}
|
|
@@ -0,0 +1,314 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module billing/firestore-provider
|
|
3
|
+
* @description Firestore-backed billing provider for production deployments.
|
|
4
|
+
*
|
|
5
|
+
* Reads subscription and license data from the `licenses` Firestore collection
|
|
6
|
+
* managed by Portal (`soulcraft.com`). Workshop, Venue, and Academy all write
|
|
7
|
+
* usage counters to the same document so Portal's Admin can see cross-product usage.
|
|
8
|
+
*
|
|
9
|
+
* This provider is activated when `FIREBASE_SERVICE_ACCOUNT_KEY` is set in the
|
|
10
|
+
* environment. `firebase-admin` is loaded lazily via dynamic import so that the
|
|
11
|
+
* SDK does not require the package when using the local provider.
|
|
12
|
+
*
|
|
13
|
+
* **Firestore document structure:**
|
|
14
|
+
* ```
|
|
15
|
+
* licenses/{docId}
|
|
16
|
+
* email: string (user email — used to look up the doc)
|
|
17
|
+
* product: 'workshop' | 'venue' | 'academy'
|
|
18
|
+
* status: 'active' | 'past_due' | 'canceled' | 'trialing'
|
|
19
|
+
* plan: 'standard' | 'byok'
|
|
20
|
+
* credits: {
|
|
21
|
+
* monthlyIncluded: number
|
|
22
|
+
* monthlyUsed: number (SDK increments this)
|
|
23
|
+
* topUpBalance: number (SDK decrements this)
|
|
24
|
+
* inputTokens: number
|
|
25
|
+
* outputTokens: number
|
|
26
|
+
* byModel: Record<model, { input, output }>
|
|
27
|
+
* periodStart: Timestamp
|
|
28
|
+
* periodEnd: Timestamp
|
|
29
|
+
* lastUpdated: Timestamp
|
|
30
|
+
* }
|
|
31
|
+
* stripeCustomerId: string
|
|
32
|
+
* stripeSubscriptionId: string
|
|
33
|
+
* cancelAtPeriodEnd: boolean
|
|
34
|
+
* ```
|
|
35
|
+
*
|
|
36
|
+
* Note: The `userId` passed to provider methods is the user's email address when
|
|
37
|
+
* using Firestore, since the `licenses` collection is keyed by email. Products
|
|
38
|
+
* must pass `user.email` (not `user.id`) when constructing the SDK in Firestore mode.
|
|
39
|
+
*/
|
|
40
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
41
|
+
// Lazy loader
|
|
42
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
43
|
+
let _db;
|
|
44
|
+
let _FieldValue;
|
|
45
|
+
async function _getFirestore() {
|
|
46
|
+
if (_db && _FieldValue)
|
|
47
|
+
return { db: _db, FieldValue: _FieldValue };
|
|
48
|
+
const key = process.env['FIREBASE_SERVICE_ACCOUNT_KEY'];
|
|
49
|
+
if (!key)
|
|
50
|
+
throw new Error('FIREBASE_SERVICE_ACCOUNT_KEY is not set — cannot use Firestore billing provider');
|
|
51
|
+
// firebase-admin is an optional peer dependency — loaded at runtime only when configured.
|
|
52
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
53
|
+
const admin = await Function('return import("firebase-admin")')();
|
|
54
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
55
|
+
const { getFirestore, FieldValue } = await Function('return import("firebase-admin/firestore")')();
|
|
56
|
+
if (!admin.apps?.length) {
|
|
57
|
+
admin.initializeApp({
|
|
58
|
+
credential: admin.credential.cert(JSON.parse(Buffer.from(key, 'base64').toString('utf-8'))),
|
|
59
|
+
preferRest: true, // Use REST transport — required for Bun compatibility (avoids gRPC).
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
_db = getFirestore();
|
|
63
|
+
_FieldValue = FieldValue;
|
|
64
|
+
return { db: _db, FieldValue: _FieldValue };
|
|
65
|
+
}
|
|
66
|
+
const _docCache = new Map();
|
|
67
|
+
const DOC_CACHE_TTL_MS = 5 * 60 * 1000;
|
|
68
|
+
async function _getLicenseDoc(email) {
|
|
69
|
+
const cached = _docCache.get(email);
|
|
70
|
+
if (cached && Date.now() < cached.expiresAt)
|
|
71
|
+
return cached;
|
|
72
|
+
const { db } = await _getFirestore();
|
|
73
|
+
const snap = await db.collection('licenses')
|
|
74
|
+
.where('email', '==', email.toLowerCase())
|
|
75
|
+
.where('status', 'in', ['active', 'trialing', 'past_due'])
|
|
76
|
+
.get();
|
|
77
|
+
if (!snap.docs.length)
|
|
78
|
+
return null;
|
|
79
|
+
const doc = snap.docs[0];
|
|
80
|
+
const data = doc.data() ?? {};
|
|
81
|
+
const result = { data, docId: doc.id, ref: doc.ref, expiresAt: Date.now() + DOC_CACHE_TTL_MS };
|
|
82
|
+
_docCache.set(email, result);
|
|
83
|
+
return result;
|
|
84
|
+
}
|
|
85
|
+
function _invalidateCache(email) {
|
|
86
|
+
_docCache.delete(email);
|
|
87
|
+
}
|
|
88
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
89
|
+
// Period helpers
|
|
90
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
91
|
+
function _toISO(ts) {
|
|
92
|
+
if (!ts)
|
|
93
|
+
return undefined;
|
|
94
|
+
if (typeof ts === 'string')
|
|
95
|
+
return ts;
|
|
96
|
+
// Firestore Timestamp object
|
|
97
|
+
if (typeof ts === 'object' && ts !== null && 'toDate' in ts) {
|
|
98
|
+
return ts.toDate().toISOString();
|
|
99
|
+
}
|
|
100
|
+
return undefined;
|
|
101
|
+
}
|
|
102
|
+
function _currentPeriod() {
|
|
103
|
+
const d = new Date();
|
|
104
|
+
return `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, '0')}`;
|
|
105
|
+
}
|
|
106
|
+
function _shouldReset(periodEnd) {
|
|
107
|
+
const iso = _toISO(periodEnd);
|
|
108
|
+
if (!iso)
|
|
109
|
+
return true;
|
|
110
|
+
return new Date() >= new Date(iso);
|
|
111
|
+
}
|
|
112
|
+
const FREE_TIER_LIMIT = 100;
|
|
113
|
+
const MODEL_PRICING = {
|
|
114
|
+
'claude-haiku-4-5-20251001': { input: 0.80, output: 4.00 },
|
|
115
|
+
'claude-sonnet-4-6': { input: 3.00, output: 15.00 },
|
|
116
|
+
'claude-opus-4-6': { input: 15.00, output: 75.00 },
|
|
117
|
+
};
|
|
118
|
+
function _estimateCostUSD(byModel) {
|
|
119
|
+
let total = 0;
|
|
120
|
+
for (const [model, counts] of Object.entries(byModel)) {
|
|
121
|
+
const pricing = MODEL_PRICING[model];
|
|
122
|
+
if (!pricing)
|
|
123
|
+
continue;
|
|
124
|
+
total += (counts.input / 1_000_000) * pricing.input;
|
|
125
|
+
total += (counts.output / 1_000_000) * pricing.output;
|
|
126
|
+
}
|
|
127
|
+
return Math.round(total * 10_000) / 10_000;
|
|
128
|
+
}
|
|
129
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
130
|
+
// Provider implementation
|
|
131
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
132
|
+
/**
|
|
133
|
+
* Firestore-backed billing provider for production deployments.
|
|
134
|
+
*
|
|
135
|
+
* The `userId` parameter on all methods must be the user's **email address**,
|
|
136
|
+
* since the Firestore `licenses` collection is indexed by email.
|
|
137
|
+
*/
|
|
138
|
+
export class FirestoreBillingProvider {
|
|
139
|
+
async checkLimit(userId, hasPersonalKey) {
|
|
140
|
+
if (hasPersonalKey) {
|
|
141
|
+
return { ok: true, remaining: Infinity, topUpBalance: 0, totalAvailable: Infinity, useTopUp: false };
|
|
142
|
+
}
|
|
143
|
+
const doc = await _getLicenseDoc(userId);
|
|
144
|
+
if (!doc) {
|
|
145
|
+
// No license — free tier.
|
|
146
|
+
return { ok: false, remaining: 0, topUpBalance: 0, totalAvailable: 0, useTopUp: false, reason: 'no_subscription' };
|
|
147
|
+
}
|
|
148
|
+
const credits = doc.data['credits'] ?? {};
|
|
149
|
+
const monthlyIncluded = credits['monthlyIncluded'] ?? FREE_TIER_LIMIT;
|
|
150
|
+
const monthlyUsed = credits['monthlyUsed'] ?? 0;
|
|
151
|
+
const topUpBalance = credits['topUpBalance'] ?? 0;
|
|
152
|
+
const periodEnd = credits['periodEnd'];
|
|
153
|
+
if (_shouldReset(periodEnd)) {
|
|
154
|
+
await this._resetPeriod(doc);
|
|
155
|
+
return { ok: true, remaining: monthlyIncluded, topUpBalance, totalAvailable: monthlyIncluded + topUpBalance, useTopUp: false };
|
|
156
|
+
}
|
|
157
|
+
const remaining = Math.max(0, monthlyIncluded - monthlyUsed);
|
|
158
|
+
const totalAvailable = remaining + topUpBalance;
|
|
159
|
+
if (remaining > 0)
|
|
160
|
+
return { ok: true, remaining, topUpBalance, totalAvailable, useTopUp: false };
|
|
161
|
+
if (topUpBalance > 0)
|
|
162
|
+
return { ok: true, remaining: 0, topUpBalance, totalAvailable, useTopUp: true };
|
|
163
|
+
return { ok: false, remaining: 0, topUpBalance: 0, totalAvailable: 0, useTopUp: false, reason: 'limit_reached' };
|
|
164
|
+
}
|
|
165
|
+
async getUsage(userId) {
|
|
166
|
+
const doc = await _getLicenseDoc(userId);
|
|
167
|
+
if (!doc) {
|
|
168
|
+
return { currentPeriod: _currentPeriod(), periodEnd: new Date(Date.now() + 30 * 24 * 60 * 60 * 1000).toISOString(), messagesUsed: 0, inputTokens: 0, outputTokens: 0, byModel: {}, lastUpdated: new Date().toISOString() };
|
|
169
|
+
}
|
|
170
|
+
const credits = doc.data['credits'] ?? {};
|
|
171
|
+
return {
|
|
172
|
+
currentPeriod: _currentPeriod(),
|
|
173
|
+
periodEnd: _toISO(credits['periodEnd']) ?? new Date(Date.now() + 30 * 24 * 60 * 60 * 1000).toISOString(),
|
|
174
|
+
messagesUsed: credits['monthlyUsed'] ?? 0,
|
|
175
|
+
inputTokens: credits['inputTokens'] ?? 0,
|
|
176
|
+
outputTokens: credits['outputTokens'] ?? 0,
|
|
177
|
+
byModel: credits['byModel'] ?? {},
|
|
178
|
+
lastUpdated: _toISO(credits['lastUpdated']) ?? new Date().toISOString(),
|
|
179
|
+
};
|
|
180
|
+
}
|
|
181
|
+
async getUsageStatus(userId, hasPersonalKey) {
|
|
182
|
+
const usage = await this.getUsage(userId);
|
|
183
|
+
const doc = await _getLicenseDoc(userId);
|
|
184
|
+
const credits = doc?.data['credits'] ?? {};
|
|
185
|
+
const topUpBalance = credits['topUpBalance'] ?? 0;
|
|
186
|
+
const monthlyIncluded = credits['monthlyIncluded'] ?? FREE_TIER_LIMIT;
|
|
187
|
+
const subscription = doc ? this._toSubscription(doc.data) : undefined;
|
|
188
|
+
if (hasPersonalKey) {
|
|
189
|
+
return { mode: 'byok', usage, limit: null, remaining: null, topUpBalance, totalAvailable: null, percentUsed: null, resetsAt: usage.periodEnd, estimatedCostUSD: _estimateCostUSD(usage.byModel), ...(subscription ? { subscription } : {}) };
|
|
190
|
+
}
|
|
191
|
+
const isSubscriber = subscription?.status === 'active' || subscription?.status === 'trialing';
|
|
192
|
+
const remaining = Math.max(0, monthlyIncluded - usage.messagesUsed);
|
|
193
|
+
const totalAvailable = remaining + topUpBalance;
|
|
194
|
+
const percentUsed = Math.round((usage.messagesUsed / monthlyIncluded) * 100);
|
|
195
|
+
return {
|
|
196
|
+
mode: isSubscriber ? 'included' : 'free',
|
|
197
|
+
usage,
|
|
198
|
+
limit: monthlyIncluded,
|
|
199
|
+
remaining,
|
|
200
|
+
topUpBalance,
|
|
201
|
+
totalAvailable,
|
|
202
|
+
percentUsed,
|
|
203
|
+
resetsAt: usage.periodEnd,
|
|
204
|
+
estimatedCostUSD: _estimateCostUSD(usage.byModel),
|
|
205
|
+
...(subscription ? { subscription } : {}),
|
|
206
|
+
isFreeUser: !isSubscriber,
|
|
207
|
+
};
|
|
208
|
+
}
|
|
209
|
+
async incrementUsage(userId, inputTokens, outputTokens, model, useTopUp) {
|
|
210
|
+
const doc = await _getLicenseDoc(userId);
|
|
211
|
+
if (!doc)
|
|
212
|
+
return; // No license doc — free tier, usage not tracked in Firestore.
|
|
213
|
+
const { FieldValue } = await _getFirestore();
|
|
214
|
+
const updates = {
|
|
215
|
+
'credits.monthlyUsed': FieldValue.increment(1),
|
|
216
|
+
'credits.inputTokens': FieldValue.increment(inputTokens),
|
|
217
|
+
'credits.outputTokens': FieldValue.increment(outputTokens),
|
|
218
|
+
[`credits.byModel.${model}.input`]: FieldValue.increment(inputTokens),
|
|
219
|
+
[`credits.byModel.${model}.output`]: FieldValue.increment(outputTokens),
|
|
220
|
+
'credits.lastUpdated': FieldValue.serverTimestamp(),
|
|
221
|
+
'updatedAt': FieldValue.serverTimestamp(),
|
|
222
|
+
};
|
|
223
|
+
if (useTopUp)
|
|
224
|
+
updates['credits.topUpBalance'] = FieldValue.increment(-1);
|
|
225
|
+
await doc.ref.update(updates);
|
|
226
|
+
_invalidateCache(userId);
|
|
227
|
+
}
|
|
228
|
+
async batchIncrementUsage(userId, batch) {
|
|
229
|
+
const doc = await _getLicenseDoc(userId);
|
|
230
|
+
if (!doc)
|
|
231
|
+
return;
|
|
232
|
+
const { FieldValue } = await _getFirestore();
|
|
233
|
+
const updates = {
|
|
234
|
+
'credits.monthlyUsed': FieldValue.increment(batch.messageCount),
|
|
235
|
+
'credits.inputTokens': FieldValue.increment(batch.inputTokens),
|
|
236
|
+
'credits.outputTokens': FieldValue.increment(batch.outputTokens),
|
|
237
|
+
'credits.lastUpdated': FieldValue.serverTimestamp(),
|
|
238
|
+
'updatedAt': FieldValue.serverTimestamp(),
|
|
239
|
+
};
|
|
240
|
+
if (batch.topUpUsed > 0)
|
|
241
|
+
updates['credits.topUpBalance'] = FieldValue.increment(-batch.topUpUsed);
|
|
242
|
+
for (const [model, counts] of Object.entries(batch.byModel)) {
|
|
243
|
+
updates[`credits.byModel.${model}.input`] = FieldValue.increment(counts.input);
|
|
244
|
+
updates[`credits.byModel.${model}.output`] = FieldValue.increment(counts.output);
|
|
245
|
+
}
|
|
246
|
+
await doc.ref.update(updates);
|
|
247
|
+
_invalidateCache(userId);
|
|
248
|
+
}
|
|
249
|
+
async getSubscription(userId) {
|
|
250
|
+
const doc = await _getLicenseDoc(userId);
|
|
251
|
+
if (!doc)
|
|
252
|
+
return undefined;
|
|
253
|
+
return this._toSubscription(doc.data);
|
|
254
|
+
}
|
|
255
|
+
async hasActiveSubscription(userId) {
|
|
256
|
+
const doc = await _getLicenseDoc(userId);
|
|
257
|
+
if (!doc)
|
|
258
|
+
return false;
|
|
259
|
+
const status = doc.data['status'];
|
|
260
|
+
return status === 'active' || status === 'trialing';
|
|
261
|
+
}
|
|
262
|
+
async getTopUpBalance(userId) {
|
|
263
|
+
const doc = await _getLicenseDoc(userId);
|
|
264
|
+
if (!doc)
|
|
265
|
+
return 0;
|
|
266
|
+
const credits = doc.data['credits'] ?? {};
|
|
267
|
+
return credits['topUpBalance'] ?? 0;
|
|
268
|
+
}
|
|
269
|
+
async canAccessPaidFeature(userId) {
|
|
270
|
+
return this.hasActiveSubscription(userId);
|
|
271
|
+
}
|
|
272
|
+
// ── Internal helpers ────────────────────────────────────────────────────
|
|
273
|
+
_toSubscription(data) {
|
|
274
|
+
const status = data['status'];
|
|
275
|
+
if (!status)
|
|
276
|
+
return undefined;
|
|
277
|
+
const stripeCustomerId = data['stripeCustomerId'];
|
|
278
|
+
const stripeSubscriptionId = data['stripeSubscriptionId'];
|
|
279
|
+
const cancelAtPeriodEnd = data['cancelAtPeriodEnd'];
|
|
280
|
+
const giftedBy = data['giftedBy'];
|
|
281
|
+
const currentPeriodEnd = _toISO(data['credits']?.['periodEnd']);
|
|
282
|
+
const giftedAt = _toISO(data['giftedAt']);
|
|
283
|
+
const expiresAt = _toISO(data['expiresAt']);
|
|
284
|
+
const note = data['note'];
|
|
285
|
+
return {
|
|
286
|
+
plan: data['plan'] ?? 'standard',
|
|
287
|
+
status: status,
|
|
288
|
+
...(stripeCustomerId ? { stripeCustomerId } : {}),
|
|
289
|
+
...(stripeSubscriptionId ? { stripeSubscriptionId } : {}),
|
|
290
|
+
...(currentPeriodEnd ? { currentPeriodEnd } : {}),
|
|
291
|
+
...(cancelAtPeriodEnd !== undefined ? { cancelAtPeriodEnd } : {}),
|
|
292
|
+
...(giftedBy ? { giftedBy } : {}),
|
|
293
|
+
...(giftedAt ? { giftedAt } : {}),
|
|
294
|
+
...(expiresAt ? { expiresAt } : {}),
|
|
295
|
+
...(note ? { note } : {}),
|
|
296
|
+
};
|
|
297
|
+
}
|
|
298
|
+
async _resetPeriod(doc) {
|
|
299
|
+
const { FieldValue } = await _getFirestore();
|
|
300
|
+
const newPeriodEnd = new Date(Date.now() + 30 * 24 * 60 * 60 * 1000);
|
|
301
|
+
await doc.ref.update({
|
|
302
|
+
'credits.monthlyUsed': 0,
|
|
303
|
+
'credits.inputTokens': 0,
|
|
304
|
+
'credits.outputTokens': 0,
|
|
305
|
+
'credits.byModel': {},
|
|
306
|
+
'credits.periodStart': FieldValue.serverTimestamp(),
|
|
307
|
+
'credits.periodEnd': newPeriodEnd,
|
|
308
|
+
'credits.lastUpdated': FieldValue.serverTimestamp(),
|
|
309
|
+
'updatedAt': FieldValue.serverTimestamp(),
|
|
310
|
+
});
|
|
311
|
+
_invalidateCache(doc.docId);
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
//# sourceMappingURL=firestore-provider.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"firestore-provider.js","sourceRoot":"","sources":["../../../src/modules/billing/firestore-provider.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module billing
|
|
3
|
+
* @description Factory for sdk.billing.* — usage metering, subscription management,
|
|
4
|
+
* top-up credits, and Stripe integration.
|
|
5
|
+
*
|
|
6
|
+
* **Provider selection:** The billing module automatically chooses the storage backend:
|
|
7
|
+
* - `FIREBASE_SERVICE_ACCOUNT_KEY` set → `FirestoreBillingProvider` (production)
|
|
8
|
+
* - Not set → `LocalBillingProvider` (dev, stores state in `.soulcraft/billing/`)
|
|
9
|
+
*
|
|
10
|
+
* **Usage metering:** Fire-and-forget via a 5-second write-behind buffer. Recording
|
|
11
|
+
* usage never blocks the AI response path.
|
|
12
|
+
*
|
|
13
|
+
* **Stripe integration:** Full subscription lifecycle — checkout sessions, customer
|
|
14
|
+
* portal, and subscription cancellation. Requires `STRIPE_SECRET_KEY`.
|
|
15
|
+
*
|
|
16
|
+
* @example
|
|
17
|
+
* ```typescript
|
|
18
|
+
* // Check credits and record usage around an AI call:
|
|
19
|
+
* const check = await sdk.billing.checkLimit(user.email, user.hasByok)
|
|
20
|
+
* if (!check.ok) return c.json({ error: 'Monthly limit reached' }, 402)
|
|
21
|
+
*
|
|
22
|
+
* const response = await sdk.ai.complete(...)
|
|
23
|
+
* sdk.billing.recordUsage(user.email, response.usage.inputTokens, response.usage.outputTokens, response.model)
|
|
24
|
+
* ```
|
|
25
|
+
*/
|
|
26
|
+
import type { BillingModule, BillingProvider } from './types.js';
|
|
27
|
+
/**
|
|
28
|
+
* Options for `createBillingModule()`.
|
|
29
|
+
*/
|
|
30
|
+
export interface CreateBillingModuleOptions {
|
|
31
|
+
/**
|
|
32
|
+
* Override the billing provider directly.
|
|
33
|
+
* When not provided, the provider is selected from the environment:
|
|
34
|
+
* Firestore when `FIREBASE_SERVICE_ACCOUNT_KEY` is set, local otherwise.
|
|
35
|
+
*/
|
|
36
|
+
provider?: BillingProvider;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Create the `sdk.billing.*` module.
|
|
40
|
+
*
|
|
41
|
+
* @param options - Optional provider override (primarily for testing).
|
|
42
|
+
* @returns A fully configured `BillingModule`.
|
|
43
|
+
*/
|
|
44
|
+
export declare function createBillingModule(options?: CreateBillingModuleOptions): BillingModule;
|
|
45
|
+
export type { BillingModule, BillingPlanType, SubscriptionStatus, ModelUsage, UsageData, UsageStatus, SubscriptionData, LimitCheckResult, BatchIncrementData, CreateCheckoutSessionOptions, CreatePortalSessionOptions, } from './types.js';
|
|
46
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/modules/billing/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAMH,OAAO,KAAK,EACV,aAAa,EACb,eAAe,EAOhB,MAAM,YAAY,CAAA;AAMnB;;GAEG;AACH,MAAM,WAAW,0BAA0B;IACzC;;;;OAIG;IACH,QAAQ,CAAC,EAAE,eAAe,CAAA;CAC3B;AAgBD;;;;;GAKG;AACH,wBAAgB,mBAAmB,CAAC,OAAO,GAAE,0BAA+B,GAAG,aAAa,CAuH3F;AAGD,YAAY,EACV,aAAa,EACb,eAAe,EACf,kBAAkB,EAClB,UAAU,EACV,SAAS,EACT,WAAW,EACX,gBAAgB,EAChB,gBAAgB,EAChB,kBAAkB,EAClB,4BAA4B,EAC5B,0BAA0B,GAC3B,MAAM,YAAY,CAAA"}
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module billing
|
|
3
|
+
* @description Factory for sdk.billing.* — usage metering, subscription management,
|
|
4
|
+
* top-up credits, and Stripe integration.
|
|
5
|
+
*
|
|
6
|
+
* **Provider selection:** The billing module automatically chooses the storage backend:
|
|
7
|
+
* - `FIREBASE_SERVICE_ACCOUNT_KEY` set → `FirestoreBillingProvider` (production)
|
|
8
|
+
* - Not set → `LocalBillingProvider` (dev, stores state in `.soulcraft/billing/`)
|
|
9
|
+
*
|
|
10
|
+
* **Usage metering:** Fire-and-forget via a 5-second write-behind buffer. Recording
|
|
11
|
+
* usage never blocks the AI response path.
|
|
12
|
+
*
|
|
13
|
+
* **Stripe integration:** Full subscription lifecycle — checkout sessions, customer
|
|
14
|
+
* portal, and subscription cancellation. Requires `STRIPE_SECRET_KEY`.
|
|
15
|
+
*
|
|
16
|
+
* @example
|
|
17
|
+
* ```typescript
|
|
18
|
+
* // Check credits and record usage around an AI call:
|
|
19
|
+
* const check = await sdk.billing.checkLimit(user.email, user.hasByok)
|
|
20
|
+
* if (!check.ok) return c.json({ error: 'Monthly limit reached' }, 402)
|
|
21
|
+
*
|
|
22
|
+
* const response = await sdk.ai.complete(...)
|
|
23
|
+
* sdk.billing.recordUsage(user.email, response.usage.inputTokens, response.usage.outputTokens, response.model)
|
|
24
|
+
* ```
|
|
25
|
+
*/
|
|
26
|
+
import Stripe from 'stripe';
|
|
27
|
+
import { LocalBillingProvider } from './local-provider.js';
|
|
28
|
+
import { FirestoreBillingProvider } from './firestore-provider.js';
|
|
29
|
+
import { UsageBuffer } from './usage-buffer.js';
|
|
30
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
31
|
+
// Stripe helper
|
|
32
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
33
|
+
function _getStripe() {
|
|
34
|
+
const key = process.env['STRIPE_SECRET_KEY'];
|
|
35
|
+
if (!key)
|
|
36
|
+
throw new Error('STRIPE_SECRET_KEY is not set — configure it to use Stripe billing features');
|
|
37
|
+
return new Stripe(key, { apiVersion: '2026-02-25.clover', typescript: true });
|
|
38
|
+
}
|
|
39
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
40
|
+
// Factory
|
|
41
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
42
|
+
/**
|
|
43
|
+
* Create the `sdk.billing.*` module.
|
|
44
|
+
*
|
|
45
|
+
* @param options - Optional provider override (primarily for testing).
|
|
46
|
+
* @returns A fully configured `BillingModule`.
|
|
47
|
+
*/
|
|
48
|
+
export function createBillingModule(options = {}) {
|
|
49
|
+
const provider = options.provider
|
|
50
|
+
?? (process.env['FIREBASE_SERVICE_ACCOUNT_KEY']
|
|
51
|
+
? new FirestoreBillingProvider()
|
|
52
|
+
: new LocalBillingProvider());
|
|
53
|
+
const buffer = new UsageBuffer(provider);
|
|
54
|
+
return {
|
|
55
|
+
// ── Usage metering ──────────────────────────────────────────────────────
|
|
56
|
+
async checkLimit(userId, hasPersonalKey) {
|
|
57
|
+
return provider.checkLimit(userId, hasPersonalKey);
|
|
58
|
+
},
|
|
59
|
+
recordUsage(userId, inputTokens, outputTokens, model) {
|
|
60
|
+
// The limit check told us whether to draw from top-up; for the buffer we
|
|
61
|
+
// default to false here — the billing provider tracks which messages used
|
|
62
|
+
// top-up internally via the LimitCheckResult.useTopUp flag returned to the
|
|
63
|
+
// caller. Products that need exact top-up tracking should call the provider
|
|
64
|
+
// directly via the flush path.
|
|
65
|
+
buffer.increment(userId, inputTokens, outputTokens, model, false);
|
|
66
|
+
},
|
|
67
|
+
async getUsage(userId) {
|
|
68
|
+
return provider.getUsage(userId);
|
|
69
|
+
},
|
|
70
|
+
async getUsageStatus(userId, hasPersonalKey) {
|
|
71
|
+
return provider.getUsageStatus(userId, hasPersonalKey);
|
|
72
|
+
},
|
|
73
|
+
// ── Subscription management ─────────────────────────────────────────────
|
|
74
|
+
async getSubscription(userId) {
|
|
75
|
+
return provider.getSubscription(userId);
|
|
76
|
+
},
|
|
77
|
+
async hasActiveSubscription(userId) {
|
|
78
|
+
return provider.hasActiveSubscription(userId);
|
|
79
|
+
},
|
|
80
|
+
async createCheckoutSession(options) {
|
|
81
|
+
const stripe = _getStripe();
|
|
82
|
+
const session = await stripe.checkout.sessions.create({
|
|
83
|
+
mode: 'subscription',
|
|
84
|
+
line_items: [{ price: options.priceId, quantity: 1 }],
|
|
85
|
+
success_url: options.successUrl,
|
|
86
|
+
cancel_url: options.cancelUrl,
|
|
87
|
+
...(options.customerId ? { customer: options.customerId } : {}),
|
|
88
|
+
...(options.customerEmail && !options.customerId ? { customer_email: options.customerEmail } : {}),
|
|
89
|
+
});
|
|
90
|
+
if (!session.url)
|
|
91
|
+
throw new Error('Stripe checkout session created but returned no URL');
|
|
92
|
+
return session.url;
|
|
93
|
+
},
|
|
94
|
+
async createPortalSession(options) {
|
|
95
|
+
const stripe = _getStripe();
|
|
96
|
+
const session = await stripe.billingPortal.sessions.create({
|
|
97
|
+
customer: options.customerId,
|
|
98
|
+
return_url: options.returnUrl,
|
|
99
|
+
});
|
|
100
|
+
return session.url;
|
|
101
|
+
},
|
|
102
|
+
async cancelSubscription(userId) {
|
|
103
|
+
const stripe = _getStripe();
|
|
104
|
+
const sub = await provider.getSubscription(userId);
|
|
105
|
+
if (!sub?.stripeSubscriptionId) {
|
|
106
|
+
throw new Error(`No active Stripe subscription found for user ${userId}`);
|
|
107
|
+
}
|
|
108
|
+
await stripe.subscriptions.update(sub.stripeSubscriptionId, { cancel_at_period_end: true });
|
|
109
|
+
},
|
|
110
|
+
// ── Top-up credits ───────────────────────────────────────────────────────
|
|
111
|
+
async getTopUpBalance(userId) {
|
|
112
|
+
return provider.getTopUpBalance(userId);
|
|
113
|
+
},
|
|
114
|
+
async addTopUp(userId, amount) {
|
|
115
|
+
// For the local provider, directly update the balance.
|
|
116
|
+
// For Firestore, we use a targeted increment to avoid race conditions.
|
|
117
|
+
if (provider instanceof LocalBillingProvider) {
|
|
118
|
+
const current = await provider.getTopUpBalance(userId);
|
|
119
|
+
const newBalance = current + amount;
|
|
120
|
+
await provider.setTopUpBalance(userId, newBalance);
|
|
121
|
+
return newBalance;
|
|
122
|
+
}
|
|
123
|
+
// Firestore: increment and then read the new balance.
|
|
124
|
+
if (provider instanceof FirestoreBillingProvider) {
|
|
125
|
+
await provider.batchIncrementUsage(userId, {
|
|
126
|
+
messageCount: 0, inputTokens: 0, outputTokens: 0,
|
|
127
|
+
topUpUsed: -amount, // Negative topUpUsed = add credits
|
|
128
|
+
byModel: {},
|
|
129
|
+
});
|
|
130
|
+
return provider.getTopUpBalance(userId);
|
|
131
|
+
}
|
|
132
|
+
// Generic fallback for custom providers — not atomic but correct.
|
|
133
|
+
const current = await provider.getTopUpBalance(userId);
|
|
134
|
+
return current + amount;
|
|
135
|
+
},
|
|
136
|
+
// ── Feature gating ───────────────────────────────────────────────────────
|
|
137
|
+
async canAccessPaidFeature(userId) {
|
|
138
|
+
return provider.canAccessPaidFeature(userId);
|
|
139
|
+
},
|
|
140
|
+
// ── Lifecycle ────────────────────────────────────────────────────────────
|
|
141
|
+
async flush() {
|
|
142
|
+
await buffer.flush();
|
|
143
|
+
},
|
|
144
|
+
stopFlush() {
|
|
145
|
+
buffer.stop();
|
|
146
|
+
},
|
|
147
|
+
};
|
|
148
|
+
}
|
|
149
|
+
//# sourceMappingURL=index.js.map
|