Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Migrated plaid API to version 2020-09-14 #144

Open
wants to merge 6 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@
"jsonc": "^2.0.0",
"lodash": "4.17.15",
"open": "^7.0.2",
"plaid": "^7.0.0",
"plaid": "^12.0.0",
"prompts": "^2.3.1",
"typescript": "3.8.3",
"typescript-json-schema": "^0.42.0",
Expand Down
126 changes: 72 additions & 54 deletions src/integrations/plaid/plaidIntegration.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,16 @@
import path from 'path'
import { parseISO, format, subMonths } from 'date-fns'
import plaid, { TransactionsResponse, CreateLinkTokenOptions } from 'plaid'
import plaid, {
AccountBase,
Configuration,
CountryCode,
ItemPublicTokenExchangeResponse,
LinkTokenCreateRequest,
PlaidApi,
PlaidEnvironments,
Products,
TransactionsGetRequest
} from 'plaid'
import { Config, updateConfig } from '../../common/config'
import { PlaidConfig, PlaidEnvironmentType } from '../../types/integrations/plaid'
import { IntegrationId } from '../../types/integrations'
Expand All @@ -17,38 +27,38 @@ export class PlaidIntegration {
config: Config
plaidConfig: PlaidConfig
environment: string
client: plaid.Client
user: plaid.User
client: PlaidApi
user: any

constructor(config: Config) {
this.config = config
this.plaidConfig = this.config.integrations[IntegrationId.Plaid] as PlaidConfig

this.environment =
this.plaidConfig.environment === PlaidEnvironmentType.Development
? plaid.environments.development
: plaid.environments.sandbox

this.client = new plaid.Client({
clientID: this.plaidConfig.credentials.clientId,
secret: this.plaidConfig.credentials.secret,
env: this.environment,
options: {
version: '2019-05-29'
? PlaidEnvironments.development
: PlaidEnvironments.sandbox

const configuration = new Configuration({
basePath: this.environment,
baseOptions: {
headers: {
'PLAID-CLIENT-ID': this.plaidConfig.credentials.clientId,
'PLAID-SECRET': this.plaidConfig.credentials.secret,
'Plaid-Version': '2020-09-14',
}
}
})

this.client = new PlaidApi(configuration)

// In production this is supposed to be a unique identifier but for Mintable we only have one user (you)
this.user = {
client_user_id: PLAID_USER_ID
}
}

public exchangeAccessToken = (accessToken: string): Promise<string> =>
// Exchange an expired API access_token for a new Link public_token
this.client.createPublicToken(accessToken).then(token => token.public_token)

public savePublicToken = (tokenResponse: plaid.TokenResponse): void => {
public savePublicToken = (tokenResponse: ItemPublicTokenExchangeResponse): void => {
updateConfig(config => {
config.accounts[tokenResponse.item_id] = {
id: tokenResponse.item_id,
Expand All @@ -71,12 +81,11 @@ export class PlaidIntegration {
let server: http.Server

app.post('/get_access_token', (req, res) => {
console.log(req.body.public_token)
if (req.body.public_token !== undefined) {
client.exchangePublicToken(req.body.public_token, (error, tokenResponse) => {
if (error != null) {
reject(logError('Encountered error exchanging Plaid public token.', error))
}
this.savePublicToken(tokenResponse)
client.itemPublicTokenExchange({ public_token: req.body.public_token }).then(res => {
this.savePublicToken(res.data)
console.log(res.data)
resolve(logInfo('Plaid access token saved.', req.body))
})
} else if (req.body.exit !== undefined) {
Expand All @@ -98,9 +107,9 @@ export class PlaidIntegration {
const accountConfig: PlaidAccountConfig = this.config.accounts[accountId] as PlaidAccountConfig
if (accountConfig.integration === IntegrationId.Plaid) {
try {
await this.client.getAccounts(accountConfig.token).then(resp => {
await this.client.accountsGet({ access_token: accountConfig.token }).then(resp => {
accounts.push({
name: resp.accounts[0].name,
name: resp.data.accounts[0].name,
token: accountConfig.token
})
})
Expand All @@ -116,29 +125,21 @@ export class PlaidIntegration {
})

app.post('/create_link_token', async (req, res) => {
const clientUserId = this.user.client_user_id
const options: CreateLinkTokenOptions = {
const options: LinkTokenCreateRequest = {
user: {
client_user_id: clientUserId
client_user_id: this.user.client_user_id
},
client_name: 'Mintable',
products: ['transactions'],
country_codes: ['US'], // TODO
products: [Products.Transactions],
country_codes: [CountryCode.Us], // TODO
language: 'en' // TODO
}
if (req.body.access_token) {
options.access_token = req.body.access_token
delete options.products
}
this.client.createLinkToken(options, (err, data) => {
if (err) {
logError('Error creating Plaid link token.', err)
}
logInfo('Successfully created Plaid link token.')
res.json({ link_token: data.link_token })
})
})

const result = await this.client.linkTokenCreate(options)

res.json({ link_token: result.data.link_token })
console.log(result.data.link_token)
})
app.post('/remove', async (req, res) => {
try {
await updateConfig(config => {
Expand Down Expand Up @@ -178,25 +179,42 @@ export class PlaidIntegration {
accountConfig: AccountConfig,
startDate: Date,
endDate: Date
): Promise<TransactionsResponse> => {
): Promise<{ accounts: AccountBase[]; transactions: plaid.Transaction[] }> => {
return new Promise(async (resolve, reject) => {
accountConfig = accountConfig as PlaidAccountConfig

const dateFormat = 'yyyy-MM-dd'
const start = format(startDate, dateFormat)
const end = format(endDate, dateFormat)

const request: TransactionsGetRequest = {
access_token: accountConfig.token,
start_date: start,
end_date: end
}

try {
const dateFormat = 'yyyy-MM-dd'
const start = format(startDate, dateFormat)
const end = format(endDate, dateFormat)
const response = await this.client.transactionsGet(request)

let options: plaid.TransactionsRequestOptions = { count: 500, offset: 0 }
let accounts = await this.client.getTransactions(accountConfig.token, start, end, options)
let transactions = response.data.transactions
const total_transactions = response.data.total_transactions
// Manipulate the offset parameter to paginate
// transactions and retrieve all available data

while (accounts.transactions.length < accounts.total_transactions) {
options.offset += options.count
const next_page = await this.client.getTransactions(accountConfig.token, start, end, options)
accounts.transactions = accounts.transactions.concat(next_page.transactions)
}
while (transactions.length < total_transactions) {
const paginatedRequest: TransactionsGetRequest = {
...request,
options: {
offset: transactions.length
}
}

return resolve(accounts)
const paginatedResponse = await this.client.transactionsGet(paginatedRequest)
transactions = transactions.concat(paginatedResponse.data.transactions)
}
return resolve({ accounts: response.data.accounts, transactions: transactions })
} catch (e) {
console.log(e)
return reject(e)
}
})
Expand Down Expand Up @@ -255,7 +273,7 @@ export class PlaidIntegration {
}))

logInfo(
`Fetched ${data.accounts.length} sub-accounts and ${data.total_transactions} transactions.`,
`Fetched ${data.accounts.length} sub-accounts and ${data.transactions.length} transactions.`,
accounts
)
return accounts
Expand Down