@skillsmith/core 0.6.3 → 0.7.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.
Files changed (130) hide show
  1. package/CHANGELOG.md +11 -1
  2. package/README.md +16 -0
  3. package/dist/.tsbuildinfo +1 -1
  4. package/dist/src/api/client.d.ts.map +1 -1
  5. package/dist/src/api/client.events.d.ts.map +1 -1
  6. package/dist/src/api/client.events.js +10 -1
  7. package/dist/src/api/client.events.js.map +1 -1
  8. package/dist/src/api/client.health.d.ts.map +1 -1
  9. package/dist/src/api/client.health.js +7 -1
  10. package/dist/src/api/client.health.js.map +1 -1
  11. package/dist/src/api/client.js +11 -1
  12. package/dist/src/api/client.js.map +1 -1
  13. package/dist/src/api/utils.d.ts +14 -3
  14. package/dist/src/api/utils.d.ts.map +1 -1
  15. package/dist/src/api/utils.js +14 -4
  16. package/dist/src/api/utils.js.map +1 -1
  17. package/dist/src/exports/services.d.ts +0 -1
  18. package/dist/src/exports/services.d.ts.map +1 -1
  19. package/dist/src/exports/services.js +14 -3
  20. package/dist/src/exports/services.js.map +1 -1
  21. package/dist/src/index.d.ts +2 -1
  22. package/dist/src/index.d.ts.map +1 -1
  23. package/dist/src/index.js +4 -1
  24. package/dist/src/index.js.map +1 -1
  25. package/dist/src/types.d.ts +12 -0
  26. package/dist/src/types.d.ts.map +1 -1
  27. package/dist/tests/api/client.auth.test.js +50 -0
  28. package/dist/tests/api/client.auth.test.js.map +1 -1
  29. package/dist/tests/api/utils.test.js +7 -3
  30. package/dist/tests/api/utils.test.js.map +1 -1
  31. package/dist/tests/{billing/stripe-validators.test.d.ts → security/sanitization-stripe-ids.test.d.ts} +1 -1
  32. package/dist/tests/security/sanitization-stripe-ids.test.d.ts.map +1 -0
  33. package/dist/tests/{billing/stripe-validators.test.js → security/sanitization-stripe-ids.test.js} +1 -1
  34. package/dist/tests/security/sanitization-stripe-ids.test.js.map +1 -0
  35. package/dist/tests/shared.test.js.map +1 -1
  36. package/package.json +1 -6
  37. package/dist/src/billing/BillingService.d.ts +0 -101
  38. package/dist/src/billing/BillingService.d.ts.map +0 -1
  39. package/dist/src/billing/BillingService.helpers.d.ts +0 -15
  40. package/dist/src/billing/BillingService.helpers.d.ts.map +0 -1
  41. package/dist/src/billing/BillingService.helpers.js +0 -45
  42. package/dist/src/billing/BillingService.helpers.js.map +0 -1
  43. package/dist/src/billing/BillingService.js +0 -263
  44. package/dist/src/billing/BillingService.js.map +0 -1
  45. package/dist/src/billing/BillingService.types.d.ts +0 -52
  46. package/dist/src/billing/BillingService.types.d.ts.map +0 -1
  47. package/dist/src/billing/BillingService.types.js +0 -6
  48. package/dist/src/billing/BillingService.types.js.map +0 -1
  49. package/dist/src/billing/GDPRComplianceService.d.ts +0 -81
  50. package/dist/src/billing/GDPRComplianceService.d.ts.map +0 -1
  51. package/dist/src/billing/GDPRComplianceService.js +0 -361
  52. package/dist/src/billing/GDPRComplianceService.js.map +0 -1
  53. package/dist/src/billing/StripeClient.d.ts +0 -119
  54. package/dist/src/billing/StripeClient.d.ts.map +0 -1
  55. package/dist/src/billing/StripeClient.js +0 -405
  56. package/dist/src/billing/StripeClient.js.map +0 -1
  57. package/dist/src/billing/StripeReconciliationJob.d.ts +0 -50
  58. package/dist/src/billing/StripeReconciliationJob.d.ts.map +0 -1
  59. package/dist/src/billing/StripeReconciliationJob.js +0 -365
  60. package/dist/src/billing/StripeReconciliationJob.js.map +0 -1
  61. package/dist/src/billing/StripeWebhookHandler.d.ts +0 -49
  62. package/dist/src/billing/StripeWebhookHandler.d.ts.map +0 -1
  63. package/dist/src/billing/StripeWebhookHandler.js +0 -162
  64. package/dist/src/billing/StripeWebhookHandler.js.map +0 -1
  65. package/dist/src/billing/gdpr-types.d.ts +0 -103
  66. package/dist/src/billing/gdpr-types.d.ts.map +0 -1
  67. package/dist/src/billing/gdpr-types.js +0 -7
  68. package/dist/src/billing/gdpr-types.js.map +0 -1
  69. package/dist/src/billing/index.d.ts +0 -18
  70. package/dist/src/billing/index.d.ts.map +0 -1
  71. package/dist/src/billing/index.js +0 -19
  72. package/dist/src/billing/index.js.map +0 -1
  73. package/dist/src/billing/reconciliation-helpers.d.ts +0 -16
  74. package/dist/src/billing/reconciliation-helpers.d.ts.map +0 -1
  75. package/dist/src/billing/reconciliation-helpers.js +0 -53
  76. package/dist/src/billing/reconciliation-helpers.js.map +0 -1
  77. package/dist/src/billing/reconciliation-types.d.ts +0 -71
  78. package/dist/src/billing/reconciliation-types.d.ts.map +0 -1
  79. package/dist/src/billing/reconciliation-types.js +0 -7
  80. package/dist/src/billing/reconciliation-types.js.map +0 -1
  81. package/dist/src/billing/stripe-client-types.d.ts +0 -45
  82. package/dist/src/billing/stripe-client-types.d.ts.map +0 -1
  83. package/dist/src/billing/stripe-client-types.js +0 -7
  84. package/dist/src/billing/stripe-client-types.js.map +0 -1
  85. package/dist/src/billing/stripe-helpers.d.ts +0 -17
  86. package/dist/src/billing/stripe-helpers.d.ts.map +0 -1
  87. package/dist/src/billing/stripe-helpers.js +0 -50
  88. package/dist/src/billing/stripe-helpers.js.map +0 -1
  89. package/dist/src/billing/types.d.ts +0 -266
  90. package/dist/src/billing/types.d.ts.map +0 -1
  91. package/dist/src/billing/types.js +0 -23
  92. package/dist/src/billing/types.js.map +0 -1
  93. package/dist/src/billing/webhook-handlers.d.ts +0 -56
  94. package/dist/src/billing/webhook-handlers.d.ts.map +0 -1
  95. package/dist/src/billing/webhook-handlers.js +0 -303
  96. package/dist/src/billing/webhook-handlers.js.map +0 -1
  97. package/dist/src/billing/webhook-types.d.ts +0 -42
  98. package/dist/src/billing/webhook-types.d.ts.map +0 -1
  99. package/dist/src/billing/webhook-types.js +0 -7
  100. package/dist/src/billing/webhook-types.js.map +0 -1
  101. package/dist/tests/billing/BillingService.test.d.ts +0 -7
  102. package/dist/tests/billing/BillingService.test.d.ts.map +0 -1
  103. package/dist/tests/billing/BillingService.test.js +0 -168
  104. package/dist/tests/billing/BillingService.test.js.map +0 -1
  105. package/dist/tests/billing/GDPRCompliance.test.d.ts +0 -7
  106. package/dist/tests/billing/GDPRCompliance.test.d.ts.map +0 -1
  107. package/dist/tests/billing/GDPRCompliance.test.js +0 -380
  108. package/dist/tests/billing/GDPRCompliance.test.js.map +0 -1
  109. package/dist/tests/billing/StripeClient.test.d.ts +0 -18
  110. package/dist/tests/billing/StripeClient.test.d.ts.map +0 -1
  111. package/dist/tests/billing/StripeClient.test.js +0 -566
  112. package/dist/tests/billing/StripeClient.test.js.map +0 -1
  113. package/dist/tests/billing/StripeReconciliation.test.d.ts +0 -7
  114. package/dist/tests/billing/StripeReconciliation.test.d.ts.map +0 -1
  115. package/dist/tests/billing/StripeReconciliation.test.js +0 -266
  116. package/dist/tests/billing/StripeReconciliation.test.js.map +0 -1
  117. package/dist/tests/billing/StripeWebhookHandler.test.d.ts +0 -16
  118. package/dist/tests/billing/StripeWebhookHandler.test.d.ts.map +0 -1
  119. package/dist/tests/billing/StripeWebhookHandler.test.js +0 -240
  120. package/dist/tests/billing/StripeWebhookHandler.test.js.map +0 -1
  121. package/dist/tests/billing/stripe-helpers.test.d.ts +0 -7
  122. package/dist/tests/billing/stripe-helpers.test.d.ts.map +0 -1
  123. package/dist/tests/billing/stripe-helpers.test.js +0 -91
  124. package/dist/tests/billing/stripe-helpers.test.js.map +0 -1
  125. package/dist/tests/billing/stripe-validators.test.d.ts.map +0 -1
  126. package/dist/tests/billing/stripe-validators.test.js.map +0 -1
  127. package/dist/tests/billing/webhook-handlers.test.d.ts +0 -16
  128. package/dist/tests/billing/webhook-handlers.test.d.ts.map +0 -1
  129. package/dist/tests/billing/webhook-handlers.test.js +0 -519
  130. package/dist/tests/billing/webhook-handlers.test.js.map +0 -1
