@skillsmith/core 2.1.0 → 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.
Files changed (204) hide show
  1. package/dist/.tsbuildinfo +1 -1
  2. package/dist/src/analysis/types.d.ts +2 -0
  3. package/dist/src/analysis/types.d.ts.map +1 -1
  4. package/dist/src/analysis/types.js +13 -1
  5. package/dist/src/analysis/types.js.map +1 -1
  6. package/dist/src/analytics/AnalyticsRepository.d.ts +4 -0
  7. package/dist/src/analytics/AnalyticsRepository.d.ts.map +1 -1
  8. package/dist/src/analytics/AnalyticsRepository.js +26 -44
  9. package/dist/src/analytics/AnalyticsRepository.js.map +1 -1
  10. package/dist/src/analytics/schema.d.ts +1 -1
  11. package/dist/src/analytics/schema.d.ts.map +1 -1
  12. package/dist/src/analytics/schema.js +68 -0
  13. package/dist/src/analytics/schema.js.map +1 -1
  14. package/dist/src/api/client.d.ts +33 -29
  15. package/dist/src/api/client.d.ts.map +1 -1
  16. package/dist/src/api/client.js +15 -10
  17. package/dist/src/api/client.js.map +1 -1
  18. package/dist/src/billing/BillingService.d.ts +139 -0
  19. package/dist/src/billing/BillingService.d.ts.map +1 -0
  20. package/dist/src/billing/BillingService.js +393 -0
  21. package/dist/src/billing/BillingService.js.map +1 -0
  22. package/dist/src/billing/GDPRComplianceService.d.ts +176 -0
  23. package/dist/src/billing/GDPRComplianceService.d.ts.map +1 -0
  24. package/dist/src/billing/GDPRComplianceService.js +361 -0
  25. package/dist/src/billing/GDPRComplianceService.js.map +1 -0
  26. package/dist/src/billing/StripeClient.d.ts +177 -0
  27. package/dist/src/billing/StripeClient.d.ts.map +1 -0
  28. package/dist/src/billing/StripeClient.js +462 -0
  29. package/dist/src/billing/StripeClient.js.map +1 -0
  30. package/dist/src/billing/StripeReconciliationJob.d.ts +95 -0
  31. package/dist/src/billing/StripeReconciliationJob.d.ts.map +1 -0
  32. package/dist/src/billing/StripeReconciliationJob.js +405 -0
  33. package/dist/src/billing/StripeReconciliationJob.js.map +1 -0
  34. package/dist/src/billing/StripeWebhookHandler.d.ts +92 -0
  35. package/dist/src/billing/StripeWebhookHandler.d.ts.map +1 -0
  36. package/dist/src/billing/StripeWebhookHandler.js +409 -0
  37. package/dist/src/billing/StripeWebhookHandler.js.map +1 -0
  38. package/dist/src/billing/index.d.ts +18 -0
  39. package/dist/src/billing/index.d.ts.map +1 -0
  40. package/dist/src/billing/index.js +19 -0
  41. package/dist/src/billing/index.js.map +1 -0
  42. package/dist/src/billing/types.d.ts +266 -0
  43. package/dist/src/billing/types.d.ts.map +1 -0
  44. package/dist/src/billing/types.js +23 -0
  45. package/dist/src/billing/types.js.map +1 -0
  46. package/dist/src/embeddings/hnsw-store.d.ts +568 -0
  47. package/dist/src/embeddings/hnsw-store.d.ts.map +1 -0
  48. package/dist/src/embeddings/hnsw-store.js +805 -0
  49. package/dist/src/embeddings/hnsw-store.js.map +1 -0
  50. package/dist/src/embeddings/index.d.ts +2 -0
  51. package/dist/src/embeddings/index.d.ts.map +1 -1
  52. package/dist/src/embeddings/index.js +2 -0
  53. package/dist/src/embeddings/index.js.map +1 -1
  54. package/dist/src/index.d.ts +1 -0
  55. package/dist/src/index.d.ts.map +1 -1
  56. package/dist/src/index.js +2 -0
  57. package/dist/src/index.js.map +1 -1
  58. package/dist/src/learning/PatternStore.d.ts +457 -0
  59. package/dist/src/learning/PatternStore.d.ts.map +1 -0
  60. package/dist/src/learning/PatternStore.js +893 -0
  61. package/dist/src/learning/PatternStore.js.map +1 -0
  62. package/dist/src/learning/ReasoningBankIntegration.d.ts +403 -0
  63. package/dist/src/learning/ReasoningBankIntegration.d.ts.map +1 -0
  64. package/dist/src/learning/ReasoningBankIntegration.js +627 -0
  65. package/dist/src/learning/ReasoningBankIntegration.js.map +1 -0
  66. package/dist/src/learning/index.d.ts +15 -0
  67. package/dist/src/learning/index.d.ts.map +1 -0
  68. package/dist/src/learning/index.js +15 -0
  69. package/dist/src/learning/index.js.map +1 -0
  70. package/dist/src/routing/SONARouter.d.ts +154 -0
  71. package/dist/src/routing/SONARouter.d.ts.map +1 -0
  72. package/dist/src/routing/SONARouter.js +679 -0
  73. package/dist/src/routing/SONARouter.js.map +1 -0
  74. package/dist/src/routing/index.d.ts +9 -0
  75. package/dist/src/routing/index.d.ts.map +1 -0
  76. package/dist/src/routing/index.js +10 -0
  77. package/dist/src/routing/index.js.map +1 -0
  78. package/dist/src/routing/types.d.ts +331 -0
  79. package/dist/src/routing/types.d.ts.map +1 -0
  80. package/dist/src/routing/types.js +203 -0
  81. package/dist/src/routing/types.js.map +1 -0
  82. package/dist/src/scripts/__tests__/scan-imported-skills.test.js +5 -0
  83. package/dist/src/scripts/__tests__/scan-imported-skills.test.js.map +1 -1
  84. package/dist/src/security/SkillSandbox.d.ts +156 -0
  85. package/dist/src/security/SkillSandbox.d.ts.map +1 -0
  86. package/dist/src/security/SkillSandbox.js +303 -0
  87. package/dist/src/security/SkillSandbox.js.map +1 -0
  88. package/dist/src/security/index.d.ts +3 -1
  89. package/dist/src/security/index.d.ts.map +1 -1
  90. package/dist/src/security/index.js +5 -1
  91. package/dist/src/security/index.js.map +1 -1
  92. package/dist/src/security/rate-limiter/presets.d.ts +12 -0
  93. package/dist/src/security/rate-limiter/presets.d.ts.map +1 -1
  94. package/dist/src/security/rate-limiter/presets.js +12 -0
  95. package/dist/src/security/rate-limiter/presets.js.map +1 -1
  96. package/dist/src/security/sanitization.d.ts +85 -0
  97. package/dist/src/security/sanitization.d.ts.map +1 -1
  98. package/dist/src/security/sanitization.js +133 -0
  99. package/dist/src/security/sanitization.js.map +1 -1
  100. package/dist/src/security/scanner/SecurityScanner.d.ts +23 -0
  101. package/dist/src/security/scanner/SecurityScanner.d.ts.map +1 -1
  102. package/dist/src/security/scanner/SecurityScanner.js +232 -28
  103. package/dist/src/security/scanner/SecurityScanner.js.map +1 -1
  104. package/dist/src/security/scanner/patterns.d.ts +13 -0
  105. package/dist/src/security/scanner/patterns.d.ts.map +1 -1
  106. package/dist/src/security/scanner/patterns.js +51 -0
  107. package/dist/src/security/scanner/patterns.js.map +1 -1
  108. package/dist/src/security/scanner/types.d.ts +13 -1
  109. package/dist/src/security/scanner/types.d.ts.map +1 -1
  110. package/dist/src/security/scanner/weights.d.ts.map +1 -1
  111. package/dist/src/security/scanner/weights.js +1 -0
  112. package/dist/src/security/scanner/weights.js.map +1 -1
  113. package/dist/src/session/SessionManager.d.ts +7 -0
  114. package/dist/src/session/SessionManager.d.ts.map +1 -1
  115. package/dist/src/session/SessionManager.js +117 -10
  116. package/dist/src/session/SessionManager.js.map +1 -1
  117. package/dist/src/sync/SyncEngine.d.ts.map +1 -1
  118. package/dist/src/sync/SyncEngine.js +52 -32
  119. package/dist/src/sync/SyncEngine.js.map +1 -1
  120. package/dist/src/testing/MultiLLMProvider.d.ts +374 -0
  121. package/dist/src/testing/MultiLLMProvider.d.ts.map +1 -0
  122. package/dist/src/testing/MultiLLMProvider.js +720 -0
  123. package/dist/src/testing/MultiLLMProvider.js.map +1 -0
  124. package/dist/src/testing/index.d.ts +8 -0
  125. package/dist/src/testing/index.d.ts.map +1 -0
  126. package/dist/src/testing/index.js +9 -0
  127. package/dist/src/testing/index.js.map +1 -0
  128. package/dist/src/types.d.ts +3 -0
  129. package/dist/src/types.d.ts.map +1 -1
  130. package/dist/tests/SecurityScanner.test.js +337 -1
  131. package/dist/tests/SecurityScanner.test.js.map +1 -1
  132. package/dist/tests/billing/BillingService.test.d.ts +7 -0
  133. package/dist/tests/billing/BillingService.test.d.ts.map +1 -0
  134. package/dist/tests/billing/BillingService.test.js +168 -0
  135. package/dist/tests/billing/BillingService.test.js.map +1 -0
  136. package/dist/tests/billing/GDPRCompliance.test.d.ts +7 -0
  137. package/dist/tests/billing/GDPRCompliance.test.d.ts.map +1 -0
  138. package/dist/tests/billing/GDPRCompliance.test.js +195 -0
  139. package/dist/tests/billing/GDPRCompliance.test.js.map +1 -0
  140. package/dist/tests/billing/StripeReconciliation.test.d.ts +7 -0
  141. package/dist/tests/billing/StripeReconciliation.test.d.ts.map +1 -0
  142. package/dist/tests/billing/StripeReconciliation.test.js +266 -0
  143. package/dist/tests/billing/StripeReconciliation.test.js.map +1 -0
  144. package/dist/tests/billing/stripe-validators.test.d.ts +7 -0
  145. package/dist/tests/billing/stripe-validators.test.d.ts.map +1 -0
  146. package/dist/tests/billing/stripe-validators.test.js +107 -0
  147. package/dist/tests/billing/stripe-validators.test.js.map +1 -0
  148. package/dist/tests/embeddings/hnsw-store.test.d.ts +7 -0
  149. package/dist/tests/embeddings/hnsw-store.test.d.ts.map +1 -0
  150. package/dist/tests/embeddings/hnsw-store.test.js +295 -0
  151. package/dist/tests/embeddings/hnsw-store.test.js.map +1 -0
  152. package/dist/tests/integration/neural/e2e-learning.test.d.ts +17 -0
  153. package/dist/tests/integration/neural/e2e-learning.test.d.ts.map +1 -0
  154. package/dist/tests/integration/neural/e2e-learning.test.js +238 -0
  155. package/dist/tests/integration/neural/e2e-learning.test.js.map +1 -0
  156. package/dist/tests/integration/neural/helpers.d.ts +132 -0
  157. package/dist/tests/integration/neural/helpers.d.ts.map +1 -0
  158. package/dist/tests/integration/neural/helpers.js +287 -0
  159. package/dist/tests/integration/neural/helpers.js.map +1 -0
  160. package/dist/tests/integration/neural/personalization.test.d.ts +21 -0
  161. package/dist/tests/integration/neural/personalization.test.d.ts.map +1 -0
  162. package/dist/tests/integration/neural/personalization.test.js +304 -0
  163. package/dist/tests/integration/neural/personalization.test.js.map +1 -0
  164. package/dist/tests/integration/neural/preference-learner.test.d.ts +23 -0
  165. package/dist/tests/integration/neural/preference-learner.test.d.ts.map +1 -0
  166. package/dist/tests/integration/neural/preference-learner.test.js +289 -0
  167. package/dist/tests/integration/neural/preference-learner.test.js.map +1 -0
  168. package/dist/tests/integration/neural/privacy.test.d.ts +19 -0
  169. package/dist/tests/integration/neural/privacy.test.d.ts.map +1 -0
  170. package/dist/tests/integration/neural/privacy.test.js +249 -0
  171. package/dist/tests/integration/neural/privacy.test.js.map +1 -0
  172. package/dist/tests/integration/neural/setup.d.ts +175 -0
  173. package/dist/tests/integration/neural/setup.d.ts.map +1 -0
  174. package/dist/tests/integration/neural/setup.js +487 -0
  175. package/dist/tests/integration/neural/setup.js.map +1 -0
  176. package/dist/tests/integration/neural/signal-collection.test.d.ts +21 -0
  177. package/dist/tests/integration/neural/signal-collection.test.d.ts.map +1 -0
  178. package/dist/tests/integration/neural/signal-collection.test.js +232 -0
  179. package/dist/tests/integration/neural/signal-collection.test.js.map +1 -0
  180. package/dist/tests/learning/PatternStore.test.d.ts +8 -0
  181. package/dist/tests/learning/PatternStore.test.d.ts.map +1 -0
  182. package/dist/tests/learning/PatternStore.test.js +589 -0
  183. package/dist/tests/learning/PatternStore.test.js.map +1 -0
  184. package/dist/tests/learning/ReasoningBankIntegration.test.d.ts +8 -0
  185. package/dist/tests/learning/ReasoningBankIntegration.test.d.ts.map +1 -0
  186. package/dist/tests/learning/ReasoningBankIntegration.test.js +269 -0
  187. package/dist/tests/learning/ReasoningBankIntegration.test.js.map +1 -0
  188. package/dist/tests/routing/SONARouter.test.d.ts +8 -0
  189. package/dist/tests/routing/SONARouter.test.d.ts.map +1 -0
  190. package/dist/tests/routing/SONARouter.test.js +400 -0
  191. package/dist/tests/routing/SONARouter.test.js.map +1 -0
  192. package/dist/tests/security/ContinuousSecurity.test.js +10 -12
  193. package/dist/tests/security/ContinuousSecurity.test.js.map +1 -1
  194. package/dist/tests/security/SkillSandbox.test.d.ts +8 -0
  195. package/dist/tests/security/SkillSandbox.test.d.ts.map +1 -0
  196. package/dist/tests/security/SkillSandbox.test.js +321 -0
  197. package/dist/tests/security/SkillSandbox.test.js.map +1 -0
  198. package/dist/tests/sync/SyncEngine.test.js +4 -2
  199. package/dist/tests/sync/SyncEngine.test.js.map +1 -1
  200. package/dist/tests/testing/MultiLLMProvider.test.d.ts +14 -0
  201. package/dist/tests/testing/MultiLLMProvider.test.d.ts.map +1 -0
  202. package/dist/tests/testing/MultiLLMProvider.test.js +438 -0
  203. package/dist/tests/testing/MultiLLMProvider.test.js.map +1 -0
  204. package/package.json +16 -3
