digital-products 2.1.3 → 2.4.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/.turbo/turbo-build.log +4 -5
- package/CHANGELOG.md +17 -0
- package/README.md +2 -0
- package/dist/api.js +7 -7
- package/dist/api.js.map +1 -1
- package/dist/app.js +6 -6
- package/dist/app.js.map +1 -1
- package/dist/client.d.ts +157 -0
- package/dist/client.d.ts.map +1 -0
- package/dist/client.js +69 -0
- package/dist/client.js.map +1 -0
- package/dist/content.js +7 -7
- package/dist/content.js.map +1 -1
- package/dist/data.d.ts.map +1 -1
- package/dist/data.js +6 -6
- package/dist/data.js.map +1 -1
- package/dist/dataset.js +5 -5
- package/dist/dataset.js.map +1 -1
- package/dist/index.d.ts +92 -13
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +139 -15
- package/dist/index.js.map +1 -1
- package/dist/mcp.d.ts +1 -1
- package/dist/mcp.d.ts.map +1 -1
- package/dist/mcp.js +17 -10
- package/dist/mcp.js.map +1 -1
- package/dist/product.js +2 -2
- package/dist/product.js.map +1 -1
- package/dist/sdk.d.ts.map +1 -1
- package/dist/sdk.js +52 -16
- package/dist/sdk.js.map +1 -1
- package/dist/site.d.ts.map +1 -1
- package/dist/site.js +12 -8
- package/dist/site.js.map +1 -1
- package/dist/types.d.ts +830 -12
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +495 -2
- package/dist/types.js.map +1 -1
- package/dist/worker.d.ts +205 -0
- package/dist/worker.d.ts.map +1 -0
- package/dist/worker.js +356 -0
- package/dist/worker.js.map +1 -0
- package/package.json +29 -13
- package/src/api.ts +7 -7
- package/src/app.ts +6 -6
- package/src/client.ts +192 -0
- package/src/content.ts +7 -7
- package/src/data.ts +12 -7
- package/src/dataset.ts +5 -5
- package/src/index.ts +151 -15
- package/src/mcp.ts +18 -11
- package/src/product.ts +2 -2
- package/src/sdk.ts +54 -15
- package/src/site.ts +12 -8
- package/src/types.ts +821 -12
- package/src/worker.ts +525 -0
- package/test/product.test.ts +53 -198
- package/test/unified-types.test.ts +589 -0
- package/test/worker.test.ts +912 -0
- package/vitest.config.ts +42 -0
- package/wrangler.jsonc +36 -0
- package/LICENSE +0 -21
- package/dist/features/define.d.ts +0 -63
- package/dist/features/define.d.ts.map +0 -1
- package/dist/features/define.js +0 -72
- package/dist/features/define.js.map +0 -1
- package/dist/features/flags.d.ts +0 -98
- package/dist/features/flags.d.ts.map +0 -1
- package/dist/features/flags.js +0 -145
- package/dist/features/flags.js.map +0 -1
- package/dist/features/toggles.d.ts +0 -75
- package/dist/features/toggles.d.ts.map +0 -1
- package/dist/features/toggles.js +0 -107
- package/dist/features/toggles.js.map +0 -1
- package/dist/tiers/define.d.ts +0 -63
- package/dist/tiers/define.d.ts.map +0 -1
- package/dist/tiers/define.js +0 -78
- package/dist/tiers/define.js.map +0 -1
- package/dist/tiers/entitlements.d.ts +0 -94
- package/dist/tiers/entitlements.d.ts.map +0 -1
- package/dist/tiers/entitlements.js +0 -94
- package/dist/tiers/entitlements.js.map +0 -1
- package/src/api.js +0 -128
- package/src/app.js +0 -106
- package/src/content.js +0 -77
- package/src/data.js +0 -106
- package/src/dataset.js +0 -49
- package/src/entities/ai.js +0 -858
- package/src/entities/content.js +0 -783
- package/src/entities/index.js +0 -88
- package/src/entities/interfaces.js +0 -929
- package/src/entities/lifecycle.js +0 -803
- package/src/entities/products.js +0 -797
- package/src/entities/web.js +0 -657
- package/src/features/define.ts +0 -130
- package/src/features/flags.ts +0 -247
- package/src/features/toggles.ts +0 -189
- package/src/index.js +0 -35
- package/src/mcp.js +0 -139
- package/src/pricing/billing.ts +0 -386
- package/src/pricing/plans.ts +0 -214
- package/src/product.js +0 -53
- package/src/registry.js +0 -31
- package/src/sdk.js +0 -127
- package/src/site.js +0 -112
- package/src/tiers/define.ts +0 -137
- package/src/tiers/entitlements.ts +0 -201
- package/src/types.js +0 -4
- package/test/analytics/events.test.ts +0 -319
- package/test/analytics/experiments.test.ts +0 -327
- package/test/features/define.test.ts +0 -187
- package/test/features/flags.test.ts +0 -259
- package/test/features/toggles.test.ts +0 -178
- package/test/lifecycle/stages.test.ts +0 -233
- package/test/lifecycle/transitions.test.ts +0 -207
- package/test/onboarding/flows.test.ts +0 -307
- package/test/pricing/billing.test.ts +0 -287
- package/test/pricing/plans.test.ts +0 -307
- package/test/roadmap/milestones.test.ts +0 -231
- package/test/roadmap/priorities.test.ts +0 -239
- package/test/tiers/define.test.ts +0 -192
- package/test/tiers/entitlements.test.ts +0 -220
package/src/mcp.js
DELETED
|
@@ -1,139 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* MCP() - Define a Model Context Protocol server
|
|
3
|
-
*/
|
|
4
|
-
import { registerProduct } from './product.js';
|
|
5
|
-
/**
|
|
6
|
-
* Create an MCP server definition
|
|
7
|
-
*
|
|
8
|
-
* @example
|
|
9
|
-
* ```ts
|
|
10
|
-
* const mcpServer = MCP({
|
|
11
|
-
* id: 'my-mcp',
|
|
12
|
-
* name: 'My MCP Server',
|
|
13
|
-
* description: 'Custom MCP server for AI tools',
|
|
14
|
-
* version: '1.0.0',
|
|
15
|
-
* transport: 'stdio',
|
|
16
|
-
* tools: [
|
|
17
|
-
* Tool('searchFiles', 'Search for files in the project', {
|
|
18
|
-
* query: 'Search query',
|
|
19
|
-
* path: 'Directory to search in',
|
|
20
|
-
* }),
|
|
21
|
-
* Tool('readFile', 'Read file contents', {
|
|
22
|
-
* path: 'File path to read',
|
|
23
|
-
* }),
|
|
24
|
-
* ],
|
|
25
|
-
* resources: [
|
|
26
|
-
* Resource('file://', 'Project Files', 'Access to project files'),
|
|
27
|
-
* ],
|
|
28
|
-
* prompts: [
|
|
29
|
-
* Prompt('codeReview', 'Review code for best practices',
|
|
30
|
-
* 'Review the following code:\n\n{{code}}'),
|
|
31
|
-
* ],
|
|
32
|
-
* })
|
|
33
|
-
* ```
|
|
34
|
-
*/
|
|
35
|
-
export function MCP(config) {
|
|
36
|
-
const mcp = {
|
|
37
|
-
type: 'mcp',
|
|
38
|
-
id: config.id,
|
|
39
|
-
name: config.name,
|
|
40
|
-
description: config.description,
|
|
41
|
-
version: config.version,
|
|
42
|
-
transport: config.transport,
|
|
43
|
-
tools: config.tools,
|
|
44
|
-
resources: config.resources,
|
|
45
|
-
prompts: config.prompts,
|
|
46
|
-
config: config.config,
|
|
47
|
-
metadata: config.metadata,
|
|
48
|
-
tags: config.tags,
|
|
49
|
-
status: config.status || 'active',
|
|
50
|
-
};
|
|
51
|
-
return registerProduct(mcp);
|
|
52
|
-
}
|
|
53
|
-
/**
|
|
54
|
-
* Helper to create an MCP tool
|
|
55
|
-
*
|
|
56
|
-
* @example
|
|
57
|
-
* ```ts
|
|
58
|
-
* const tool = Tool(
|
|
59
|
-
* 'searchCode',
|
|
60
|
-
* 'Search code using regex',
|
|
61
|
-
* {
|
|
62
|
-
* pattern: 'Regex pattern to search for',
|
|
63
|
-
* path: 'Directory to search in',
|
|
64
|
-
* },
|
|
65
|
-
* async (input) => {
|
|
66
|
-
* // Tool implementation
|
|
67
|
-
* return { matches: [] }
|
|
68
|
-
* }
|
|
69
|
-
* )
|
|
70
|
-
* ```
|
|
71
|
-
*/
|
|
72
|
-
export function Tool(name, description, inputSchema, handler) {
|
|
73
|
-
return {
|
|
74
|
-
name,
|
|
75
|
-
description,
|
|
76
|
-
inputSchema,
|
|
77
|
-
handler,
|
|
78
|
-
};
|
|
79
|
-
}
|
|
80
|
-
/**
|
|
81
|
-
* Helper to create an MCP resource
|
|
82
|
-
*
|
|
83
|
-
* @example
|
|
84
|
-
* ```ts
|
|
85
|
-
* const resource = Resource(
|
|
86
|
-
* 'file://project',
|
|
87
|
-
* 'Project Files',
|
|
88
|
-
* 'Access to all project files',
|
|
89
|
-
* 'application/json'
|
|
90
|
-
* )
|
|
91
|
-
* ```
|
|
92
|
-
*/
|
|
93
|
-
export function Resource(uri, name, description, mimeType) {
|
|
94
|
-
return {
|
|
95
|
-
uri,
|
|
96
|
-
name,
|
|
97
|
-
description,
|
|
98
|
-
mimeType,
|
|
99
|
-
};
|
|
100
|
-
}
|
|
101
|
-
/**
|
|
102
|
-
* Helper to create an MCP prompt
|
|
103
|
-
*
|
|
104
|
-
* @example
|
|
105
|
-
* ```ts
|
|
106
|
-
* const prompt = Prompt(
|
|
107
|
-
* 'summarize',
|
|
108
|
-
* 'Summarize text',
|
|
109
|
-
* 'Summarize the following:\n\n{{text}}',
|
|
110
|
-
* { text: 'Text to summarize' }
|
|
111
|
-
* )
|
|
112
|
-
* ```
|
|
113
|
-
*/
|
|
114
|
-
export function Prompt(name, description, template, args) {
|
|
115
|
-
return {
|
|
116
|
-
name,
|
|
117
|
-
description,
|
|
118
|
-
template,
|
|
119
|
-
arguments: args,
|
|
120
|
-
};
|
|
121
|
-
}
|
|
122
|
-
/**
|
|
123
|
-
* Helper to configure MCP server
|
|
124
|
-
*
|
|
125
|
-
* @example
|
|
126
|
-
* ```ts
|
|
127
|
-
* const config = MCPConfig({
|
|
128
|
-
* port: 3000,
|
|
129
|
-
* host: 'localhost',
|
|
130
|
-
* auth: {
|
|
131
|
-
* type: 'bearer',
|
|
132
|
-
* token: process.env.MCP_TOKEN,
|
|
133
|
-
* },
|
|
134
|
-
* })
|
|
135
|
-
* ```
|
|
136
|
-
*/
|
|
137
|
-
export function MCPConfig(config) {
|
|
138
|
-
return config;
|
|
139
|
-
}
|
package/src/pricing/billing.ts
DELETED
|
@@ -1,386 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Billing Service
|
|
3
|
-
* Subscription and invoice management
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
import { randomUUID } from 'crypto'
|
|
7
|
-
|
|
8
|
-
/**
|
|
9
|
-
* Subscription status
|
|
10
|
-
*/
|
|
11
|
-
export type SubscriptionStatus = 'active' | 'trialing' | 'canceled' | 'past_due' | 'paused'
|
|
12
|
-
|
|
13
|
-
/**
|
|
14
|
-
* Invoice status
|
|
15
|
-
*/
|
|
16
|
-
export type InvoiceStatus = 'draft' | 'open' | 'paid' | 'void' | 'uncollectible'
|
|
17
|
-
|
|
18
|
-
/**
|
|
19
|
-
* Payment method type
|
|
20
|
-
*/
|
|
21
|
-
export type PaymentMethodType = 'card' | 'bank_transfer' | 'paypal'
|
|
22
|
-
|
|
23
|
-
/**
|
|
24
|
-
* Subscription definition
|
|
25
|
-
*/
|
|
26
|
-
export interface Subscription {
|
|
27
|
-
id: string
|
|
28
|
-
customerId: string
|
|
29
|
-
planId: string
|
|
30
|
-
status: SubscriptionStatus
|
|
31
|
-
startDate: Date
|
|
32
|
-
currentPeriodStart: Date
|
|
33
|
-
currentPeriodEnd: Date
|
|
34
|
-
trialEnd?: Date
|
|
35
|
-
cancelAtPeriodEnd: boolean
|
|
36
|
-
scheduledPlanId?: string
|
|
37
|
-
metadata?: Record<string, unknown>
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
/**
|
|
41
|
-
* Invoice definition
|
|
42
|
-
*/
|
|
43
|
-
export interface Invoice {
|
|
44
|
-
id: string
|
|
45
|
-
subscriptionId: string
|
|
46
|
-
customerId: string
|
|
47
|
-
status: InvoiceStatus
|
|
48
|
-
subtotal: number
|
|
49
|
-
discountAmount: number
|
|
50
|
-
total: number
|
|
51
|
-
currency: string
|
|
52
|
-
usageCharges?: { metric: string; quantity: number; amount: number }[]
|
|
53
|
-
paidAt?: Date
|
|
54
|
-
createdAt: Date
|
|
55
|
-
dueDate: Date
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
/**
|
|
59
|
-
* Payment method
|
|
60
|
-
*/
|
|
61
|
-
export interface PaymentMethod {
|
|
62
|
-
id: string
|
|
63
|
-
customerId: string
|
|
64
|
-
type: PaymentMethodType
|
|
65
|
-
details: {
|
|
66
|
-
last4?: string
|
|
67
|
-
brand?: string
|
|
68
|
-
expMonth?: number
|
|
69
|
-
expYear?: number
|
|
70
|
-
}
|
|
71
|
-
isDefault: boolean
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
/**
|
|
75
|
-
* Usage record
|
|
76
|
-
*/
|
|
77
|
-
export interface UsageRecord {
|
|
78
|
-
subscriptionId: string
|
|
79
|
-
metric: string
|
|
80
|
-
quantity: number
|
|
81
|
-
timestamp: Date
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
/**
|
|
85
|
-
* Billing event
|
|
86
|
-
*/
|
|
87
|
-
export interface BillingEvent {
|
|
88
|
-
type: string
|
|
89
|
-
timestamp: Date
|
|
90
|
-
data: Record<string, unknown>
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
/**
|
|
94
|
-
* Billing service options
|
|
95
|
-
*/
|
|
96
|
-
export interface BillingServiceOptions {
|
|
97
|
-
planPrices?: Record<string, number>
|
|
98
|
-
coupons?: Record<string, number>
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
/**
|
|
102
|
-
* Billing service interface
|
|
103
|
-
*/
|
|
104
|
-
export interface BillingService {
|
|
105
|
-
// Subscriptions
|
|
106
|
-
createSubscription(params: {
|
|
107
|
-
customerId: string
|
|
108
|
-
planId: string
|
|
109
|
-
startDate: Date
|
|
110
|
-
trialDays?: number
|
|
111
|
-
}): Subscription
|
|
112
|
-
|
|
113
|
-
cancelSubscription(
|
|
114
|
-
subscriptionId: string,
|
|
115
|
-
options: { immediate: boolean }
|
|
116
|
-
): Subscription
|
|
117
|
-
|
|
118
|
-
changeSubscription(
|
|
119
|
-
subscriptionId: string,
|
|
120
|
-
options: { newPlanId: string; prorate: boolean }
|
|
121
|
-
): Subscription
|
|
122
|
-
|
|
123
|
-
getSubscription(id: string): Subscription | undefined
|
|
124
|
-
getSubscriptionsByCustomer(customerId: string): Subscription[]
|
|
125
|
-
|
|
126
|
-
// Invoices
|
|
127
|
-
generateInvoice(
|
|
128
|
-
subscriptionId: string,
|
|
129
|
-
options?: { couponCode?: string }
|
|
130
|
-
): Invoice
|
|
131
|
-
|
|
132
|
-
markInvoicePaid(
|
|
133
|
-
invoiceId: string,
|
|
134
|
-
options: { paymentMethodId: string; paidAt: Date }
|
|
135
|
-
): Invoice
|
|
136
|
-
|
|
137
|
-
getInvoice(id: string): Invoice | undefined
|
|
138
|
-
getInvoicesByCustomer(customerId: string): Invoice[]
|
|
139
|
-
|
|
140
|
-
// Usage
|
|
141
|
-
recordUsage(
|
|
142
|
-
subscriptionId: string,
|
|
143
|
-
usage: { metric: string; quantity: number; timestamp: Date }
|
|
144
|
-
): void
|
|
145
|
-
|
|
146
|
-
// Payment methods
|
|
147
|
-
addPaymentMethod(params: {
|
|
148
|
-
customerId: string
|
|
149
|
-
type: PaymentMethodType
|
|
150
|
-
details: PaymentMethod['details']
|
|
151
|
-
}): PaymentMethod
|
|
152
|
-
|
|
153
|
-
setDefaultPaymentMethod(customerId: string, paymentMethodId: string): void
|
|
154
|
-
getDefaultPaymentMethod(customerId: string): PaymentMethod | undefined
|
|
155
|
-
getPaymentMethods(customerId: string): PaymentMethod[]
|
|
156
|
-
removePaymentMethod(paymentMethodId: string): void
|
|
157
|
-
|
|
158
|
-
// Events
|
|
159
|
-
on(event: string, handler: (e: BillingEvent) => void): void
|
|
160
|
-
off(event: string, handler: (e: BillingEvent) => void): void
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
/**
|
|
164
|
-
* Create a billing service
|
|
165
|
-
*/
|
|
166
|
-
export function createBillingService(options: BillingServiceOptions = {}): BillingService {
|
|
167
|
-
const subscriptions = new Map<string, Subscription>()
|
|
168
|
-
const invoices = new Map<string, Invoice>()
|
|
169
|
-
const paymentMethods = new Map<string, PaymentMethod>()
|
|
170
|
-
const usageRecords: UsageRecord[] = []
|
|
171
|
-
const eventHandlers = new Map<string, Set<(e: BillingEvent) => void>>()
|
|
172
|
-
|
|
173
|
-
const defaultPrices: Record<string, number> = {
|
|
174
|
-
'pro-monthly': 29,
|
|
175
|
-
'enterprise-monthly': 99,
|
|
176
|
-
metered: 0,
|
|
177
|
-
...options.planPrices,
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
const coupons: Record<string, number> = {
|
|
181
|
-
SAVE20: 20,
|
|
182
|
-
...options.coupons,
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
const emit = (type: string, data: Record<string, unknown>) => {
|
|
186
|
-
const event: BillingEvent = { type, timestamp: new Date(), data }
|
|
187
|
-
const handlers = eventHandlers.get(type)
|
|
188
|
-
if (handlers) {
|
|
189
|
-
for (const handler of handlers) {
|
|
190
|
-
handler(event)
|
|
191
|
-
}
|
|
192
|
-
}
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
return {
|
|
196
|
-
createSubscription(params): Subscription {
|
|
197
|
-
const id = randomUUID()
|
|
198
|
-
const periodEnd = new Date(params.startDate)
|
|
199
|
-
periodEnd.setMonth(periodEnd.getMonth() + 1)
|
|
200
|
-
|
|
201
|
-
let trialEnd: Date | undefined
|
|
202
|
-
let status: SubscriptionStatus = 'active'
|
|
203
|
-
|
|
204
|
-
if (params.trialDays) {
|
|
205
|
-
trialEnd = new Date(params.startDate)
|
|
206
|
-
trialEnd.setDate(trialEnd.getDate() + params.trialDays)
|
|
207
|
-
status = 'trialing'
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
const subscription: Subscription = {
|
|
211
|
-
id,
|
|
212
|
-
customerId: params.customerId,
|
|
213
|
-
planId: params.planId,
|
|
214
|
-
status,
|
|
215
|
-
startDate: params.startDate,
|
|
216
|
-
currentPeriodStart: params.startDate,
|
|
217
|
-
currentPeriodEnd: periodEnd,
|
|
218
|
-
trialEnd,
|
|
219
|
-
cancelAtPeriodEnd: false,
|
|
220
|
-
}
|
|
221
|
-
|
|
222
|
-
subscriptions.set(id, subscription)
|
|
223
|
-
emit('subscription.created', { subscriptionId: id, customerId: params.customerId })
|
|
224
|
-
return subscription
|
|
225
|
-
},
|
|
226
|
-
|
|
227
|
-
cancelSubscription(subscriptionId, options): Subscription {
|
|
228
|
-
const subscription = subscriptions.get(subscriptionId)
|
|
229
|
-
if (!subscription) {
|
|
230
|
-
throw new Error('Subscription not found')
|
|
231
|
-
}
|
|
232
|
-
|
|
233
|
-
if (options.immediate) {
|
|
234
|
-
subscription.status = 'canceled'
|
|
235
|
-
} else {
|
|
236
|
-
subscription.cancelAtPeriodEnd = true
|
|
237
|
-
}
|
|
238
|
-
|
|
239
|
-
emit('subscription.canceled', { subscriptionId })
|
|
240
|
-
return subscription
|
|
241
|
-
},
|
|
242
|
-
|
|
243
|
-
changeSubscription(subscriptionId, options): Subscription {
|
|
244
|
-
const subscription = subscriptions.get(subscriptionId)
|
|
245
|
-
if (!subscription) {
|
|
246
|
-
throw new Error('Subscription not found')
|
|
247
|
-
}
|
|
248
|
-
|
|
249
|
-
if (options.prorate) {
|
|
250
|
-
subscription.planId = options.newPlanId
|
|
251
|
-
} else {
|
|
252
|
-
subscription.scheduledPlanId = options.newPlanId
|
|
253
|
-
}
|
|
254
|
-
|
|
255
|
-
emit('subscription.updated', { subscriptionId, newPlanId: options.newPlanId })
|
|
256
|
-
return subscription
|
|
257
|
-
},
|
|
258
|
-
|
|
259
|
-
getSubscription(id): Subscription | undefined {
|
|
260
|
-
return subscriptions.get(id)
|
|
261
|
-
},
|
|
262
|
-
|
|
263
|
-
getSubscriptionsByCustomer(customerId): Subscription[] {
|
|
264
|
-
return Array.from(subscriptions.values()).filter((s) => s.customerId === customerId)
|
|
265
|
-
},
|
|
266
|
-
|
|
267
|
-
generateInvoice(subscriptionId, invoiceOptions = {}): Invoice {
|
|
268
|
-
const subscription = subscriptions.get(subscriptionId)
|
|
269
|
-
if (!subscription) {
|
|
270
|
-
throw new Error('Subscription not found')
|
|
271
|
-
}
|
|
272
|
-
|
|
273
|
-
const id = randomUUID()
|
|
274
|
-
const subtotal = defaultPrices[subscription.planId] ?? 29
|
|
275
|
-
let discountAmount = 0
|
|
276
|
-
|
|
277
|
-
if (invoiceOptions.couponCode && coupons[invoiceOptions.couponCode]) {
|
|
278
|
-
discountAmount = (subtotal * coupons[invoiceOptions.couponCode]) / 100
|
|
279
|
-
}
|
|
280
|
-
|
|
281
|
-
// Add usage charges if applicable
|
|
282
|
-
let usageCharges: Invoice['usageCharges']
|
|
283
|
-
const subscriptionUsage = usageRecords.filter((u) => u.subscriptionId === subscriptionId)
|
|
284
|
-
if (subscriptionUsage.length > 0) {
|
|
285
|
-
usageCharges = subscriptionUsage.map((u) => ({
|
|
286
|
-
metric: u.metric,
|
|
287
|
-
quantity: u.quantity,
|
|
288
|
-
amount: u.quantity * 0.001, // Example rate
|
|
289
|
-
}))
|
|
290
|
-
}
|
|
291
|
-
|
|
292
|
-
const invoice: Invoice = {
|
|
293
|
-
id,
|
|
294
|
-
subscriptionId,
|
|
295
|
-
customerId: subscription.customerId,
|
|
296
|
-
status: 'draft',
|
|
297
|
-
subtotal,
|
|
298
|
-
discountAmount,
|
|
299
|
-
total: subtotal - discountAmount + (usageCharges?.reduce((sum, c) => sum + c.amount, 0) ?? 0),
|
|
300
|
-
currency: 'USD',
|
|
301
|
-
usageCharges,
|
|
302
|
-
createdAt: new Date(),
|
|
303
|
-
dueDate: new Date(Date.now() + 30 * 24 * 60 * 60 * 1000),
|
|
304
|
-
}
|
|
305
|
-
|
|
306
|
-
invoices.set(id, invoice)
|
|
307
|
-
return invoice
|
|
308
|
-
},
|
|
309
|
-
|
|
310
|
-
markInvoicePaid(invoiceId, options): Invoice {
|
|
311
|
-
const invoice = invoices.get(invoiceId)
|
|
312
|
-
if (!invoice) {
|
|
313
|
-
throw new Error('Invoice not found')
|
|
314
|
-
}
|
|
315
|
-
|
|
316
|
-
invoice.status = 'paid'
|
|
317
|
-
invoice.paidAt = options.paidAt
|
|
318
|
-
|
|
319
|
-
emit('invoice.paid', { invoiceId, amount: invoice.total })
|
|
320
|
-
return invoice
|
|
321
|
-
},
|
|
322
|
-
|
|
323
|
-
getInvoice(id): Invoice | undefined {
|
|
324
|
-
return invoices.get(id)
|
|
325
|
-
},
|
|
326
|
-
|
|
327
|
-
getInvoicesByCustomer(customerId): Invoice[] {
|
|
328
|
-
return Array.from(invoices.values()).filter((i) => i.customerId === customerId)
|
|
329
|
-
},
|
|
330
|
-
|
|
331
|
-
recordUsage(subscriptionId, usage): void {
|
|
332
|
-
usageRecords.push({ subscriptionId, ...usage })
|
|
333
|
-
},
|
|
334
|
-
|
|
335
|
-
addPaymentMethod(params): PaymentMethod {
|
|
336
|
-
const id = randomUUID()
|
|
337
|
-
const existingMethods = Array.from(paymentMethods.values()).filter(
|
|
338
|
-
(pm) => pm.customerId === params.customerId
|
|
339
|
-
)
|
|
340
|
-
|
|
341
|
-
const method: PaymentMethod = {
|
|
342
|
-
id,
|
|
343
|
-
customerId: params.customerId,
|
|
344
|
-
type: params.type,
|
|
345
|
-
details: params.details,
|
|
346
|
-
isDefault: existingMethods.length === 0,
|
|
347
|
-
}
|
|
348
|
-
|
|
349
|
-
paymentMethods.set(id, method)
|
|
350
|
-
return method
|
|
351
|
-
},
|
|
352
|
-
|
|
353
|
-
setDefaultPaymentMethod(customerId, paymentMethodId): void {
|
|
354
|
-
for (const method of paymentMethods.values()) {
|
|
355
|
-
if (method.customerId === customerId) {
|
|
356
|
-
method.isDefault = method.id === paymentMethodId
|
|
357
|
-
}
|
|
358
|
-
}
|
|
359
|
-
},
|
|
360
|
-
|
|
361
|
-
getDefaultPaymentMethod(customerId): PaymentMethod | undefined {
|
|
362
|
-
return Array.from(paymentMethods.values()).find(
|
|
363
|
-
(pm) => pm.customerId === customerId && pm.isDefault
|
|
364
|
-
)
|
|
365
|
-
},
|
|
366
|
-
|
|
367
|
-
getPaymentMethods(customerId): PaymentMethod[] {
|
|
368
|
-
return Array.from(paymentMethods.values()).filter((pm) => pm.customerId === customerId)
|
|
369
|
-
},
|
|
370
|
-
|
|
371
|
-
removePaymentMethod(paymentMethodId): void {
|
|
372
|
-
paymentMethods.delete(paymentMethodId)
|
|
373
|
-
},
|
|
374
|
-
|
|
375
|
-
on(event, handler): void {
|
|
376
|
-
if (!eventHandlers.has(event)) {
|
|
377
|
-
eventHandlers.set(event, new Set())
|
|
378
|
-
}
|
|
379
|
-
eventHandlers.get(event)!.add(handler)
|
|
380
|
-
},
|
|
381
|
-
|
|
382
|
-
off(event, handler): void {
|
|
383
|
-
eventHandlers.get(event)?.delete(handler)
|
|
384
|
-
},
|
|
385
|
-
}
|
|
386
|
-
}
|