@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,462 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SMI-1062: Stripe Client Wrapper
|
|
3
|
+
*
|
|
4
|
+
* Provides a type-safe wrapper around the Stripe SDK with:
|
|
5
|
+
* - Customer management
|
|
6
|
+
* - Subscription management
|
|
7
|
+
* - Checkout session creation
|
|
8
|
+
* - Customer portal sessions
|
|
9
|
+
* - Invoice retrieval
|
|
10
|
+
* - Webhook signature verification
|
|
11
|
+
*/
|
|
12
|
+
import Stripe from 'stripe';
|
|
13
|
+
import { createLogger } from '../utils/logger.js';
|
|
14
|
+
import { BillingError } from './types.js';
|
|
15
|
+
const logger = createLogger('StripeClient');
|
|
16
|
+
// ============================================================================
|
|
17
|
+
// StripeClient Class
|
|
18
|
+
// ============================================================================
|
|
19
|
+
/**
|
|
20
|
+
* Stripe API client wrapper
|
|
21
|
+
*
|
|
22
|
+
* @example
|
|
23
|
+
* ```typescript
|
|
24
|
+
* const client = new StripeClient({
|
|
25
|
+
* secretKey: process.env.STRIPE_SECRET_KEY!,
|
|
26
|
+
* webhookSecret: process.env.STRIPE_WEBHOOK_SECRET!,
|
|
27
|
+
* prices: {
|
|
28
|
+
* individual: { monthly: 'price_xxx', annual: 'price_yyy' },
|
|
29
|
+
* team: { monthly: 'price_xxx', annual: 'price_yyy' },
|
|
30
|
+
* enterprise: { monthly: 'price_xxx', annual: 'price_yyy' },
|
|
31
|
+
* },
|
|
32
|
+
* });
|
|
33
|
+
*
|
|
34
|
+
* const session = await client.createCheckoutSession({
|
|
35
|
+
* tier: 'team',
|
|
36
|
+
* billingPeriod: 'monthly',
|
|
37
|
+
* seatCount: 5,
|
|
38
|
+
* successUrl: 'https://app.example.com/success',
|
|
39
|
+
* cancelUrl: 'https://app.example.com/cancel',
|
|
40
|
+
* });
|
|
41
|
+
* ```
|
|
42
|
+
*/
|
|
43
|
+
export class StripeClient {
|
|
44
|
+
stripe;
|
|
45
|
+
webhookSecret;
|
|
46
|
+
prices;
|
|
47
|
+
appUrl;
|
|
48
|
+
constructor(config) {
|
|
49
|
+
this.stripe = new Stripe(config.secretKey, {
|
|
50
|
+
apiVersion: '2023-10-16',
|
|
51
|
+
typescript: true,
|
|
52
|
+
});
|
|
53
|
+
this.webhookSecret = config.webhookSecret;
|
|
54
|
+
this.prices = config.prices;
|
|
55
|
+
this.appUrl = config.appUrl ?? 'https://skillsmith.app';
|
|
56
|
+
logger.info('Stripe client initialized');
|
|
57
|
+
}
|
|
58
|
+
// ==========================================================================
|
|
59
|
+
// Customer Management
|
|
60
|
+
// ==========================================================================
|
|
61
|
+
/**
|
|
62
|
+
* Create a new Stripe customer
|
|
63
|
+
*/
|
|
64
|
+
async createCustomer(params) {
|
|
65
|
+
try {
|
|
66
|
+
const customer = await this.stripe.customers.create({
|
|
67
|
+
email: params.email,
|
|
68
|
+
name: params.name,
|
|
69
|
+
metadata: {
|
|
70
|
+
...params.metadata,
|
|
71
|
+
source: 'skillsmith',
|
|
72
|
+
},
|
|
73
|
+
});
|
|
74
|
+
logger.info('Customer created', { customerId: customer.id });
|
|
75
|
+
return customer.id;
|
|
76
|
+
}
|
|
77
|
+
catch (error) {
|
|
78
|
+
const errorMsg = error instanceof Error ? error.message : String(error);
|
|
79
|
+
logger.error('Failed to create customer', undefined, {
|
|
80
|
+
errorDetails: errorMsg,
|
|
81
|
+
email: params.email,
|
|
82
|
+
});
|
|
83
|
+
throw new BillingError('Failed to create customer', 'STRIPE_API_ERROR', {
|
|
84
|
+
originalError: errorMsg,
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Get a Stripe customer by ID
|
|
90
|
+
*/
|
|
91
|
+
async getCustomer(customerId) {
|
|
92
|
+
try {
|
|
93
|
+
const customer = await this.stripe.customers.retrieve(customerId);
|
|
94
|
+
if (customer.deleted) {
|
|
95
|
+
return null;
|
|
96
|
+
}
|
|
97
|
+
return customer;
|
|
98
|
+
}
|
|
99
|
+
catch (error) {
|
|
100
|
+
if (error instanceof Stripe.errors.StripeError && error.code === 'resource_missing') {
|
|
101
|
+
return null;
|
|
102
|
+
}
|
|
103
|
+
throw error;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* Update a Stripe customer
|
|
108
|
+
*/
|
|
109
|
+
async updateCustomer(customerId, params) {
|
|
110
|
+
return this.stripe.customers.update(customerId, params);
|
|
111
|
+
}
|
|
112
|
+
// ==========================================================================
|
|
113
|
+
// Checkout Session
|
|
114
|
+
// ==========================================================================
|
|
115
|
+
/**
|
|
116
|
+
* Create a Stripe Checkout session for subscription
|
|
117
|
+
*/
|
|
118
|
+
async createCheckoutSession(request) {
|
|
119
|
+
const priceId = this.getPriceId(request.tier, request.billingPeriod);
|
|
120
|
+
if (!priceId) {
|
|
121
|
+
throw new BillingError(`No price configured for tier: ${request.tier}`, 'INVALID_TIER', {
|
|
122
|
+
tier: request.tier,
|
|
123
|
+
billingPeriod: request.billingPeriod,
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
const lineItems = [
|
|
127
|
+
{
|
|
128
|
+
price: priceId,
|
|
129
|
+
quantity: request.seatCount ?? 1,
|
|
130
|
+
},
|
|
131
|
+
];
|
|
132
|
+
const sessionParams = {
|
|
133
|
+
mode: 'subscription',
|
|
134
|
+
line_items: lineItems,
|
|
135
|
+
success_url: request.successUrl,
|
|
136
|
+
cancel_url: request.cancelUrl,
|
|
137
|
+
metadata: {
|
|
138
|
+
tier: request.tier,
|
|
139
|
+
billingPeriod: request.billingPeriod,
|
|
140
|
+
seatCount: String(request.seatCount ?? 1),
|
|
141
|
+
...request.metadata,
|
|
142
|
+
},
|
|
143
|
+
subscription_data: {
|
|
144
|
+
metadata: {
|
|
145
|
+
tier: request.tier,
|
|
146
|
+
seatCount: String(request.seatCount ?? 1),
|
|
147
|
+
},
|
|
148
|
+
},
|
|
149
|
+
};
|
|
150
|
+
// Attach to existing customer or create new
|
|
151
|
+
if (request.customerId) {
|
|
152
|
+
sessionParams.customer = request.customerId;
|
|
153
|
+
}
|
|
154
|
+
else if (request.email) {
|
|
155
|
+
sessionParams.customer_email = request.email;
|
|
156
|
+
}
|
|
157
|
+
// Allow seat quantity changes for team/enterprise
|
|
158
|
+
if (request.tier === 'team' || request.tier === 'enterprise') {
|
|
159
|
+
sessionParams.line_items[0].adjustable_quantity = {
|
|
160
|
+
enabled: true,
|
|
161
|
+
minimum: 1,
|
|
162
|
+
maximum: 1000,
|
|
163
|
+
};
|
|
164
|
+
}
|
|
165
|
+
try {
|
|
166
|
+
const session = await this.stripe.checkout.sessions.create(sessionParams);
|
|
167
|
+
logger.info('Checkout session created', {
|
|
168
|
+
sessionId: session.id,
|
|
169
|
+
tier: request.tier,
|
|
170
|
+
seatCount: request.seatCount,
|
|
171
|
+
});
|
|
172
|
+
return {
|
|
173
|
+
sessionId: session.id,
|
|
174
|
+
url: session.url,
|
|
175
|
+
};
|
|
176
|
+
}
|
|
177
|
+
catch (error) {
|
|
178
|
+
const errorMsg = error instanceof Error ? error.message : String(error);
|
|
179
|
+
logger.error('Failed to create checkout session', undefined, { errorDetails: errorMsg });
|
|
180
|
+
throw new BillingError('Failed to create checkout session', 'STRIPE_API_ERROR', {
|
|
181
|
+
originalError: errorMsg,
|
|
182
|
+
});
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
/**
|
|
186
|
+
* Retrieve a checkout session
|
|
187
|
+
*/
|
|
188
|
+
async getCheckoutSession(sessionId) {
|
|
189
|
+
return this.stripe.checkout.sessions.retrieve(sessionId, {
|
|
190
|
+
expand: ['subscription', 'customer'],
|
|
191
|
+
});
|
|
192
|
+
}
|
|
193
|
+
// ==========================================================================
|
|
194
|
+
// Subscription Management
|
|
195
|
+
// ==========================================================================
|
|
196
|
+
/**
|
|
197
|
+
* Get a subscription by ID
|
|
198
|
+
*/
|
|
199
|
+
async getSubscription(subscriptionId) {
|
|
200
|
+
try {
|
|
201
|
+
return await this.stripe.subscriptions.retrieve(subscriptionId);
|
|
202
|
+
}
|
|
203
|
+
catch (error) {
|
|
204
|
+
if (error instanceof Stripe.errors.StripeError && error.code === 'resource_missing') {
|
|
205
|
+
return null;
|
|
206
|
+
}
|
|
207
|
+
throw error;
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
/**
|
|
211
|
+
* List subscriptions for a customer
|
|
212
|
+
*/
|
|
213
|
+
async listSubscriptions(customerId) {
|
|
214
|
+
const subscriptions = await this.stripe.subscriptions.list({
|
|
215
|
+
customer: customerId,
|
|
216
|
+
status: 'all',
|
|
217
|
+
expand: ['data.default_payment_method'],
|
|
218
|
+
});
|
|
219
|
+
return subscriptions.data;
|
|
220
|
+
}
|
|
221
|
+
/**
|
|
222
|
+
* Update a subscription (tier change or seat count)
|
|
223
|
+
*/
|
|
224
|
+
async updateSubscription(subscriptionId, params) {
|
|
225
|
+
const subscription = await this.getSubscription(subscriptionId);
|
|
226
|
+
if (!subscription) {
|
|
227
|
+
throw new BillingError('Subscription not found', 'SUBSCRIPTION_NOT_FOUND');
|
|
228
|
+
}
|
|
229
|
+
const updateParams = {
|
|
230
|
+
proration_behavior: params.prorate !== false ? 'create_prorations' : 'none',
|
|
231
|
+
};
|
|
232
|
+
// Update price if tier or billing period changed
|
|
233
|
+
if (params.tier && params.billingPeriod) {
|
|
234
|
+
const newPriceId = this.getPriceId(params.tier, params.billingPeriod);
|
|
235
|
+
if (newPriceId) {
|
|
236
|
+
updateParams.items = [
|
|
237
|
+
{
|
|
238
|
+
id: subscription.items.data[0].id,
|
|
239
|
+
price: newPriceId,
|
|
240
|
+
quantity: params.seatCount ?? subscription.items.data[0].quantity,
|
|
241
|
+
},
|
|
242
|
+
];
|
|
243
|
+
updateParams.metadata = {
|
|
244
|
+
...subscription.metadata,
|
|
245
|
+
tier: params.tier,
|
|
246
|
+
seatCount: String(params.seatCount ?? subscription.items.data[0].quantity),
|
|
247
|
+
};
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
else if (params.seatCount !== undefined) {
|
|
251
|
+
// Just update seat count
|
|
252
|
+
updateParams.items = [
|
|
253
|
+
{
|
|
254
|
+
id: subscription.items.data[0].id,
|
|
255
|
+
quantity: params.seatCount,
|
|
256
|
+
},
|
|
257
|
+
];
|
|
258
|
+
updateParams.metadata = {
|
|
259
|
+
...subscription.metadata,
|
|
260
|
+
seatCount: String(params.seatCount),
|
|
261
|
+
};
|
|
262
|
+
}
|
|
263
|
+
try {
|
|
264
|
+
const updated = await this.stripe.subscriptions.update(subscriptionId, updateParams);
|
|
265
|
+
logger.info('Subscription updated', { subscriptionId, params });
|
|
266
|
+
return updated;
|
|
267
|
+
}
|
|
268
|
+
catch (error) {
|
|
269
|
+
const errorMsg = error instanceof Error ? error.message : String(error);
|
|
270
|
+
logger.error('Failed to update subscription', undefined, {
|
|
271
|
+
errorDetails: errorMsg,
|
|
272
|
+
subscriptionId,
|
|
273
|
+
});
|
|
274
|
+
throw new BillingError('Failed to update subscription', 'STRIPE_API_ERROR', {
|
|
275
|
+
originalError: errorMsg,
|
|
276
|
+
});
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
/**
|
|
280
|
+
* Cancel a subscription
|
|
281
|
+
*/
|
|
282
|
+
async cancelSubscription(subscriptionId, options) {
|
|
283
|
+
try {
|
|
284
|
+
if (options?.immediately) {
|
|
285
|
+
return await this.stripe.subscriptions.cancel(subscriptionId, {
|
|
286
|
+
cancellation_details: {
|
|
287
|
+
comment: options.feedback,
|
|
288
|
+
},
|
|
289
|
+
});
|
|
290
|
+
}
|
|
291
|
+
else {
|
|
292
|
+
// Cancel at period end
|
|
293
|
+
return await this.stripe.subscriptions.update(subscriptionId, {
|
|
294
|
+
cancel_at_period_end: true,
|
|
295
|
+
cancellation_details: {
|
|
296
|
+
comment: options?.feedback,
|
|
297
|
+
},
|
|
298
|
+
});
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
catch (error) {
|
|
302
|
+
const errorMsg = error instanceof Error ? error.message : String(error);
|
|
303
|
+
logger.error('Failed to cancel subscription', undefined, {
|
|
304
|
+
errorDetails: errorMsg,
|
|
305
|
+
subscriptionId,
|
|
306
|
+
});
|
|
307
|
+
throw new BillingError('Failed to cancel subscription', 'STRIPE_API_ERROR', {
|
|
308
|
+
originalError: errorMsg,
|
|
309
|
+
});
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
/**
|
|
313
|
+
* Reactivate a canceled subscription (before period end)
|
|
314
|
+
*/
|
|
315
|
+
async reactivateSubscription(subscriptionId) {
|
|
316
|
+
return this.stripe.subscriptions.update(subscriptionId, {
|
|
317
|
+
cancel_at_period_end: false,
|
|
318
|
+
});
|
|
319
|
+
}
|
|
320
|
+
// ==========================================================================
|
|
321
|
+
// Customer Portal
|
|
322
|
+
// ==========================================================================
|
|
323
|
+
/**
|
|
324
|
+
* Create a Stripe Customer Portal session
|
|
325
|
+
*/
|
|
326
|
+
async createPortalSession(request) {
|
|
327
|
+
try {
|
|
328
|
+
// Need to get Stripe customer ID from our customer ID
|
|
329
|
+
// This assumes customerId is the Stripe customer ID
|
|
330
|
+
const session = await this.stripe.billingPortal.sessions.create({
|
|
331
|
+
customer: request.customerId,
|
|
332
|
+
return_url: request.returnUrl,
|
|
333
|
+
});
|
|
334
|
+
logger.info('Portal session created', { customerId: request.customerId });
|
|
335
|
+
return {
|
|
336
|
+
url: session.url,
|
|
337
|
+
};
|
|
338
|
+
}
|
|
339
|
+
catch (error) {
|
|
340
|
+
const errorMsg = error instanceof Error ? error.message : String(error);
|
|
341
|
+
logger.error('Failed to create portal session', undefined, {
|
|
342
|
+
errorDetails: errorMsg,
|
|
343
|
+
customerId: request.customerId,
|
|
344
|
+
});
|
|
345
|
+
throw new BillingError('Failed to create portal session', 'STRIPE_API_ERROR', {
|
|
346
|
+
originalError: errorMsg,
|
|
347
|
+
});
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
// ==========================================================================
|
|
351
|
+
// Invoice Management
|
|
352
|
+
// ==========================================================================
|
|
353
|
+
/**
|
|
354
|
+
* List invoices for a customer
|
|
355
|
+
*/
|
|
356
|
+
async listInvoices(customerId, options) {
|
|
357
|
+
const invoices = await this.stripe.invoices.list({
|
|
358
|
+
customer: customerId,
|
|
359
|
+
limit: options?.limit ?? 10,
|
|
360
|
+
starting_after: options?.startingAfter,
|
|
361
|
+
});
|
|
362
|
+
return {
|
|
363
|
+
invoices: invoices.data,
|
|
364
|
+
hasMore: invoices.has_more,
|
|
365
|
+
};
|
|
366
|
+
}
|
|
367
|
+
/**
|
|
368
|
+
* Get a specific invoice
|
|
369
|
+
*/
|
|
370
|
+
async getInvoice(invoiceId) {
|
|
371
|
+
try {
|
|
372
|
+
return await this.stripe.invoices.retrieve(invoiceId);
|
|
373
|
+
}
|
|
374
|
+
catch (error) {
|
|
375
|
+
if (error instanceof Stripe.errors.StripeError && error.code === 'resource_missing') {
|
|
376
|
+
return null;
|
|
377
|
+
}
|
|
378
|
+
throw error;
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
/**
|
|
382
|
+
* Get upcoming invoice (preview of next charge)
|
|
383
|
+
*/
|
|
384
|
+
async getUpcomingInvoice(customerId) {
|
|
385
|
+
try {
|
|
386
|
+
return await this.stripe.invoices.retrieveUpcoming({
|
|
387
|
+
customer: customerId,
|
|
388
|
+
});
|
|
389
|
+
}
|
|
390
|
+
catch (error) {
|
|
391
|
+
if (error instanceof Stripe.errors.StripeError && error.code === 'invoice_upcoming_none') {
|
|
392
|
+
return null;
|
|
393
|
+
}
|
|
394
|
+
throw error;
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
// ==========================================================================
|
|
398
|
+
// Webhook Handling
|
|
399
|
+
// ==========================================================================
|
|
400
|
+
/**
|
|
401
|
+
* Verify and parse a webhook event
|
|
402
|
+
*/
|
|
403
|
+
verifyWebhookSignature(payload, signature) {
|
|
404
|
+
try {
|
|
405
|
+
return this.stripe.webhooks.constructEvent(payload, signature, this.webhookSecret);
|
|
406
|
+
}
|
|
407
|
+
catch (error) {
|
|
408
|
+
logger.warn('Webhook signature verification failed', {
|
|
409
|
+
error: error instanceof Error ? error.message : String(error),
|
|
410
|
+
});
|
|
411
|
+
throw new BillingError('Invalid webhook signature', 'WEBHOOK_SIGNATURE_INVALID');
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
// ==========================================================================
|
|
415
|
+
// Helper Methods
|
|
416
|
+
// ==========================================================================
|
|
417
|
+
/**
|
|
418
|
+
* Get the price ID for a tier and billing period
|
|
419
|
+
*/
|
|
420
|
+
getPriceId(tier, billingPeriod) {
|
|
421
|
+
if (tier === 'community') {
|
|
422
|
+
return null; // Community tier is free, no Stripe price
|
|
423
|
+
}
|
|
424
|
+
const tierPrices = this.prices[tier];
|
|
425
|
+
if (!tierPrices) {
|
|
426
|
+
return null;
|
|
427
|
+
}
|
|
428
|
+
return tierPrices[billingPeriod];
|
|
429
|
+
}
|
|
430
|
+
/**
|
|
431
|
+
* Map Stripe subscription status to our status
|
|
432
|
+
*/
|
|
433
|
+
static mapSubscriptionStatus(stripeStatus) {
|
|
434
|
+
switch (stripeStatus) {
|
|
435
|
+
case 'active':
|
|
436
|
+
return 'active';
|
|
437
|
+
case 'past_due':
|
|
438
|
+
return 'past_due';
|
|
439
|
+
case 'canceled':
|
|
440
|
+
return 'canceled';
|
|
441
|
+
case 'trialing':
|
|
442
|
+
return 'trialing';
|
|
443
|
+
case 'paused':
|
|
444
|
+
return 'paused';
|
|
445
|
+
case 'incomplete':
|
|
446
|
+
return 'incomplete';
|
|
447
|
+
case 'incomplete_expired':
|
|
448
|
+
return 'incomplete_expired';
|
|
449
|
+
case 'unpaid':
|
|
450
|
+
return 'unpaid';
|
|
451
|
+
default:
|
|
452
|
+
return 'active';
|
|
453
|
+
}
|
|
454
|
+
}
|
|
455
|
+
/**
|
|
456
|
+
* Get the underlying Stripe instance (for advanced operations)
|
|
457
|
+
*/
|
|
458
|
+
getStripeInstance() {
|
|
459
|
+
return this.stripe;
|
|
460
|
+
}
|
|
461
|
+
}
|
|
462
|
+
//# sourceMappingURL=StripeClient.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"StripeClient.js","sourceRoot":"","sources":["../../../src/billing/StripeClient.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,MAAM,MAAM,QAAQ,CAAA;AAC3B,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAA;AAcjD,OAAO,EAAE,YAAY,EAAE,MAAM,YAAY,CAAA;AAEzC,MAAM,MAAM,GAAG,YAAY,CAAC,cAAc,CAAC,CAAA;AAiD3C,+EAA+E;AAC/E,qBAAqB;AACrB,+EAA+E;AAE/E;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,MAAM,OAAO,YAAY;IACN,MAAM,CAAQ;IACd,aAAa,CAAQ;IACrB,MAAM,CAAkB;IACxB,MAAM,CAAQ;IAE/B,YAAY,MAA0B;QACpC,IAAI,CAAC,MAAM,GAAG,IAAI,MAAM,CAAC,MAAM,CAAC,SAAS,EAAE;YACzC,UAAU,EAAE,YAAY;YACxB,UAAU,EAAE,IAAI;SACjB,CAAC,CAAA;QACF,IAAI,CAAC,aAAa,GAAG,MAAM,CAAC,aAAa,CAAA;QACzC,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC,MAAM,CAAA;QAC3B,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC,MAAM,IAAI,wBAAwB,CAAA;QAEvD,MAAM,CAAC,IAAI,CAAC,2BAA2B,CAAC,CAAA;IAC1C,CAAC;IAED,6EAA6E;IAC7E,sBAAsB;IACtB,6EAA6E;IAE7E;;OAEG;IACH,KAAK,CAAC,cAAc,CAAC,MAIpB;QACC,IAAI,CAAC;YACH,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,MAAM,CAAC;gBAClD,KAAK,EAAE,MAAM,CAAC,KAAK;gBACnB,IAAI,EAAE,MAAM,CAAC,IAAI;gBACjB,QAAQ,EAAE;oBACR,GAAG,MAAM,CAAC,QAAQ;oBAClB,MAAM,EAAE,YAAY;iBACrB;aACF,CAAC,CAAA;YAEF,MAAM,CAAC,IAAI,CAAC,kBAAkB,EAAE,EAAE,UAAU,EAAE,QAAQ,CAAC,EAAE,EAAE,CAAC,CAAA;YAC5D,OAAO,QAAQ,CAAC,EAAsB,CAAA;QACxC,CAAC;QAAC,OAAO,KAAc,EAAE,CAAC;YACxB,MAAM,QAAQ,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAA;YACvE,MAAM,CAAC,KAAK,CAAC,2BAA2B,EAAE,SAAS,EAAE;gBACnD,YAAY,EAAE,QAAQ;gBACtB,KAAK,EAAE,MAAM,CAAC,KAAK;aACpB,CAAC,CAAA;YACF,MAAM,IAAI,YAAY,CAAC,2BAA2B,EAAE,kBAAkB,EAAE;gBACtE,aAAa,EAAE,QAAQ;aACxB,CAAC,CAAA;QACJ,CAAC;IACH,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,WAAW,CAAC,UAA4B;QAC5C,IAAI,CAAC;YACH,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,QAAQ,CAAC,UAAU,CAAC,CAAA;YACjE,IAAI,QAAQ,CAAC,OAAO,EAAE,CAAC;gBACrB,OAAO,IAAI,CAAA;YACb,CAAC;YACD,OAAO,QAAQ,CAAA;QACjB,CAAC;QAAC,OAAO,KAAc,EAAE,CAAC;YACxB,IAAI,KAAK,YAAY,MAAM,CAAC,MAAM,CAAC,WAAW,IAAI,KAAK,CAAC,IAAI,KAAK,kBAAkB,EAAE,CAAC;gBACpF,OAAO,IAAI,CAAA;YACb,CAAC;YACD,MAAM,KAAK,CAAA;QACb,CAAC;IACH,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,cAAc,CAClB,UAA4B,EAC5B,MAIC;QAED,OAAO,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,MAAM,CAAC,UAAU,EAAE,MAAM,CAAC,CAAA;IACzD,CAAC;IAED,6EAA6E;IAC7E,mBAAmB;IACnB,6EAA6E;IAE7E;;OAEG;IACH,KAAK,CAAC,qBAAqB,CACzB,OAAqC;QAErC,MAAM,OAAO,GAAG,IAAI,CAAC,UAAU,CAAC,OAAO,CAAC,IAAI,EAAE,OAAO,CAAC,aAAa,CAAC,CAAA;QAEpE,IAAI,CAAC,OAAO,EAAE,CAAC;YACb,MAAM,IAAI,YAAY,CAAC,iCAAiC,OAAO,CAAC,IAAI,EAAE,EAAE,cAAc,EAAE;gBACtF,IAAI,EAAE,OAAO,CAAC,IAAI;gBAClB,aAAa,EAAE,OAAO,CAAC,aAAa;aACrC,CAAC,CAAA;QACJ,CAAC;QAED,MAAM,SAAS,GAAmD;YAChE;gBACE,KAAK,EAAE,OAAO;gBACd,QAAQ,EAAE,OAAO,CAAC,SAAS,IAAI,CAAC;aACjC;SACF,CAAA;QAED,MAAM,aAAa,GAAwC;YACzD,IAAI,EAAE,cAAc;YACpB,UAAU,EAAE,SAAS;YACrB,WAAW,EAAE,OAAO,CAAC,UAAU;YAC/B,UAAU,EAAE,OAAO,CAAC,SAAS;YAC7B,QAAQ,EAAE;gBACR,IAAI,EAAE,OAAO,CAAC,IAAI;gBAClB,aAAa,EAAE,OAAO,CAAC,aAAa;gBACpC,SAAS,EAAE,MAAM,CAAC,OAAO,CAAC,SAAS,IAAI,CAAC,CAAC;gBACzC,GAAG,OAAO,CAAC,QAAQ;aACpB;YACD,iBAAiB,EAAE;gBACjB,QAAQ,EAAE;oBACR,IAAI,EAAE,OAAO,CAAC,IAAI;oBAClB,SAAS,EAAE,MAAM,CAAC,OAAO,CAAC,SAAS,IAAI,CAAC,CAAC;iBAC1C;aACF;SACF,CAAA;QAED,4CAA4C;QAC5C,IAAI,OAAO,CAAC,UAAU,EAAE,CAAC;YACvB,aAAa,CAAC,QAAQ,GAAG,OAAO,CAAC,UAAU,CAAA;QAC7C,CAAC;aAAM,IAAI,OAAO,CAAC,KAAK,EAAE,CAAC;YACzB,aAAa,CAAC,cAAc,GAAG,OAAO,CAAC,KAAK,CAAA;QAC9C,CAAC;QAED,kDAAkD;QAClD,IAAI,OAAO,CAAC,IAAI,KAAK,MAAM,IAAI,OAAO,CAAC,IAAI,KAAK,YAAY,EAAE,CAAC;YAC7D,aAAa,CAAC,UAAW,CAAC,CAAC,CAAC,CAAC,mBAAmB,GAAG;gBACjD,OAAO,EAAE,IAAI;gBACb,OAAO,EAAE,CAAC;gBACV,OAAO,EAAE,IAAI;aACd,CAAA;QACH,CAAC;QAED,IAAI,CAAC;YACH,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,QAAQ,CAAC,MAAM,CAAC,aAAa,CAAC,CAAA;YAEzE,MAAM,CAAC,IAAI,CAAC,0BAA0B,EAAE;gBACtC,SAAS,EAAE,OAAO,CAAC,EAAE;gBACrB,IAAI,EAAE,OAAO,CAAC,IAAI;gBAClB,SAAS,EAAE,OAAO,CAAC,SAAS;aAC7B,CAAC,CAAA;YAEF,OAAO;gBACL,SAAS,EAAE,OAAO,CAAC,EAA6B;gBAChD,GAAG,EAAE,OAAO,CAAC,GAAI;aAClB,CAAA;QACH,CAAC;QAAC,OAAO,KAAc,EAAE,CAAC;YACxB,MAAM,QAAQ,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAA;YACvE,MAAM,CAAC,KAAK,CAAC,mCAAmC,EAAE,SAAS,EAAE,EAAE,YAAY,EAAE,QAAQ,EAAE,CAAC,CAAA;YACxF,MAAM,IAAI,YAAY,CAAC,mCAAmC,EAAE,kBAAkB,EAAE;gBAC9E,aAAa,EAAE,QAAQ;aACxB,CAAC,CAAA;QACJ,CAAC;IACH,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,kBAAkB,CAAC,SAAkC;QACzD,OAAO,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,QAAQ,CAAC,QAAQ,CAAC,SAAS,EAAE;YACvD,MAAM,EAAE,CAAC,cAAc,EAAE,UAAU,CAAC;SACrC,CAAC,CAAA;IACJ,CAAC;IAED,6EAA6E;IAC7E,0BAA0B;IAC1B,6EAA6E;IAE7E;;OAEG;IACH,KAAK,CAAC,eAAe,CAAC,cAAoC;QACxD,IAAI,CAAC;YACH,OAAO,MAAM,IAAI,CAAC,MAAM,CAAC,aAAa,CAAC,QAAQ,CAAC,cAAc,CAAC,CAAA;QACjE,CAAC;QAAC,OAAO,KAAc,EAAE,CAAC;YACxB,IAAI,KAAK,YAAY,MAAM,CAAC,MAAM,CAAC,WAAW,IAAI,KAAK,CAAC,IAAI,KAAK,kBAAkB,EAAE,CAAC;gBACpF,OAAO,IAAI,CAAA;YACb,CAAC;YACD,MAAM,KAAK,CAAA;QACb,CAAC;IACH,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,iBAAiB,CAAC,UAA4B;QAClD,MAAM,aAAa,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,aAAa,CAAC,IAAI,CAAC;YACzD,QAAQ,EAAE,UAAU;YACpB,MAAM,EAAE,KAAK;YACb,MAAM,EAAE,CAAC,6BAA6B,CAAC;SACxC,CAAC,CAAA;QACF,OAAO,aAAa,CAAC,IAAI,CAAA;IAC3B,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,kBAAkB,CACtB,cAAoC,EACpC,MAKC;QAED,MAAM,YAAY,GAAG,MAAM,IAAI,CAAC,eAAe,CAAC,cAAc,CAAC,CAAA;QAC/D,IAAI,CAAC,YAAY,EAAE,CAAC;YAClB,MAAM,IAAI,YAAY,CAAC,wBAAwB,EAAE,wBAAwB,CAAC,CAAA;QAC5E,CAAC;QAED,MAAM,YAAY,GAAoC;YACpD,kBAAkB,EAAE,MAAM,CAAC,OAAO,KAAK,KAAK,CAAC,CAAC,CAAC,mBAAmB,CAAC,CAAC,CAAC,MAAM;SAC5E,CAAA;QAED,iDAAiD;QACjD,IAAI,MAAM,CAAC,IAAI,IAAI,MAAM,CAAC,aAAa,EAAE,CAAC;YACxC,MAAM,UAAU,GAAG,IAAI,CAAC,UAAU,CAAC,MAAM,CAAC,IAAI,EAAE,MAAM,CAAC,aAAa,CAAC,CAAA;YACrE,IAAI,UAAU,EAAE,CAAC;gBACf,YAAY,CAAC,KAAK,GAAG;oBACnB;wBACE,EAAE,EAAE,YAAY,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,EAAE;wBACjC,KAAK,EAAE,UAAU;wBACjB,QAAQ,EAAE,MAAM,CAAC,SAAS,IAAI,YAAY,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,QAAQ;qBAClE;iBACF,CAAA;gBACD,YAAY,CAAC,QAAQ,GAAG;oBACtB,GAAG,YAAY,CAAC,QAAQ;oBACxB,IAAI,EAAE,MAAM,CAAC,IAAI;oBACjB,SAAS,EAAE,MAAM,CAAC,MAAM,CAAC,SAAS,IAAI,YAAY,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC;iBAC3E,CAAA;YACH,CAAC;QACH,CAAC;aAAM,IAAI,MAAM,CAAC,SAAS,KAAK,SAAS,EAAE,CAAC;YAC1C,yBAAyB;YACzB,YAAY,CAAC,KAAK,GAAG;gBACnB;oBACE,EAAE,EAAE,YAAY,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,EAAE;oBACjC,QAAQ,EAAE,MAAM,CAAC,SAAS;iBAC3B;aACF,CAAA;YACD,YAAY,CAAC,QAAQ,GAAG;gBACtB,GAAG,YAAY,CAAC,QAAQ;gBACxB,SAAS,EAAE,MAAM,CAAC,MAAM,CAAC,SAAS,CAAC;aACpC,CAAA;QACH,CAAC;QAED,IAAI,CAAC;YACH,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,aAAa,CAAC,MAAM,CAAC,cAAc,EAAE,YAAY,CAAC,CAAA;YACpF,MAAM,CAAC,IAAI,CAAC,sBAAsB,EAAE,EAAE,cAAc,EAAE,MAAM,EAAE,CAAC,CAAA;YAC/D,OAAO,OAAO,CAAA;QAChB,CAAC;QAAC,OAAO,KAAc,EAAE,CAAC;YACxB,MAAM,QAAQ,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAA;YACvE,MAAM,CAAC,KAAK,CAAC,+BAA+B,EAAE,SAAS,EAAE;gBACvD,YAAY,EAAE,QAAQ;gBACtB,cAAc;aACf,CAAC,CAAA;YACF,MAAM,IAAI,YAAY,CAAC,+BAA+B,EAAE,kBAAkB,EAAE;gBAC1E,aAAa,EAAE,QAAQ;aACxB,CAAC,CAAA;QACJ,CAAC;IACH,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,kBAAkB,CACtB,cAAoC,EACpC,OAGC;QAED,IAAI,CAAC;YACH,IAAI,OAAO,EAAE,WAAW,EAAE,CAAC;gBACzB,OAAO,MAAM,IAAI,CAAC,MAAM,CAAC,aAAa,CAAC,MAAM,CAAC,cAAc,EAAE;oBAC5D,oBAAoB,EAAE;wBACpB,OAAO,EAAE,OAAO,CAAC,QAAQ;qBAC1B;iBACF,CAAC,CAAA;YACJ,CAAC;iBAAM,CAAC;gBACN,uBAAuB;gBACvB,OAAO,MAAM,IAAI,CAAC,MAAM,CAAC,aAAa,CAAC,MAAM,CAAC,cAAc,EAAE;oBAC5D,oBAAoB,EAAE,IAAI;oBAC1B,oBAAoB,EAAE;wBACpB,OAAO,EAAE,OAAO,EAAE,QAAQ;qBAC3B;iBACF,CAAC,CAAA;YACJ,CAAC;QACH,CAAC;QAAC,OAAO,KAAc,EAAE,CAAC;YACxB,MAAM,QAAQ,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAA;YACvE,MAAM,CAAC,KAAK,CAAC,+BAA+B,EAAE,SAAS,EAAE;gBACvD,YAAY,EAAE,QAAQ;gBACtB,cAAc;aACf,CAAC,CAAA;YACF,MAAM,IAAI,YAAY,CAAC,+BAA+B,EAAE,kBAAkB,EAAE;gBAC1E,aAAa,EAAE,QAAQ;aACxB,CAAC,CAAA;QACJ,CAAC;IACH,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,sBAAsB,CAAC,cAAoC;QAC/D,OAAO,IAAI,CAAC,MAAM,CAAC,aAAa,CAAC,MAAM,CAAC,cAAc,EAAE;YACtD,oBAAoB,EAAE,KAAK;SAC5B,CAAC,CAAA;IACJ,CAAC;IAED,6EAA6E;IAC7E,kBAAkB;IAClB,6EAA6E;IAE7E;;OAEG;IACH,KAAK,CAAC,mBAAmB,CACvB,OAAmC;QAEnC,IAAI,CAAC;YACH,sDAAsD;YACtD,oDAAoD;YACpD,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,aAAa,CAAC,QAAQ,CAAC,MAAM,CAAC;gBAC9D,QAAQ,EAAE,OAAO,CAAC,UAAU;gBAC5B,UAAU,EAAE,OAAO,CAAC,SAAS;aAC9B,CAAC,CAAA;YAEF,MAAM,CAAC,IAAI,CAAC,wBAAwB,EAAE,EAAE,UAAU,EAAE,OAAO,CAAC,UAAU,EAAE,CAAC,CAAA;YAEzE,OAAO;gBACL,GAAG,EAAE,OAAO,CAAC,GAAG;aACjB,CAAA;QACH,CAAC;QAAC,OAAO,KAAc,EAAE,CAAC;YACxB,MAAM,QAAQ,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAA;YACvE,MAAM,CAAC,KAAK,CAAC,iCAAiC,EAAE,SAAS,EAAE;gBACzD,YAAY,EAAE,QAAQ;gBACtB,UAAU,EAAE,OAAO,CAAC,UAAU;aAC/B,CAAC,CAAA;YACF,MAAM,IAAI,YAAY,CAAC,iCAAiC,EAAE,kBAAkB,EAAE;gBAC5E,aAAa,EAAE,QAAQ;aACxB,CAAC,CAAA;QACJ,CAAC;IACH,CAAC;IAED,6EAA6E;IAC7E,qBAAqB;IACrB,6EAA6E;IAE7E;;OAEG;IACH,KAAK,CAAC,YAAY,CAChB,UAA4B,EAC5B,OAGC;QAKD,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC;YAC/C,QAAQ,EAAE,UAAU;YACpB,KAAK,EAAE,OAAO,EAAE,KAAK,IAAI,EAAE;YAC3B,cAAc,EAAE,OAAO,EAAE,aAAa;SACvC,CAAC,CAAA;QAEF,OAAO;YACL,QAAQ,EAAE,QAAQ,CAAC,IAAI;YACvB,OAAO,EAAE,QAAQ,CAAC,QAAQ;SAC3B,CAAA;IACH,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,UAAU,CAAC,SAAiB;QAChC,IAAI,CAAC;YACH,OAAO,MAAM,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAA;QACvD,CAAC;QAAC,OAAO,KAAc,EAAE,CAAC;YACxB,IAAI,KAAK,YAAY,MAAM,CAAC,MAAM,CAAC,WAAW,IAAI,KAAK,CAAC,IAAI,KAAK,kBAAkB,EAAE,CAAC;gBACpF,OAAO,IAAI,CAAA;YACb,CAAC;YACD,MAAM,KAAK,CAAA;QACb,CAAC;IACH,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,kBAAkB,CAAC,UAA4B;QACnD,IAAI,CAAC;YACH,OAAO,MAAM,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,gBAAgB,CAAC;gBACjD,QAAQ,EAAE,UAAU;aACrB,CAAC,CAAA;QACJ,CAAC;QAAC,OAAO,KAAc,EAAE,CAAC;YACxB,IAAI,KAAK,YAAY,MAAM,CAAC,MAAM,CAAC,WAAW,IAAI,KAAK,CAAC,IAAI,KAAK,uBAAuB,EAAE,CAAC;gBACzF,OAAO,IAAI,CAAA;YACb,CAAC;YACD,MAAM,KAAK,CAAA;QACb,CAAC;IACH,CAAC;IAED,6EAA6E;IAC7E,mBAAmB;IACnB,6EAA6E;IAE7E;;OAEG;IACH,sBAAsB,CAAC,OAAe,EAAE,SAAiB;QACvD,IAAI,CAAC;YACH,OAAO,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,cAAc,CAAC,OAAO,EAAE,SAAS,EAAE,IAAI,CAAC,aAAa,CAAC,CAAA;QACpF,CAAC;QAAC,OAAO,KAAc,EAAE,CAAC;YACxB,MAAM,CAAC,IAAI,CAAC,uCAAuC,EAAE;gBACnD,KAAK,EAAE,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC;aAC9D,CAAC,CAAA;YACF,MAAM,IAAI,YAAY,CAAC,2BAA2B,EAAE,2BAA2B,CAAC,CAAA;QAClF,CAAC;IACH,CAAC;IAED,6EAA6E;IAC7E,iBAAiB;IACjB,6EAA6E;IAE7E;;OAEG;IACH,UAAU,CAAC,IAAiB,EAAE,aAA4B;QACxD,IAAI,IAAI,KAAK,WAAW,EAAE,CAAC;YACzB,OAAO,IAAI,CAAA,CAAC,0CAA0C;QACxD,CAAC;QAED,MAAM,UAAU,GAAG,IAAI,CAAC,MAAM,CAAC,IAA8B,CAAC,CAAA;QAC9D,IAAI,CAAC,UAAU,EAAE,CAAC;YAChB,OAAO,IAAI,CAAA;QACb,CAAC;QAED,OAAO,UAAU,CAAC,aAAa,CAAC,CAAA;IAClC,CAAC;IAED;;OAEG;IACH,MAAM,CAAC,qBAAqB,CAAC,YAAwC;QACnE,QAAQ,YAAY,EAAE,CAAC;YACrB,KAAK,QAAQ;gBACX,OAAO,QAAQ,CAAA;YACjB,KAAK,UAAU;gBACb,OAAO,UAAU,CAAA;YACnB,KAAK,UAAU;gBACb,OAAO,UAAU,CAAA;YACnB,KAAK,UAAU;gBACb,OAAO,UAAU,CAAA;YACnB,KAAK,QAAQ;gBACX,OAAO,QAAQ,CAAA;YACjB,KAAK,YAAY;gBACf,OAAO,YAAY,CAAA;YACrB,KAAK,oBAAoB;gBACvB,OAAO,oBAAoB,CAAA;YAC7B,KAAK,QAAQ;gBACX,OAAO,QAAQ,CAAA;YACjB;gBACE,OAAO,QAAQ,CAAA;QACnB,CAAC;IACH,CAAC;IAED;;OAEG;IACH,iBAAiB;QACf,OAAO,IAAI,CAAC,MAAM,CAAA;IACpB,CAAC;CACF"}
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SMI-1069: Stripe Reconciliation Job
|
|
3
|
+
*
|
|
4
|
+
* Periodically reconciles local subscription and invoice data with Stripe.
|
|
5
|
+
* Identifies and reports discrepancies to ensure data consistency.
|
|
6
|
+
*
|
|
7
|
+
* Features:
|
|
8
|
+
* - Subscription status sync
|
|
9
|
+
* - Invoice status verification
|
|
10
|
+
* - Missing data detection
|
|
11
|
+
* - Automatic correction (optional)
|
|
12
|
+
* - Detailed reporting
|
|
13
|
+
*/
|
|
14
|
+
import type { Database as BetterSqliteDatabase } from 'better-sqlite3';
|
|
15
|
+
import type { StripeClient } from './StripeClient.js';
|
|
16
|
+
export interface StripeReconciliationJobConfig {
|
|
17
|
+
/**
|
|
18
|
+
* StripeClient for API calls
|
|
19
|
+
*/
|
|
20
|
+
stripeClient: StripeClient;
|
|
21
|
+
/**
|
|
22
|
+
* Database connection
|
|
23
|
+
*/
|
|
24
|
+
db: BetterSqliteDatabase;
|
|
25
|
+
/**
|
|
26
|
+
* Whether to automatically fix discrepancies
|
|
27
|
+
* @default false
|
|
28
|
+
*/
|
|
29
|
+
autoFix?: boolean;
|
|
30
|
+
/**
|
|
31
|
+
* Maximum subscriptions to process per run
|
|
32
|
+
* @default 100
|
|
33
|
+
*/
|
|
34
|
+
batchSize?: number;
|
|
35
|
+
}
|
|
36
|
+
export type DiscrepancyType = 'status_mismatch' | 'missing_local' | 'missing_stripe' | 'tier_mismatch' | 'seat_count_mismatch' | 'period_mismatch' | 'invoice_status_mismatch' | 'invoice_amount_mismatch';
|
|
37
|
+
export interface Discrepancy {
|
|
38
|
+
type: DiscrepancyType;
|
|
39
|
+
entityType: 'subscription' | 'invoice';
|
|
40
|
+
entityId: string;
|
|
41
|
+
stripeId: string;
|
|
42
|
+
localValue: string | number | null;
|
|
43
|
+
stripeValue: string | number | null;
|
|
44
|
+
description: string;
|
|
45
|
+
fixed: boolean;
|
|
46
|
+
}
|
|
47
|
+
export interface ReconciliationResult {
|
|
48
|
+
success: boolean;
|
|
49
|
+
startedAt: string;
|
|
50
|
+
completedAt: string;
|
|
51
|
+
durationMs: number;
|
|
52
|
+
stats: {
|
|
53
|
+
subscriptionsChecked: number;
|
|
54
|
+
invoicesChecked: number;
|
|
55
|
+
discrepanciesFound: number;
|
|
56
|
+
discrepanciesFixed: number;
|
|
57
|
+
};
|
|
58
|
+
discrepancies: Discrepancy[];
|
|
59
|
+
errors: string[];
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Stripe Reconciliation Job
|
|
63
|
+
*
|
|
64
|
+
* @example
|
|
65
|
+
* ```typescript
|
|
66
|
+
* const job = new StripeReconciliationJob({
|
|
67
|
+
* stripeClient,
|
|
68
|
+
* db,
|
|
69
|
+
* autoFix: true,
|
|
70
|
+
* });
|
|
71
|
+
*
|
|
72
|
+
* // Run reconciliation
|
|
73
|
+
* const result = await job.run();
|
|
74
|
+
* console.log(`Found ${result.discrepancies.length} discrepancies`);
|
|
75
|
+
* ```
|
|
76
|
+
*/
|
|
77
|
+
export declare class StripeReconciliationJob {
|
|
78
|
+
private readonly stripe;
|
|
79
|
+
private readonly db;
|
|
80
|
+
private readonly autoFix;
|
|
81
|
+
private readonly batchSize;
|
|
82
|
+
constructor(config: StripeReconciliationJobConfig);
|
|
83
|
+
/**
|
|
84
|
+
* Run the reconciliation job
|
|
85
|
+
*/
|
|
86
|
+
run(): Promise<ReconciliationResult>;
|
|
87
|
+
private getLocalSubscriptions;
|
|
88
|
+
private reconcileSubscription;
|
|
89
|
+
private getLocalInvoices;
|
|
90
|
+
private reconcileInvoice;
|
|
91
|
+
private fixDiscrepancy;
|
|
92
|
+
private fixSubscriptionDiscrepancy;
|
|
93
|
+
private fixInvoiceDiscrepancy;
|
|
94
|
+
}
|
|
95
|
+
//# sourceMappingURL=StripeReconciliationJob.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"StripeReconciliationJob.d.ts","sourceRoot":"","sources":["../../../src/billing/StripeReconciliationJob.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,KAAK,EAAE,QAAQ,IAAI,oBAAoB,EAAE,MAAM,gBAAgB,CAAA;AAGtE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAA;AASrD,MAAM,WAAW,6BAA6B;IAC5C;;OAEG;IACH,YAAY,EAAE,YAAY,CAAA;IAE1B;;OAEG;IACH,EAAE,EAAE,oBAAoB,CAAA;IAExB;;;OAGG;IACH,OAAO,CAAC,EAAE,OAAO,CAAA;IAEjB;;;OAGG;IACH,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB;AAMD,MAAM,MAAM,eAAe,GACvB,iBAAiB,GACjB,eAAe,GACf,gBAAgB,GAChB,eAAe,GACf,qBAAqB,GACrB,iBAAiB,GACjB,yBAAyB,GACzB,yBAAyB,CAAA;AAE7B,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,eAAe,CAAA;IACrB,UAAU,EAAE,cAAc,GAAG,SAAS,CAAA;IACtC,QAAQ,EAAE,MAAM,CAAA;IAChB,QAAQ,EAAE,MAAM,CAAA;IAChB,UAAU,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAAA;IAClC,WAAW,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAAA;IACnC,WAAW,EAAE,MAAM,CAAA;IACnB,KAAK,EAAE,OAAO,CAAA;CACf;AAED,MAAM,WAAW,oBAAoB;IACnC,OAAO,EAAE,OAAO,CAAA;IAChB,SAAS,EAAE,MAAM,CAAA;IACjB,WAAW,EAAE,MAAM,CAAA;IACnB,UAAU,EAAE,MAAM,CAAA;IAClB,KAAK,EAAE;QACL,oBAAoB,EAAE,MAAM,CAAA;QAC5B,eAAe,EAAE,MAAM,CAAA;QACvB,kBAAkB,EAAE,MAAM,CAAA;QAC1B,kBAAkB,EAAE,MAAM,CAAA;KAC3B,CAAA;IACD,aAAa,EAAE,WAAW,EAAE,CAAA;IAC5B,MAAM,EAAE,MAAM,EAAE,CAAA;CACjB;AAMD;;;;;;;;;;;;;;;GAeG;AACH,qBAAa,uBAAuB;IAClC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAc;IACrC,OAAO,CAAC,QAAQ,CAAC,EAAE,CAAsB;IACzC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAS;IACjC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAQ;gBAEtB,MAAM,EAAE,6BAA6B;IAgBjD;;OAEG;IACG,GAAG,IAAI,OAAO,CAAC,oBAAoB,CAAC;IAuF1C,OAAO,CAAC,qBAAqB;YAoBf,qBAAqB;IA0EnC,OAAO,CAAC,gBAAgB;YAgBV,gBAAgB;IAyD9B,OAAO,CAAC,cAAc;IAmBtB,OAAO,CAAC,0BAA0B;IA+ClC,OAAO,CAAC,qBAAqB;CA0B9B"}
|