@@ -1,266 +0,0 @@
1
- /**
2
- * SMI-1062: Billing Types and Interfaces
3
- *
4
- * Type definitions for Stripe billing integration including:
5
- * - Subscription management
6
- * - Customer management
7
- * - Invoice handling
8
- * - Webhook events
9
- */
10
- /**
11
- * Available license tiers
12
- * - community: Free tier (1,000 API calls/month)
13
- * - individual: Solo developers ($9.99/mo, 10,000 API calls/month)
14
- * - team: Teams ($25/user/mo, 100,000 API calls/month)
15
- * - enterprise: Full enterprise ($55/user/mo, unlimited)
16
- */
17
- export type LicenseTier = 'community' | 'individual' | 'team' | 'enterprise';
18
- /**
19
- * Stripe Customer ID (cus_xxx)
20
- */
21
- export type StripeCustomerId = string & {
22
- readonly __brand: 'StripeCustomerId';
23
- };
24
- /**
25
- * Stripe Subscription ID (sub_xxx)
26
- */
27
- export type StripeSubscriptionId = string & {
28
- readonly __brand: 'StripeSubscriptionId';
29
- };
30
- /**
31
- * Stripe Price ID (price_xxx)
32
- */
33
- export type StripePriceId = string & {
34
- readonly __brand: 'StripePriceId';
35
- };
36
- /**
37
- * Stripe Invoice ID (in_xxx)
38
- */
39
- export type StripeInvoiceId = string & {
40
- readonly __brand: 'StripeInvoiceId';
41
- };
42
- /**
43
- * Stripe Checkout Session ID (cs_xxx)
44
- */
45
- export type StripeCheckoutSessionId = string & {
46
- readonly __brand: 'StripeCheckoutSessionId';
47
- };
48
- /**
49
- * Stripe Event ID (evt_xxx)
50
- */
51
- export type StripeEventId = string & {
52
- readonly __brand: 'StripeEventId';
53
- };
54
- /**
55
- * Subscription status values
56
- */
57
- export type SubscriptionStatus = 'active' | 'past_due' | 'canceled' | 'trialing' | 'paused' | 'incomplete' | 'incomplete_expired' | 'unpaid';
58
- /**
59
- * Customer record in our database
60
- */
61
- export interface Customer {
62
- id: string;
63
- customerId: string;
64
- email: string;
65
- stripeCustomerId: StripeCustomerId | null;
66
- tier: LicenseTier;
67
- status: SubscriptionStatus;
68
- createdAt: Date;
69
- updatedAt: Date;
70
- }
71
- /**
72
- * Create customer request
73
- */
74
- export interface CreateCustomerRequest {
75
- email: string;
76
- name?: string;
77
- metadata?: Record<string, string>;
78
- }
79
- /**
80
- * Create customer response
81
- */
82
- export interface CreateCustomerResponse {
83
- customerId: string;
84
- stripeCustomerId: StripeCustomerId;
85
- }
86
- /**
87
- * Subscription record in our database
88
- */
89
- export interface Subscription {
90
- id: string;
91
- customerId: string;
92
- stripeSubscriptionId: StripeSubscriptionId | null;
93
- stripePriceId: StripePriceId | null;
94
- tier: LicenseTier;
95
- status: SubscriptionStatus;
96
- seatCount: number;
97
- currentPeriodStart: Date | null;
98
- currentPeriodEnd: Date | null;
99
- canceledAt: Date | null;
100
- createdAt: Date;
101
- updatedAt: Date;
102
- }
103
- /**
104
- * Billing period (monthly or annual)
105
- */
106
- export type BillingPeriod = 'monthly' | 'annual';
107
- /**
108
- * Create checkout session request
109
- */
110
- export interface CreateCheckoutSessionRequest {
111
- tier: LicenseTier;
112
- billingPeriod: BillingPeriod;
113
- seatCount?: number;
114
- successUrl: string;
115
- cancelUrl: string;
116
- customerId?: string;
117
- email?: string;
118
- metadata?: Record<string, string>;
119
- }
120
- /**
121
- * Create checkout session response
122
- */
123
- export interface CreateCheckoutSessionResponse {
124
- sessionId: StripeCheckoutSessionId;
125
- url: string;
126
- }
127
- /**
128
- * Update subscription request
129
- */
130
- export interface UpdateSubscriptionRequest {
131
- subscriptionId: StripeSubscriptionId;
132
- tier?: LicenseTier;
133
- seatCount?: number;
134
- prorate?: boolean;
135
- }
136
- /**
137
- * Cancel subscription request
138
- */
139
- export interface CancelSubscriptionRequest {
140
- subscriptionId: StripeSubscriptionId;
141
- immediately?: boolean;
142
- feedback?: string;
143
- }
144
- /**
145
- * Invoice status values
146
- */
147
- export type InvoiceStatus = 'draft' | 'open' | 'paid' | 'void' | 'uncollectible';
148
- /**
149
- * Invoice record in our database
150
- */
151
- export interface Invoice {
152
- id: string;
153
- customerId: string;
154
- stripeInvoiceId: StripeInvoiceId;
155
- subscriptionId: string | null;
156
- amountCents: number;
157
- currency: string;
158
- status: InvoiceStatus;
159
- pdfUrl: string | null;
160
- hostedInvoiceUrl: string | null;
161
- invoiceNumber: string | null;
162
- paidAt: Date | null;
163
- periodStart: Date | null;
164
- periodEnd: Date | null;
165
- createdAt: Date;
166
- }
167
- /**
168
- * Invoice list request
169
- */
170
- export interface ListInvoicesRequest {
171
- customerId: string;
172
- limit?: number;
173
- startingAfter?: string;
174
- }
175
- /**
176
- * Invoice list response
177
- */
178
- export interface ListInvoicesResponse {
179
- invoices: Invoice[];
180
- hasMore: boolean;
181
- }
182
- /**
183
- * Stripe webhook event types we handle
184
- */
185
- export type StripeWebhookEventType = 'customer.subscription.created' | 'customer.subscription.updated' | 'customer.subscription.deleted' | 'invoice.payment_succeeded' | 'invoice.payment_failed' | 'checkout.session.completed' | 'customer.created' | 'customer.updated';
186
- /**
187
- * Webhook event record in our database
188
- */
189
- export interface WebhookEvent {
190
- id: string;
191
- stripeEventId: StripeEventId;
192
- eventType: StripeWebhookEventType;
193
- processedAt: Date;
194
- payload: string;
195
- success: boolean;
196
- errorMessage: string | null;
197
- createdAt: Date;
198
- }
199
- /**
200
- * Webhook processing result
201
- */
202
- export interface WebhookProcessResult {
203
- success: boolean;
204
- message: string;
205
- eventId: StripeEventId;
206
- processed: boolean;
207
- error?: string;
208
- }
209
- /**
210
- * Create portal session request
211
- */
212
- export interface CreatePortalSessionRequest {
213
- customerId: string;
214
- returnUrl: string;
215
- }
216
- /**
217
- * Create portal session response
218
- */
219
- export interface CreatePortalSessionResponse {
220
- url: string;
221
- }
222
- /**
223
- * Seat update request
224
- */
225
- export interface UpdateSeatsRequest {
226
- subscriptionId: StripeSubscriptionId;
227
- seatCount: number;
228
- prorate?: boolean;
229
- }
230
- /**
231
- * Seat update response
232
- */
233
- export interface UpdateSeatsResponse {
234
- success: boolean;
235
- seatCount: number;
236
- proratedAmount?: number;
237
- message: string;
238
- }
239
- /**
240
- * Price configuration for a tier
241
- */
242
- export interface TierPriceConfig {
243
- tier: LicenseTier;
244
- monthlyPriceId: StripePriceId;
245
- annualPriceId: StripePriceId;
246
- monthlyPrice: number;
247
- annualPrice: number;
248
- perUser: boolean;
249
- }
250
- /**
251
- * All price configurations
252
- */
253
- export type PriceConfigs = Record<LicenseTier, TierPriceConfig>;
254
- /**
255
- * Billing-specific error codes
256
- */
257
- export type BillingErrorCode = 'CUSTOMER_NOT_FOUND' | 'SUBSCRIPTION_NOT_FOUND' | 'INVALID_TIER' | 'PAYMENT_FAILED' | 'WEBHOOK_SIGNATURE_INVALID' | 'WEBHOOK_DUPLICATE_EVENT' | 'STRIPE_API_ERROR' | 'SEAT_LIMIT_EXCEEDED' | 'DOWNGRADE_NOT_ALLOWED';
258
- /**
259
- * Billing error class
260
- */
261
- export declare class BillingError extends Error {
262
- readonly code: BillingErrorCode;
263
- readonly details?: Record<string, unknown> | undefined;
264
- constructor(message: string, code: BillingErrorCode, details?: Record<string, unknown> | undefined);
265
- }
266
- //# sourceMappingURL=types.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/billing/types.ts"],"names":[],"mappings":"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"}
@@ -1,23 +0,0 @@
1
- /**
2
- * SMI-1062: Billing Types and Interfaces
3
- *
4
- * Type definitions for Stripe billing integration including:
5
- * - Subscription management
6
- * - Customer management
7
- * - Invoice handling
8
- * - Webhook events
9
- */
10
- /**
11
- * Billing error class
12
- */
13
- export class BillingError extends Error {
14
- code;
15
- details;
16
- constructor(message, code, details) {
17
- super(message);
18
- this.code = code;
19
- this.details = details;
20
- this.name = 'BillingError';
21
- }
22
- }
23
- //# sourceMappingURL=types.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"types.js","sourceRoot":"","sources":["../../../src/billing/types.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAkVH;;GAEG;AACH,MAAM,OAAO,YAAa,SAAQ,KAAK;IAGnB;IACA;IAHlB,YACE,OAAe,EACC,IAAsB,EACtB,OAAiC;QAEjD,KAAK,CAAC,OAAO,CAAC,CAAA;QAHE,SAAI,GAAJ,IAAI,CAAkB;QACtB,YAAO,GAAP,OAAO,CAA0B;QAGjD,IAAI,CAAC,IAAI,GAAG,cAAc,CAAA;IAC5B,CAAC;CACF"}
@@ -1,56 +0,0 @@
1
- /**
2
- * SMI-1070: Stripe Webhook Event Handlers
3
- *
4
- * Individual event handler functions for Stripe webhooks.
5
- * These are extracted to reduce the size of the main handler file.
6
- */
7
- import type Stripe from 'stripe';
8
- import type { Database as DatabaseType } from '../db/database-interface.js';
9
- import { StripeClient } from './StripeClient.js';
10
- import type { BillingService } from './BillingService.js';
11
- import type { LicenseTier } from './types.js';
12
- export interface WebhookHandlerContext {
13
- stripe: StripeClient;
14
- billing: BillingService;
15
- db: DatabaseType;
16
- onLicenseKeyNeeded?: (params: {
17
- customerId: string;
18
- tier: LicenseTier;
19
- expiresAt: Date;
20
- subscriptionId: string;
21
- }) => Promise<string>;
22
- onEmailNeeded?: (params: {
23
- type: 'license_key' | 'payment_failed' | 'subscription_canceled';
24
- email: string;
25
- data: Record<string, unknown>;
26
- }) => Promise<void>;
27
- }
28
- export declare function handleSubscriptionCreated(ctx: WebhookHandlerContext, subscription: Stripe.Subscription): Promise<void>;
29
- export declare function handleSubscriptionUpdated(ctx: WebhookHandlerContext, subscription: Stripe.Subscription): Promise<void>;
30
- export declare function handleSubscriptionDeleted(ctx: WebhookHandlerContext, subscription: Stripe.Subscription): Promise<void>;
31
- export declare function handleInvoicePaymentSucceeded(ctx: WebhookHandlerContext, invoice: Stripe.Invoice): Promise<void>;
32
- export declare function handleInvoicePaymentFailed(ctx: WebhookHandlerContext, invoice: Stripe.Invoice): Promise<void>;
33
- export declare function handleCheckoutSessionCompleted(session: Stripe.Checkout.Session): void;
34
- export declare function storeLicenseKey(db: DatabaseType, params: {
35
- subscriptionId: string;
36
- organizationId: string;
37
- keyJwt: string;
38
- keyExpiry: Date;
39
- }): void;
40
- export declare function revokeLicenseKey(db: DatabaseType, subscriptionId: string, reason: string): void;
41
- export declare function extractTier(subscription: Stripe.Subscription): LicenseTier;
42
- export declare function extractSeatCount(subscription: Stripe.Subscription): number;
43
- /**
44
- * Get current period end from subscription (Stripe v20+ moved this to items)
45
- */
46
- export declare function getCurrentPeriodEnd(subscription: Stripe.Subscription): number;
47
- /**
48
- * Get current period start from subscription (Stripe v20+ moved this to items)
49
- */
50
- export declare function getCurrentPeriodStart(subscription: Stripe.Subscription): number;
51
- /**
52
- * Extract subscription ID from invoice (Stripe v20+ structure)
53
- * The subscription is now at invoice.parent.subscription_details.subscription
54
- */
55
- export declare function extractSubscriptionIdFromInvoice(invoice: Stripe.Invoice): string | undefined;
56
- //# sourceMappingURL=webhook-handlers.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"webhook-handlers.d.ts","sourceRoot":"","sources":["../../../src/billing/webhook-handlers.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,MAAM,MAAM,QAAQ,CAAA;AAEhC,OAAO,KAAK,EAAE,QAAQ,IAAI,YAAY,EAAE,MAAM,6BAA6B,CAAA;AAE3E,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAA;AAChD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAA;AACzD,OAAO,KAAK,EAAE,WAAW,EAA0C,MAAM,YAAY,CAAA;AASrF,MAAM,WAAW,qBAAqB;IACpC,MAAM,EAAE,YAAY,CAAA;IACpB,OAAO,EAAE,cAAc,CAAA;IACvB,EAAE,EAAE,YAAY,CAAA;IAChB,kBAAkB,CAAC,EAAE,CAAC,MAAM,EAAE;QAC5B,UAAU,EAAE,MAAM,CAAA;QAClB,IAAI,EAAE,WAAW,CAAA;QACjB,SAAS,EAAE,IAAI,CAAA;QACf,cAAc,EAAE,MAAM,CAAA;KACvB,KAAK,OAAO,CAAC,MAAM,CAAC,CAAA;IACrB,aAAa,CAAC,EAAE,CAAC,MAAM,EAAE;QACvB,IAAI,EAAE,aAAa,GAAG,gBAAgB,GAAG,uBAAuB,CAAA;QAChE,KAAK,EAAE,MAAM,CAAA;QACb,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;KAC9B,KAAK,OAAO,CAAC,IAAI,CAAC,CAAA;CACpB;AAMD,wBAAsB,yBAAyB,CAC7C,GAAG,EAAE,qBAAqB,EAC1B,YAAY,EAAE,MAAM,CAAC,YAAY,GAChC,OAAO,CAAC,IAAI,CAAC,CA4Df;AAED,wBAAsB,yBAAyB,CAC7C,GAAG,EAAE,qBAAqB,EAC1B,YAAY,EAAE,MAAM,CAAC,YAAY,GAChC,OAAO,CAAC,IAAI,CAAC,CAiDf;AAED,wBAAsB,yBAAyB,CAC7C,GAAG,EAAE,qBAAqB,EAC1B,YAAY,EAAE,MAAM,CAAC,YAAY,GAChC,OAAO,CAAC,IAAI,CAAC,CAiCf;AAMD,wBAAsB,6BAA6B,CACjD,GAAG,EAAE,qBAAqB,EAC1B,OAAO,EAAE,MAAM,CAAC,OAAO,GACtB,OAAO,CAAC,IAAI,CAAC,CA4Bf;AAED,wBAAsB,0BAA0B,CAC9C,GAAG,EAAE,qBAAqB,EAC1B,OAAO,EAAE,MAAM,CAAC,OAAO,GACtB,OAAO,CAAC,IAAI,CAAC,CA2Cf;AAMD,wBAAgB,8BAA8B,CAAC,OAAO,EAAE,MAAM,CAAC,QAAQ,CAAC,OAAO,GAAG,IAAI,CASrF;AAMD,wBAAgB,eAAe,CAC7B,EAAE,EAAE,YAAY,EAChB,MAAM,EAAE;IACN,cAAc,EAAE,MAAM,CAAA;IACtB,cAAc,EAAE,MAAM,CAAA;IACtB,MAAM,EAAE,MAAM,CAAA;IACd,SAAS,EAAE,IAAI,CAAA;CAChB,GACA,IAAI,CAwBN;AAED,wBAAgB,gBAAgB,CAAC,EAAE,EAAE,YAAY,EAAE,cAAc,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,IAAI,CAU/F;AAMD,wBAAgB,WAAW,CAAC,YAAY,EAAE,MAAM,CAAC,YAAY,GAAG,WAAW,CAS1E;AAED,wBAAgB,gBAAgB,CAAC,YAAY,EAAE,MAAM,CAAC,YAAY,GAAG,MAAM,CAY1E;AAED;;GAEG;AACH,wBAAgB,mBAAmB,CAAC,YAAY,EAAE,MAAM,CAAC,YAAY,GAAG,MAAM,CAU7E;AAED;;GAEG;AACH,wBAAgB,qBAAqB,CAAC,YAAY,EAAE,MAAM,CAAC,YAAY,GAAG,MAAM,CAU/E;AAED;;;GAGG;AACH,wBAAgB,gCAAgC,CAAC,OAAO,EAAE,MAAM,CAAC,OAAO,GAAG,MAAM,GAAG,SAAS,CAM5F"}
@@ -1,303 +0,0 @@
1
- /**
2
- * SMI-1070: Stripe Webhook Event Handlers
3
- *
4
- * Individual event handler functions for Stripe webhooks.
5
- * These are extracted to reduce the size of the main handler file.
6
- */
7
- import { createHash, randomUUID } from 'crypto';
8
- import { createLogger } from '../utils/logger.js';
9
- import { StripeClient } from './StripeClient.js';
10
- import { BillingError } from './types.js';
11
- const logger = createLogger('WebhookHandlers');
12
- // ============================================================================
13
- // Subscription Event Handlers
14
- // ============================================================================
15
- export async function handleSubscriptionCreated(ctx, subscription) {
16
- logger.info('Processing subscription.created', {
17
- subscriptionId: subscription.id,
18
- customerId: subscription.customer,
19
- status: subscription.status,
20
- });
21
- const customer = await ctx.stripe.getCustomer(subscription.customer);
22
- if (!customer) {
23
- throw new BillingError('Customer not found', 'CUSTOMER_NOT_FOUND');
24
- }
25
- const tier = extractTier(subscription);
26
- const seatCount = extractSeatCount(subscription);
27
- // Create/update subscription record
28
- const sub = ctx.billing.upsertSubscription({
29
- customerId: customer.id,
30
- email: customer.email,
31
- stripeCustomerId: customer.id,
32
- stripeSubscriptionId: subscription.id,
33
- stripePriceId: subscription.items.data[0]?.price.id ?? '',
34
- tier,
35
- status: StripeClient.mapSubscriptionStatus(subscription.status),
36
- seatCount,
37
- currentPeriodStart: new Date(getCurrentPeriodStart(subscription) * 1000),
38
- currentPeriodEnd: new Date(getCurrentPeriodEnd(subscription) * 1000),
39
- });
40
- // Generate license key if subscription is active
41
- if (subscription.status === 'active' && ctx.onLicenseKeyNeeded) {
42
- const periodEnd = getCurrentPeriodEnd(subscription);
43
- const licenseKey = await ctx.onLicenseKeyNeeded({
44
- customerId: customer.id,
45
- tier,
46
- expiresAt: new Date(periodEnd * 1000),
47
- subscriptionId: sub.id,
48
- });
49
- // Store license key
50
- storeLicenseKey(ctx.db, {
51
- subscriptionId: sub.id,
52
- organizationId: customer.id,
53
- keyJwt: licenseKey,
54
- keyExpiry: new Date(periodEnd * 1000),
55
- });
56
- // Send license key email
57
- if (ctx.onEmailNeeded) {
58
- await ctx.onEmailNeeded({
59
- type: 'license_key',
60
- email: customer.email,
61
- data: {
62
- licenseKey,
63
- tier,
64
- expiresAt: new Date(periodEnd * 1000).toISOString(),
65
- },
66
- });
67
- }
68
- }
69
- }
70
- export async function handleSubscriptionUpdated(ctx, subscription) {
71
- logger.info('Processing subscription.updated', {
72
- subscriptionId: subscription.id,
73
- status: subscription.status,
74
- });
75
- const existingSub = ctx.billing.getSubscriptionByStripeId(subscription.id);
76
- if (!existingSub) {
77
- // Subscription doesn't exist locally, create it
78
- await handleSubscriptionCreated(ctx, subscription);
79
- return;
80
- }
81
- // Update status
82
- const newStatus = StripeClient.mapSubscriptionStatus(subscription.status);
83
- const canceledAt = subscription.canceled_at ? new Date(subscription.canceled_at * 1000) : null;
84
- ctx.billing.updateSubscriptionStatus(subscription.id, newStatus, canceledAt);
85
- // Check if tier changed (regenerate license key)
86
- const newTier = extractTier(subscription);
87
- if (existingSub.tier !== newTier && ctx.onLicenseKeyNeeded) {
88
- const customer = await ctx.stripe.getCustomer(subscription.customer);
89
- if (customer) {
90
- // Revoke old license key
91
- revokeLicenseKey(ctx.db, existingSub.id, 'tier_change');
92
- // Generate new license key
93
- const periodEnd = getCurrentPeriodEnd(subscription);
94
- const licenseKey = await ctx.onLicenseKeyNeeded({
95
- customerId: customer.id,
96
- tier: newTier,
97
- expiresAt: new Date(periodEnd * 1000),
98
- subscriptionId: existingSub.id,
99
- });
100
- storeLicenseKey(ctx.db, {
101
- subscriptionId: existingSub.id,
102
- organizationId: customer.id,
103
- keyJwt: licenseKey,
104
- keyExpiry: new Date(periodEnd * 1000),
105
- });
106
- }
107
- }
108
- }
109
- export async function handleSubscriptionDeleted(ctx, subscription) {
110
- logger.info('Processing subscription.deleted', {
111
- subscriptionId: subscription.id,
112
- });
113
- const existingSub = ctx.billing.getSubscriptionByStripeId(subscription.id);
114
- if (existingSub) {
115
- // Update status to canceled
116
- ctx.billing.updateSubscriptionStatus(subscription.id, 'canceled', new Date());
117
- // Revoke license key
118
- revokeLicenseKey(ctx.db, existingSub.id, 'subscription_canceled');
119
- // Send cancellation email
120
- if (ctx.onEmailNeeded) {
121
- const customer = await ctx.stripe.getCustomer(subscription.customer);
122
- if (customer?.email) {
123
- await ctx.onEmailNeeded({
124
- type: 'subscription_canceled',
125
- email: customer.email,
126
- data: {
127
- subscriptionId: subscription.id,
128
- canceledAt: new Date().toISOString(),
129
- },
130
- });
131
- }
132
- }
133
- }
134
- }
135
- // ============================================================================
136
- // Invoice Event Handlers
137
- // ============================================================================
138
- export async function handleInvoicePaymentSucceeded(ctx, invoice) {
139
- logger.info('Processing invoice.payment_succeeded', {
140
- invoiceId: invoice.id,
141
- customerId: invoice.customer,
142
- amount: invoice.amount_paid,
143
- });
144
- const customerId = typeof invoice.customer === 'string' ? invoice.customer : invoice.customer?.id;
145
- if (!customerId)
146
- return;
147
- // Store invoice
148
- ctx.billing.storeInvoice({
149
- customerId,
150
- stripeInvoiceId: invoice.id,
151
- subscriptionId: extractSubscriptionIdFromInvoice(invoice),
152
- amountCents: invoice.amount_paid,
153
- currency: invoice.currency,
154
- status: 'paid',
155
- pdfUrl: invoice.invoice_pdf ?? undefined,
156
- hostedInvoiceUrl: invoice.hosted_invoice_url ?? undefined,
157
- invoiceNumber: invoice.number ?? undefined,
158
- paidAt: invoice.status_transitions?.paid_at
159
- ? new Date(invoice.status_transitions.paid_at * 1000)
160
- : new Date(),
161
- periodStart: invoice.period_start ? new Date(invoice.period_start * 1000) : undefined,
162
- periodEnd: invoice.period_end ? new Date(invoice.period_end * 1000) : undefined,
163
- });
164
- }
165
- export async function handleInvoicePaymentFailed(ctx, invoice) {
166
- logger.warn('Processing invoice.payment_failed', {
167
- invoiceId: invoice.id,
168
- customerId: invoice.customer,
169
- amount: invoice.amount_due,
170
- });
171
- const customerId = typeof invoice.customer === 'string' ? invoice.customer : invoice.customer?.id;
172
- if (!customerId)
173
- return;
174
- // Get subscription ID from parent.subscription_details (Stripe v20+ structure)
175
- const subscriptionId = extractSubscriptionIdFromInvoice(invoice);
176
- // Store invoice with failed status
177
- ctx.billing.storeInvoice({
178
- customerId,
179
- stripeInvoiceId: invoice.id,
180
- subscriptionId,
181
- amountCents: invoice.amount_due,
182
- currency: invoice.currency,
183
- status: 'open',
184
- pdfUrl: invoice.invoice_pdf ?? undefined,
185
- hostedInvoiceUrl: invoice.hosted_invoice_url ?? undefined,
186
- invoiceNumber: invoice.number ?? undefined,
187
- });
188
- // Send payment failed email
189
- if (ctx.onEmailNeeded) {
190
- const customer = await ctx.stripe.getCustomer(customerId);
191
- if (customer?.email) {
192
- await ctx.onEmailNeeded({
193
- type: 'payment_failed',
194
- email: customer.email,
195
- data: {
196
- invoiceId: invoice.id,
197
- amount: invoice.amount_due,
198
- currency: invoice.currency,
199
- hostedInvoiceUrl: invoice.hosted_invoice_url,
200
- },
201
- });
202
- }
203
- }
204
- }
205
- // ============================================================================
206
- // Checkout Event Handlers
207
- // ============================================================================
208
- export function handleCheckoutSessionCompleted(session) {
209
- logger.info('Processing checkout.session.completed', {
210
- sessionId: session.id,
211
- customerId: session.customer,
212
- subscriptionId: session.subscription,
213
- });
214
- // The subscription.created event will handle the actual subscription setup
215
- // This handler is useful for tracking successful checkouts and analytics
216
- }
217
- // ============================================================================
218
- // License Key Management
219
- // ============================================================================
220
- export function storeLicenseKey(db, params) {
221
- const id = randomUUID();
222
- const keyHash = createHash('sha256').update(params.keyJwt).digest('hex');
223
- const now = new Date().toISOString();
224
- db.prepare(`INSERT INTO license_keys (
225
- id, subscription_id, organization_id, key_jwt, key_hash,
226
- key_expiry, is_active, generated_at
227
- ) VALUES (?, ?, ?, ?, ?, ?, 1, ?)`).run(id, params.subscriptionId, params.organizationId, params.keyJwt, keyHash, params.keyExpiry.toISOString(), now);
228
- logger.info('License key stored', {
229
- subscriptionId: params.subscriptionId,
230
- keyHash: keyHash.slice(0, 16) + '...',
231
- });
232
- }
233
- export function revokeLicenseKey(db, subscriptionId, reason) {
234
- const now = new Date().toISOString();
235
- db.prepare(`UPDATE license_keys
236
- SET is_active = 0, revoked_at = ?, revocation_reason = ?
237
- WHERE subscription_id = ? AND is_active = 1`).run(now, reason, subscriptionId);
238
- logger.info('License key revoked', { subscriptionId, reason });
239
- }
240
- // ============================================================================
241
- // Helper Functions
242
- // ============================================================================
243
- export function extractTier(subscription) {
244
- // Try to get tier from metadata first
245
- const metadataTier = subscription.metadata?.tier;
246
- if (metadataTier && ['community', 'individual', 'team', 'enterprise'].includes(metadataTier)) {
247
- return metadataTier;
248
- }
249
- // Fallback: infer from price ID or default to individual
250
- return 'individual';
251
- }
252
- export function extractSeatCount(subscription) {
253
- // Try metadata first
254
- const metadataSeats = subscription.metadata?.seatCount;
255
- if (metadataSeats) {
256
- const count = parseInt(metadataSeats, 10);
257
- if (!isNaN(count) && count > 0) {
258
- return count;
259
- }
260
- }
261
- // Fallback to quantity from first item
262
- return subscription.items.data[0]?.quantity ?? 1;
263
- }
264
- /**
265
- * Get current period end from subscription (Stripe v20+ moved this to items)
266
- */
267
- export function getCurrentPeriodEnd(subscription) {
268
- const periodEnd = subscription.items.data[0]?.current_period_end;
269
- if (!periodEnd) {
270
- logger.warn('No subscription items found for period end', {
271
- subscriptionId: subscription.id,
272
- itemCount: subscription.items.data.length,
273
- });
274
- return Math.floor(Date.now() / 1000);
275
- }
276
- return periodEnd;
277
- }
278
- /**
279
- * Get current period start from subscription (Stripe v20+ moved this to items)
280
- */
281
- export function getCurrentPeriodStart(subscription) {
282
- const periodStart = subscription.items.data[0]?.current_period_start;
283
- if (!periodStart) {
284
- logger.warn('No subscription items found for period start', {
285
- subscriptionId: subscription.id,
286
- itemCount: subscription.items.data.length,
287
- });
288
- return Math.floor(Date.now() / 1000);
289
- }
290
- return periodStart;
291
- }
292
- /**
293
- * Extract subscription ID from invoice (Stripe v20+ structure)
294
- * The subscription is now at invoice.parent.subscription_details.subscription
295
- */
296
- export function extractSubscriptionIdFromInvoice(invoice) {
297
- const subscription = invoice.parent?.subscription_details?.subscription;
298
- if (!subscription) {
299
- return undefined;
300
- }
301
- return typeof subscription === 'string' ? subscription : subscription.id;
302
- }
303
- //# sourceMappingURL=webhook-handlers.js.map