@skillsmith/core 2.1.1 → 2.1.2
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/.tsbuildinfo +1 -1
- package/dist/src/analysis/types.d.ts +2 -0
- package/dist/src/analysis/types.d.ts.map +1 -1
- package/dist/src/analysis/types.js +13 -1
- package/dist/src/analysis/types.js.map +1 -1
- package/dist/src/analytics/schema.d.ts +1 -1
- package/dist/src/analytics/schema.d.ts.map +1 -1
- package/dist/src/analytics/schema.js +68 -0
- package/dist/src/analytics/schema.js.map +1 -1
- package/dist/src/api/client.d.ts +25 -21
- package/dist/src/api/client.d.ts.map +1 -1
- package/dist/src/api/client.js +13 -8
- package/dist/src/api/client.js.map +1 -1
- package/dist/src/billing/BillingService.d.ts +139 -0
- package/dist/src/billing/BillingService.d.ts.map +1 -0
- package/dist/src/billing/BillingService.js +393 -0
- package/dist/src/billing/BillingService.js.map +1 -0
- package/dist/src/billing/GDPRComplianceService.d.ts +176 -0
- package/dist/src/billing/GDPRComplianceService.d.ts.map +1 -0
- package/dist/src/billing/GDPRComplianceService.js +361 -0
- package/dist/src/billing/GDPRComplianceService.js.map +1 -0
- package/dist/src/billing/StripeClient.d.ts +177 -0
- package/dist/src/billing/StripeClient.d.ts.map +1 -0
- package/dist/src/billing/StripeClient.js +462 -0
- package/dist/src/billing/StripeClient.js.map +1 -0
- package/dist/src/billing/StripeReconciliationJob.d.ts +95 -0
- package/dist/src/billing/StripeReconciliationJob.d.ts.map +1 -0
- package/dist/src/billing/StripeReconciliationJob.js +405 -0
- package/dist/src/billing/StripeReconciliationJob.js.map +1 -0
- package/dist/src/billing/StripeWebhookHandler.d.ts +92 -0
- package/dist/src/billing/StripeWebhookHandler.d.ts.map +1 -0
- package/dist/src/billing/StripeWebhookHandler.js +409 -0
- package/dist/src/billing/StripeWebhookHandler.js.map +1 -0
- package/dist/src/billing/index.d.ts +18 -0
- package/dist/src/billing/index.d.ts.map +1 -0
- package/dist/src/billing/index.js +19 -0
- package/dist/src/billing/index.js.map +1 -0
- package/dist/src/billing/types.d.ts +266 -0
- package/dist/src/billing/types.d.ts.map +1 -0
- package/dist/src/billing/types.js +23 -0
- package/dist/src/billing/types.js.map +1 -0
- package/dist/src/embeddings/hnsw-store.d.ts +568 -0
- package/dist/src/embeddings/hnsw-store.d.ts.map +1 -0
- package/dist/src/embeddings/hnsw-store.js +805 -0
- package/dist/src/embeddings/hnsw-store.js.map +1 -0
- package/dist/src/embeddings/index.d.ts +2 -0
- package/dist/src/embeddings/index.d.ts.map +1 -1
- package/dist/src/embeddings/index.js +2 -0
- package/dist/src/embeddings/index.js.map +1 -1
- package/dist/src/index.d.ts +1 -0
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +2 -0
- package/dist/src/index.js.map +1 -1
- package/dist/src/learning/PatternStore.d.ts +457 -0
- package/dist/src/learning/PatternStore.d.ts.map +1 -0
- package/dist/src/learning/PatternStore.js +893 -0
- package/dist/src/learning/PatternStore.js.map +1 -0
- package/dist/src/learning/ReasoningBankIntegration.d.ts +403 -0
- package/dist/src/learning/ReasoningBankIntegration.d.ts.map +1 -0
- package/dist/src/learning/ReasoningBankIntegration.js +627 -0
- package/dist/src/learning/ReasoningBankIntegration.js.map +1 -0
- package/dist/src/learning/index.d.ts +15 -0
- package/dist/src/learning/index.d.ts.map +1 -0
- package/dist/src/learning/index.js +15 -0
- package/dist/src/learning/index.js.map +1 -0
- package/dist/src/routing/SONARouter.d.ts +154 -0
- package/dist/src/routing/SONARouter.d.ts.map +1 -0
- package/dist/src/routing/SONARouter.js +679 -0
- package/dist/src/routing/SONARouter.js.map +1 -0
- package/dist/src/routing/index.d.ts +9 -0
- package/dist/src/routing/index.d.ts.map +1 -0
- package/dist/src/routing/index.js +10 -0
- package/dist/src/routing/index.js.map +1 -0
- package/dist/src/routing/types.d.ts +331 -0
- package/dist/src/routing/types.d.ts.map +1 -0
- package/dist/src/routing/types.js +203 -0
- package/dist/src/routing/types.js.map +1 -0
- package/dist/src/scripts/__tests__/scan-imported-skills.test.js +5 -0
- package/dist/src/scripts/__tests__/scan-imported-skills.test.js.map +1 -1
- package/dist/src/security/SkillSandbox.d.ts +156 -0
- package/dist/src/security/SkillSandbox.d.ts.map +1 -0
- package/dist/src/security/SkillSandbox.js +303 -0
- package/dist/src/security/SkillSandbox.js.map +1 -0
- package/dist/src/security/index.d.ts +3 -1
- package/dist/src/security/index.d.ts.map +1 -1
- package/dist/src/security/index.js +5 -1
- package/dist/src/security/index.js.map +1 -1
- package/dist/src/security/rate-limiter/presets.d.ts +12 -0
- package/dist/src/security/rate-limiter/presets.d.ts.map +1 -1
- package/dist/src/security/rate-limiter/presets.js +12 -0
- package/dist/src/security/rate-limiter/presets.js.map +1 -1
- package/dist/src/security/sanitization.d.ts +85 -0
- package/dist/src/security/sanitization.d.ts.map +1 -1
- package/dist/src/security/sanitization.js +133 -0
- package/dist/src/security/sanitization.js.map +1 -1
- package/dist/src/security/scanner/SecurityScanner.d.ts +22 -1
- package/dist/src/security/scanner/SecurityScanner.d.ts.map +1 -1
- package/dist/src/security/scanner/SecurityScanner.js +190 -35
- package/dist/src/security/scanner/SecurityScanner.js.map +1 -1
- package/dist/src/security/scanner/patterns.d.ts +13 -0
- package/dist/src/security/scanner/patterns.d.ts.map +1 -1
- package/dist/src/security/scanner/patterns.js +51 -0
- package/dist/src/security/scanner/patterns.js.map +1 -1
- package/dist/src/security/scanner/types.d.ts +13 -1
- package/dist/src/security/scanner/types.d.ts.map +1 -1
- package/dist/src/security/scanner/weights.d.ts.map +1 -1
- package/dist/src/security/scanner/weights.js +1 -0
- package/dist/src/security/scanner/weights.js.map +1 -1
- package/dist/src/session/SessionManager.d.ts +7 -0
- package/dist/src/session/SessionManager.d.ts.map +1 -1
- package/dist/src/session/SessionManager.js +117 -10
- package/dist/src/session/SessionManager.js.map +1 -1
- package/dist/src/sync/SyncEngine.d.ts.map +1 -1
- package/dist/src/sync/SyncEngine.js +9 -1
- package/dist/src/sync/SyncEngine.js.map +1 -1
- package/dist/src/testing/MultiLLMProvider.d.ts +374 -0
- package/dist/src/testing/MultiLLMProvider.d.ts.map +1 -0
- package/dist/src/testing/MultiLLMProvider.js +720 -0
- package/dist/src/testing/MultiLLMProvider.js.map +1 -0
- package/dist/src/testing/index.d.ts +8 -0
- package/dist/src/testing/index.d.ts.map +1 -0
- package/dist/src/testing/index.js +9 -0
- package/dist/src/testing/index.js.map +1 -0
- package/dist/tests/SecurityScanner.test.js +337 -1
- package/dist/tests/SecurityScanner.test.js.map +1 -1
- package/dist/tests/billing/BillingService.test.d.ts +7 -0
- package/dist/tests/billing/BillingService.test.d.ts.map +1 -0
- package/dist/tests/billing/BillingService.test.js +168 -0
- package/dist/tests/billing/BillingService.test.js.map +1 -0
- package/dist/tests/billing/GDPRCompliance.test.d.ts +7 -0
- package/dist/tests/billing/GDPRCompliance.test.d.ts.map +1 -0
- package/dist/tests/billing/GDPRCompliance.test.js +195 -0
- package/dist/tests/billing/GDPRCompliance.test.js.map +1 -0
- package/dist/tests/billing/StripeReconciliation.test.d.ts +7 -0
- package/dist/tests/billing/StripeReconciliation.test.d.ts.map +1 -0
- package/dist/tests/billing/StripeReconciliation.test.js +266 -0
- package/dist/tests/billing/StripeReconciliation.test.js.map +1 -0
- package/dist/tests/billing/stripe-validators.test.d.ts +7 -0
- package/dist/tests/billing/stripe-validators.test.d.ts.map +1 -0
- package/dist/tests/billing/stripe-validators.test.js +107 -0
- package/dist/tests/billing/stripe-validators.test.js.map +1 -0
- package/dist/tests/embeddings/hnsw-store.test.d.ts +7 -0
- package/dist/tests/embeddings/hnsw-store.test.d.ts.map +1 -0
- package/dist/tests/embeddings/hnsw-store.test.js +295 -0
- package/dist/tests/embeddings/hnsw-store.test.js.map +1 -0
- package/dist/tests/integration/neural/e2e-learning.test.d.ts +17 -0
- package/dist/tests/integration/neural/e2e-learning.test.d.ts.map +1 -0
- package/dist/tests/integration/neural/e2e-learning.test.js +238 -0
- package/dist/tests/integration/neural/e2e-learning.test.js.map +1 -0
- package/dist/tests/integration/neural/helpers.d.ts +132 -0
- package/dist/tests/integration/neural/helpers.d.ts.map +1 -0
- package/dist/tests/integration/neural/helpers.js +287 -0
- package/dist/tests/integration/neural/helpers.js.map +1 -0
- package/dist/tests/integration/neural/personalization.test.d.ts +21 -0
- package/dist/tests/integration/neural/personalization.test.d.ts.map +1 -0
- package/dist/tests/integration/neural/personalization.test.js +304 -0
- package/dist/tests/integration/neural/personalization.test.js.map +1 -0
- package/dist/tests/integration/neural/preference-learner.test.d.ts +23 -0
- package/dist/tests/integration/neural/preference-learner.test.d.ts.map +1 -0
- package/dist/tests/integration/neural/preference-learner.test.js +289 -0
- package/dist/tests/integration/neural/preference-learner.test.js.map +1 -0
- package/dist/tests/integration/neural/privacy.test.d.ts +19 -0
- package/dist/tests/integration/neural/privacy.test.d.ts.map +1 -0
- package/dist/tests/integration/neural/privacy.test.js +249 -0
- package/dist/tests/integration/neural/privacy.test.js.map +1 -0
- package/dist/tests/integration/neural/setup.d.ts +175 -0
- package/dist/tests/integration/neural/setup.d.ts.map +1 -0
- package/dist/tests/integration/neural/setup.js +487 -0
- package/dist/tests/integration/neural/setup.js.map +1 -0
- package/dist/tests/integration/neural/signal-collection.test.d.ts +21 -0
- package/dist/tests/integration/neural/signal-collection.test.d.ts.map +1 -0
- package/dist/tests/integration/neural/signal-collection.test.js +232 -0
- package/dist/tests/integration/neural/signal-collection.test.js.map +1 -0
- package/dist/tests/learning/PatternStore.test.d.ts +8 -0
- package/dist/tests/learning/PatternStore.test.d.ts.map +1 -0
- package/dist/tests/learning/PatternStore.test.js +589 -0
- package/dist/tests/learning/PatternStore.test.js.map +1 -0
- package/dist/tests/learning/ReasoningBankIntegration.test.d.ts +8 -0
- package/dist/tests/learning/ReasoningBankIntegration.test.d.ts.map +1 -0
- package/dist/tests/learning/ReasoningBankIntegration.test.js +269 -0
- package/dist/tests/learning/ReasoningBankIntegration.test.js.map +1 -0
- package/dist/tests/routing/SONARouter.test.d.ts +8 -0
- package/dist/tests/routing/SONARouter.test.d.ts.map +1 -0
- package/dist/tests/routing/SONARouter.test.js +400 -0
- package/dist/tests/routing/SONARouter.test.js.map +1 -0
- package/dist/tests/security/SkillSandbox.test.d.ts +8 -0
- package/dist/tests/security/SkillSandbox.test.d.ts.map +1 -0
- package/dist/tests/security/SkillSandbox.test.js +321 -0
- package/dist/tests/security/SkillSandbox.test.js.map +1 -0
- package/dist/tests/testing/MultiLLMProvider.test.d.ts +14 -0
- package/dist/tests/testing/MultiLLMProvider.test.d.ts.map +1 -0
- package/dist/tests/testing/MultiLLMProvider.test.js +438 -0
- package/dist/tests/testing/MultiLLMProvider.test.js.map +1 -0
- package/package.json +16 -3
|
@@ -0,0 +1,393 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SMI-1063: Billing Service
|
|
3
|
+
*
|
|
4
|
+
* High-level billing operations that coordinate between:
|
|
5
|
+
* - StripeClient (Stripe API)
|
|
6
|
+
* - Database (subscription/invoice storage)
|
|
7
|
+
* - LicenseKeyGenerator (license key generation)
|
|
8
|
+
*/
|
|
9
|
+
import { randomUUID } from 'crypto';
|
|
10
|
+
import { createLogger } from '../utils/logger.js';
|
|
11
|
+
import { BillingError } from './types.js';
|
|
12
|
+
const logger = createLogger('BillingService');
|
|
13
|
+
// ============================================================================
|
|
14
|
+
// BillingService Class
|
|
15
|
+
// ============================================================================
|
|
16
|
+
/**
|
|
17
|
+
* Billing service for subscription management
|
|
18
|
+
*
|
|
19
|
+
* @example
|
|
20
|
+
* ```typescript
|
|
21
|
+
* const billing = new BillingService({
|
|
22
|
+
* stripeClient,
|
|
23
|
+
* db,
|
|
24
|
+
* });
|
|
25
|
+
*
|
|
26
|
+
* // Create checkout session
|
|
27
|
+
* const session = await billing.createCheckoutSession({
|
|
28
|
+
* tier: 'team',
|
|
29
|
+
* billingPeriod: 'monthly',
|
|
30
|
+
* seatCount: 5,
|
|
31
|
+
* email: 'admin@company.com',
|
|
32
|
+
* successUrl: '/success',
|
|
33
|
+
* cancelUrl: '/cancel',
|
|
34
|
+
* });
|
|
35
|
+
*
|
|
36
|
+
* // Get subscription
|
|
37
|
+
* const subscription = await billing.getSubscriptionByCustomerId('cust_123');
|
|
38
|
+
* ```
|
|
39
|
+
*/
|
|
40
|
+
export class BillingService {
|
|
41
|
+
stripe;
|
|
42
|
+
db;
|
|
43
|
+
constructor(config) {
|
|
44
|
+
this.stripe = config.stripeClient;
|
|
45
|
+
this.db = config.db;
|
|
46
|
+
logger.info('Billing service initialized');
|
|
47
|
+
}
|
|
48
|
+
// ==========================================================================
|
|
49
|
+
// Checkout Flow
|
|
50
|
+
// ==========================================================================
|
|
51
|
+
/**
|
|
52
|
+
* Create a Stripe Checkout session
|
|
53
|
+
*/
|
|
54
|
+
async createCheckoutSession(request) {
|
|
55
|
+
return this.stripe.createCheckoutSession(request);
|
|
56
|
+
}
|
|
57
|
+
// ==========================================================================
|
|
58
|
+
// Subscription Management
|
|
59
|
+
// ==========================================================================
|
|
60
|
+
/**
|
|
61
|
+
* Get subscription by customer ID (our internal ID)
|
|
62
|
+
*/
|
|
63
|
+
getSubscriptionByCustomerId(customerId) {
|
|
64
|
+
const row = this.db
|
|
65
|
+
.prepare(`SELECT
|
|
66
|
+
id,
|
|
67
|
+
customer_id as customerId,
|
|
68
|
+
stripe_subscription_id as stripeSubscriptionId,
|
|
69
|
+
stripe_price_id as stripePriceId,
|
|
70
|
+
tier,
|
|
71
|
+
status,
|
|
72
|
+
seat_count as seatCount,
|
|
73
|
+
current_period_start as currentPeriodStart,
|
|
74
|
+
current_period_end as currentPeriodEnd,
|
|
75
|
+
canceled_at as canceledAt,
|
|
76
|
+
created_at as createdAt,
|
|
77
|
+
updated_at as updatedAt
|
|
78
|
+
FROM user_subscriptions
|
|
79
|
+
WHERE customer_id = ?`)
|
|
80
|
+
.get(customerId);
|
|
81
|
+
if (!row)
|
|
82
|
+
return null;
|
|
83
|
+
return this.mapRowToSubscription(row);
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Get subscription by Stripe subscription ID
|
|
87
|
+
*/
|
|
88
|
+
getSubscriptionByStripeId(stripeSubscriptionId) {
|
|
89
|
+
const row = this.db
|
|
90
|
+
.prepare(`SELECT
|
|
91
|
+
id,
|
|
92
|
+
customer_id as customerId,
|
|
93
|
+
stripe_subscription_id as stripeSubscriptionId,
|
|
94
|
+
stripe_price_id as stripePriceId,
|
|
95
|
+
tier,
|
|
96
|
+
status,
|
|
97
|
+
seat_count as seatCount,
|
|
98
|
+
current_period_start as currentPeriodStart,
|
|
99
|
+
current_period_end as currentPeriodEnd,
|
|
100
|
+
canceled_at as canceledAt,
|
|
101
|
+
created_at as createdAt,
|
|
102
|
+
updated_at as updatedAt
|
|
103
|
+
FROM user_subscriptions
|
|
104
|
+
WHERE stripe_subscription_id = ?`)
|
|
105
|
+
.get(stripeSubscriptionId);
|
|
106
|
+
if (!row)
|
|
107
|
+
return null;
|
|
108
|
+
return this.mapRowToSubscription(row);
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* Create or update subscription from Stripe data
|
|
112
|
+
*/
|
|
113
|
+
upsertSubscription(params) {
|
|
114
|
+
const id = randomUUID();
|
|
115
|
+
const now = new Date().toISOString();
|
|
116
|
+
this.db
|
|
117
|
+
.prepare(`INSERT INTO user_subscriptions (
|
|
118
|
+
id, customer_id, email, stripe_customer_id, stripe_subscription_id,
|
|
119
|
+
stripe_price_id, tier, status, seat_count,
|
|
120
|
+
current_period_start, current_period_end, canceled_at,
|
|
121
|
+
created_at, updated_at
|
|
122
|
+
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
123
|
+
ON CONFLICT(customer_id) DO UPDATE SET
|
|
124
|
+
stripe_subscription_id = excluded.stripe_subscription_id,
|
|
125
|
+
stripe_price_id = excluded.stripe_price_id,
|
|
126
|
+
tier = excluded.tier,
|
|
127
|
+
status = excluded.status,
|
|
128
|
+
seat_count = excluded.seat_count,
|
|
129
|
+
current_period_start = excluded.current_period_start,
|
|
130
|
+
current_period_end = excluded.current_period_end,
|
|
131
|
+
canceled_at = excluded.canceled_at,
|
|
132
|
+
updated_at = excluded.updated_at`)
|
|
133
|
+
.run(id, params.customerId, params.email, params.stripeCustomerId, params.stripeSubscriptionId, params.stripePriceId, params.tier, params.status, params.seatCount, params.currentPeriodStart.toISOString(), params.currentPeriodEnd.toISOString(), params.canceledAt?.toISOString() ?? null, now, now);
|
|
134
|
+
logger.info('Subscription upserted', {
|
|
135
|
+
customerId: params.customerId,
|
|
136
|
+
tier: params.tier,
|
|
137
|
+
status: params.status,
|
|
138
|
+
});
|
|
139
|
+
return this.getSubscriptionByCustomerId(params.customerId);
|
|
140
|
+
}
|
|
141
|
+
/**
|
|
142
|
+
* Update subscription status
|
|
143
|
+
*/
|
|
144
|
+
updateSubscriptionStatus(stripeSubscriptionId, status, canceledAt) {
|
|
145
|
+
this.db
|
|
146
|
+
.prepare(`UPDATE user_subscriptions
|
|
147
|
+
SET status = ?, canceled_at = ?, updated_at = ?
|
|
148
|
+
WHERE stripe_subscription_id = ?`)
|
|
149
|
+
.run(status, canceledAt?.toISOString() ?? null, new Date().toISOString(), stripeSubscriptionId);
|
|
150
|
+
logger.info('Subscription status updated', { stripeSubscriptionId, status });
|
|
151
|
+
}
|
|
152
|
+
/**
|
|
153
|
+
* Update seat count
|
|
154
|
+
*/
|
|
155
|
+
async updateSeatCount(stripeSubscriptionId, seatCount, prorate = true) {
|
|
156
|
+
// Update in Stripe
|
|
157
|
+
await this.stripe.updateSubscription(stripeSubscriptionId, {
|
|
158
|
+
seatCount,
|
|
159
|
+
prorate,
|
|
160
|
+
});
|
|
161
|
+
// Update locally
|
|
162
|
+
this.db
|
|
163
|
+
.prepare(`UPDATE user_subscriptions
|
|
164
|
+
SET seat_count = ?, updated_at = ?
|
|
165
|
+
WHERE stripe_subscription_id = ?`)
|
|
166
|
+
.run(seatCount, new Date().toISOString(), stripeSubscriptionId);
|
|
167
|
+
logger.info('Seat count updated', { stripeSubscriptionId, seatCount });
|
|
168
|
+
return this.getSubscriptionByStripeId(stripeSubscriptionId);
|
|
169
|
+
}
|
|
170
|
+
/**
|
|
171
|
+
* Cancel a subscription
|
|
172
|
+
*/
|
|
173
|
+
async cancelSubscription(customerId, options) {
|
|
174
|
+
const subscription = this.getSubscriptionByCustomerId(customerId);
|
|
175
|
+
if (!subscription?.stripeSubscriptionId) {
|
|
176
|
+
throw new BillingError('Subscription not found', 'SUBSCRIPTION_NOT_FOUND');
|
|
177
|
+
}
|
|
178
|
+
await this.stripe.cancelSubscription(subscription.stripeSubscriptionId, options);
|
|
179
|
+
// Update local status
|
|
180
|
+
const newStatus = options?.immediately ? 'canceled' : subscription.status;
|
|
181
|
+
const canceledAt = options?.immediately ? new Date() : null;
|
|
182
|
+
this.updateSubscriptionStatus(subscription.stripeSubscriptionId, newStatus, canceledAt);
|
|
183
|
+
return this.getSubscriptionByCustomerId(customerId);
|
|
184
|
+
}
|
|
185
|
+
/**
|
|
186
|
+
* Upgrade subscription tier
|
|
187
|
+
*/
|
|
188
|
+
async upgradeTier(customerId, newTier, billingPeriod) {
|
|
189
|
+
const subscription = this.getSubscriptionByCustomerId(customerId);
|
|
190
|
+
if (!subscription?.stripeSubscriptionId) {
|
|
191
|
+
throw new BillingError('Subscription not found', 'SUBSCRIPTION_NOT_FOUND');
|
|
192
|
+
}
|
|
193
|
+
// Validate tier upgrade
|
|
194
|
+
const tierOrder = ['community', 'individual', 'team', 'enterprise'];
|
|
195
|
+
const currentIndex = tierOrder.indexOf(subscription.tier);
|
|
196
|
+
const newIndex = tierOrder.indexOf(newTier);
|
|
197
|
+
if (newIndex <= currentIndex) {
|
|
198
|
+
throw new BillingError('Can only upgrade to a higher tier', 'DOWNGRADE_NOT_ALLOWED', {
|
|
199
|
+
currentTier: subscription.tier,
|
|
200
|
+
requestedTier: newTier,
|
|
201
|
+
});
|
|
202
|
+
}
|
|
203
|
+
await this.stripe.updateSubscription(subscription.stripeSubscriptionId, {
|
|
204
|
+
tier: newTier,
|
|
205
|
+
billingPeriod,
|
|
206
|
+
prorate: true,
|
|
207
|
+
});
|
|
208
|
+
// Update local tier
|
|
209
|
+
this.db
|
|
210
|
+
.prepare(`UPDATE user_subscriptions
|
|
211
|
+
SET tier = ?, updated_at = ?
|
|
212
|
+
WHERE customer_id = ?`)
|
|
213
|
+
.run(newTier, new Date().toISOString(), customerId);
|
|
214
|
+
logger.info('Tier upgraded', {
|
|
215
|
+
customerId,
|
|
216
|
+
fromTier: subscription.tier,
|
|
217
|
+
toTier: newTier,
|
|
218
|
+
});
|
|
219
|
+
return this.getSubscriptionByCustomerId(customerId);
|
|
220
|
+
}
|
|
221
|
+
// ==========================================================================
|
|
222
|
+
// Customer Portal
|
|
223
|
+
// ==========================================================================
|
|
224
|
+
/**
|
|
225
|
+
* Create a Customer Portal session
|
|
226
|
+
*/
|
|
227
|
+
async createPortalSession(customerId, returnUrl) {
|
|
228
|
+
const subscription = this.getSubscriptionByCustomerId(customerId);
|
|
229
|
+
if (!subscription) {
|
|
230
|
+
throw new BillingError('Customer not found', 'CUSTOMER_NOT_FOUND');
|
|
231
|
+
}
|
|
232
|
+
const row = this.db
|
|
233
|
+
.prepare(`SELECT stripe_customer_id FROM user_subscriptions WHERE customer_id = ?`)
|
|
234
|
+
.get(customerId);
|
|
235
|
+
if (!row?.stripe_customer_id) {
|
|
236
|
+
throw new BillingError('No Stripe customer found', 'CUSTOMER_NOT_FOUND');
|
|
237
|
+
}
|
|
238
|
+
return this.stripe.createPortalSession({
|
|
239
|
+
customerId: row.stripe_customer_id,
|
|
240
|
+
returnUrl,
|
|
241
|
+
});
|
|
242
|
+
}
|
|
243
|
+
// ==========================================================================
|
|
244
|
+
// Invoice Management
|
|
245
|
+
// ==========================================================================
|
|
246
|
+
/**
|
|
247
|
+
* Get invoices for a customer
|
|
248
|
+
*/
|
|
249
|
+
getInvoices(customerId, limit = 10) {
|
|
250
|
+
const rows = this.db
|
|
251
|
+
.prepare(`SELECT
|
|
252
|
+
id,
|
|
253
|
+
customer_id as customerId,
|
|
254
|
+
stripe_invoice_id as stripeInvoiceId,
|
|
255
|
+
subscription_id as subscriptionId,
|
|
256
|
+
amount_cents as amountCents,
|
|
257
|
+
currency,
|
|
258
|
+
status,
|
|
259
|
+
pdf_url as pdfUrl,
|
|
260
|
+
hosted_invoice_url as hostedInvoiceUrl,
|
|
261
|
+
invoice_number as invoiceNumber,
|
|
262
|
+
paid_at as paidAt,
|
|
263
|
+
period_start as periodStart,
|
|
264
|
+
period_end as periodEnd,
|
|
265
|
+
created_at as createdAt
|
|
266
|
+
FROM invoices
|
|
267
|
+
WHERE customer_id = ?
|
|
268
|
+
ORDER BY created_at DESC
|
|
269
|
+
LIMIT ?`)
|
|
270
|
+
.all(customerId, limit);
|
|
271
|
+
return rows.map(this.mapRowToInvoice);
|
|
272
|
+
}
|
|
273
|
+
/**
|
|
274
|
+
* Store an invoice from Stripe
|
|
275
|
+
*/
|
|
276
|
+
storeInvoice(params) {
|
|
277
|
+
const id = randomUUID();
|
|
278
|
+
const now = new Date().toISOString();
|
|
279
|
+
this.db
|
|
280
|
+
.prepare(`INSERT INTO invoices (
|
|
281
|
+
id, customer_id, stripe_invoice_id, subscription_id,
|
|
282
|
+
amount_cents, currency, status, pdf_url, hosted_invoice_url,
|
|
283
|
+
invoice_number, paid_at, period_start, period_end, created_at
|
|
284
|
+
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
285
|
+
ON CONFLICT(stripe_invoice_id) DO UPDATE SET
|
|
286
|
+
status = excluded.status,
|
|
287
|
+
pdf_url = excluded.pdf_url,
|
|
288
|
+
paid_at = excluded.paid_at`)
|
|
289
|
+
.run(id, params.customerId, params.stripeInvoiceId, params.subscriptionId ?? null, params.amountCents, params.currency, params.status, params.pdfUrl ?? null, params.hostedInvoiceUrl ?? null, params.invoiceNumber ?? null, params.paidAt?.toISOString() ?? null, params.periodStart?.toISOString() ?? null, params.periodEnd?.toISOString() ?? null, now);
|
|
290
|
+
logger.info('Invoice stored', {
|
|
291
|
+
invoiceId: id,
|
|
292
|
+
stripeInvoiceId: params.stripeInvoiceId,
|
|
293
|
+
status: params.status,
|
|
294
|
+
});
|
|
295
|
+
const row = this.db
|
|
296
|
+
.prepare(`SELECT
|
|
297
|
+
id,
|
|
298
|
+
customer_id as customerId,
|
|
299
|
+
stripe_invoice_id as stripeInvoiceId,
|
|
300
|
+
subscription_id as subscriptionId,
|
|
301
|
+
amount_cents as amountCents,
|
|
302
|
+
currency,
|
|
303
|
+
status,
|
|
304
|
+
pdf_url as pdfUrl,
|
|
305
|
+
hosted_invoice_url as hostedInvoiceUrl,
|
|
306
|
+
invoice_number as invoiceNumber,
|
|
307
|
+
paid_at as paidAt,
|
|
308
|
+
period_start as periodStart,
|
|
309
|
+
period_end as periodEnd,
|
|
310
|
+
created_at as createdAt
|
|
311
|
+
FROM invoices WHERE stripe_invoice_id = ?`)
|
|
312
|
+
.get(params.stripeInvoiceId);
|
|
313
|
+
return this.mapRowToInvoice(row);
|
|
314
|
+
}
|
|
315
|
+
// ==========================================================================
|
|
316
|
+
// Webhook Event Tracking (Idempotency)
|
|
317
|
+
// ==========================================================================
|
|
318
|
+
/**
|
|
319
|
+
* Check if a webhook event has already been processed
|
|
320
|
+
*/
|
|
321
|
+
isEventProcessed(stripeEventId) {
|
|
322
|
+
const row = this.db
|
|
323
|
+
.prepare(`SELECT id FROM stripe_webhook_events WHERE stripe_event_id = ?`)
|
|
324
|
+
.get(stripeEventId);
|
|
325
|
+
return !!row;
|
|
326
|
+
}
|
|
327
|
+
/**
|
|
328
|
+
* Record a processed webhook event
|
|
329
|
+
*/
|
|
330
|
+
recordWebhookEvent(params) {
|
|
331
|
+
const id = randomUUID();
|
|
332
|
+
const now = new Date().toISOString();
|
|
333
|
+
this.db
|
|
334
|
+
.prepare(`INSERT INTO stripe_webhook_events (
|
|
335
|
+
id, stripe_event_id, event_type, processed_at,
|
|
336
|
+
payload, success, error_message, created_at
|
|
337
|
+
) VALUES (?, ?, ?, ?, ?, ?, ?, ?)`)
|
|
338
|
+
.run(id, params.stripeEventId, params.eventType, now, params.payload ?? null, params.success !== false ? 1 : 0, params.errorMessage ?? null, now);
|
|
339
|
+
logger.info('Webhook event recorded', {
|
|
340
|
+
eventId: params.stripeEventId,
|
|
341
|
+
eventType: params.eventType,
|
|
342
|
+
success: params.success !== false,
|
|
343
|
+
});
|
|
344
|
+
return {
|
|
345
|
+
id,
|
|
346
|
+
stripeEventId: params.stripeEventId,
|
|
347
|
+
eventType: params.eventType,
|
|
348
|
+
processedAt: new Date(now),
|
|
349
|
+
payload: params.payload ?? '',
|
|
350
|
+
success: params.success !== false,
|
|
351
|
+
errorMessage: params.errorMessage ?? null,
|
|
352
|
+
createdAt: new Date(now),
|
|
353
|
+
};
|
|
354
|
+
}
|
|
355
|
+
// ==========================================================================
|
|
356
|
+
// Helper Methods
|
|
357
|
+
// ==========================================================================
|
|
358
|
+
mapRowToSubscription(row) {
|
|
359
|
+
return {
|
|
360
|
+
id: row.id,
|
|
361
|
+
customerId: row.customerId,
|
|
362
|
+
stripeSubscriptionId: row.stripeSubscriptionId,
|
|
363
|
+
stripePriceId: row.stripePriceId,
|
|
364
|
+
tier: row.tier,
|
|
365
|
+
status: row.status,
|
|
366
|
+
seatCount: row.seatCount ?? 1,
|
|
367
|
+
currentPeriodStart: row.currentPeriodStart ? new Date(row.currentPeriodStart) : null,
|
|
368
|
+
currentPeriodEnd: row.currentPeriodEnd ? new Date(row.currentPeriodEnd) : null,
|
|
369
|
+
canceledAt: row.canceledAt ? new Date(row.canceledAt) : null,
|
|
370
|
+
createdAt: new Date(row.createdAt),
|
|
371
|
+
updatedAt: new Date(row.updatedAt),
|
|
372
|
+
};
|
|
373
|
+
}
|
|
374
|
+
mapRowToInvoice(row) {
|
|
375
|
+
return {
|
|
376
|
+
id: row.id,
|
|
377
|
+
customerId: row.customerId,
|
|
378
|
+
stripeInvoiceId: row.stripeInvoiceId,
|
|
379
|
+
subscriptionId: row.subscriptionId,
|
|
380
|
+
amountCents: row.amountCents,
|
|
381
|
+
currency: row.currency,
|
|
382
|
+
status: row.status,
|
|
383
|
+
pdfUrl: row.pdfUrl,
|
|
384
|
+
hostedInvoiceUrl: row.hostedInvoiceUrl,
|
|
385
|
+
invoiceNumber: row.invoiceNumber,
|
|
386
|
+
paidAt: row.paidAt ? new Date(row.paidAt) : null,
|
|
387
|
+
periodStart: row.periodStart ? new Date(row.periodStart) : null,
|
|
388
|
+
periodEnd: row.periodEnd ? new Date(row.periodEnd) : null,
|
|
389
|
+
createdAt: new Date(row.createdAt),
|
|
390
|
+
};
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
//# sourceMappingURL=BillingService.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"BillingService.js","sourceRoot":"","sources":["../../../src/billing/BillingService.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SMI-1068: GDPR Compliance Service
|
|
3
|
+
*
|
|
4
|
+
* Provides data subject rights implementation for billing data:
|
|
5
|
+
* - Article 17: Right to Erasure (data deletion)
|
|
6
|
+
* - Article 20: Right to Data Portability (data export)
|
|
7
|
+
*
|
|
8
|
+
* All operations are logged for audit purposes.
|
|
9
|
+
*/
|
|
10
|
+
import type { Database as BetterSqliteDatabase } from 'better-sqlite3';
|
|
11
|
+
import type { StripeClient } from './StripeClient.js';
|
|
12
|
+
export interface GDPRComplianceServiceConfig {
|
|
13
|
+
/**
|
|
14
|
+
* Database connection (better-sqlite3)
|
|
15
|
+
*/
|
|
16
|
+
db: BetterSqliteDatabase;
|
|
17
|
+
/**
|
|
18
|
+
* Optional StripeClient for Stripe data operations
|
|
19
|
+
*/
|
|
20
|
+
stripeClient?: StripeClient;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Exported customer data format
|
|
24
|
+
*/
|
|
25
|
+
export interface CustomerDataExport {
|
|
26
|
+
/**
|
|
27
|
+
* Export metadata
|
|
28
|
+
*/
|
|
29
|
+
metadata: {
|
|
30
|
+
exportedAt: string;
|
|
31
|
+
customerId: string;
|
|
32
|
+
format: 'json';
|
|
33
|
+
version: '1.0';
|
|
34
|
+
};
|
|
35
|
+
/**
|
|
36
|
+
* Subscription data
|
|
37
|
+
*/
|
|
38
|
+
subscriptions: SubscriptionExportData[];
|
|
39
|
+
/**
|
|
40
|
+
* Invoice data
|
|
41
|
+
*/
|
|
42
|
+
invoices: InvoiceExportData[];
|
|
43
|
+
/**
|
|
44
|
+
* License key data (without the actual JWT for security)
|
|
45
|
+
*/
|
|
46
|
+
licenseKeys: LicenseKeyExportData[];
|
|
47
|
+
/**
|
|
48
|
+
* Webhook events related to this customer
|
|
49
|
+
*/
|
|
50
|
+
webhookEvents: WebhookEventExportData[];
|
|
51
|
+
}
|
|
52
|
+
export interface SubscriptionExportData {
|
|
53
|
+
id: string;
|
|
54
|
+
stripeSubscriptionId: string | null;
|
|
55
|
+
tier: string;
|
|
56
|
+
status: string;
|
|
57
|
+
seatCount: number;
|
|
58
|
+
currentPeriodStart: string | null;
|
|
59
|
+
currentPeriodEnd: string | null;
|
|
60
|
+
canceledAt: string | null;
|
|
61
|
+
createdAt: string;
|
|
62
|
+
updatedAt: string;
|
|
63
|
+
}
|
|
64
|
+
export interface InvoiceExportData {
|
|
65
|
+
id: string;
|
|
66
|
+
stripeInvoiceId: string;
|
|
67
|
+
amountCents: number;
|
|
68
|
+
currency: string;
|
|
69
|
+
status: string;
|
|
70
|
+
invoiceNumber: string | null;
|
|
71
|
+
paidAt: string | null;
|
|
72
|
+
periodStart: string | null;
|
|
73
|
+
periodEnd: string | null;
|
|
74
|
+
createdAt: string;
|
|
75
|
+
}
|
|
76
|
+
export interface LicenseKeyExportData {
|
|
77
|
+
id: string;
|
|
78
|
+
keyExpiry: string;
|
|
79
|
+
isActive: boolean;
|
|
80
|
+
generatedAt: string;
|
|
81
|
+
revokedAt: string | null;
|
|
82
|
+
revocationReason: string | null;
|
|
83
|
+
}
|
|
84
|
+
export interface WebhookEventExportData {
|
|
85
|
+
id: string;
|
|
86
|
+
stripeEventId: string;
|
|
87
|
+
eventType: string;
|
|
88
|
+
processedAt: string;
|
|
89
|
+
success: boolean;
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Result of a data deletion operation
|
|
93
|
+
*/
|
|
94
|
+
export interface DeletionResult {
|
|
95
|
+
success: boolean;
|
|
96
|
+
customerId: string;
|
|
97
|
+
deletedAt: string;
|
|
98
|
+
counts: {
|
|
99
|
+
subscriptions: number;
|
|
100
|
+
invoices: number;
|
|
101
|
+
licenseKeys: number;
|
|
102
|
+
webhookEvents: number;
|
|
103
|
+
};
|
|
104
|
+
stripeDeleted: boolean;
|
|
105
|
+
errors: string[];
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* GDPR Compliance Service for billing data
|
|
109
|
+
*
|
|
110
|
+
* @example
|
|
111
|
+
* ```typescript
|
|
112
|
+
* const gdpr = new GDPRComplianceService({
|
|
113
|
+
* db,
|
|
114
|
+
* stripeClient,
|
|
115
|
+
* });
|
|
116
|
+
*
|
|
117
|
+
* // Export customer data (Article 20)
|
|
118
|
+
* const data = gdpr.exportCustomerData('customer_123');
|
|
119
|
+
*
|
|
120
|
+
* // Delete customer data (Article 17)
|
|
121
|
+
* const result = await gdpr.deleteCustomerData('customer_123');
|
|
122
|
+
* ```
|
|
123
|
+
*/
|
|
124
|
+
export declare class GDPRComplianceService {
|
|
125
|
+
private readonly db;
|
|
126
|
+
private readonly stripe?;
|
|
127
|
+
constructor(config: GDPRComplianceServiceConfig);
|
|
128
|
+
/**
|
|
129
|
+
* Export all billing data for a customer
|
|
130
|
+
*
|
|
131
|
+
* Returns a structured JSON export of all data associated with the customer.
|
|
132
|
+
* This fulfills GDPR Article 20 requirements.
|
|
133
|
+
*
|
|
134
|
+
* @param customerId - The customer ID to export data for
|
|
135
|
+
* @returns Complete data export in JSON format
|
|
136
|
+
*/
|
|
137
|
+
exportCustomerData(customerId: string): CustomerDataExport;
|
|
138
|
+
private exportSubscriptions;
|
|
139
|
+
private exportInvoices;
|
|
140
|
+
private exportLicenseKeys;
|
|
141
|
+
private exportWebhookEvents;
|
|
142
|
+
/**
|
|
143
|
+
* Delete all billing data for a customer
|
|
144
|
+
*
|
|
145
|
+
* Performs cascading deletion of:
|
|
146
|
+
* 1. License keys
|
|
147
|
+
* 2. Invoices
|
|
148
|
+
* 3. Webhook events (those containing customer data)
|
|
149
|
+
* 4. Subscriptions
|
|
150
|
+
* 5. Stripe customer (if stripeClient provided)
|
|
151
|
+
*
|
|
152
|
+
* This fulfills GDPR Article 17 requirements.
|
|
153
|
+
*
|
|
154
|
+
* @param customerId - The customer ID to delete data for
|
|
155
|
+
* @param options - Deletion options
|
|
156
|
+
* @returns Result of the deletion operation
|
|
157
|
+
*/
|
|
158
|
+
deleteCustomerData(customerId: string, options?: {
|
|
159
|
+
deleteFromStripe?: boolean;
|
|
160
|
+
dryRun?: boolean;
|
|
161
|
+
}): Promise<DeletionResult>;
|
|
162
|
+
/**
|
|
163
|
+
* Check if a customer has any billing data
|
|
164
|
+
*/
|
|
165
|
+
hasCustomerData(customerId: string): boolean;
|
|
166
|
+
/**
|
|
167
|
+
* Get a summary of customer data (for consent/overview purposes)
|
|
168
|
+
*/
|
|
169
|
+
getDataSummary(customerId: string): {
|
|
170
|
+
hasSubscription: boolean;
|
|
171
|
+
invoiceCount: number;
|
|
172
|
+
licenseKeyCount: number;
|
|
173
|
+
stripeCustomerId: string | null;
|
|
174
|
+
};
|
|
175
|
+
}
|
|
176
|
+
//# sourceMappingURL=GDPRComplianceService.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"GDPRComplianceService.d.ts","sourceRoot":"","sources":["../../../src/billing/GDPRComplianceService.ts"],"names":[],"mappings":"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"}
|