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.
- package/LICENSE +21 -0
- package/README.md +311 -0
- package/docs/api.md +278 -0
- package/docs/architecture.md +281 -0
- package/docs/configuration.md +292 -0
- package/docs/getting-started.md +155 -0
- package/examples/advancedConfig.ts +123 -0
- package/examples/basicUsage.ts +98 -0
- package/examples/stripeIntegration.ts +106 -0
- package/package.json +181 -0
- package/src/ai/fraudDetection.ts +261 -0
- package/src/ai/patternRecognition.ts +218 -0
- package/src/analytics/dashboard.ts +195 -0
- package/src/analytics/metrics.ts +175 -0
- package/src/analytics/predictions.ts +135 -0
- package/src/analytics/reports.ts +221 -0
- package/src/api/controllers.ts +339 -0
- package/src/api/middleware.ts +172 -0
- package/src/api/routes.ts +141 -0
- package/src/config.ts +231 -0
- package/src/core/chargebackGuard.ts +616 -0
- package/src/core/eventEmitter.ts +118 -0
- package/src/core/lifecycle.ts +215 -0
- package/src/database/schema.ts +392 -0
- package/src/dispute/analyzer.ts +317 -0
- package/src/dispute/bankIntegration.ts +274 -0
- package/src/dispute/detector.ts +239 -0
- package/src/dispute/responseEngine.ts +440 -0
- package/src/evidence/collector.ts +426 -0
- package/src/evidence/encryption.ts +168 -0
- package/src/evidence/storage.ts +197 -0
- package/src/evidence/validator.ts +184 -0
- package/src/index.ts +43 -0
- package/src/integrations/paypal.ts +258 -0
- package/src/integrations/stripe.ts +280 -0
- package/src/integrations/webhook.ts +332 -0
- package/src/notifications/email.ts +161 -0
- package/src/notifications/inApp.ts +319 -0
- package/src/notifications/sms.ts +58 -0
- package/src/security/auth.ts +153 -0
- package/src/security/rateLimit.ts +77 -0
- package/src/security/validation.ts +166 -0
- package/src/server.ts +122 -0
- package/src/types/index.ts +790 -0
- package/src/utils/formatters.ts +72 -0
- package/src/utils/helpers.ts +193 -0
- package/src/utils/logger.ts +88 -0
- package/src/utils/validators.ts +39 -0
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
# Getting Started with chargeback-guard
|
|
2
|
+
|
|
3
|
+
chargeback-guard is an ultra-pro chargeback protection system for e-commerce merchants. It automatically collects transaction evidence, detects payment disputes, scores fraud risk with AI, and submits structured replies to banks via Stripe / PayPal / Square / Braintree webhooks.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Installation
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
npm install chargeback-guard
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
### Peer Dependencies
|
|
14
|
+
|
|
15
|
+
| Dependency | Purpose |
|
|
16
|
+
|---|---|
|
|
17
|
+
| `stripe` | Stripe webhook handling & dispute submission |
|
|
18
|
+
| `paypal-rest-sdk` | PayPal dispute API (optional) |
|
|
19
|
+
| `dotenv` | Environment variable loading |
|
|
20
|
+
|
|
21
|
+
---
|
|
22
|
+
|
|
23
|
+
## Quick Start
|
|
24
|
+
|
|
25
|
+
### 1. Set Environment Variables
|
|
26
|
+
|
|
27
|
+
Create a `.env` file:
|
|
28
|
+
|
|
29
|
+
```dotenv
|
|
30
|
+
CHARGEBACK_API_KEY=your-api-key-here
|
|
31
|
+
STRIPE_SECRET_KEY=sk_live_...
|
|
32
|
+
STRIPE_WEBHOOK_SECRET=whsec_...
|
|
33
|
+
DATABASE_URL=sqlite://./data/chargeback-guard.db
|
|
34
|
+
NODE_ENV=production
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
### 2. Initialize the Guard
|
|
38
|
+
|
|
39
|
+
```typescript
|
|
40
|
+
import { createChargebackGuard } from 'chargeback-guard';
|
|
41
|
+
|
|
42
|
+
const guard = await createChargebackGuard({
|
|
43
|
+
apiKey: process.env.STRIPE_SECRET_KEY!,
|
|
44
|
+
webhookSecret: process.env.STRIPE_WEBHOOK_SECRET!,
|
|
45
|
+
autoReply: true,
|
|
46
|
+
evidenceCollection: true,
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
console.log('ChargebackGuard is running ✓');
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
### 3. Register a Payment
|
|
53
|
+
|
|
54
|
+
Call `registerPayment()` immediately after every successful charge. This pre-collects evidence so it is ready if a dispute arrives later.
|
|
55
|
+
|
|
56
|
+
```typescript
|
|
57
|
+
const result = await guard.registerPayment({
|
|
58
|
+
orderId: 'order_abc123',
|
|
59
|
+
customerId: 'cust_xyz',
|
|
60
|
+
amount: 9999, // cents
|
|
61
|
+
currency: 'USD',
|
|
62
|
+
provider: 'stripe',
|
|
63
|
+
transactionId: 'ch_3Pabcdef...',
|
|
64
|
+
timestamp: new Date(),
|
|
65
|
+
metadata: {
|
|
66
|
+
email: 'customer@example.com',
|
|
67
|
+
ipAddress: '203.0.113.42',
|
|
68
|
+
deviceId: 'fp_abc123',
|
|
69
|
+
shippingAddress: '123 Main St, Austin TX 78701',
|
|
70
|
+
},
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
console.log('Risk level:', result.riskLevel); // 'very_low' | 'low' | 'medium' | 'high' | 'critical'
|
|
74
|
+
console.log('Trust score:', result.trustScore); // 0–100
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
### 4. Handle Disputes via Webhook
|
|
78
|
+
|
|
79
|
+
```typescript
|
|
80
|
+
import express from 'express';
|
|
81
|
+
|
|
82
|
+
const app = express();
|
|
83
|
+
|
|
84
|
+
// Raw body needed for Stripe signature verification
|
|
85
|
+
app.post('/webhooks/stripe', express.raw({ type: 'application/json' }), async (req, res) => {
|
|
86
|
+
const sig = req.headers['stripe-signature'] as string;
|
|
87
|
+
|
|
88
|
+
// Verify + parse the Stripe event yourself, then call handleDispute()
|
|
89
|
+
const dispute = {
|
|
90
|
+
id: event.data.object.id,
|
|
91
|
+
orderId: event.data.object.metadata.orderId,
|
|
92
|
+
amount: event.data.object.amount,
|
|
93
|
+
currency: event.data.object.currency,
|
|
94
|
+
reason: event.data.object.reason,
|
|
95
|
+
status: event.data.object.status,
|
|
96
|
+
provider: 'stripe' as const,
|
|
97
|
+
dueBy: new Date(event.data.object.evidence_details.due_by * 1000),
|
|
98
|
+
createdAt: new Date(),
|
|
99
|
+
};
|
|
100
|
+
|
|
101
|
+
const outcome = await guard.handleDispute(dispute);
|
|
102
|
+
console.log('Action taken:', outcome.actionTaken);
|
|
103
|
+
// 'auto-reply_submitted' | 'manual_review_required' | 'accepted' | 'skipped'
|
|
104
|
+
|
|
105
|
+
res.json({ received: true });
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
app.listen(3000);
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
### 5. Graceful Shutdown
|
|
112
|
+
|
|
113
|
+
```typescript
|
|
114
|
+
process.on('SIGTERM', async () => {
|
|
115
|
+
await guard.shutdown();
|
|
116
|
+
process.exit(0);
|
|
117
|
+
});
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
---
|
|
121
|
+
|
|
122
|
+
## Core Concepts
|
|
123
|
+
|
|
124
|
+
### Payment Registration
|
|
125
|
+
Every successful transaction should be registered. chargeback-guard:
|
|
126
|
+
- Collects device fingerprints, geolocation, and behavioral signals
|
|
127
|
+
- Runs an AI fraud pre-check and assigns a trust score (0–100)
|
|
128
|
+
- Stores all evidence in the configured database, indexed by `orderId`
|
|
129
|
+
|
|
130
|
+
### Dispute Detection
|
|
131
|
+
When a chargeback is filed, `handleDispute()`:
|
|
132
|
+
1. Fetches all pre-collected evidence for the associated `orderId`
|
|
133
|
+
2. Analyzes the dispute reason and selects a response strategy
|
|
134
|
+
3. Optionally auto-submits a reply to the payment provider
|
|
135
|
+
|
|
136
|
+
### Auto-Reply
|
|
137
|
+
When `autoReply: true`, chargeback-guard submits evidence to the bank on your behalf within minutes of dispute detection — maximizing win rates by responding before the deadline.
|
|
138
|
+
|
|
139
|
+
### Risk Levels
|
|
140
|
+
|
|
141
|
+
| Level | Trust Score | Meaning |
|
|
142
|
+
|---|---|---|
|
|
143
|
+
| `very_low` | 80–100 | Fully trusted — registered customer, normal pattern |
|
|
144
|
+
| `low` | 60–79 | Minor signals — investigate if disputed |
|
|
145
|
+
| `medium` | 40–59 | Moderate risk — watch for chargebacks |
|
|
146
|
+
| `high` | 20–39 | High fraud probability — consider hold |
|
|
147
|
+
| `critical` | 0–19 | Very high risk — block or manual review |
|
|
148
|
+
|
|
149
|
+
---
|
|
150
|
+
|
|
151
|
+
## Next Steps
|
|
152
|
+
|
|
153
|
+
- [API Reference](./api.md)
|
|
154
|
+
- [Configuration](./configuration.md)
|
|
155
|
+
- [Architecture](./architecture.md)
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
// ============================================================
|
|
2
|
+
// CHARGEBACK GUARD — Advanced Configuration Example
|
|
3
|
+
// ============================================================
|
|
4
|
+
|
|
5
|
+
import {
|
|
6
|
+
ChargebackGuard,
|
|
7
|
+
Environment,
|
|
8
|
+
NotificationEvent,
|
|
9
|
+
NotificationChannel,
|
|
10
|
+
SubscriptionPlan,
|
|
11
|
+
} from '../src';
|
|
12
|
+
|
|
13
|
+
async function advancedSetup(): Promise<void> {
|
|
14
|
+
// ── Full configuration ────────────────────────────────────
|
|
15
|
+
const guard = new ChargebackGuard({
|
|
16
|
+
environment: Environment.PRODUCTION,
|
|
17
|
+
autoReply: true,
|
|
18
|
+
evidenceCollection: true,
|
|
19
|
+
|
|
20
|
+
database: {
|
|
21
|
+
type: 'postgresql',
|
|
22
|
+
url: process.env.DATABASE_URL ?? 'postgresql://localhost/chargebackguard',
|
|
23
|
+
poolMin: 2,
|
|
24
|
+
poolMax: 20,
|
|
25
|
+
},
|
|
26
|
+
|
|
27
|
+
redis: {
|
|
28
|
+
url: process.env.REDIS_URL ?? 'redis://localhost:6379',
|
|
29
|
+
ttl: 7200,
|
|
30
|
+
},
|
|
31
|
+
|
|
32
|
+
security: {
|
|
33
|
+
jwtSecret: process.env.JWT_SECRET ?? 'change-me',
|
|
34
|
+
encryptionKey: process.env.ENCRYPTION_KEY ?? 'change-this-32-char-key-produc!!',
|
|
35
|
+
bcryptRounds: 14,
|
|
36
|
+
rateLimit: {
|
|
37
|
+
windowMs: 900000,
|
|
38
|
+
maxRequests: 200,
|
|
39
|
+
},
|
|
40
|
+
},
|
|
41
|
+
|
|
42
|
+
notifications: {
|
|
43
|
+
channels: [
|
|
44
|
+
NotificationChannel.EMAIL,
|
|
45
|
+
NotificationChannel.SMS,
|
|
46
|
+
NotificationChannel.WEBHOOK,
|
|
47
|
+
NotificationChannel.IN_APP,
|
|
48
|
+
],
|
|
49
|
+
email: {
|
|
50
|
+
host: 'smtp.sendgrid.net',
|
|
51
|
+
port: 587,
|
|
52
|
+
secure: false,
|
|
53
|
+
user: 'apikey',
|
|
54
|
+
pass: process.env.SENDGRID_API_KEY ?? '',
|
|
55
|
+
fromName: 'Chargeback Guard',
|
|
56
|
+
fromAddress: 'alerts@chargebackguard.io',
|
|
57
|
+
},
|
|
58
|
+
sms: {
|
|
59
|
+
accountSid: process.env.TWILIO_SID ?? '',
|
|
60
|
+
authToken: process.env.TWILIO_TOKEN ?? '',
|
|
61
|
+
phoneNumber: process.env.TWILIO_PHONE ?? '',
|
|
62
|
+
},
|
|
63
|
+
webhooks: [
|
|
64
|
+
{
|
|
65
|
+
url: 'https://your-app.com/webhooks/chargebackguard',
|
|
66
|
+
secret: process.env.WEBHOOK_SECRET,
|
|
67
|
+
events: [
|
|
68
|
+
NotificationEvent.DISPUTE_DETECTED,
|
|
69
|
+
NotificationEvent.DISPUTE_WON,
|
|
70
|
+
NotificationEvent.DISPUTE_LOST,
|
|
71
|
+
NotificationEvent.FRAUD_DETECTED,
|
|
72
|
+
],
|
|
73
|
+
retries: 3,
|
|
74
|
+
},
|
|
75
|
+
],
|
|
76
|
+
},
|
|
77
|
+
|
|
78
|
+
ai: {
|
|
79
|
+
fraudThreshold: 0.70,
|
|
80
|
+
riskScoreHigh: 0.85,
|
|
81
|
+
riskScoreMedium: 0.50,
|
|
82
|
+
},
|
|
83
|
+
|
|
84
|
+
billing: {
|
|
85
|
+
plan: SubscriptionPlan.ENTERPRISE,
|
|
86
|
+
revenueSharePercentage: 0.8,
|
|
87
|
+
monthlyPrice: 499,
|
|
88
|
+
},
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
// ── Custom event handlers ─────────────────────────────────
|
|
92
|
+
|
|
93
|
+
guard.events.onAsync(NotificationEvent.DISPUTE_DETECTED, async ({ data }) => {
|
|
94
|
+
// Your custom logic: update CRM, send Slack notification, etc.
|
|
95
|
+
console.log(`[CUSTOM] Dispute received: ${data['disputeId']}`);
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
guard.events.onAsync(NotificationEvent.DISPUTE_WON, async ({ data }) => {
|
|
99
|
+
const amount = (Number(data['amount']) || 0) / 100;
|
|
100
|
+
console.log(`[CUSTOM] $${amount.toFixed(2)} recovered for dispute ${data['disputeId']}`);
|
|
101
|
+
// e.g., update accounting system, notify customer success team
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
guard.events.onAsync(NotificationEvent.FRAUD_DETECTED, async ({ data }) => {
|
|
105
|
+
console.log(`[CUSTOM] FRAUD on order ${data['orderId']} — probability: ${data['probability']}`);
|
|
106
|
+
// e.g., block the customer account, flag for review
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
// ── Initialize ────────────────────────────────────────────
|
|
110
|
+
await guard.initialize();
|
|
111
|
+
|
|
112
|
+
// ── Check health ──────────────────────────────────────────
|
|
113
|
+
const health = await guard.getHealth();
|
|
114
|
+
console.log('Health:', JSON.stringify(health.data, null, 2));
|
|
115
|
+
|
|
116
|
+
// ── Lifetime stats ────────────────────────────────────────
|
|
117
|
+
const stats = await guard.getProtectionStats();
|
|
118
|
+
console.log('\nStats:', JSON.stringify(stats, null, 2));
|
|
119
|
+
|
|
120
|
+
await guard.shutdown();
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
advancedSetup().catch(console.error);
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
// ============================================================
|
|
2
|
+
// CHARGEBACK GUARD — Basic Usage Example
|
|
3
|
+
// ============================================================
|
|
4
|
+
|
|
5
|
+
import { createChargebackGuard, NotificationEvent, PaymentProvider } from '../src';
|
|
6
|
+
|
|
7
|
+
async function main(): Promise<void> {
|
|
8
|
+
// ── 1. Initialize ─────────────────────────────────────────
|
|
9
|
+
console.log('🚀 Initializing ChargebackGuard...');
|
|
10
|
+
|
|
11
|
+
const guard = await createChargebackGuard({
|
|
12
|
+
autoReply: true,
|
|
13
|
+
evidenceCollection: true,
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
// ── 2. Listen for events ──────────────────────────────────
|
|
17
|
+
guard.events.on(NotificationEvent.PAYMENT_REGISTERED, ({ data }) => {
|
|
18
|
+
console.log(`✅ Payment protected: ${data['orderId']} | Trust: ${data['trustScore']}`);
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
guard.events.on(NotificationEvent.DISPUTE_DETECTED, ({ data }) => {
|
|
22
|
+
console.log(`⚠️ Dispute detected: ${data['disputeId']} — auto-reply in progress`);
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
guard.events.on(NotificationEvent.DISPUTE_REPLIED, ({ data }) => {
|
|
26
|
+
console.log(`📤 Reply submitted for: ${data['disputeId']}`);
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
guard.events.on(NotificationEvent.DISPUTE_WON, ({ data }) => {
|
|
30
|
+
console.log(`🏆 WON: ${data['disputeId']} | $${((Number(data['amount']) || 0) / 100).toFixed(2)} recovered`);
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
// ── 3. Register a payment ──────────────────────────────────
|
|
34
|
+
const registration = await guard.registerPayment({
|
|
35
|
+
orderId: 'ORD-2025-12345',
|
|
36
|
+
amount: 29999, // cents = $299.99
|
|
37
|
+
currency: 'USD',
|
|
38
|
+
customerEmail: 'alice@example.com',
|
|
39
|
+
customerName: 'Alice Johnson',
|
|
40
|
+
customerIp: '203.0.113.42',
|
|
41
|
+
transactionId: 'ch_3OqExample123',
|
|
42
|
+
provider: PaymentProvider.STRIPE,
|
|
43
|
+
shippingAddress: {
|
|
44
|
+
line1: '123 Main Street',
|
|
45
|
+
city: 'Cairo',
|
|
46
|
+
postalCode: '11511',
|
|
47
|
+
country: 'EG',
|
|
48
|
+
},
|
|
49
|
+
sessionData: {
|
|
50
|
+
sessionId: 'sess_abc123',
|
|
51
|
+
duration: 340,
|
|
52
|
+
pageViews: [
|
|
53
|
+
{ url: '/products/item-123', duration: 120, timestamp: new Date().toISOString() },
|
|
54
|
+
{ url: '/cart', duration: 45, timestamp: new Date().toISOString() },
|
|
55
|
+
{ url: '/checkout', duration: 90, timestamp: new Date().toISOString() },
|
|
56
|
+
],
|
|
57
|
+
timeOnCheckout: 90,
|
|
58
|
+
},
|
|
59
|
+
deviceFingerprint: {
|
|
60
|
+
screenResolution: '1920x1080',
|
|
61
|
+
timezone: 'UTC+2',
|
|
62
|
+
language: 'en-US',
|
|
63
|
+
platform: 'Win32',
|
|
64
|
+
},
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
console.log('\n📦 Registration Result:');
|
|
68
|
+
console.log(JSON.stringify(registration, null, 2));
|
|
69
|
+
|
|
70
|
+
// ── 4. Simulate a dispute ──────────────────────────────────
|
|
71
|
+
console.log('\n🚨 Simulating incoming dispute...');
|
|
72
|
+
|
|
73
|
+
const disputeResult = await guard.handleDispute({
|
|
74
|
+
id: 'dp_2OqTestDispute001',
|
|
75
|
+
orderId: 'ORD-2025-12345',
|
|
76
|
+
amount: 29999,
|
|
77
|
+
currency: 'usd',
|
|
78
|
+
reason: 'product_not_received' as never,
|
|
79
|
+
status: 'needs_response' as never,
|
|
80
|
+
provider: PaymentProvider.STRIPE,
|
|
81
|
+
evidenceDueBy: new Date(Date.now() + 7 * 86400000).toISOString(), // 7 days
|
|
82
|
+
createdAt: new Date().toISOString(),
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
console.log('\n📋 Dispute Handling Result:');
|
|
86
|
+
console.log(JSON.stringify(disputeResult, null, 2));
|
|
87
|
+
|
|
88
|
+
// ── 5. Get stats ───────────────────────────────────────────
|
|
89
|
+
const stats = await guard.getProtectionStats();
|
|
90
|
+
console.log('\n📊 Protection Stats:');
|
|
91
|
+
console.log(JSON.stringify(stats, null, 2));
|
|
92
|
+
|
|
93
|
+
// ── 6. Shutdown ────────────────────────────────────────────
|
|
94
|
+
await guard.shutdown();
|
|
95
|
+
console.log('\n✅ ChargebackGuard shut down gracefully');
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
main().catch(console.error);
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
// ============================================================
|
|
2
|
+
// CHARGEBACK GUARD — Stripe Integration Example (Express App)
|
|
3
|
+
// ============================================================
|
|
4
|
+
|
|
5
|
+
import express from 'express';
|
|
6
|
+
import { createChargebackGuard, PaymentProvider, DisputeStatus } from '../src';
|
|
7
|
+
import { WebhookHandler } from '../src/integrations/webhook';
|
|
8
|
+
import { createApp } from '../src/api/routes';
|
|
9
|
+
|
|
10
|
+
async function startServer(): Promise<void> {
|
|
11
|
+
// ── 1. Initialize guard ───────────────────────────────────
|
|
12
|
+
const guard = await createChargebackGuard({
|
|
13
|
+
autoReply: true,
|
|
14
|
+
evidenceCollection: true,
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
// ── 2. Setup webhook handler ──────────────────────────────
|
|
18
|
+
const webhookHandler = new WebhookHandler();
|
|
19
|
+
|
|
20
|
+
// When a dispute is detected via Stripe webhook
|
|
21
|
+
webhookHandler.onDisputeDetected(async (dispute) => {
|
|
22
|
+
console.log(`🚨 Handling dispute: ${dispute.id}`);
|
|
23
|
+
await guard.handleDispute(dispute);
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
// When a dispute is resolved (won/lost)
|
|
27
|
+
webhookHandler.onDisputeResolved(async (disputeId, status, provider) => {
|
|
28
|
+
if (status === DisputeStatus.WON) {
|
|
29
|
+
console.log(`🏆 Dispute WON: ${disputeId}`);
|
|
30
|
+
} else if (status === DisputeStatus.LOST) {
|
|
31
|
+
console.log(`❌ Dispute LOST: ${disputeId}`);
|
|
32
|
+
}
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
// ── 3. Create Express app ─────────────────────────────────
|
|
36
|
+
const app = createApp(webhookHandler);
|
|
37
|
+
|
|
38
|
+
// Share guard instance with controllers
|
|
39
|
+
app.locals['chargebackGuard'] = guard;
|
|
40
|
+
|
|
41
|
+
// ── 4. Custom route: Register payment after Stripe charge ──
|
|
42
|
+
app.post('/charge-and-protect', express.json(), async (req, res) => {
|
|
43
|
+
try {
|
|
44
|
+
const {
|
|
45
|
+
orderId, amount, currency,
|
|
46
|
+
customerEmail, customerIp,
|
|
47
|
+
stripeChargeId,
|
|
48
|
+
} = req.body as {
|
|
49
|
+
orderId: string;
|
|
50
|
+
amount: number;
|
|
51
|
+
currency: string;
|
|
52
|
+
customerEmail: string;
|
|
53
|
+
customerIp?: string;
|
|
54
|
+
stripeChargeId: string;
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
// Register for protection
|
|
58
|
+
const result = await guard.registerPayment({
|
|
59
|
+
orderId,
|
|
60
|
+
amount,
|
|
61
|
+
currency,
|
|
62
|
+
customerEmail,
|
|
63
|
+
customerIp: customerIp ?? req.ip,
|
|
64
|
+
userAgent: req.headers['user-agent'],
|
|
65
|
+
transactionId: stripeChargeId,
|
|
66
|
+
provider: PaymentProvider.STRIPE,
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
res.json({
|
|
70
|
+
success: true,
|
|
71
|
+
message: 'Payment registered and protected',
|
|
72
|
+
...result,
|
|
73
|
+
});
|
|
74
|
+
} catch (err) {
|
|
75
|
+
res.status(500).json({
|
|
76
|
+
success: false,
|
|
77
|
+
error: err instanceof Error ? err.message : 'Unknown error',
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
// ── 5. Start server ───────────────────────────────────────
|
|
83
|
+
const PORT = process.env.PORT ?? 3000;
|
|
84
|
+
const server = app.listen(PORT, () => {
|
|
85
|
+
console.log(`🛡️ ChargebackGuard API running at http://localhost:${PORT}`);
|
|
86
|
+
console.log(` POST /api/v1/payments — Register payment`);
|
|
87
|
+
console.log(` GET /api/v1/disputes — List disputes`);
|
|
88
|
+
console.log(` POST /api/v1/webhooks/stripe — Stripe webhook`);
|
|
89
|
+
console.log(` GET /api/v1/analytics/stats — Get stats`);
|
|
90
|
+
console.log(` GET /api/v1/health — Health check`);
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
// ── 6. Graceful shutdown ──────────────────────────────────
|
|
94
|
+
const shutdown = async (): Promise<void> => {
|
|
95
|
+
console.log('\n🛑 Shutting down...');
|
|
96
|
+
server.close(async () => {
|
|
97
|
+
await guard.shutdown();
|
|
98
|
+
process.exit(0);
|
|
99
|
+
});
|
|
100
|
+
};
|
|
101
|
+
|
|
102
|
+
process.on('SIGTERM', shutdown);
|
|
103
|
+
process.on('SIGINT', shutdown);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
startServer().catch(console.error);
|
package/package.json
ADDED
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "chargeback-guard",
|
|
3
|
+
"version": "2.0.0",
|
|
4
|
+
"description": "Ultra Pro chargeback protection system for e-commerce merchants. Automatically collects evidence, detects disputes, and submits AI-powered replies to banks via Stripe/PayPal webhooks.",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"types": "dist/index.d.ts",
|
|
7
|
+
"files": [
|
|
8
|
+
"dist",
|
|
9
|
+
"src",
|
|
10
|
+
"docs",
|
|
11
|
+
"examples",
|
|
12
|
+
"README.md",
|
|
13
|
+
"LICENSE"
|
|
14
|
+
],
|
|
15
|
+
"scripts": {
|
|
16
|
+
"build": "tsc",
|
|
17
|
+
"build:watch": "tsc --watch",
|
|
18
|
+
"dev": "ts-node src/index.ts",
|
|
19
|
+
"dev:watch": "nodemon --exec ts-node src/index.ts",
|
|
20
|
+
"test": "jest --coverage",
|
|
21
|
+
"test:unit": "jest tests/unit --coverage",
|
|
22
|
+
"test:integration": "jest tests/integration",
|
|
23
|
+
"test:e2e": "jest tests/e2e",
|
|
24
|
+
"test:watch": "jest --watch",
|
|
25
|
+
"lint": "eslint src/**/*.ts",
|
|
26
|
+
"lint:fix": "eslint src/**/*.ts --fix",
|
|
27
|
+
"format": "prettier --write 'src/**/*.ts'",
|
|
28
|
+
"clean": "rimraf dist",
|
|
29
|
+
"prepublish": "npm run clean && npm run build",
|
|
30
|
+
"docs:generate": "typedoc --out docs/api src/index.ts",
|
|
31
|
+
"migrate": "ts-node src/database/migrate.ts",
|
|
32
|
+
"seed": "ts-node src/database/seed.ts",
|
|
33
|
+
"start": "node dist/index.js",
|
|
34
|
+
"start:pm2": "pm2 start ecosystem.config.js"
|
|
35
|
+
},
|
|
36
|
+
"keywords": [
|
|
37
|
+
"chargeback",
|
|
38
|
+
"fraud-detection",
|
|
39
|
+
"stripe",
|
|
40
|
+
"paypal",
|
|
41
|
+
"payment-protection",
|
|
42
|
+
"dispute-management",
|
|
43
|
+
"e-commerce",
|
|
44
|
+
"machine-learning",
|
|
45
|
+
"fintech",
|
|
46
|
+
"evidence-collection"
|
|
47
|
+
],
|
|
48
|
+
"author": {
|
|
49
|
+
"name": "ChargebackGuard Team",
|
|
50
|
+
"email": "support@chargebackguard.io",
|
|
51
|
+
"url": "https://chargebackguard.io"
|
|
52
|
+
},
|
|
53
|
+
"license": "MIT",
|
|
54
|
+
"engines": {
|
|
55
|
+
"node": ">=18.0.0",
|
|
56
|
+
"npm": ">=9.0.0"
|
|
57
|
+
},
|
|
58
|
+
"dependencies": {
|
|
59
|
+
"stripe": "^14.0.0",
|
|
60
|
+
"@paypal/paypal-server-sdk": "^0.6.0",
|
|
61
|
+
"express": "^4.18.2",
|
|
62
|
+
"express-rate-limit": "^7.1.5",
|
|
63
|
+
"express-validator": "^7.0.1",
|
|
64
|
+
"cors": "^2.8.5",
|
|
65
|
+
"helmet": "^7.1.0",
|
|
66
|
+
"compression": "^1.7.4",
|
|
67
|
+
"morgan": "^1.10.0",
|
|
68
|
+
"dotenv": "^16.3.1",
|
|
69
|
+
"jsonwebtoken": "^9.0.2",
|
|
70
|
+
"bcryptjs": "^2.4.3",
|
|
71
|
+
"axios": "^1.6.2",
|
|
72
|
+
"uuid": "^9.0.1",
|
|
73
|
+
"date-fns": "^3.0.0",
|
|
74
|
+
"lodash": "^4.17.21",
|
|
75
|
+
"winston": "^3.11.0",
|
|
76
|
+
"winston-daily-rotate-file": "^4.7.1",
|
|
77
|
+
"ioredis": "^5.3.2",
|
|
78
|
+
"knex": "^3.1.0",
|
|
79
|
+
"pg": "^8.11.3",
|
|
80
|
+
"better-sqlite3": "^9.4.3",
|
|
81
|
+
"nodemailer": "^6.9.7",
|
|
82
|
+
"twilio": "^4.19.3",
|
|
83
|
+
"handlebars": "^4.7.8",
|
|
84
|
+
"node-cron": "^3.0.3",
|
|
85
|
+
"bull": "^4.12.0",
|
|
86
|
+
"socket.io": "^4.6.2",
|
|
87
|
+
"multer": "^1.4.5-lts.1",
|
|
88
|
+
"sharp": "^0.33.1",
|
|
89
|
+
"pdf-lib": "^1.17.1",
|
|
90
|
+
"archiver": "^6.0.1",
|
|
91
|
+
"zod": "^3.22.4",
|
|
92
|
+
"pino": "^8.17.2",
|
|
93
|
+
"pino-http": "^9.0.0",
|
|
94
|
+
"ms": "^2.1.3",
|
|
95
|
+
"p-retry": "^6.2.0",
|
|
96
|
+
"p-limit": "^5.0.0",
|
|
97
|
+
"node-cache": "^5.1.2",
|
|
98
|
+
"crypto-js": "^4.2.0",
|
|
99
|
+
"ip": "^2.0.1",
|
|
100
|
+
"geoip-lite": "^1.4.10",
|
|
101
|
+
"ua-parser-js": "^1.0.37"
|
|
102
|
+
},
|
|
103
|
+
"devDependencies": {
|
|
104
|
+
"@types/node": "^20.10.6",
|
|
105
|
+
"@types/express": "^4.17.21",
|
|
106
|
+
"@types/cors": "^2.8.17",
|
|
107
|
+
"@types/compression": "^1.7.5",
|
|
108
|
+
"@types/morgan": "^1.9.9",
|
|
109
|
+
"@types/jsonwebtoken": "^9.0.5",
|
|
110
|
+
"@types/bcryptjs": "^2.4.6",
|
|
111
|
+
"@types/lodash": "^4.14.202",
|
|
112
|
+
"@types/nodemailer": "^6.4.14",
|
|
113
|
+
"@types/multer": "^1.4.11",
|
|
114
|
+
"@types/archiver": "^6.0.2",
|
|
115
|
+
"@types/uuid": "^9.0.7",
|
|
116
|
+
"@types/better-sqlite3": "^7.6.8",
|
|
117
|
+
"@types/pg": "^8.10.9",
|
|
118
|
+
"@types/ms": "^0.7.34",
|
|
119
|
+
"@types/geoip-lite": "^1.4.4",
|
|
120
|
+
"@types/ua-parser-js": "^0.7.39",
|
|
121
|
+
"@types/jest": "^29.5.11",
|
|
122
|
+
"@types/supertest": "^6.0.2",
|
|
123
|
+
"typescript": "^5.3.3",
|
|
124
|
+
"ts-node": "^10.9.2",
|
|
125
|
+
"nodemon": "^3.0.2",
|
|
126
|
+
"jest": "^29.7.0",
|
|
127
|
+
"ts-jest": "^29.1.1",
|
|
128
|
+
"supertest": "^6.3.4",
|
|
129
|
+
"eslint": "^8.56.0",
|
|
130
|
+
"@typescript-eslint/eslint-plugin": "^6.17.0",
|
|
131
|
+
"@typescript-eslint/parser": "^6.17.0",
|
|
132
|
+
"prettier": "^3.1.1",
|
|
133
|
+
"rimraf": "^5.0.5",
|
|
134
|
+
"typedoc": "^0.25.6",
|
|
135
|
+
"husky": "^8.0.3",
|
|
136
|
+
"lint-staged": "^15.2.0"
|
|
137
|
+
},
|
|
138
|
+
"jest": {
|
|
139
|
+
"preset": "ts-jest",
|
|
140
|
+
"testEnvironment": "node",
|
|
141
|
+
"coverageDirectory": "coverage",
|
|
142
|
+
"collectCoverageFrom": [
|
|
143
|
+
"src/**/*.ts",
|
|
144
|
+
"!src/**/*.d.ts",
|
|
145
|
+
"!src/**/index.ts"
|
|
146
|
+
],
|
|
147
|
+
"coverageThreshold": {
|
|
148
|
+
"global": {
|
|
149
|
+
"branches": 75,
|
|
150
|
+
"functions": 80,
|
|
151
|
+
"lines": 80,
|
|
152
|
+
"statements": 80
|
|
153
|
+
}
|
|
154
|
+
},
|
|
155
|
+
"testMatch": [
|
|
156
|
+
"<rootDir>/tests/**/*.test.ts"
|
|
157
|
+
],
|
|
158
|
+
"setupFilesAfterEnv": [
|
|
159
|
+
"<rootDir>/tests/setup.ts"
|
|
160
|
+
]
|
|
161
|
+
},
|
|
162
|
+
"husky": {
|
|
163
|
+
"hooks": {
|
|
164
|
+
"pre-commit": "lint-staged"
|
|
165
|
+
}
|
|
166
|
+
},
|
|
167
|
+
"lint-staged": {
|
|
168
|
+
"src/**/*.ts": [
|
|
169
|
+
"eslint --fix",
|
|
170
|
+
"prettier --write"
|
|
171
|
+
]
|
|
172
|
+
},
|
|
173
|
+
"repository": {
|
|
174
|
+
"type": "git",
|
|
175
|
+
"url": "https://github.com/brah-timo/chargeback-guard.git"
|
|
176
|
+
},
|
|
177
|
+
"bugs": {
|
|
178
|
+
"url": "https://github.com/brah-timo/chargeback-guard/issues"
|
|
179
|
+
},
|
|
180
|
+
"homepage": "https://chargebackguard.io"
|
|
181
|
+
}
|