@@ -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"}
@@ -0,0 +1,405 @@
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 { createLogger } from '../utils/logger.js';
15
+ const logger = createLogger('StripeReconciliation');
16
+ // ============================================================================
17
+ // StripeReconciliationJob Class
18
+ // ============================================================================
19
+ /**
20
+ * Stripe Reconciliation Job
21
+ *
22
+ * @example
23
+ * ```typescript
24
+ * const job = new StripeReconciliationJob({
25
+ * stripeClient,
26
+ * db,
27
+ * autoFix: true,
28
+ * });
29
+ *
30
+ * // Run reconciliation
31
+ * const result = await job.run();
32
+ * console.log(`Found ${result.discrepancies.length} discrepancies`);
33
+ * ```
34
+ */
35
+ export class StripeReconciliationJob {
36
+ stripe;
37
+ db;
38
+ autoFix;
39
+ batchSize;
40
+ constructor(config) {
41
+ this.stripe = config.stripeClient;
42
+ this.db = config.db;
43
+ this.autoFix = config.autoFix ?? false;
44
+ this.batchSize = config.batchSize ?? 100;
45
+ logger.info('Stripe reconciliation job initialized', {
46
+ autoFix: this.autoFix,
47
+ batchSize: this.batchSize,
48
+ });
49
+ }
50
+ // ==========================================================================
51
+ // Main Entry Point
52
+ // ==========================================================================
53
+ /**
54
+ * Run the reconciliation job
55
+ */
56
+ async run() {
57
+ const startedAt = new Date();
58
+ const discrepancies = [];
59
+ const errors = [];
60
+ let subscriptionsChecked = 0;
61
+ let invoicesChecked = 0;
62
+ let discrepanciesFixed = 0;
63
+ logger.info('Starting Stripe reconciliation job');
64
+ try {
65
+ // 1. Get all local subscriptions with Stripe IDs
66
+ const localSubscriptions = this.getLocalSubscriptions();
67
+ subscriptionsChecked = localSubscriptions.length;
68
+ // 2. Reconcile each subscription
69
+ for (const local of localSubscriptions) {
70
+ try {
71
+ const subDiscrepancies = await this.reconcileSubscription(local);
72
+ discrepancies.push(...subDiscrepancies);
73
+ }
74
+ catch (error) {
75
+ const errorMsg = error instanceof Error ? error.message : String(error);
76
+ errors.push(`Subscription ${local.stripeSubscriptionId}: ${errorMsg}`);
77
+ }
78
+ }
79
+ // 3. Reconcile invoices
80
+ const localInvoices = this.getLocalInvoices();
81
+ invoicesChecked = localInvoices.length;
82
+ for (const invoice of localInvoices) {
83
+ try {
84
+ const invDiscrepancies = await this.reconcileInvoice(invoice);
85
+ discrepancies.push(...invDiscrepancies);
86
+ }
87
+ catch (error) {
88
+ const errorMsg = error instanceof Error ? error.message : String(error);
89
+ errors.push(`Invoice ${invoice.stripeInvoiceId}: ${errorMsg}`);
90
+ }
91
+ }
92
+ // 4. Apply fixes if autoFix is enabled
93
+ if (this.autoFix) {
94
+ for (const discrepancy of discrepancies) {
95
+ if (this.fixDiscrepancy(discrepancy)) {
96
+ discrepancy.fixed = true;
97
+ discrepanciesFixed++;
98
+ }
99
+ }
100
+ }
101
+ }
102
+ catch (error) {
103
+ const errorMsg = error instanceof Error ? error.message : String(error);
104
+ errors.push(`Job failed: ${errorMsg}`);
105
+ logger.error('Reconciliation job failed', undefined, { error: errorMsg });
106
+ }
107
+ const completedAt = new Date();
108
+ const result = {
109
+ success: errors.length === 0,
110
+ startedAt: startedAt.toISOString(),
111
+ completedAt: completedAt.toISOString(),
112
+ durationMs: completedAt.getTime() - startedAt.getTime(),
113
+ stats: {
114
+ subscriptionsChecked,
115
+ invoicesChecked,
116
+ discrepanciesFound: discrepancies.length,
117
+ discrepanciesFixed,
118
+ },
119
+ discrepancies,
120
+ errors,
121
+ };
122
+ logger.info('Stripe reconciliation job completed', {
123
+ success: result.success,
124
+ subscriptionsChecked,
125
+ invoicesChecked,
126
+ discrepanciesFound: discrepancies.length,
127
+ discrepanciesFixed,
128
+ durationMs: result.durationMs,
129
+ });
130
+ return result;
131
+ }
132
+ // ==========================================================================
133
+ // Subscription Reconciliation
134
+ // ==========================================================================
135
+ getLocalSubscriptions() {
136
+ return this.db
137
+ .prepare(`SELECT
138
+ id,
139
+ customer_id as customerId,
140
+ stripe_subscription_id as stripeSubscriptionId,
141
+ stripe_customer_id as stripeCustomerId,
142
+ tier,
143
+ status,
144
+ seat_count as seatCount,
145
+ current_period_start as currentPeriodStart,
146
+ current_period_end as currentPeriodEnd
147
+ FROM user_subscriptions
148
+ WHERE stripe_subscription_id IS NOT NULL
149
+ LIMIT ?`)
150
+ .all(this.batchSize);
151
+ }
152
+ async reconcileSubscription(local) {
153
+ const discrepancies = [];
154
+ // Fetch from Stripe
155
+ const stripeSubscription = await this.stripe.getSubscription(local.stripeSubscriptionId);
156
+ if (!stripeSubscription) {
157
+ discrepancies.push({
158
+ type: 'missing_stripe',
159
+ entityType: 'subscription',
160
+ entityId: local.id,
161
+ stripeId: local.stripeSubscriptionId,
162
+ localValue: local.status,
163
+ stripeValue: null,
164
+ description: 'Subscription exists locally but not in Stripe',
165
+ fixed: false,
166
+ });
167
+ return discrepancies;
168
+ }
169
+ // Compare status
170
+ const stripeStatus = mapStripeStatus(stripeSubscription.status);
171
+ if (local.status !== stripeStatus) {
172
+ discrepancies.push({
173
+ type: 'status_mismatch',
174
+ entityType: 'subscription',
175
+ entityId: local.id,
176
+ stripeId: local.stripeSubscriptionId,
177
+ localValue: local.status,
178
+ stripeValue: stripeStatus,
179
+ description: `Status mismatch: local=${local.status}, stripe=${stripeStatus}`,
180
+ fixed: false,
181
+ });
182
+ }
183
+ // Compare tier (from metadata)
184
+ const stripeTier = stripeSubscription.metadata?.tier;
185
+ if (stripeTier && local.tier !== stripeTier) {
186
+ discrepancies.push({
187
+ type: 'tier_mismatch',
188
+ entityType: 'subscription',
189
+ entityId: local.id,
190
+ stripeId: local.stripeSubscriptionId,
191
+ localValue: local.tier,
192
+ stripeValue: stripeTier,
193
+ description: `Tier mismatch: local=${local.tier}, stripe=${stripeTier}`,
194
+ fixed: false,
195
+ });
196
+ }
197
+ // Compare seat count
198
+ const stripeQuantity = stripeSubscription.items.data[0]?.quantity ?? 1;
199
+ if (local.seatCount !== stripeQuantity) {
200
+ discrepancies.push({
201
+ type: 'seat_count_mismatch',
202
+ entityType: 'subscription',
203
+ entityId: local.id,
204
+ stripeId: local.stripeSubscriptionId,
205
+ localValue: local.seatCount,
206
+ stripeValue: stripeQuantity,
207
+ description: `Seat count mismatch: local=${local.seatCount}, stripe=${stripeQuantity}`,
208
+ fixed: false,
209
+ });
210
+ }
211
+ return discrepancies;
212
+ }
213
+ // ==========================================================================
214
+ // Invoice Reconciliation
215
+ // ==========================================================================
216
+ getLocalInvoices() {
217
+ return this.db
218
+ .prepare(`SELECT
219
+ id,
220
+ customer_id as customerId,
221
+ stripe_invoice_id as stripeInvoiceId,
222
+ amount_cents as amountCents,
223
+ status
224
+ FROM invoices
225
+ WHERE stripe_invoice_id IS NOT NULL
226
+ LIMIT ?`)
227
+ .all(this.batchSize);
228
+ }
229
+ async reconcileInvoice(local) {
230
+ const discrepancies = [];
231
+ // Fetch from Stripe
232
+ const stripeInvoice = await this.stripe.getInvoice(local.stripeInvoiceId);
233
+ if (!stripeInvoice) {
234
+ discrepancies.push({
235
+ type: 'missing_stripe',
236
+ entityType: 'invoice',
237
+ entityId: local.id,
238
+ stripeId: local.stripeInvoiceId,
239
+ localValue: local.status,
240
+ stripeValue: null,
241
+ description: 'Invoice exists locally but not in Stripe',
242
+ fixed: false,
243
+ });
244
+ return discrepancies;
245
+ }
246
+ // Compare status
247
+ const stripeStatus = mapInvoiceStatus(stripeInvoice.status);
248
+ if (local.status !== stripeStatus) {
249
+ discrepancies.push({
250
+ type: 'invoice_status_mismatch',
251
+ entityType: 'invoice',
252
+ entityId: local.id,
253
+ stripeId: local.stripeInvoiceId,
254
+ localValue: local.status,
255
+ stripeValue: stripeStatus,
256
+ description: `Invoice status mismatch: local=${local.status}, stripe=${stripeStatus}`,
257
+ fixed: false,
258
+ });
259
+ }
260
+ // Compare amount
261
+ const stripeAmount = stripeInvoice.amount_paid ?? stripeInvoice.amount_due;
262
+ if (local.amountCents !== stripeAmount) {
263
+ discrepancies.push({
264
+ type: 'invoice_amount_mismatch',
265
+ entityType: 'invoice',
266
+ entityId: local.id,
267
+ stripeId: local.stripeInvoiceId,
268
+ localValue: local.amountCents,
269
+ stripeValue: stripeAmount,
270
+ description: `Invoice amount mismatch: local=${local.amountCents}, stripe=${stripeAmount}`,
271
+ fixed: false,
272
+ });
273
+ }
274
+ return discrepancies;
275
+ }
276
+ // ==========================================================================
277
+ // Fix Discrepancies
278
+ // ==========================================================================
279
+ fixDiscrepancy(discrepancy) {
280
+ try {
281
+ if (discrepancy.entityType === 'subscription') {
282
+ return this.fixSubscriptionDiscrepancy(discrepancy);
283
+ }
284
+ else if (discrepancy.entityType === 'invoice') {
285
+ return this.fixInvoiceDiscrepancy(discrepancy);
286
+ }
287
+ return false;
288
+ }
289
+ catch (error) {
290
+ const errorMsg = error instanceof Error ? error.message : String(error);
291
+ logger.error('Failed to fix discrepancy', undefined, {
292
+ type: discrepancy.type,
293
+ entityId: discrepancy.entityId,
294
+ error: errorMsg,
295
+ });
296
+ return false;
297
+ }
298
+ }
299
+ fixSubscriptionDiscrepancy(discrepancy) {
300
+ switch (discrepancy.type) {
301
+ case 'status_mismatch':
302
+ this.db
303
+ .prepare(`UPDATE user_subscriptions SET status = ?, updated_at = ? WHERE id = ?`)
304
+ .run(discrepancy.stripeValue, new Date().toISOString(), discrepancy.entityId);
305
+ logger.info('Fixed subscription status', {
306
+ entityId: discrepancy.entityId,
307
+ newStatus: discrepancy.stripeValue,
308
+ });
309
+ return true;
310
+ case 'tier_mismatch':
311
+ this.db
312
+ .prepare(`UPDATE user_subscriptions SET tier = ?, updated_at = ? WHERE id = ?`)
313
+ .run(discrepancy.stripeValue, new Date().toISOString(), discrepancy.entityId);
314
+ logger.info('Fixed subscription tier', {
315
+ entityId: discrepancy.entityId,
316
+ newTier: discrepancy.stripeValue,
317
+ });
318
+ return true;
319
+ case 'seat_count_mismatch':
320
+ this.db
321
+ .prepare(`UPDATE user_subscriptions SET seat_count = ?, updated_at = ? WHERE id = ?`)
322
+ .run(discrepancy.stripeValue, new Date().toISOString(), discrepancy.entityId);
323
+ logger.info('Fixed subscription seat count', {
324
+ entityId: discrepancy.entityId,
325
+ newSeatCount: discrepancy.stripeValue,
326
+ });
327
+ return true;
328
+ case 'missing_stripe':
329
+ // Mark local subscription as canceled if missing in Stripe
330
+ this.db
331
+ .prepare(`UPDATE user_subscriptions SET status = 'canceled', canceled_at = ?, updated_at = ? WHERE id = ?`)
332
+ .run(new Date().toISOString(), new Date().toISOString(), discrepancy.entityId);
333
+ logger.info('Marked missing subscription as canceled', { entityId: discrepancy.entityId });
334
+ return true;
335
+ default:
336
+ return false;
337
+ }
338
+ }
339
+ fixInvoiceDiscrepancy(discrepancy) {
340
+ switch (discrepancy.type) {
341
+ case 'invoice_status_mismatch':
342
+ this.db
343
+ .prepare(`UPDATE invoices SET status = ? WHERE id = ?`)
344
+ .run(discrepancy.stripeValue, discrepancy.entityId);
345
+ logger.info('Fixed invoice status', {
346
+ entityId: discrepancy.entityId,
347
+ newStatus: discrepancy.stripeValue,
348
+ });
349
+ return true;
350
+ case 'invoice_amount_mismatch':
351
+ this.db
352
+ .prepare(`UPDATE invoices SET amount_cents = ? WHERE id = ?`)
353
+ .run(discrepancy.stripeValue, discrepancy.entityId);
354
+ logger.info('Fixed invoice amount', {
355
+ entityId: discrepancy.entityId,
356
+ newAmount: discrepancy.stripeValue,
357
+ });
358
+ return true;
359
+ default:
360
+ return false;
361
+ }
362
+ }
363
+ }
364
+ // ============================================================================
365
+ // Helper Functions
366
+ // ============================================================================
367
+ function mapStripeStatus(stripeStatus) {
368
+ switch (stripeStatus) {
369
+ case 'active':
370
+ return 'active';
371
+ case 'past_due':
372
+ return 'past_due';
373
+ case 'canceled':
374
+ return 'canceled';
375
+ case 'trialing':
376
+ return 'trialing';
377
+ case 'paused':
378
+ return 'paused';
379
+ case 'incomplete':
380
+ return 'incomplete';
381
+ case 'incomplete_expired':
382
+ return 'incomplete_expired';
383
+ case 'unpaid':
384
+ return 'unpaid';
385
+ default:
386
+ return 'active';
387
+ }
388
+ }
389
+ function mapInvoiceStatus(stripeStatus) {
390
+ switch (stripeStatus) {
391
+ case 'paid':
392
+ return 'paid';
393
+ case 'open':
394
+ return 'open';
395
+ case 'draft':
396
+ return 'draft';
397
+ case 'void':
398
+ return 'void';
399
+ case 'uncollectible':
400
+ return 'uncollectible';
401
+ default:
402
+ return 'open';
403
+ }
404
+ }
405
+ //# sourceMappingURL=StripeReconciliationJob.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"StripeReconciliationJob.js","sourceRoot":"","sources":["../../../src/billing/StripeReconciliationJob.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,92 @@
1
+ /**
2
+ * SMI-1070: Stripe Webhook Handler
3
+ *
4
+ * Processes Stripe webhook events for:
5
+ * - Subscription lifecycle (created, updated, deleted)
6
+ * - Invoice events (payment succeeded, payment failed)
7
+ * - Checkout session completion
8
+ *
9
+ * Features:
10
+ * - Idempotent event processing
11
+ * - Signature verification
12
+ * - License key generation on subscription creation
13
+ * - Status synchronization
14
+ */
15
+ import type { Database as BetterSqliteDatabase } from 'better-sqlite3';
16
+ import { StripeClient } from './StripeClient.js';
17
+ import type { BillingService } from './BillingService.js';
18
+ import type { LicenseTier, WebhookProcessResult } from './types.js';
19
+ export interface StripeWebhookHandlerConfig {
20
+ /**
21
+ * StripeClient for API calls
22
+ */
23
+ stripeClient: StripeClient;
24
+ /**
25
+ * BillingService for database operations
26
+ */
27
+ billingService: BillingService;
28
+ /**
29
+ * Database connection (for license key storage)
30
+ */
31
+ db: BetterSqliteDatabase;
32
+ /**
33
+ * Callback for license key generation
34
+ * Called when a subscription is created/activated
35
+ */
36
+ onLicenseKeyNeeded?: (params: {
37
+ customerId: string;
38
+ tier: LicenseTier;
39
+ expiresAt: Date;
40
+ subscriptionId: string;
41
+ }) => Promise<string>;
42
+ /**
43
+ * Callback for sending emails
44
+ */
45
+ onEmailNeeded?: (params: {
46
+ type: 'license_key' | 'payment_failed' | 'subscription_canceled';
47
+ email: string;
48
+ data: Record<string, unknown>;
49
+ }) => Promise<void>;
50
+ }
51
+ /**
52
+ * Handles Stripe webhook events
53
+ *
54
+ * @example
55
+ * ```typescript
56
+ * const handler = new StripeWebhookHandler({
57
+ * stripeClient,
58
+ * billingService,
59
+ * db,
60
+ * onLicenseKeyNeeded: async (params) => {
61
+ * return licenseGenerator.createLicense(params);
62
+ * },
63
+ * });
64
+ *
65
+ * // In webhook endpoint:
66
+ * const result = await handler.handleWebhook(payload, signature);
67
+ * ```
68
+ */
69
+ export declare class StripeWebhookHandler {
70
+ private readonly stripe;
71
+ private readonly billing;
72
+ private readonly db;
73
+ private readonly onLicenseKeyNeeded?;
74
+ private readonly onEmailNeeded?;
75
+ constructor(config: StripeWebhookHandlerConfig);
76
+ /**
77
+ * Handle an incoming Stripe webhook
78
+ */
79
+ handleWebhook(payload: string, signature: string): Promise<WebhookProcessResult>;
80
+ private routeEvent;
81
+ private handleSubscriptionCreated;
82
+ private handleSubscriptionUpdated;
83
+ private handleSubscriptionDeleted;
84
+ private handleInvoicePaymentSucceeded;
85
+ private handleInvoicePaymentFailed;
86
+ private handleCheckoutSessionCompleted;
87
+ private storeLicenseKey;
88
+ private revokeLicenseKey;
89
+ private extractTier;
90
+ private extractSeatCount;
91
+ }
92
+ //# sourceMappingURL=StripeWebhookHandler.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"StripeWebhookHandler.d.ts","sourceRoot":"","sources":["../../../src/billing/StripeWebhookHandler.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAIH,OAAO,KAAK,EAAE,QAAQ,IAAI,oBAAoB,EAAE,MAAM,gBAAgB,CAAA;AAEtE,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAA;AAChD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAA;AACzD,OAAO,KAAK,EACV,WAAW,EAIX,oBAAoB,EACrB,MAAM,YAAY,CAAA;AASnB,MAAM,WAAW,0BAA0B;IACzC;;OAEG;IACH,YAAY,EAAE,YAAY,CAAA;IAE1B;;OAEG;IACH,cAAc,EAAE,cAAc,CAAA;IAE9B;;OAEG;IACH,EAAE,EAAE,oBAAoB,CAAA;IAExB;;;OAGG;IACH,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;IAErB;;OAEG;IACH,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;;;;;;;;;;;;;;;;;GAiBG;AACH,qBAAa,oBAAoB;IAC/B,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAc;IACrC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAgB;IACxC,OAAO,CAAC,QAAQ,CAAC,EAAE,CAAsB;IACzC,OAAO,CAAC,QAAQ,CAAC,kBAAkB,CAAC,CAAkD;IACtF,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAC,CAA6C;gBAEhE,MAAM,EAAE,0BAA0B;IAc9C;;OAEG;IACG,aAAa,CAAC,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,oBAAoB,CAAC;YAgFxE,UAAU;YAmCV,yBAAyB;YA6DzB,yBAAyB;YAoDzB,yBAAyB;YAyCzB,6BAA6B;YAgC7B,0BAA0B;YAgD1B,8BAA8B;IAe5C,OAAO,CAAC,eAAe;IAiCvB,OAAO,CAAC,gBAAgB;IAkBxB,OAAO,CAAC,WAAW;IAWnB,OAAO,CAAC,gBAAgB;CAazB"}