@venturekit-pro/billing 0.0.0-dev.20260323005827
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/LICENSE +19 -0
- package/README.md +93 -0
- package/dist/gating/feature-gate.d.ts +53 -0
- package/dist/gating/feature-gate.d.ts.map +1 -0
- package/dist/gating/feature-gate.js +92 -0
- package/dist/gating/feature-gate.js.map +1 -0
- package/dist/index.d.ts +37 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +43 -0
- package/dist/index.js.map +1 -0
- package/dist/invoices/generator.d.ts +70 -0
- package/dist/invoices/generator.d.ts.map +1 -0
- package/dist/invoices/generator.js +190 -0
- package/dist/invoices/generator.js.map +1 -0
- package/dist/plans/index.d.ts +124 -0
- package/dist/plans/index.d.ts.map +1 -0
- package/dist/plans/index.js +116 -0
- package/dist/plans/index.js.map +1 -0
- package/dist/subscriptions/manager.d.ts +98 -0
- package/dist/subscriptions/manager.d.ts.map +1 -0
- package/dist/subscriptions/manager.js +245 -0
- package/dist/subscriptions/manager.js.map +1 -0
- package/dist/types/config.d.ts +36 -0
- package/dist/types/config.d.ts.map +1 -0
- package/dist/types/config.js +8 -0
- package/dist/types/config.js.map +1 -0
- package/dist/types/index.d.ts +8 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +8 -0
- package/dist/types/index.js.map +1 -0
- package/dist/types/invoice.d.ts +78 -0
- package/dist/types/invoice.d.ts.map +1 -0
- package/dist/types/invoice.js +5 -0
- package/dist/types/invoice.js.map +1 -0
- package/dist/types/payment.d.ts +33 -0
- package/dist/types/payment.d.ts.map +1 -0
- package/dist/types/payment.js +8 -0
- package/dist/types/payment.js.map +1 -0
- package/dist/types/subscription.d.ts +87 -0
- package/dist/types/subscription.d.ts.map +1 -0
- package/dist/types/subscription.js +5 -0
- package/dist/types/subscription.js.map +1 -0
- package/dist/usage/tracker.d.ts +75 -0
- package/dist/usage/tracker.d.ts.map +1 -0
- package/dist/usage/tracker.js +91 -0
- package/dist/usage/tracker.js.map +1 -0
- package/package.json +39 -0
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Plans & Feature Mapping
|
|
3
|
+
*
|
|
4
|
+
* Defines plans with feature limits and provides a generic mapping
|
|
5
|
+
* between app features and invoice line items.
|
|
6
|
+
*
|
|
7
|
+
* The feature_key is the bridge:
|
|
8
|
+
* - billing_plan_features.feature_key defines what a plan includes
|
|
9
|
+
* - billing_invoice_items.feature_key links usage to the line item
|
|
10
|
+
*
|
|
11
|
+
* This is generic enough to cover most apps:
|
|
12
|
+
* - SaaS seats: feature_key = 'seats'
|
|
13
|
+
* - Storage: feature_key = 'storage_gb'
|
|
14
|
+
* - API calls: feature_key = 'api_calls'
|
|
15
|
+
* - Custom: feature_key = 'whatever_you_need'
|
|
16
|
+
*/
|
|
17
|
+
import type { BillingInterval } from '../types/index.js';
|
|
18
|
+
/**
|
|
19
|
+
* A plan feature with its limit and pricing for invoicing.
|
|
20
|
+
*/
|
|
21
|
+
export interface PlanFeature {
|
|
22
|
+
/** Unique key identifying the feature (e.g. 'seats', 'storage_gb') */
|
|
23
|
+
featureKey: string;
|
|
24
|
+
/** Whether this feature is enabled in this plan */
|
|
25
|
+
enabled: boolean;
|
|
26
|
+
/** Usage limit (null = unlimited) */
|
|
27
|
+
limit?: number | null;
|
|
28
|
+
/** Unit price for overage/metered billing (in smallest currency unit) */
|
|
29
|
+
unitPrice?: number;
|
|
30
|
+
/** Human-readable description for invoice line items */
|
|
31
|
+
description: string;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* A plan definition with its features.
|
|
35
|
+
*/
|
|
36
|
+
export interface PlanDefinition {
|
|
37
|
+
id: string;
|
|
38
|
+
name: string;
|
|
39
|
+
description?: string;
|
|
40
|
+
price: number;
|
|
41
|
+
currency: string;
|
|
42
|
+
interval: BillingInterval;
|
|
43
|
+
intervalCount?: number;
|
|
44
|
+
trialDays?: number;
|
|
45
|
+
active: boolean;
|
|
46
|
+
features: PlanFeature[];
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Define a set of plans for your application.
|
|
50
|
+
*
|
|
51
|
+
* @example
|
|
52
|
+
* ```typescript
|
|
53
|
+
* const plans = definePlans([
|
|
54
|
+
* {
|
|
55
|
+
* id: 'free',
|
|
56
|
+
* name: 'Free',
|
|
57
|
+
* price: 0,
|
|
58
|
+
* currency: 'USD',
|
|
59
|
+
* interval: 'month',
|
|
60
|
+
* active: true,
|
|
61
|
+
* features: [
|
|
62
|
+
* { featureKey: 'seats', enabled: true, limit: 1, description: 'Team seats' },
|
|
63
|
+
* { featureKey: 'storage_gb', enabled: true, limit: 1, description: 'Storage (GB)' },
|
|
64
|
+
* { featureKey: 'api_calls', enabled: true, limit: 1000, description: 'API calls / month' },
|
|
65
|
+
* ],
|
|
66
|
+
* },
|
|
67
|
+
* {
|
|
68
|
+
* id: 'pro',
|
|
69
|
+
* name: 'Pro',
|
|
70
|
+
* price: 2900, // $29.00
|
|
71
|
+
* currency: 'USD',
|
|
72
|
+
* interval: 'month',
|
|
73
|
+
* active: true,
|
|
74
|
+
* features: [
|
|
75
|
+
* { featureKey: 'seats', enabled: true, limit: 10, unitPrice: 500, description: 'Team seats' },
|
|
76
|
+
* { featureKey: 'storage_gb', enabled: true, limit: 50, description: 'Storage (GB)' },
|
|
77
|
+
* { featureKey: 'api_calls', enabled: true, limit: 100000, description: 'API calls / month' },
|
|
78
|
+
* { featureKey: 'priority_support', enabled: true, description: 'Priority support' },
|
|
79
|
+
* ],
|
|
80
|
+
* },
|
|
81
|
+
* ]);
|
|
82
|
+
* ```
|
|
83
|
+
*/
|
|
84
|
+
export declare function definePlans(plans: PlanDefinition[]): PlanDefinition[];
|
|
85
|
+
/**
|
|
86
|
+
* Look up a feature limit for a given plan.
|
|
87
|
+
* Returns the limit (number), null (unlimited), or undefined (feature not in plan).
|
|
88
|
+
*/
|
|
89
|
+
export declare function getFeatureLimit(plan: PlanDefinition, featureKey: string): number | null | undefined;
|
|
90
|
+
/**
|
|
91
|
+
* Check if a feature is enabled for a plan.
|
|
92
|
+
*/
|
|
93
|
+
export declare function hasFeature(plan: PlanDefinition, featureKey: string): boolean;
|
|
94
|
+
/**
|
|
95
|
+
* Map feature usage to invoice line items.
|
|
96
|
+
*
|
|
97
|
+
* Given a plan and a usage record (featureKey → quantity used),
|
|
98
|
+
* generates invoice line items. This is the bridge between
|
|
99
|
+
* "what the customer did" and "what we charge them".
|
|
100
|
+
*
|
|
101
|
+
* For features within limits, generates a line item at the plan's base rate.
|
|
102
|
+
* For metered/overage features with unitPrice, generates overage items.
|
|
103
|
+
*
|
|
104
|
+
* @example
|
|
105
|
+
* ```typescript
|
|
106
|
+
* const items = mapUsageToLineItems(proPlan, {
|
|
107
|
+
* seats: 12, // 10 included, 2 overage at $5 each
|
|
108
|
+
* storage_gb: 30, // within limit
|
|
109
|
+
* api_calls: 95000, // within limit
|
|
110
|
+
* });
|
|
111
|
+
* // Returns:
|
|
112
|
+
* // [
|
|
113
|
+
* // { featureKey: 'seats', description: 'Team seats (2 extra)', quantity: 2, unitPrice: 500, amount: 1000 },
|
|
114
|
+
* // ]
|
|
115
|
+
* ```
|
|
116
|
+
*/
|
|
117
|
+
export declare function mapUsageToLineItems(plan: PlanDefinition, usage: Record<string, number>): Array<{
|
|
118
|
+
featureKey: string;
|
|
119
|
+
description: string;
|
|
120
|
+
quantity: number;
|
|
121
|
+
unitPrice: number;
|
|
122
|
+
amount: number;
|
|
123
|
+
}>;
|
|
124
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/plans/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,KAAK,EAAoB,eAAe,EAAE,MAAM,mBAAmB,CAAC;AAE3E;;GAEG;AACH,MAAM,WAAW,WAAW;IAC1B,sEAAsE;IACtE,UAAU,EAAE,MAAM,CAAC;IACnB,mDAAmD;IACnD,OAAO,EAAE,OAAO,CAAC;IACjB,qCAAqC;IACrC,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,yEAAyE;IACzE,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,wDAAwD;IACxD,WAAW,EAAE,MAAM,CAAC;CACrB;AAED;;GAEG;AACH,MAAM,WAAW,cAAc;IAC7B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,eAAe,CAAC;IAC1B,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,OAAO,CAAC;IAChB,QAAQ,EAAE,WAAW,EAAE,CAAC;CACzB;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmCG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE,cAAc,EAAE,GAAG,cAAc,EAAE,CAErE;AAED;;;GAGG;AACH,wBAAgB,eAAe,CAC7B,IAAI,EAAE,cAAc,EACpB,UAAU,EAAE,MAAM,GACjB,MAAM,GAAG,IAAI,GAAG,SAAS,CAI3B;AAED;;GAEG;AACH,wBAAgB,UAAU,CAAC,IAAI,EAAE,cAAc,EAAE,UAAU,EAAE,MAAM,GAAG,OAAO,CAG5E;AAED;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,wBAAgB,mBAAmB,CACjC,IAAI,EAAE,cAAc,EACpB,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAC5B,KAAK,CAAC;IACP,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;CAChB,CAAC,CA2BD"}
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Plans & Feature Mapping
|
|
3
|
+
*
|
|
4
|
+
* Defines plans with feature limits and provides a generic mapping
|
|
5
|
+
* between app features and invoice line items.
|
|
6
|
+
*
|
|
7
|
+
* The feature_key is the bridge:
|
|
8
|
+
* - billing_plan_features.feature_key defines what a plan includes
|
|
9
|
+
* - billing_invoice_items.feature_key links usage to the line item
|
|
10
|
+
*
|
|
11
|
+
* This is generic enough to cover most apps:
|
|
12
|
+
* - SaaS seats: feature_key = 'seats'
|
|
13
|
+
* - Storage: feature_key = 'storage_gb'
|
|
14
|
+
* - API calls: feature_key = 'api_calls'
|
|
15
|
+
* - Custom: feature_key = 'whatever_you_need'
|
|
16
|
+
*/
|
|
17
|
+
/**
|
|
18
|
+
* Define a set of plans for your application.
|
|
19
|
+
*
|
|
20
|
+
* @example
|
|
21
|
+
* ```typescript
|
|
22
|
+
* const plans = definePlans([
|
|
23
|
+
* {
|
|
24
|
+
* id: 'free',
|
|
25
|
+
* name: 'Free',
|
|
26
|
+
* price: 0,
|
|
27
|
+
* currency: 'USD',
|
|
28
|
+
* interval: 'month',
|
|
29
|
+
* active: true,
|
|
30
|
+
* features: [
|
|
31
|
+
* { featureKey: 'seats', enabled: true, limit: 1, description: 'Team seats' },
|
|
32
|
+
* { featureKey: 'storage_gb', enabled: true, limit: 1, description: 'Storage (GB)' },
|
|
33
|
+
* { featureKey: 'api_calls', enabled: true, limit: 1000, description: 'API calls / month' },
|
|
34
|
+
* ],
|
|
35
|
+
* },
|
|
36
|
+
* {
|
|
37
|
+
* id: 'pro',
|
|
38
|
+
* name: 'Pro',
|
|
39
|
+
* price: 2900, // $29.00
|
|
40
|
+
* currency: 'USD',
|
|
41
|
+
* interval: 'month',
|
|
42
|
+
* active: true,
|
|
43
|
+
* features: [
|
|
44
|
+
* { featureKey: 'seats', enabled: true, limit: 10, unitPrice: 500, description: 'Team seats' },
|
|
45
|
+
* { featureKey: 'storage_gb', enabled: true, limit: 50, description: 'Storage (GB)' },
|
|
46
|
+
* { featureKey: 'api_calls', enabled: true, limit: 100000, description: 'API calls / month' },
|
|
47
|
+
* { featureKey: 'priority_support', enabled: true, description: 'Priority support' },
|
|
48
|
+
* ],
|
|
49
|
+
* },
|
|
50
|
+
* ]);
|
|
51
|
+
* ```
|
|
52
|
+
*/
|
|
53
|
+
export function definePlans(plans) {
|
|
54
|
+
return plans;
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Look up a feature limit for a given plan.
|
|
58
|
+
* Returns the limit (number), null (unlimited), or undefined (feature not in plan).
|
|
59
|
+
*/
|
|
60
|
+
export function getFeatureLimit(plan, featureKey) {
|
|
61
|
+
const feature = plan.features.find((f) => f.featureKey === featureKey);
|
|
62
|
+
if (!feature || !feature.enabled)
|
|
63
|
+
return undefined;
|
|
64
|
+
return feature.limit ?? null;
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Check if a feature is enabled for a plan.
|
|
68
|
+
*/
|
|
69
|
+
export function hasFeature(plan, featureKey) {
|
|
70
|
+
const feature = plan.features.find((f) => f.featureKey === featureKey);
|
|
71
|
+
return feature?.enabled ?? false;
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Map feature usage to invoice line items.
|
|
75
|
+
*
|
|
76
|
+
* Given a plan and a usage record (featureKey → quantity used),
|
|
77
|
+
* generates invoice line items. This is the bridge between
|
|
78
|
+
* "what the customer did" and "what we charge them".
|
|
79
|
+
*
|
|
80
|
+
* For features within limits, generates a line item at the plan's base rate.
|
|
81
|
+
* For metered/overage features with unitPrice, generates overage items.
|
|
82
|
+
*
|
|
83
|
+
* @example
|
|
84
|
+
* ```typescript
|
|
85
|
+
* const items = mapUsageToLineItems(proPlan, {
|
|
86
|
+
* seats: 12, // 10 included, 2 overage at $5 each
|
|
87
|
+
* storage_gb: 30, // within limit
|
|
88
|
+
* api_calls: 95000, // within limit
|
|
89
|
+
* });
|
|
90
|
+
* // Returns:
|
|
91
|
+
* // [
|
|
92
|
+
* // { featureKey: 'seats', description: 'Team seats (2 extra)', quantity: 2, unitPrice: 500, amount: 1000 },
|
|
93
|
+
* // ]
|
|
94
|
+
* ```
|
|
95
|
+
*/
|
|
96
|
+
export function mapUsageToLineItems(plan, usage) {
|
|
97
|
+
const items = [];
|
|
98
|
+
for (const [featureKey, used] of Object.entries(usage)) {
|
|
99
|
+
const feature = plan.features.find((f) => f.featureKey === featureKey);
|
|
100
|
+
if (!feature || !feature.enabled)
|
|
101
|
+
continue;
|
|
102
|
+
// If feature has a limit and usage exceeds it, and there's a unit price for overage
|
|
103
|
+
if (feature.limit != null && used > feature.limit && feature.unitPrice) {
|
|
104
|
+
const overage = used - feature.limit;
|
|
105
|
+
items.push({
|
|
106
|
+
featureKey,
|
|
107
|
+
description: `${feature.description} (${overage} extra)`,
|
|
108
|
+
quantity: overage,
|
|
109
|
+
unitPrice: feature.unitPrice,
|
|
110
|
+
amount: overage * feature.unitPrice,
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
return items;
|
|
115
|
+
}
|
|
116
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/plans/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAoCH;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmCG;AACH,MAAM,UAAU,WAAW,CAAC,KAAuB;IACjD,OAAO,KAAK,CAAC;AACf,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,eAAe,CAC7B,IAAoB,EACpB,UAAkB;IAElB,MAAM,OAAO,GAAG,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,UAAU,KAAK,UAAU,CAAC,CAAC;IACvE,IAAI,CAAC,OAAO,IAAI,CAAC,OAAO,CAAC,OAAO;QAAE,OAAO,SAAS,CAAC;IACnD,OAAO,OAAO,CAAC,KAAK,IAAI,IAAI,CAAC;AAC/B,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,UAAU,CAAC,IAAoB,EAAE,UAAkB;IACjE,MAAM,OAAO,GAAG,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,UAAU,KAAK,UAAU,CAAC,CAAC;IACvE,OAAO,OAAO,EAAE,OAAO,IAAI,KAAK,CAAC;AACnC,CAAC;AAED;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,MAAM,UAAU,mBAAmB,CACjC,IAAoB,EACpB,KAA6B;IAQ7B,MAAM,KAAK,GAMN,EAAE,CAAC;IAER,KAAK,MAAM,CAAC,UAAU,EAAE,IAAI,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;QACvD,MAAM,OAAO,GAAG,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,UAAU,KAAK,UAAU,CAAC,CAAC;QACvE,IAAI,CAAC,OAAO,IAAI,CAAC,OAAO,CAAC,OAAO;YAAE,SAAS;QAE3C,oFAAoF;QACpF,IAAI,OAAO,CAAC,KAAK,IAAI,IAAI,IAAI,IAAI,GAAG,OAAO,CAAC,KAAK,IAAI,OAAO,CAAC,SAAS,EAAE,CAAC;YACvE,MAAM,OAAO,GAAG,IAAI,GAAG,OAAO,CAAC,KAAK,CAAC;YACrC,KAAK,CAAC,IAAI,CAAC;gBACT,UAAU;gBACV,WAAW,EAAE,GAAG,OAAO,CAAC,WAAW,KAAK,OAAO,SAAS;gBACxD,QAAQ,EAAE,OAAO;gBACjB,SAAS,EAAE,OAAO,CAAC,SAAS;gBAC5B,MAAM,EAAE,OAAO,GAAG,OAAO,CAAC,SAAS;aACpC,CAAC,CAAC;QACL,CAAC;IACH,CAAC;IAED,OAAO,KAAK,CAAC;AACf,CAAC"}
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* VentureKit Subscription Manager
|
|
3
|
+
*
|
|
4
|
+
* Manages subscription lifecycle: create, upgrade, downgrade,
|
|
5
|
+
* cancel, pause, resume, and trial management.
|
|
6
|
+
*
|
|
7
|
+
* Storage backend is pluggable via SubscriptionStore interface.
|
|
8
|
+
*/
|
|
9
|
+
import type { Subscription, CreateSubscriptionInput } from '../types/subscription.js';
|
|
10
|
+
import type { PlanDefinition } from '../plans/index.js';
|
|
11
|
+
/**
|
|
12
|
+
* Subscription store interface
|
|
13
|
+
*/
|
|
14
|
+
export interface SubscriptionStore {
|
|
15
|
+
create(subscription: Subscription): Promise<Subscription>;
|
|
16
|
+
get(id: string): Promise<Subscription | null>;
|
|
17
|
+
getByCustomer(customerId: string): Promise<Subscription | null>;
|
|
18
|
+
update(id: string, updates: Partial<Subscription>): Promise<void>;
|
|
19
|
+
delete(id: string): Promise<void>;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Subscription manager options
|
|
23
|
+
*/
|
|
24
|
+
export interface SubscriptionManagerOptions {
|
|
25
|
+
store: SubscriptionStore;
|
|
26
|
+
plans: PlanDefinition[];
|
|
27
|
+
/** Default trial days if plan doesn't specify */
|
|
28
|
+
defaultTrialDays?: number;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Proration calculation result
|
|
32
|
+
*/
|
|
33
|
+
export interface ProrationResult {
|
|
34
|
+
/** Credit for unused time on current plan */
|
|
35
|
+
credit: number;
|
|
36
|
+
/** Charge for remaining time on new plan */
|
|
37
|
+
charge: number;
|
|
38
|
+
/** Net amount (charge - credit, can be negative) */
|
|
39
|
+
netAmount: number;
|
|
40
|
+
/** Days remaining in current period */
|
|
41
|
+
daysRemaining: number;
|
|
42
|
+
/** Total days in current period */
|
|
43
|
+
totalDays: number;
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Calculate proration for a mid-cycle plan change
|
|
47
|
+
*/
|
|
48
|
+
export declare function calculateProration(currentPlan: PlanDefinition, newPlan: PlanDefinition, periodStart: Date, periodEnd: Date, changeDate?: Date): ProrationResult;
|
|
49
|
+
/**
|
|
50
|
+
* Create a subscription manager
|
|
51
|
+
*/
|
|
52
|
+
export declare function createSubscriptionManager(options: SubscriptionManagerOptions): {
|
|
53
|
+
/**
|
|
54
|
+
* Create a new subscription
|
|
55
|
+
*/
|
|
56
|
+
create(input: CreateSubscriptionInput): Promise<Subscription>;
|
|
57
|
+
/**
|
|
58
|
+
* Get a subscription by ID
|
|
59
|
+
*/
|
|
60
|
+
get(id: string): Promise<Subscription | null>;
|
|
61
|
+
/**
|
|
62
|
+
* Get a customer's active subscription
|
|
63
|
+
*/
|
|
64
|
+
getByCustomer(customerId: string): Promise<Subscription | null>;
|
|
65
|
+
/**
|
|
66
|
+
* Change plan (upgrade or downgrade)
|
|
67
|
+
*/
|
|
68
|
+
changePlan(subscriptionId: string, newPlanId: string, options?: {
|
|
69
|
+
immediate?: boolean;
|
|
70
|
+
}): Promise<{
|
|
71
|
+
subscription: Subscription;
|
|
72
|
+
proration?: ProrationResult;
|
|
73
|
+
}>;
|
|
74
|
+
/**
|
|
75
|
+
* Cancel subscription
|
|
76
|
+
*/
|
|
77
|
+
cancel(subscriptionId: string, options?: {
|
|
78
|
+
immediate?: boolean;
|
|
79
|
+
reason?: string;
|
|
80
|
+
}): Promise<Subscription>;
|
|
81
|
+
/**
|
|
82
|
+
* Pause subscription
|
|
83
|
+
*/
|
|
84
|
+
pause(subscriptionId: string): Promise<Subscription>;
|
|
85
|
+
/**
|
|
86
|
+
* Resume a paused subscription
|
|
87
|
+
*/
|
|
88
|
+
resume(subscriptionId: string): Promise<Subscription>;
|
|
89
|
+
/**
|
|
90
|
+
* Calculate proration for a plan change
|
|
91
|
+
*/
|
|
92
|
+
previewProration(subscriptionId: string, newPlanId: string): Promise<ProrationResult>;
|
|
93
|
+
};
|
|
94
|
+
/**
|
|
95
|
+
* In-memory subscription store (for dev/testing)
|
|
96
|
+
*/
|
|
97
|
+
export declare function createMemorySubscriptionStore(): SubscriptionStore;
|
|
98
|
+
//# sourceMappingURL=manager.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"manager.d.ts","sourceRoot":"","sources":["../../src/subscriptions/manager.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EACV,YAAY,EAEZ,uBAAuB,EAExB,MAAM,0BAA0B,CAAC;AAClC,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AAExD;;GAEG;AACH,MAAM,WAAW,iBAAiB;IAChC,MAAM,CAAC,YAAY,EAAE,YAAY,GAAG,OAAO,CAAC,YAAY,CAAC,CAAC;IAC1D,GAAG,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,GAAG,IAAI,CAAC,CAAC;IAC9C,aAAa,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,GAAG,IAAI,CAAC,CAAC;IAChE,MAAM,CAAC,EAAE,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,CAAC,YAAY,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAClE,MAAM,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CACnC;AAED;;GAEG;AACH,MAAM,WAAW,0BAA0B;IACzC,KAAK,EAAE,iBAAiB,CAAC;IACzB,KAAK,EAAE,cAAc,EAAE,CAAC;IACxB,iDAAiD;IACjD,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC3B;AAED;;GAEG;AACH,MAAM,WAAW,eAAe;IAC9B,6CAA6C;IAC7C,MAAM,EAAE,MAAM,CAAC;IACf,4CAA4C;IAC5C,MAAM,EAAE,MAAM,CAAC;IACf,oDAAoD;IACpD,SAAS,EAAE,MAAM,CAAC;IAClB,uCAAuC;IACvC,aAAa,EAAE,MAAM,CAAC;IACtB,mCAAmC;IACnC,SAAS,EAAE,MAAM,CAAC;CACnB;AAED;;GAEG;AACH,wBAAgB,kBAAkB,CAChC,WAAW,EAAE,cAAc,EAC3B,OAAO,EAAE,cAAc,EACvB,WAAW,EAAE,IAAI,EACjB,SAAS,EAAE,IAAI,EACf,UAAU,GAAE,IAAiB,GAC5B,eAAe,CAqBjB;AAED;;GAEG;AACH,wBAAgB,yBAAyB,CAAC,OAAO,EAAE,0BAA0B;IAezE;;OAEG;kBACiB,uBAAuB,GAAG,OAAO,CAAC,YAAY,CAAC;IAmDnE;;OAEG;YACW,MAAM,GAAG,OAAO,CAAC,YAAY,GAAG,IAAI,CAAC;IAInD;;OAEG;8BAC6B,MAAM,GAAG,OAAO,CAAC,YAAY,GAAG,IAAI,CAAC;IAIrE;;OAEG;+BAEe,MAAM,aACX,MAAM,YACP;QAAE,SAAS,CAAC,EAAE,OAAO,CAAA;KAAE,GAChC,OAAO,CAAC;QAAE,YAAY,EAAE,YAAY,CAAC;QAAC,SAAS,CAAC,EAAE,eAAe,CAAA;KAAE,CAAC;IAyCvE;;OAEG;2BAEe,MAAM,YACZ;QAAE,SAAS,CAAC,EAAE,OAAO,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,CAAA;KAAE,GACjD,OAAO,CAAC,YAAY,CAAC;IA2BxB;;OAEG;0BACyB,MAAM,GAAG,OAAO,CAAC,YAAY,CAAC;IAa1D;;OAEG;2BAC0B,MAAM,GAAG,OAAO,CAAC,YAAY,CAAC;IAa3D;;OAEG;qCAEe,MAAM,aACX,MAAM,GAChB,OAAO,CAAC,eAAe,CAAC;EAe9B;AAED;;GAEG;AACH,wBAAgB,6BAA6B,IAAI,iBAAiB,CA6BjE"}
|
|
@@ -0,0 +1,245 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* VentureKit Subscription Manager
|
|
3
|
+
*
|
|
4
|
+
* Manages subscription lifecycle: create, upgrade, downgrade,
|
|
5
|
+
* cancel, pause, resume, and trial management.
|
|
6
|
+
*
|
|
7
|
+
* Storage backend is pluggable via SubscriptionStore interface.
|
|
8
|
+
*/
|
|
9
|
+
/**
|
|
10
|
+
* Calculate proration for a mid-cycle plan change
|
|
11
|
+
*/
|
|
12
|
+
export function calculateProration(currentPlan, newPlan, periodStart, periodEnd, changeDate = new Date()) {
|
|
13
|
+
const totalMs = periodEnd.getTime() - periodStart.getTime();
|
|
14
|
+
const elapsedMs = changeDate.getTime() - periodStart.getTime();
|
|
15
|
+
const remainingMs = totalMs - elapsedMs;
|
|
16
|
+
const totalDays = Math.max(1, Math.round(totalMs / (1000 * 60 * 60 * 24)));
|
|
17
|
+
const daysRemaining = Math.max(0, Math.round(remainingMs / (1000 * 60 * 60 * 24)));
|
|
18
|
+
const dailyRateCurrent = currentPlan.price / totalDays;
|
|
19
|
+
const dailyRateNew = newPlan.price / totalDays;
|
|
20
|
+
const credit = Math.round(dailyRateCurrent * daysRemaining);
|
|
21
|
+
const charge = Math.round(dailyRateNew * daysRemaining);
|
|
22
|
+
return {
|
|
23
|
+
credit,
|
|
24
|
+
charge,
|
|
25
|
+
netAmount: charge - credit,
|
|
26
|
+
daysRemaining,
|
|
27
|
+
totalDays,
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Create a subscription manager
|
|
32
|
+
*/
|
|
33
|
+
export function createSubscriptionManager(options) {
|
|
34
|
+
const { store, plans, defaultTrialDays } = options;
|
|
35
|
+
function getPlan(planId) {
|
|
36
|
+
const plan = plans.find(p => p.id === planId);
|
|
37
|
+
if (!plan)
|
|
38
|
+
throw new Error(`Plan '${planId}' not found`);
|
|
39
|
+
if (!plan.active)
|
|
40
|
+
throw new Error(`Plan '${planId}' is not active`);
|
|
41
|
+
return plan;
|
|
42
|
+
}
|
|
43
|
+
function generateId() {
|
|
44
|
+
return `sub-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
|
|
45
|
+
}
|
|
46
|
+
return {
|
|
47
|
+
/**
|
|
48
|
+
* Create a new subscription
|
|
49
|
+
*/
|
|
50
|
+
async create(input) {
|
|
51
|
+
// Check for existing active subscription
|
|
52
|
+
const existing = await store.getByCustomer(input.customerId);
|
|
53
|
+
if (existing && !['canceled', 'unpaid'].includes(existing.status)) {
|
|
54
|
+
throw new Error(`Customer '${input.customerId}' already has an active subscription`);
|
|
55
|
+
}
|
|
56
|
+
const plan = getPlan(input.planId);
|
|
57
|
+
const now = new Date();
|
|
58
|
+
const trialDays = input.trialDays ?? plan.trialDays ?? defaultTrialDays ?? 0;
|
|
59
|
+
const isTrialing = trialDays > 0;
|
|
60
|
+
const periodEnd = new Date(now);
|
|
61
|
+
if (isTrialing) {
|
|
62
|
+
periodEnd.setDate(periodEnd.getDate() + trialDays);
|
|
63
|
+
}
|
|
64
|
+
else {
|
|
65
|
+
// Set period based on interval
|
|
66
|
+
switch (plan.interval) {
|
|
67
|
+
case 'day':
|
|
68
|
+
periodEnd.setDate(periodEnd.getDate() + (plan.intervalCount ?? 1));
|
|
69
|
+
break;
|
|
70
|
+
case 'week':
|
|
71
|
+
periodEnd.setDate(periodEnd.getDate() + 7 * (plan.intervalCount ?? 1));
|
|
72
|
+
break;
|
|
73
|
+
case 'month':
|
|
74
|
+
periodEnd.setMonth(periodEnd.getMonth() + (plan.intervalCount ?? 1));
|
|
75
|
+
break;
|
|
76
|
+
case 'year':
|
|
77
|
+
periodEnd.setFullYear(periodEnd.getFullYear() + (plan.intervalCount ?? 1));
|
|
78
|
+
break;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
const subscription = {
|
|
82
|
+
id: generateId(),
|
|
83
|
+
customerId: input.customerId,
|
|
84
|
+
planId: input.planId,
|
|
85
|
+
status: isTrialing ? 'trialing' : 'active',
|
|
86
|
+
currentPeriodStart: now,
|
|
87
|
+
currentPeriodEnd: periodEnd,
|
|
88
|
+
cancelAtPeriodEnd: false,
|
|
89
|
+
trialStart: isTrialing ? now : undefined,
|
|
90
|
+
trialEnd: isTrialing ? periodEnd : undefined,
|
|
91
|
+
createdAt: now,
|
|
92
|
+
updatedAt: now,
|
|
93
|
+
metadata: input.metadata,
|
|
94
|
+
};
|
|
95
|
+
return store.create(subscription);
|
|
96
|
+
},
|
|
97
|
+
/**
|
|
98
|
+
* Get a subscription by ID
|
|
99
|
+
*/
|
|
100
|
+
async get(id) {
|
|
101
|
+
return store.get(id);
|
|
102
|
+
},
|
|
103
|
+
/**
|
|
104
|
+
* Get a customer's active subscription
|
|
105
|
+
*/
|
|
106
|
+
async getByCustomer(customerId) {
|
|
107
|
+
return store.getByCustomer(customerId);
|
|
108
|
+
},
|
|
109
|
+
/**
|
|
110
|
+
* Change plan (upgrade or downgrade)
|
|
111
|
+
*/
|
|
112
|
+
async changePlan(subscriptionId, newPlanId, options) {
|
|
113
|
+
const sub = await store.get(subscriptionId);
|
|
114
|
+
if (!sub)
|
|
115
|
+
throw new Error(`Subscription '${subscriptionId}' not found`);
|
|
116
|
+
if (sub.status === 'canceled')
|
|
117
|
+
throw new Error('Cannot change plan on a canceled subscription');
|
|
118
|
+
const currentPlan = getPlan(sub.planId);
|
|
119
|
+
const newPlan = getPlan(newPlanId);
|
|
120
|
+
if (sub.planId === newPlanId) {
|
|
121
|
+
throw new Error('Already on this plan');
|
|
122
|
+
}
|
|
123
|
+
const immediate = options?.immediate ?? true;
|
|
124
|
+
if (immediate) {
|
|
125
|
+
const proration = calculateProration(currentPlan, newPlan, sub.currentPeriodStart, sub.currentPeriodEnd);
|
|
126
|
+
await store.update(subscriptionId, {
|
|
127
|
+
planId: newPlanId,
|
|
128
|
+
updatedAt: new Date(),
|
|
129
|
+
});
|
|
130
|
+
const updated = await store.get(subscriptionId);
|
|
131
|
+
return { subscription: updated, proration };
|
|
132
|
+
}
|
|
133
|
+
// Schedule change at period end (no proration)
|
|
134
|
+
await store.update(subscriptionId, {
|
|
135
|
+
metadata: { ...sub.metadata, pendingPlanId: newPlanId },
|
|
136
|
+
updatedAt: new Date(),
|
|
137
|
+
});
|
|
138
|
+
const updated = await store.get(subscriptionId);
|
|
139
|
+
return { subscription: updated };
|
|
140
|
+
},
|
|
141
|
+
/**
|
|
142
|
+
* Cancel subscription
|
|
143
|
+
*/
|
|
144
|
+
async cancel(subscriptionId, options) {
|
|
145
|
+
const sub = await store.get(subscriptionId);
|
|
146
|
+
if (!sub)
|
|
147
|
+
throw new Error(`Subscription '${subscriptionId}' not found`);
|
|
148
|
+
if (sub.status === 'canceled')
|
|
149
|
+
throw new Error('Subscription is already canceled');
|
|
150
|
+
const immediate = options?.immediate ?? false;
|
|
151
|
+
const now = new Date();
|
|
152
|
+
if (immediate) {
|
|
153
|
+
await store.update(subscriptionId, {
|
|
154
|
+
status: 'canceled',
|
|
155
|
+
canceledAt: now,
|
|
156
|
+
cancelAtPeriodEnd: false,
|
|
157
|
+
updatedAt: now,
|
|
158
|
+
metadata: { ...sub.metadata, cancelReason: options?.reason },
|
|
159
|
+
});
|
|
160
|
+
}
|
|
161
|
+
else {
|
|
162
|
+
await store.update(subscriptionId, {
|
|
163
|
+
cancelAtPeriodEnd: true,
|
|
164
|
+
updatedAt: now,
|
|
165
|
+
metadata: { ...sub.metadata, cancelReason: options?.reason },
|
|
166
|
+
});
|
|
167
|
+
}
|
|
168
|
+
return (await store.get(subscriptionId));
|
|
169
|
+
},
|
|
170
|
+
/**
|
|
171
|
+
* Pause subscription
|
|
172
|
+
*/
|
|
173
|
+
async pause(subscriptionId) {
|
|
174
|
+
const sub = await store.get(subscriptionId);
|
|
175
|
+
if (!sub)
|
|
176
|
+
throw new Error(`Subscription '${subscriptionId}' not found`);
|
|
177
|
+
if (sub.status !== 'active')
|
|
178
|
+
throw new Error('Can only pause active subscriptions');
|
|
179
|
+
await store.update(subscriptionId, {
|
|
180
|
+
status: 'paused',
|
|
181
|
+
updatedAt: new Date(),
|
|
182
|
+
});
|
|
183
|
+
return (await store.get(subscriptionId));
|
|
184
|
+
},
|
|
185
|
+
/**
|
|
186
|
+
* Resume a paused subscription
|
|
187
|
+
*/
|
|
188
|
+
async resume(subscriptionId) {
|
|
189
|
+
const sub = await store.get(subscriptionId);
|
|
190
|
+
if (!sub)
|
|
191
|
+
throw new Error(`Subscription '${subscriptionId}' not found`);
|
|
192
|
+
if (sub.status !== 'paused')
|
|
193
|
+
throw new Error('Can only resume paused subscriptions');
|
|
194
|
+
await store.update(subscriptionId, {
|
|
195
|
+
status: 'active',
|
|
196
|
+
updatedAt: new Date(),
|
|
197
|
+
});
|
|
198
|
+
return (await store.get(subscriptionId));
|
|
199
|
+
},
|
|
200
|
+
/**
|
|
201
|
+
* Calculate proration for a plan change
|
|
202
|
+
*/
|
|
203
|
+
async previewProration(subscriptionId, newPlanId) {
|
|
204
|
+
const sub = await store.get(subscriptionId);
|
|
205
|
+
if (!sub)
|
|
206
|
+
throw new Error(`Subscription '${subscriptionId}' not found`);
|
|
207
|
+
const currentPlan = getPlan(sub.planId);
|
|
208
|
+
const newPlan = getPlan(newPlanId);
|
|
209
|
+
return calculateProration(currentPlan, newPlan, sub.currentPeriodStart, sub.currentPeriodEnd);
|
|
210
|
+
},
|
|
211
|
+
};
|
|
212
|
+
}
|
|
213
|
+
/**
|
|
214
|
+
* In-memory subscription store (for dev/testing)
|
|
215
|
+
*/
|
|
216
|
+
export function createMemorySubscriptionStore() {
|
|
217
|
+
const subscriptions = new Map();
|
|
218
|
+
return {
|
|
219
|
+
async create(subscription) {
|
|
220
|
+
subscriptions.set(subscription.id, { ...subscription });
|
|
221
|
+
return subscription;
|
|
222
|
+
},
|
|
223
|
+
async get(id) {
|
|
224
|
+
return subscriptions.get(id) ?? null;
|
|
225
|
+
},
|
|
226
|
+
async getByCustomer(customerId) {
|
|
227
|
+
for (const sub of subscriptions.values()) {
|
|
228
|
+
if (sub.customerId === customerId && sub.status !== 'canceled') {
|
|
229
|
+
return sub;
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
return null;
|
|
233
|
+
},
|
|
234
|
+
async update(id, updates) {
|
|
235
|
+
const existing = subscriptions.get(id);
|
|
236
|
+
if (existing) {
|
|
237
|
+
subscriptions.set(id, { ...existing, ...updates });
|
|
238
|
+
}
|
|
239
|
+
},
|
|
240
|
+
async delete(id) {
|
|
241
|
+
subscriptions.delete(id);
|
|
242
|
+
},
|
|
243
|
+
};
|
|
244
|
+
}
|
|
245
|
+
//# sourceMappingURL=manager.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"manager.js","sourceRoot":"","sources":["../../src/subscriptions/manager.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Billing Configuration Types
|
|
3
|
+
*
|
|
4
|
+
* This package handles invoicing, plans, usage tracking, and feature gating.
|
|
5
|
+
* Payment processing is NOT included — integrate any provider separately.
|
|
6
|
+
*/
|
|
7
|
+
import type { SubscriptionPlan } from './subscription.js';
|
|
8
|
+
/**
|
|
9
|
+
* Billing configuration
|
|
10
|
+
*/
|
|
11
|
+
export interface BillingConfig {
|
|
12
|
+
/** Default currency (ISO 4217) */
|
|
13
|
+
defaultCurrency: string;
|
|
14
|
+
/** Available plans */
|
|
15
|
+
plans?: SubscriptionPlan[];
|
|
16
|
+
/** Tax rate as percentage (e.g., 20 for 20%) */
|
|
17
|
+
taxRate?: number;
|
|
18
|
+
/** Tax ID label (e.g., "VAT", "ICE" for Morocco) */
|
|
19
|
+
taxIdLabel?: string;
|
|
20
|
+
/** Invoice number prefix (e.g., "INV-") */
|
|
21
|
+
invoicePrefix?: string;
|
|
22
|
+
/** Default payment terms in days (default: 30) */
|
|
23
|
+
paymentTermsDays?: number;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Billing config input (partial for overrides)
|
|
27
|
+
*/
|
|
28
|
+
export interface BillingConfigInput {
|
|
29
|
+
defaultCurrency?: string;
|
|
30
|
+
plans?: SubscriptionPlan[];
|
|
31
|
+
taxRate?: number;
|
|
32
|
+
taxIdLabel?: string;
|
|
33
|
+
invoicePrefix?: string;
|
|
34
|
+
paymentTermsDays?: number;
|
|
35
|
+
}
|
|
36
|
+
//# sourceMappingURL=config.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/types/config.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AAE1D;;GAEG;AACH,MAAM,WAAW,aAAa;IAC5B,kCAAkC;IAClC,eAAe,EAAE,MAAM,CAAC;IAExB,sBAAsB;IACtB,KAAK,CAAC,EAAE,gBAAgB,EAAE,CAAC;IAE3B,gDAAgD;IAChD,OAAO,CAAC,EAAE,MAAM,CAAC;IAEjB,oDAAoD;IACpD,UAAU,CAAC,EAAE,MAAM,CAAC;IAEpB,2CAA2C;IAC3C,aAAa,CAAC,EAAE,MAAM,CAAC;IAEvB,kDAAkD;IAClD,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC3B;AAED;;GAEG;AACH,MAAM,WAAW,kBAAkB;IACjC,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,KAAK,CAAC,EAAE,gBAAgB,EAAE,CAAC;IAC3B,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC3B"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../../src/types/config.ts"],"names":[],"mappings":"AAAA;;;;;GAKG"}
|