chargeback-guard 2.0.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 (48) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +311 -0
  3. package/docs/api.md +278 -0
  4. package/docs/architecture.md +281 -0
  5. package/docs/configuration.md +292 -0
  6. package/docs/getting-started.md +155 -0
  7. package/examples/advancedConfig.ts +123 -0
  8. package/examples/basicUsage.ts +98 -0
  9. package/examples/stripeIntegration.ts +106 -0
  10. package/package.json +181 -0
  11. package/src/ai/fraudDetection.ts +261 -0
  12. package/src/ai/patternRecognition.ts +218 -0
  13. package/src/analytics/dashboard.ts +195 -0
  14. package/src/analytics/metrics.ts +175 -0
  15. package/src/analytics/predictions.ts +135 -0
  16. package/src/analytics/reports.ts +221 -0
  17. package/src/api/controllers.ts +339 -0
  18. package/src/api/middleware.ts +172 -0
  19. package/src/api/routes.ts +141 -0
  20. package/src/config.ts +231 -0
  21. package/src/core/chargebackGuard.ts +616 -0
  22. package/src/core/eventEmitter.ts +118 -0
  23. package/src/core/lifecycle.ts +215 -0
  24. package/src/database/schema.ts +392 -0
  25. package/src/dispute/analyzer.ts +317 -0
  26. package/src/dispute/bankIntegration.ts +274 -0
  27. package/src/dispute/detector.ts +239 -0
  28. package/src/dispute/responseEngine.ts +440 -0
  29. package/src/evidence/collector.ts +426 -0
  30. package/src/evidence/encryption.ts +168 -0
  31. package/src/evidence/storage.ts +197 -0
  32. package/src/evidence/validator.ts +184 -0
  33. package/src/index.ts +43 -0
  34. package/src/integrations/paypal.ts +258 -0
  35. package/src/integrations/stripe.ts +280 -0
  36. package/src/integrations/webhook.ts +332 -0
  37. package/src/notifications/email.ts +161 -0
  38. package/src/notifications/inApp.ts +319 -0
  39. package/src/notifications/sms.ts +58 -0
  40. package/src/security/auth.ts +153 -0
  41. package/src/security/rateLimit.ts +77 -0
  42. package/src/security/validation.ts +166 -0
  43. package/src/server.ts +122 -0
  44. package/src/types/index.ts +790 -0
  45. package/src/utils/formatters.ts +72 -0
  46. package/src/utils/helpers.ts +193 -0
  47. package/src/utils/logger.ts +88 -0
  48. package/src/utils/validators.ts +39 -0
