@xenterprises/fastify-xplaid 1.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/.gitlab-ci.yml +45 -0
- package/README.md +393 -0
- package/package.json +36 -0
- package/src/xPlaid.js +764 -0
- package/test/xPlaid.test.js +563 -0
package/.gitlab-ci.yml
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
# ============================================================================
|
|
2
|
+
# GitLab CI/CD Pipeline - xPlaid
|
|
3
|
+
# ============================================================================
|
|
4
|
+
# Runs tests on merge requests and commits to main/master
|
|
5
|
+
|
|
6
|
+
stages:
|
|
7
|
+
- test
|
|
8
|
+
|
|
9
|
+
variables:
|
|
10
|
+
NODE_ENV: test
|
|
11
|
+
|
|
12
|
+
# ============================================================================
|
|
13
|
+
# Shared Configuration
|
|
14
|
+
# ============================================================================
|
|
15
|
+
.shared_rules: &shared_rules
|
|
16
|
+
rules:
|
|
17
|
+
- if: '$CI_PIPELINE_SOURCE == "merge_request_event"'
|
|
18
|
+
- if: '$CI_COMMIT_BRANCH == "main"'
|
|
19
|
+
- if: '$CI_COMMIT_BRANCH == "master"'
|
|
20
|
+
- if: '$CI_COMMIT_TAG'
|
|
21
|
+
|
|
22
|
+
# ============================================================================
|
|
23
|
+
# STAGE: TEST
|
|
24
|
+
# ============================================================================
|
|
25
|
+
test:
|
|
26
|
+
stage: test
|
|
27
|
+
image: node:20-alpine
|
|
28
|
+
<<: *shared_rules
|
|
29
|
+
|
|
30
|
+
cache:
|
|
31
|
+
key: ${CI_COMMIT_REF_SLUG}
|
|
32
|
+
paths:
|
|
33
|
+
- node_modules/
|
|
34
|
+
|
|
35
|
+
before_script:
|
|
36
|
+
- npm ci
|
|
37
|
+
|
|
38
|
+
script:
|
|
39
|
+
- echo "Running xPlaid tests..."
|
|
40
|
+
- npm test
|
|
41
|
+
- npm audit --audit-level=high || true
|
|
42
|
+
|
|
43
|
+
retry:
|
|
44
|
+
max: 2
|
|
45
|
+
when: runner_system_failure
|
package/README.md
ADDED
|
@@ -0,0 +1,393 @@
|
|
|
1
|
+
# xPlaid
|
|
2
|
+
|
|
3
|
+
Fastify plugin for Plaid financial data integration - bank account linking, transactions, identity verification, and more.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install xplaid plaid
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Usage
|
|
12
|
+
|
|
13
|
+
```javascript
|
|
14
|
+
import Fastify from "fastify";
|
|
15
|
+
import xPlaid from "xplaid";
|
|
16
|
+
|
|
17
|
+
const fastify = Fastify();
|
|
18
|
+
|
|
19
|
+
await fastify.register(xPlaid, {
|
|
20
|
+
clientId: process.env.PLAID_CLIENT_ID,
|
|
21
|
+
secret: process.env.PLAID_SECRET,
|
|
22
|
+
environment: "sandbox", // 'sandbox' | 'development' | 'production'
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
// The plugin is now available at fastify.xplaid
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
## Configuration Options
|
|
29
|
+
|
|
30
|
+
| Option | Type | Required | Default | Description |
|
|
31
|
+
|--------|------|----------|---------|-------------|
|
|
32
|
+
| `clientId` | string | Yes | - | Your Plaid client ID |
|
|
33
|
+
| `secret` | string | Yes | - | Your Plaid secret |
|
|
34
|
+
| `environment` | string | No | `'sandbox'` | Plaid environment: `'sandbox'`, `'development'`, or `'production'` |
|
|
35
|
+
| `active` | boolean | No | `true` | Enable/disable the plugin |
|
|
36
|
+
|
|
37
|
+
## Link Flow
|
|
38
|
+
|
|
39
|
+
The Plaid Link flow is the primary way users connect their bank accounts:
|
|
40
|
+
|
|
41
|
+
### 1. Create a Link Token
|
|
42
|
+
|
|
43
|
+
```javascript
|
|
44
|
+
// Create a link token for the frontend
|
|
45
|
+
const linkToken = await fastify.xplaid.link.createToken({
|
|
46
|
+
userId: "user_123",
|
|
47
|
+
clientName: "My App",
|
|
48
|
+
products: ["transactions", "auth"],
|
|
49
|
+
countryCodes: ["US"],
|
|
50
|
+
language: "en",
|
|
51
|
+
// Optional: webhook for real-time updates
|
|
52
|
+
webhook: "https://myapp.com/webhooks/plaid",
|
|
53
|
+
// Optional: redirect URI for OAuth flows
|
|
54
|
+
redirectUri: "https://myapp.com/oauth-callback",
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
// Return linkToken.link_token to your frontend
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
### 2. Exchange Public Token
|
|
61
|
+
|
|
62
|
+
After the user completes Plaid Link, exchange the public token for an access token:
|
|
63
|
+
|
|
64
|
+
```javascript
|
|
65
|
+
// Exchange the public token from Plaid Link
|
|
66
|
+
const result = await fastify.xplaid.link.exchangePublicToken("public-token-from-link");
|
|
67
|
+
|
|
68
|
+
// Store these securely
|
|
69
|
+
const accessToken = result.access_token;
|
|
70
|
+
const itemId = result.item_id;
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
### 3. Get Link Token Info
|
|
74
|
+
|
|
75
|
+
```javascript
|
|
76
|
+
const tokenInfo = await fastify.xplaid.link.getToken("link-token-id");
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
## Accounts
|
|
80
|
+
|
|
81
|
+
```javascript
|
|
82
|
+
// Get all accounts for an item
|
|
83
|
+
const accounts = await fastify.xplaid.accounts.get(accessToken);
|
|
84
|
+
|
|
85
|
+
// Get real-time balance
|
|
86
|
+
const balance = await fastify.xplaid.accounts.getBalance(accessToken, {
|
|
87
|
+
accountIds: ["account_id_1", "account_id_2"], // Optional: filter by accounts
|
|
88
|
+
});
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
## Transactions
|
|
92
|
+
|
|
93
|
+
### Sync Transactions (Recommended)
|
|
94
|
+
|
|
95
|
+
The sync endpoint is the recommended way to get transactions:
|
|
96
|
+
|
|
97
|
+
```javascript
|
|
98
|
+
// Initial sync - get all available transactions
|
|
99
|
+
let cursor = null;
|
|
100
|
+
let allTransactions = [];
|
|
101
|
+
|
|
102
|
+
let response = await fastify.xplaid.transactions.sync(accessToken, { cursor });
|
|
103
|
+
|
|
104
|
+
allTransactions.push(...response.added);
|
|
105
|
+
cursor = response.next_cursor;
|
|
106
|
+
|
|
107
|
+
// Store the cursor for future syncs
|
|
108
|
+
// On subsequent syncs, pass the cursor to get only new/updated transactions
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
### Get Transactions (Legacy)
|
|
112
|
+
|
|
113
|
+
```javascript
|
|
114
|
+
const transactions = await fastify.xplaid.transactions.get(accessToken, {
|
|
115
|
+
startDate: "2024-01-01",
|
|
116
|
+
endDate: "2024-12-31",
|
|
117
|
+
count: 100,
|
|
118
|
+
offset: 0,
|
|
119
|
+
accountIds: ["account_id"], // Optional
|
|
120
|
+
});
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
### Refresh Transactions
|
|
124
|
+
|
|
125
|
+
Force a refresh of transaction data:
|
|
126
|
+
|
|
127
|
+
```javascript
|
|
128
|
+
await fastify.xplaid.transactions.refresh(accessToken);
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
## Identity
|
|
132
|
+
|
|
133
|
+
Get identity information (name, email, phone, address) from the bank:
|
|
134
|
+
|
|
135
|
+
```javascript
|
|
136
|
+
const identity = await fastify.xplaid.identity.get(accessToken, {
|
|
137
|
+
accountIds: ["account_id"], // Optional
|
|
138
|
+
});
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
## Auth
|
|
142
|
+
|
|
143
|
+
Get ACH routing and account numbers:
|
|
144
|
+
|
|
145
|
+
```javascript
|
|
146
|
+
const auth = await fastify.xplaid.auth.get(accessToken, {
|
|
147
|
+
accountIds: ["account_id"], // Optional
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
// Returns account numbers including:
|
|
151
|
+
// - ACH routing numbers
|
|
152
|
+
// - Account numbers
|
|
153
|
+
// - Wire routing numbers (if available)
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
## Investments
|
|
157
|
+
|
|
158
|
+
### Get Holdings
|
|
159
|
+
|
|
160
|
+
```javascript
|
|
161
|
+
const holdings = await fastify.xplaid.investments.getHoldings(accessToken, {
|
|
162
|
+
accountIds: ["account_id"], // Optional
|
|
163
|
+
});
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
### Get Investment Transactions
|
|
167
|
+
|
|
168
|
+
```javascript
|
|
169
|
+
const investmentTx = await fastify.xplaid.investments.getTransactions(accessToken, {
|
|
170
|
+
startDate: "2024-01-01",
|
|
171
|
+
endDate: "2024-12-31",
|
|
172
|
+
count: 100,
|
|
173
|
+
offset: 0,
|
|
174
|
+
accountIds: ["account_id"], // Optional
|
|
175
|
+
});
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
## Liabilities
|
|
179
|
+
|
|
180
|
+
Get loan and credit card liability information:
|
|
181
|
+
|
|
182
|
+
```javascript
|
|
183
|
+
const liabilities = await fastify.xplaid.liabilities.get(accessToken, {
|
|
184
|
+
accountIds: ["account_id"], // Optional
|
|
185
|
+
});
|
|
186
|
+
|
|
187
|
+
// Returns credit, mortgage, and student loan details
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
## Item Management
|
|
191
|
+
|
|
192
|
+
### Get Item Info
|
|
193
|
+
|
|
194
|
+
```javascript
|
|
195
|
+
const item = await fastify.xplaid.items.get(accessToken);
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
### Remove an Item
|
|
199
|
+
|
|
200
|
+
Permanently delete an item and revoke access:
|
|
201
|
+
|
|
202
|
+
```javascript
|
|
203
|
+
await fastify.xplaid.items.remove(accessToken);
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
### Get Institution
|
|
207
|
+
|
|
208
|
+
```javascript
|
|
209
|
+
const institution = await fastify.xplaid.items.getInstitution("ins_123", {
|
|
210
|
+
countryCodes: ["US"],
|
|
211
|
+
includeOptionalMetadata: true,
|
|
212
|
+
includeStatus: true,
|
|
213
|
+
});
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
### Search Institutions
|
|
217
|
+
|
|
218
|
+
```javascript
|
|
219
|
+
const institutions = await fastify.xplaid.items.searchInstitutions({
|
|
220
|
+
query: "Chase",
|
|
221
|
+
products: ["transactions"],
|
|
222
|
+
countryCodes: ["US"],
|
|
223
|
+
count: 10,
|
|
224
|
+
offset: 0,
|
|
225
|
+
});
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
## Webhooks
|
|
229
|
+
|
|
230
|
+
### Update Webhook URL
|
|
231
|
+
|
|
232
|
+
```javascript
|
|
233
|
+
await fastify.xplaid.webhooks.update(accessToken, "https://myapp.com/new-webhook-url");
|
|
234
|
+
```
|
|
235
|
+
|
|
236
|
+
### Get Verification Key
|
|
237
|
+
|
|
238
|
+
Verify webhook signatures:
|
|
239
|
+
|
|
240
|
+
```javascript
|
|
241
|
+
const key = await fastify.xplaid.webhooks.getVerificationKey("key_id_from_webhook_header");
|
|
242
|
+
```
|
|
243
|
+
|
|
244
|
+
## Constants
|
|
245
|
+
|
|
246
|
+
The plugin exposes useful constants:
|
|
247
|
+
|
|
248
|
+
```javascript
|
|
249
|
+
const { ENVIRONMENTS, PRODUCTS, COUNTRY_CODES } = fastify.xplaid.constants;
|
|
250
|
+
|
|
251
|
+
// Or import directly
|
|
252
|
+
import { ENVIRONMENTS, PRODUCTS, COUNTRY_CODES } from "xplaid";
|
|
253
|
+
|
|
254
|
+
// ENVIRONMENTS
|
|
255
|
+
ENVIRONMENTS.SANDBOX // 'sandbox'
|
|
256
|
+
ENVIRONMENTS.DEVELOPMENT // 'development'
|
|
257
|
+
ENVIRONMENTS.PRODUCTION // 'production'
|
|
258
|
+
|
|
259
|
+
// PRODUCTS
|
|
260
|
+
PRODUCTS.AUTH // 'auth'
|
|
261
|
+
PRODUCTS.TRANSACTIONS // 'transactions'
|
|
262
|
+
PRODUCTS.IDENTITY // 'identity'
|
|
263
|
+
PRODUCTS.INVESTMENTS // 'investments'
|
|
264
|
+
PRODUCTS.LIABILITIES // 'liabilities'
|
|
265
|
+
PRODUCTS.ASSETS // 'assets'
|
|
266
|
+
|
|
267
|
+
// COUNTRY_CODES
|
|
268
|
+
COUNTRY_CODES.US // 'US'
|
|
269
|
+
COUNTRY_CODES.CA // 'CA'
|
|
270
|
+
COUNTRY_CODES.GB // 'GB'
|
|
271
|
+
COUNTRY_CODES.IE // 'IE'
|
|
272
|
+
COUNTRY_CODES.FR // 'FR'
|
|
273
|
+
COUNTRY_CODES.ES // 'ES'
|
|
274
|
+
COUNTRY_CODES.NL // 'NL'
|
|
275
|
+
```
|
|
276
|
+
|
|
277
|
+
## Raw Client Access
|
|
278
|
+
|
|
279
|
+
For advanced use cases, access the underlying Plaid client directly:
|
|
280
|
+
|
|
281
|
+
```javascript
|
|
282
|
+
const plaidClient = fastify.xplaid.raw;
|
|
283
|
+
|
|
284
|
+
// Use any Plaid API method
|
|
285
|
+
const response = await plaidClient.sandboxItemFireWebhook({
|
|
286
|
+
access_token: accessToken,
|
|
287
|
+
webhook_code: "DEFAULT_UPDATE",
|
|
288
|
+
});
|
|
289
|
+
```
|
|
290
|
+
|
|
291
|
+
## Error Handling
|
|
292
|
+
|
|
293
|
+
Plaid errors include detailed error codes:
|
|
294
|
+
|
|
295
|
+
```javascript
|
|
296
|
+
try {
|
|
297
|
+
const accounts = await fastify.xplaid.accounts.get(accessToken);
|
|
298
|
+
} catch (error) {
|
|
299
|
+
if (error.response?.data) {
|
|
300
|
+
const plaidError = error.response.data;
|
|
301
|
+
console.error("Plaid error:", {
|
|
302
|
+
errorType: plaidError.error_type,
|
|
303
|
+
errorCode: plaidError.error_code,
|
|
304
|
+
errorMessage: plaidError.error_message,
|
|
305
|
+
displayMessage: plaidError.display_message,
|
|
306
|
+
});
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
```
|
|
310
|
+
|
|
311
|
+
Common error types:
|
|
312
|
+
- `INVALID_REQUEST` - Invalid parameters
|
|
313
|
+
- `INVALID_INPUT` - Invalid input data
|
|
314
|
+
- `INSTITUTION_ERROR` - Bank is unavailable
|
|
315
|
+
- `RATE_LIMIT_EXCEEDED` - Too many requests
|
|
316
|
+
- `API_ERROR` - Plaid internal error
|
|
317
|
+
- `ITEM_ERROR` - Item-specific error (e.g., login required)
|
|
318
|
+
|
|
319
|
+
## Configuration Access
|
|
320
|
+
|
|
321
|
+
```javascript
|
|
322
|
+
const config = fastify.xplaid.config;
|
|
323
|
+
// {
|
|
324
|
+
// clientId: '***id', // Masked for security
|
|
325
|
+
// environment: 'sandbox'
|
|
326
|
+
// }
|
|
327
|
+
```
|
|
328
|
+
|
|
329
|
+
## Complete Example
|
|
330
|
+
|
|
331
|
+
```javascript
|
|
332
|
+
import Fastify from "fastify";
|
|
333
|
+
import xPlaid from "xplaid";
|
|
334
|
+
|
|
335
|
+
const fastify = Fastify({ logger: true });
|
|
336
|
+
|
|
337
|
+
await fastify.register(xPlaid, {
|
|
338
|
+
clientId: process.env.PLAID_CLIENT_ID,
|
|
339
|
+
secret: process.env.PLAID_SECRET,
|
|
340
|
+
environment: process.env.PLAID_ENV || "sandbox",
|
|
341
|
+
});
|
|
342
|
+
|
|
343
|
+
// Create link token endpoint
|
|
344
|
+
fastify.post("/api/create-link-token", async (request, reply) => {
|
|
345
|
+
const { userId } = request.body;
|
|
346
|
+
|
|
347
|
+
const linkToken = await fastify.xplaid.link.createToken({
|
|
348
|
+
userId,
|
|
349
|
+
clientName: "My Finance App",
|
|
350
|
+
products: ["transactions"],
|
|
351
|
+
countryCodes: ["US"],
|
|
352
|
+
language: "en",
|
|
353
|
+
});
|
|
354
|
+
|
|
355
|
+
return { linkToken: linkToken.link_token };
|
|
356
|
+
});
|
|
357
|
+
|
|
358
|
+
// Exchange public token endpoint
|
|
359
|
+
fastify.post("/api/exchange-token", async (request, reply) => {
|
|
360
|
+
const { publicToken, userId } = request.body;
|
|
361
|
+
|
|
362
|
+
const result = await fastify.xplaid.link.exchangePublicToken(publicToken);
|
|
363
|
+
|
|
364
|
+
// Store accessToken and itemId in your database
|
|
365
|
+
// associated with the userId
|
|
366
|
+
|
|
367
|
+
return { success: true };
|
|
368
|
+
});
|
|
369
|
+
|
|
370
|
+
// Get transactions endpoint
|
|
371
|
+
fastify.get("/api/transactions", async (request, reply) => {
|
|
372
|
+
const { userId } = request.user; // From your auth middleware
|
|
373
|
+
|
|
374
|
+
// Retrieve accessToken from your database for this user
|
|
375
|
+
const accessToken = await getAccessTokenForUser(userId);
|
|
376
|
+
|
|
377
|
+
const transactions = await fastify.xplaid.transactions.sync(accessToken);
|
|
378
|
+
|
|
379
|
+
return { transactions: transactions.added };
|
|
380
|
+
});
|
|
381
|
+
|
|
382
|
+
await fastify.listen({ port: 3000 });
|
|
383
|
+
```
|
|
384
|
+
|
|
385
|
+
## Testing
|
|
386
|
+
|
|
387
|
+
```bash
|
|
388
|
+
npm test
|
|
389
|
+
```
|
|
390
|
+
|
|
391
|
+
## License
|
|
392
|
+
|
|
393
|
+
ISC
|
package/package.json
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@xenterprises/fastify-xplaid",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Fastify plugin for Plaid financial data integration - bank account linking, transactions, and identity verification",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "src/xPlaid.js",
|
|
7
|
+
"exports": {
|
|
8
|
+
".": "./src/xPlaid.js"
|
|
9
|
+
},
|
|
10
|
+
"scripts": {
|
|
11
|
+
"test": "node --test test/xPlaid.test.js"
|
|
12
|
+
},
|
|
13
|
+
"keywords": [
|
|
14
|
+
"fastify",
|
|
15
|
+
"fastify-plugin",
|
|
16
|
+
"plaid",
|
|
17
|
+
"banking",
|
|
18
|
+
"financial",
|
|
19
|
+
"transactions",
|
|
20
|
+
"identity",
|
|
21
|
+
"fintech",
|
|
22
|
+
"open-banking"
|
|
23
|
+
],
|
|
24
|
+
"author": "",
|
|
25
|
+
"license": "ISC",
|
|
26
|
+
"dependencies": {
|
|
27
|
+
"fastify-plugin": "^5.0.1",
|
|
28
|
+
"plaid": "^28.0.0"
|
|
29
|
+
},
|
|
30
|
+
"devDependencies": {
|
|
31
|
+
"fastify": "^5.0.0"
|
|
32
|
+
},
|
|
33
|
+
"engines": {
|
|
34
|
+
"node": ">=20.0.0"
|
|
35
|
+
}
|
|
36
|
+
}
|