179 lines
5.9 KiB
JavaScript
179 lines
5.9 KiB
JavaScript
|
|
const express = require('express');
|
||
|
|
const { Configuration, PlaidApi, PlaidEnvironments } = require('plaid');
|
||
|
|
const cors = require('cors');
|
||
|
|
require('dotenv').config();
|
||
|
|
|
||
|
|
const app = express();
|
||
|
|
|
||
|
|
// Enable CORS for all routes
|
||
|
|
app.use(cors());
|
||
|
|
app.use(express.json());
|
||
|
|
|
||
|
|
// Configure Plaid client
|
||
|
|
const configuration = new Configuration({
|
||
|
|
basePath: PlaidEnvironments[process.env.PLAID_ENV || 'sandbox'],
|
||
|
|
baseOptions: {
|
||
|
|
headers: {
|
||
|
|
'PLAID-CLIENT-ID': process.env.PLAID_CLIENT_ID,
|
||
|
|
'PLAID-SECRET': process.env.PLAID_SECRET,
|
||
|
|
},
|
||
|
|
},
|
||
|
|
});
|
||
|
|
|
||
|
|
const plaidClient = new PlaidApi(configuration);
|
||
|
|
|
||
|
|
// Create a link token
|
||
|
|
app.post('/api/create_link_token', async (req, res) => {
|
||
|
|
try {
|
||
|
|
const tokenRequest = {
|
||
|
|
user: { client_user_id: req.body.userId || 'user-id' },
|
||
|
|
client_name: 'user_good',
|
||
|
|
products: ['auth', 'transactions'],
|
||
|
|
country_codes: ['US', 'CA'],
|
||
|
|
language: 'en'
|
||
|
|
};
|
||
|
|
|
||
|
|
console.log('Creating link token with request:', tokenRequest);
|
||
|
|
const createTokenResponse = await plaidClient.linkTokenCreate(tokenRequest);
|
||
|
|
console.log('Link token created:', createTokenResponse.data);
|
||
|
|
res.json(createTokenResponse.data);
|
||
|
|
} catch (error) {
|
||
|
|
console.error('Error creating link token:', error);
|
||
|
|
res.status(500).json({
|
||
|
|
error: error.message,
|
||
|
|
error_code: error.response?.data?.error_code,
|
||
|
|
error_type: error.response?.data?.error_type
|
||
|
|
});
|
||
|
|
}
|
||
|
|
});
|
||
|
|
|
||
|
|
// Exchange public token for access token
|
||
|
|
app.post('/api/exchange_public_token', async (req, res) => {
|
||
|
|
try {
|
||
|
|
if (!req.body.public_token) {
|
||
|
|
return res.status(400).json({
|
||
|
|
error: 'Missing public_token in request body'
|
||
|
|
});
|
||
|
|
}
|
||
|
|
|
||
|
|
console.log('Exchanging public token:', req.body.public_token);
|
||
|
|
const exchangeResponse = await plaidClient.itemPublicTokenExchange({
|
||
|
|
public_token: req.body.public_token
|
||
|
|
});
|
||
|
|
|
||
|
|
console.log('Token exchange successful:', exchangeResponse.data);
|
||
|
|
res.json({
|
||
|
|
access_token: exchangeResponse.data.access_token,
|
||
|
|
item_id: exchangeResponse.data.item_id,
|
||
|
|
});
|
||
|
|
} catch (error) {
|
||
|
|
console.error('Error exchanging public token:', error);
|
||
|
|
res.status(500).json({
|
||
|
|
error: error.message,
|
||
|
|
error_code: error.response?.data?.error_code,
|
||
|
|
error_type: error.response?.data?.error_type
|
||
|
|
});
|
||
|
|
}
|
||
|
|
});
|
||
|
|
|
||
|
|
// Get accounts
|
||
|
|
app.get('/api/accounts', async (req, res) => {
|
||
|
|
try {
|
||
|
|
const accessToken = req.headers['plaid-access-token'];
|
||
|
|
|
||
|
|
if (!accessToken) {
|
||
|
|
return res.status(400).json({
|
||
|
|
error: 'Missing access_token in request headers'
|
||
|
|
});
|
||
|
|
}
|
||
|
|
|
||
|
|
console.log('Getting accounts for access token:', accessToken);
|
||
|
|
const accountsResponse = await plaidClient.accountsGet({
|
||
|
|
access_token: accessToken
|
||
|
|
});
|
||
|
|
|
||
|
|
console.log('Got accounts:', accountsResponse.data);
|
||
|
|
res.json(accountsResponse.data);
|
||
|
|
} catch (error) {
|
||
|
|
console.error('Error getting accounts:', error);
|
||
|
|
res.status(500).json({
|
||
|
|
error: error.message,
|
||
|
|
error_code: error.response?.data?.error_code,
|
||
|
|
error_type: error.response?.data?.error_type
|
||
|
|
});
|
||
|
|
}
|
||
|
|
});
|
||
|
|
|
||
|
|
// Get transactions
|
||
|
|
app.get('/api/transactions', async (req, res) => {
|
||
|
|
try {
|
||
|
|
const accessToken = req.headers['plaid-access-token'];
|
||
|
|
|
||
|
|
if (!accessToken) {
|
||
|
|
return res.status(400).json({
|
||
|
|
error: 'Missing access_token in request headers'
|
||
|
|
});
|
||
|
|
}
|
||
|
|
|
||
|
|
// Get current date and date 30 days ago
|
||
|
|
const endDate = new Date().toISOString().slice(0, 10);
|
||
|
|
const startDate = new Date(Date.now() - 30 * 24 * 60 * 60 * 1000).toISOString().slice(0, 10);
|
||
|
|
|
||
|
|
console.log('Getting transactions for access token:', accessToken);
|
||
|
|
|
||
|
|
// Initial request
|
||
|
|
const request = {
|
||
|
|
access_token: accessToken,
|
||
|
|
start_date: startDate,
|
||
|
|
end_date: endDate,
|
||
|
|
options: {
|
||
|
|
include_personal_finance_category: true,
|
||
|
|
count: 100,
|
||
|
|
offset: 0
|
||
|
|
}
|
||
|
|
};
|
||
|
|
|
||
|
|
let allTransactions = [];
|
||
|
|
const response = await plaidClient.transactionsGet(request);
|
||
|
|
let transactions = response.data.transactions;
|
||
|
|
const total_transactions = response.data.total_transactions;
|
||
|
|
allTransactions = allTransactions.concat(transactions);
|
||
|
|
|
||
|
|
// Handle pagination if there are more transactions
|
||
|
|
while (allTransactions.length < total_transactions) {
|
||
|
|
const paginatedRequest = {
|
||
|
|
access_token: accessToken,
|
||
|
|
start_date: startDate,
|
||
|
|
end_date: endDate,
|
||
|
|
options: {
|
||
|
|
include_personal_finance_category: true,
|
||
|
|
count: 100,
|
||
|
|
offset: allTransactions.length
|
||
|
|
}
|
||
|
|
};
|
||
|
|
const paginatedResponse = await plaidClient.transactionsGet(paginatedRequest);
|
||
|
|
allTransactions = allTransactions.concat(paginatedResponse.data.transactions);
|
||
|
|
}
|
||
|
|
|
||
|
|
console.log(`Got ${allTransactions.length} transactions`);
|
||
|
|
res.json({
|
||
|
|
transactions: allTransactions,
|
||
|
|
accounts: response.data.accounts,
|
||
|
|
item: response.data.item,
|
||
|
|
total_transactions: total_transactions,
|
||
|
|
request_id: response.data.request_id
|
||
|
|
});
|
||
|
|
} catch (error) {
|
||
|
|
console.error('Error getting transactions:', error);
|
||
|
|
res.status(500).json({
|
||
|
|
error: error.message,
|
||
|
|
error_code: error.response?.data?.error_code,
|
||
|
|
error_type: error.response?.data?.error_type
|
||
|
|
});
|
||
|
|
}
|
||
|
|
});
|
||
|
|
|
||
|
|
const PORT = process.env.PORT || 3000;
|
||
|
|
app.listen(PORT, () => {
|
||
|
|
console.log(`Server running on port ${PORT}`);
|
||
|
|
});
|