@@ -0,0 +1,616 @@
1
+ // ============================================================
2
+ // CHARGEBACK GUARD — Main Core Engine (Ultra Pro)
3
+ // ============================================================
4
+
5
+ import { ChargebackEventEmitter } from './eventEmitter';
6
+ import { LifecycleManager } from './lifecycle';
7
+ import { createLogger } from '../utils/logger';
8
+ import config, { defaultConfig } from '../config';
9
+ import {
10
+ ChargebackGuardConfig,
11
+ PaymentData,
12
+ Dispute,
13
+ Evidence,
14
+ DisputeReply,
15
+ DisputeAnalysis,
16
+ ProtectionStats,
17
+ NotificationEvent,
18
+ RiskLevel,
19
+ DisputeStatus,
20
+ PaymentProvider,
21
+ ApiResponse,
22
+ } from '../types';
23
+
24
+ const log = createLogger('ChargebackGuard');
25
+
26
+ // ────────────────────────────────────────────────────────────
27
+ // LAZY MODULE IMPORTS (to avoid circular deps)
28
+ // ────────────────────────────────────────────────────────────
29
+
30
+ /* eslint-disable @typescript-eslint/no-var-requires */
31
+ const getEvidenceCollector = () => require('../evidence/collector').EvidenceCollector;
32
+ const getEvidenceValidator = () => require('../evidence/validator').EvidenceValidator;
33
+ const getEvidenceStorage = () => require('../evidence/storage').EvidenceStorage;
34
+ const getDisputeDetector = () => require('../dispute/detector').DisputeDetector;
35
+ const getDisputeAnalyzer = () => require('../dispute/analyzer').DisputeAnalyzer;
36
+ const getResponseEngine = () => require('../dispute/responseEngine').ResponseEngine;
37
+ const getFraudDetection = () => require('../ai/fraudDetection').FraudDetection;
38
+ const getDatabaseManager = () => require('../database/schema').DatabaseManager;
39
+ const getNotificationSvc = () => require('../notifications/inApp').NotificationService;
40
+ /* eslint-enable @typescript-eslint/no-var-requires */
41
+
42
+ // ────────────────────────────────────────────────────────────
43
+ // REGISTRATION RESULT
44
+ // ────────────────────────────────────────────────────────────
45
+
46
+ export interface RegistrationResult {
47
+ success: boolean;
48
+ orderId: string;
49
+ protectionActive: boolean;
50
+ trustScore: number;
51
+ riskLevel: RiskLevel;
52
+ evidenceCollectionId?: string;
53
+ warnings?: string[];
54
+ }
55
+
56
+ export interface DisputeHandlingResult {
57
+ success: boolean;
58
+ disputeId: string;
59
+ orderId?: string;
60
+ actionTaken: 'auto-reply_submitted' | 'manual_review_required' | 'accepted' | 'skipped';
61
+ confidence?: number;
62
+ riskLevel?: RiskLevel;
63
+ replySubmitted: boolean;
64
+ }
65
+
66
+ // ────────────────────────────────────────────────────────────
67
+ // MAIN CLASS
68
+ // ────────────────────────────────────────────────────────────
69
+
70
+ export class ChargebackGuard {
71
+ public readonly events: ChargebackEventEmitter;
72
+ public readonly lifecycle: LifecycleManager;
73
+
74
+ private readonly cfg: ChargebackGuardConfig;
75
+ private evidenceCollector: unknown;
76
+ private evidenceValidator: unknown;
77
+ private evidenceStorage: unknown;
78
+ private disputeDetector: unknown;
79
+ private disputeAnalyzer: unknown;
80
+ private responseEngine: unknown;
81
+ private fraudDetection: unknown;
82
+ private db: unknown;
83
+ private notificationSvc: unknown;
84
+
85
+ constructor(options: Partial<ChargebackGuardConfig> = {}) {
86
+ this.cfg = { ...defaultConfig, ...options } as ChargebackGuardConfig;
87
+ this.events = new ChargebackEventEmitter();
88
+ this.lifecycle = new LifecycleManager();
89
+
90
+ this._registerLifecycleHooks();
91
+ this._registerDefaultEventHandlers();
92
+
93
+ log.info('ChargebackGuard instance created', {
94
+ environment: this.cfg.environment,
95
+ autoReply: this.cfg.autoReply,
96
+ evidenceCollection: this.cfg.evidenceCollection,
97
+ });
98
+ }
99
+
100
+ // ──────────────────────────────────────────
101
+ // INITIALIZATION
102
+ // ──────────────────────────────────────────
103
+
104
+ async initialize(): Promise<void> {
105
+ await this.lifecycle.initialize();
106
+ }
107
+
108
+ async shutdown(): Promise<void> {
109
+ await this.lifecycle.shutdown();
110
+ }
111
+
112
+ // ──────────────────────────────────────────
113
+ // CORE: REGISTER PAYMENT
114
+ // ──────────────────────────────────────────
115
+
116
+ /**
117
+ * Register a new payment for chargeback protection.
118
+ * Call this immediately after a successful charge.
119
+ */
120
+ async registerPayment(paymentData: PaymentData): Promise<RegistrationResult> {
121
+ const operationStart = Date.now();
122
+ log.info(`Registering payment: ${paymentData.orderId}`, {
123
+ amount: paymentData.amount,
124
+ currency: paymentData.currency,
125
+ });
126
+
127
+ try {
128
+ // ── 1. Validate input ──────────────────
129
+ this._validatePaymentData(paymentData);
130
+
131
+ // ── 2. Run fraud pre-check ─────────────
132
+ const fraudResult = await this._runFraudPreCheck(paymentData);
133
+ if (fraudResult.isFraud && fraudResult.probability > 0.95) {
134
+ this.events.emitEvent(NotificationEvent.FRAUD_DETECTED, {
135
+ orderId: paymentData.orderId,
136
+ probability: fraudResult.probability,
137
+ });
138
+ log.warn(`Potential fraud detected for order ${paymentData.orderId}`, {
139
+ probability: fraudResult.probability,
140
+ });
141
+ }
142
+
143
+ // ── 3. Store the order ─────────────────
144
+ const db = this._getDb();
145
+ await db.orders.create({
146
+ orderId: paymentData.orderId,
147
+ amount: paymentData.amount,
148
+ currency: paymentData.currency,
149
+ customerEmail: paymentData.customerEmail,
150
+ provider: paymentData.provider ?? PaymentProvider.STRIPE,
151
+ status: 'active',
152
+ registeredAt: new Date().toISOString(),
153
+ });
154
+
155
+ // ── 4. Collect evidence ────────────────
156
+ let evidence: Evidence | undefined;
157
+ let evidenceCollectionId: string | undefined;
158
+
159
+ if (this.cfg.evidenceCollection !== false) {
160
+ const collector = this._getEvidenceCollector();
161
+ const validator = this._getEvidenceValidator();
162
+ const storage = this._getEvidenceStorage();
163
+
164
+ const rawEvidence = await collector.collect({
165
+ orderId: paymentData.orderId,
166
+ customerIp: paymentData.customerIp,
167
+ userAgent: paymentData.userAgent,
168
+ sessionData: paymentData.sessionData,
169
+ deviceFingerprint: paymentData.deviceFingerprint,
170
+ amount: paymentData.amount,
171
+ currency: paymentData.currency,
172
+ transactionId: paymentData.transactionId,
173
+ shippingAddress: paymentData.shippingAddress,
174
+ customerEmail: paymentData.customerEmail,
175
+ });
176
+
177
+ evidence = await validator.validate(rawEvidence);
178
+ evidenceCollectionId = await storage.store(evidence);
179
+ }
180
+
181
+ // ── 5. Emit success event ──────────────
182
+ const trustScore = evidence?.trustScore ?? 75;
183
+ const riskLevel = this._scoreToRiskLevel(trustScore);
184
+
185
+ this.events.emitEvent(NotificationEvent.PAYMENT_REGISTERED, {
186
+ orderId: paymentData.orderId,
187
+ trustScore,
188
+ riskLevel,
189
+ durationMs: Date.now() - operationStart,
190
+ });
191
+
192
+ if (riskLevel === RiskLevel.HIGH || riskLevel === RiskLevel.CRITICAL) {
193
+ this.events.emitEvent(NotificationEvent.HIGH_RISK_TRANSACTION, {
194
+ orderId: paymentData.orderId,
195
+ riskLevel,
196
+ fraudProbability: fraudResult?.probability ?? 0,
197
+ });
198
+ }
199
+
200
+ const result: RegistrationResult = {
201
+ success: true,
202
+ orderId: paymentData.orderId,
203
+ protectionActive: true,
204
+ trustScore,
205
+ riskLevel,
206
+ evidenceCollectionId,
207
+ };
208
+
209
+ log.info(`Payment registered successfully: ${paymentData.orderId}`, {
210
+ trustScore,
211
+ riskLevel,
212
+ durationMs: Date.now() - operationStart,
213
+ });
214
+
215
+ return result;
216
+
217
+ } catch (err) {
218
+ const message = err instanceof Error ? err.message : String(err);
219
+ log.error(`Payment registration failed: ${paymentData.orderId}`, { error: message });
220
+ this.events.emitEvent(NotificationEvent.ERROR, {
221
+ type: 'payment_registration_failed',
222
+ orderId: paymentData.orderId,
223
+ error: message,
224
+ });
225
+ throw err;
226
+ }
227
+ }
228
+
229
+ // ──────────────────────────────────────────
230
+ // CORE: HANDLE DISPUTE
231
+ // ──────────────────────────────────────────
232
+
233
+ /**
234
+ * Handle an incoming dispute/chargeback notification from the bank.
235
+ * This is the heart of the auto-reply engine.
236
+ */
237
+ async handleDispute(dispute: Dispute): Promise<DisputeHandlingResult> {
238
+ const operationStart = Date.now();
239
+ log.info(`Handling dispute: ${dispute.id}`, {
240
+ reason: dispute.reason,
241
+ amount: dispute.amount,
242
+ orderId: dispute.orderId,
243
+ });
244
+
245
+ this.events.emitEvent(NotificationEvent.DISPUTE_DETECTED, {
246
+ disputeId: dispute.id,
247
+ reason: dispute.reason,
248
+ amount: dispute.amount,
249
+ });
250
+
251
+ try {
252
+ // ── 1. Analyze the dispute ─────────────
253
+ const analyzer = this._getDisputeAnalyzer();
254
+ const analysis: DisputeAnalysis = await analyzer.analyze(dispute);
255
+
256
+ log.info(`Dispute analysis complete: ${dispute.id}`, {
257
+ riskLevel: analysis.riskLevel,
258
+ confidenceScore: analysis.confidenceScore,
259
+ recommendedAction: analysis.recommendedAction,
260
+ });
261
+
262
+ // ── 2. Load stored evidence ────────────
263
+ const storage = this._getEvidenceStorage();
264
+ const evidence: Evidence | null = dispute.orderId
265
+ ? await storage.findByOrderId(dispute.orderId)
266
+ : null;
267
+
268
+ if (!evidence) {
269
+ log.warn(`No evidence found for order: ${dispute.orderId}`);
270
+ }
271
+
272
+ // ── 3. Decide action ───────────────────
273
+ if (analysis.recommendedAction === 'accept') {
274
+ log.info(`Accepting dispute (low confidence): ${dispute.id}`);
275
+ await this._updateDisputeRecord(dispute.id, dispute.orderId, DisputeStatus.CHARGE_REFUNDED);
276
+ return {
277
+ success: true,
278
+ disputeId: dispute.id,
279
+ orderId: dispute.orderId,
280
+ actionTaken: 'accepted',
281
+ confidence: analysis.confidenceScore,
282
+ riskLevel: analysis.riskLevel,
283
+ replySubmitted: false,
284
+ };
285
+ }
286
+
287
+ // ── 4. Generate reply ──────────────────
288
+ const engine = this._getResponseEngine();
289
+ const reply: DisputeReply = await engine.generateReply({
290
+ dispute,
291
+ evidence,
292
+ analysis,
293
+ });
294
+
295
+ // ── 5. Submit to bank (auto-reply) ─────
296
+ let replySubmitted = false;
297
+ if (this.cfg.autoReply !== false) {
298
+ try {
299
+ await this._submitReplyToProvider(dispute, reply);
300
+ replySubmitted = true;
301
+ log.info(`Reply submitted to provider: ${dispute.id}`);
302
+ } catch (submitErr) {
303
+ const msg = submitErr instanceof Error ? submitErr.message : String(submitErr);
304
+ log.error(`Failed to submit reply: ${dispute.id}`, { error: msg });
305
+ }
306
+ }
307
+
308
+ // ── 6. Persist dispute record ──────────
309
+ await this._updateDisputeRecord(
310
+ dispute.id,
311
+ dispute.orderId,
312
+ replySubmitted ? DisputeStatus.UNDER_REVIEW : DisputeStatus.NEEDS_RESPONSE,
313
+ reply
314
+ );
315
+
316
+ // ── 7. Emit events ─────────────────────
317
+ this.events.emitEvent(NotificationEvent.DISPUTE_REPLIED, {
318
+ disputeId: dispute.id,
319
+ orderId: dispute.orderId,
320
+ replySubmitted,
321
+ confidenceScore: analysis.confidenceScore,
322
+ durationMs: Date.now() - operationStart,
323
+ });
324
+
325
+ return {
326
+ success: true,
327
+ disputeId: dispute.id,
328
+ orderId: dispute.orderId,
329
+ actionTaken: replySubmitted ? 'auto-reply_submitted' : 'manual_review_required',
330
+ confidence: analysis.confidenceScore,
331
+ riskLevel: analysis.riskLevel,
332
+ replySubmitted,
333
+ };
334
+
335
+ } catch (err) {
336
+ const message = err instanceof Error ? err.message : String(err);
337
+ log.error(`Dispute handling failed: ${dispute.id}`, { error: message });
338
+ this.events.emitEvent(NotificationEvent.ERROR, {
339
+ type: 'dispute_handling_failed',
340
+ disputeId: dispute.id,
341
+ error: message,
342
+ });
343
+ throw err;
344
+ }
345
+ }
346
+
347
+ // ──────────────────────────────────────────
348
+ // STATS & ANALYTICS
349
+ // ──────────────────────────────────────────
350
+
351
+ async getProtectionStats(fromDate?: Date, toDate?: Date): Promise<ProtectionStats> {
352
+ const db = this._getDb();
353
+
354
+ const [totalOrders, totalDisputes, wonDisputes, lostDisputes, pendingDisputes] =
355
+ await Promise.all([
356
+ db.orders.count(fromDate, toDate),
357
+ db.disputes.count(fromDate, toDate),
358
+ db.disputes.countByStatus(DisputeStatus.WON, fromDate, toDate),
359
+ db.disputes.countByStatus(DisputeStatus.LOST, fromDate, toDate),
360
+ db.disputes.countByStatus(DisputeStatus.NEEDS_RESPONSE, fromDate, toDate),
361
+ ]);
362
+
363
+ const resolvedDisputes = wonDisputes + lostDisputes;
364
+ const winRate = totalDisputes > 0 ? (wonDisputes / totalDisputes) * 100 : 0;
365
+ const chargebackRate = totalOrders > 0 ? (totalDisputes / totalOrders) * 100 : 0;
366
+
367
+ const totalMoneyRecovered = await db.disputes.sumAmountByStatus(DisputeStatus.WON, fromDate, toDate);
368
+ const totalMoneyLost = await db.disputes.sumAmountByStatus(DisputeStatus.LOST, fromDate, toDate);
369
+ const estimatedSavings = totalMoneyRecovered * 1.25; // including avoided fees
370
+
371
+ const avgResolutionTime = await db.disputes.avgResolutionTime(fromDate, toDate);
372
+
373
+ return {
374
+ totalOrders,
375
+ totalDisputes,
376
+ resolvedDisputes,
377
+ wonDisputes,
378
+ lostDisputes,
379
+ pendingDisputes,
380
+ winRate: parseFloat(winRate.toFixed(2)),
381
+ totalMoneyRecovered,
382
+ totalMoneyLost,
383
+ averageResolutionTime: avgResolutionTime,
384
+ chargebackRate: parseFloat(chargebackRate.toFixed(4)),
385
+ estimatedSavings,
386
+ };
387
+ }
388
+
389
+ async getHealth(): Promise<ApiResponse<unknown>> {
390
+ const health = await this.lifecycle.getSystemHealth();
391
+ return {
392
+ success: health.status !== 'unhealthy',
393
+ data: health,
394
+ };
395
+ }
396
+
397
+ // ──────────────────────────────────────────
398
+ // PRIVATE HELPERS
399
+ // ──────────────────────────────────────────
400
+
401
+ private _validatePaymentData(data: PaymentData): void {
402
+ if (!data.orderId?.trim()) { throw new Error('orderId is required'); }
403
+ if (!data.amount || data.amount <= 0) { throw new Error('amount must be positive'); }
404
+ if (!data.currency?.trim()) { throw new Error('currency is required'); }
405
+ if (!data.customerEmail?.trim()) { throw new Error('customerEmail is required'); }
406
+ if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(data.customerEmail)) {
407
+ throw new Error('customerEmail is invalid');
408
+ }
409
+ }
410
+
411
+ private async _runFraudPreCheck(data: PaymentData): Promise<{
412
+ isFraud: boolean;
413
+ probability: number;
414
+ }> {
415
+ try {
416
+ const fraud = this._getFraudDetection();
417
+ return await fraud.quickCheck(data);
418
+ } catch {
419
+ return { isFraud: false, probability: 0 };
420
+ }
421
+ }
422
+
423
+ private async _submitReplyToProvider(dispute: Dispute, reply: DisputeReply): Promise<void> {
424
+ // Dynamic import to avoid circular deps at module load time
425
+ if (dispute.provider === PaymentProvider.STRIPE) {
426
+ const { StripeIntegration } = require('../integrations/stripe');
427
+ const stripe = new StripeIntegration(config.stripe.secretKey);
428
+ await stripe.submitDisputeEvidence(dispute.id, reply);
429
+ } else if (dispute.provider === PaymentProvider.PAYPAL) {
430
+ const { PayPalIntegration } = require('../integrations/paypal');
431
+ const paypal = new PayPalIntegration(
432
+ config.paypal.clientId,
433
+ config.paypal.clientSecret,
434
+ config.paypal.mode
435
+ );
436
+ await paypal.submitDisputeEvidence(dispute.id, reply);
437
+ }
438
+ }
439
+
440
+ private async _updateDisputeRecord(
441
+ disputeId: string,
442
+ orderId: string | undefined,
443
+ status: DisputeStatus,
444
+ reply?: DisputeReply
445
+ ): Promise<void> {
446
+ try {
447
+ const db = this._getDb();
448
+ await db.disputes.upsert({
449
+ disputeId,
450
+ orderId: orderId ?? '',
451
+ status,
452
+ replyJson: reply ? JSON.stringify(reply) : undefined,
453
+ replySubmittedAt: reply ? new Date().toISOString() : undefined,
454
+ });
455
+ } catch (err) {
456
+ log.warn('Failed to update dispute record', {
457
+ disputeId,
458
+ error: err instanceof Error ? err.message : String(err),
459
+ });
460
+ }
461
+ }
462
+
463
+ private _scoreToRiskLevel(trustScore: number): RiskLevel {
464
+ if (trustScore >= 90) { return RiskLevel.VERY_LOW; }
465
+ if (trustScore >= 75) { return RiskLevel.LOW; }
466
+ if (trustScore >= 55) { return RiskLevel.MEDIUM; }
467
+ if (trustScore >= 35) { return RiskLevel.HIGH; }
468
+ return RiskLevel.CRITICAL;
469
+ }
470
+
471
+ // ──────────────────────────────────────────
472
+ // LIFECYCLE HOOKS
473
+ // ──────────────────────────────────────────
474
+
475
+ private _registerLifecycleHooks(): void {
476
+ this.lifecycle.registerInitHook({
477
+ name: 'database',
478
+ fn: async () => {
479
+ const db = this._getDb();
480
+ await db.initialize();
481
+ },
482
+ timeout: 15000,
483
+ });
484
+
485
+ this.lifecycle.registerInitHook({
486
+ name: 'fraud-model',
487
+ fn: async () => {
488
+ const fraud = this._getFraudDetection();
489
+ await fraud.loadModel();
490
+ },
491
+ timeout: 30000,
492
+ });
493
+
494
+ this.lifecycle.registerShutdownHook({
495
+ name: 'database',
496
+ fn: async () => {
497
+ const db = this._getDb();
498
+ await db.destroy();
499
+ },
500
+ });
501
+
502
+ // Register health checkers
503
+ this.lifecycle.registerHealthChecker('database', async () => {
504
+ try {
505
+ const db = this._getDb();
506
+ await db.healthCheck();
507
+ return {
508
+ name: 'database',
509
+ status: 'healthy' as const,
510
+ checkedAt: new Date().toISOString(),
511
+ };
512
+ } catch (err) {
513
+ return {
514
+ name: 'database',
515
+ status: 'unhealthy' as const,
516
+ message: err instanceof Error ? err.message : String(err),
517
+ checkedAt: new Date().toISOString(),
518
+ };
519
+ }
520
+ });
521
+ }
522
+
523
+ // ──────────────────────────────────────────
524
+ // DEFAULT EVENT HANDLERS
525
+ // ──────────────────────────────────────────
526
+
527
+ private _registerDefaultEventHandlers(): void {
528
+ this.events.on(NotificationEvent.PAYMENT_REGISTERED, ({ data }) => {
529
+ log.info(`✅ Payment registered: ${data['orderId']} | Trust: ${data['trustScore']} | Risk: ${data['riskLevel']}`);
530
+ });
531
+
532
+ this.events.on(NotificationEvent.DISPUTE_DETECTED, ({ data }) => {
533
+ log.warn(`🚨 Dispute detected: ${data['disputeId']} | Reason: ${data['reason']} | Amount: $${data['amount']}`);
534
+ });
535
+
536
+ this.events.on(NotificationEvent.DISPUTE_REPLIED, ({ data }) => {
537
+ log.info(`📤 Reply sent for: ${data['disputeId']} | Confidence: ${data['confidenceScore']}`);
538
+ });
539
+
540
+ this.events.on(NotificationEvent.DISPUTE_WON, ({ data }) => {
541
+ log.info(`🏆 Dispute WON: ${data['disputeId']} | Recovered: $${data['amount']}`);
542
+ });
543
+
544
+ this.events.on(NotificationEvent.DISPUTE_LOST, ({ data }) => {
545
+ log.warn(`❌ Dispute LOST: ${data['disputeId']} | Amount: $${data['amount']}`);
546
+ });
547
+
548
+ this.events.on(NotificationEvent.FRAUD_DETECTED, ({ data }) => {
549
+ log.warn(`🔴 FRAUD ALERT: Order ${data['orderId']} | Probability: ${data['probability']}`);
550
+ });
551
+
552
+ this.events.on(NotificationEvent.ERROR, ({ data }) => {
553
+ log.error(`❌ Error: ${data['type']}`, { error: data['error'] });
554
+ });
555
+ }
556
+
557
+ // ──────────────────────────────────────────
558
+ // LAZY SERVICE GETTERS
559
+ // ──────────────────────────────────────────
560
+
561
+ private _getEvidenceCollector(): any { // eslint-disable-line @typescript-eslint/no-explicit-any
562
+ if (!this.evidenceCollector) {
563
+ const Cls = getEvidenceCollector();
564
+ this.evidenceCollector = new Cls();
565
+ }
566
+ return this.evidenceCollector;
567
+ }
568
+
569
+ private _getEvidenceValidator(): any { // eslint-disable-line @typescript-eslint/no-explicit-any
570
+ if (!this.evidenceValidator) {
571
+ const Cls = getEvidenceValidator();
572
+ this.evidenceValidator = new Cls();
573
+ }
574
+ return this.evidenceValidator;
575
+ }
576
+
577
+ private _getEvidenceStorage(): any { // eslint-disable-line @typescript-eslint/no-explicit-any
578
+ if (!this.evidenceStorage) {
579
+ const Cls = getEvidenceStorage();
580
+ this.evidenceStorage = new Cls();
581
+ }
582
+ return this.evidenceStorage;
583
+ }
584
+
585
+ private _getDisputeAnalyzer(): any { // eslint-disable-line @typescript-eslint/no-explicit-any
586
+ if (!this.disputeAnalyzer) {
587
+ const Cls = getDisputeAnalyzer();
588
+ this.disputeAnalyzer = new Cls();
589
+ }
590
+ return this.disputeAnalyzer;
591
+ }
592
+
593
+ private _getResponseEngine(): any { // eslint-disable-line @typescript-eslint/no-explicit-any
594
+ if (!this.responseEngine) {
595
+ const Cls = getResponseEngine();
596
+ this.responseEngine = new Cls();
597
+ }
598
+ return this.responseEngine;
599
+ }
600
+
601
+ private _getFraudDetection(): any { // eslint-disable-line @typescript-eslint/no-explicit-any
602
+ if (!this.fraudDetection) {
603
+ const Cls = getFraudDetection();
604
+ this.fraudDetection = new Cls(config.ai);
605
+ }
606
+ return this.fraudDetection;
607
+ }
608
+
609
+ private _getDb(): any { // eslint-disable-line @typescript-eslint/no-explicit-any
610
+ if (!this.db) {
611
+ const Cls = getDatabaseManager();
612
+ this.db = new Cls(config.db);
613
+ }
614
+ return this.db;
615
+ }
616
+ }