mirror of
https://github.com/actualbudget/actual.git
synced 2026-04-28 10:33:02 -05:00
🔥 stripping out plaid integration (#2616)
This commit is contained in:
committed by
GitHub
parent
02e7d036d5
commit
1c04aeae39
@@ -38,7 +38,6 @@ import { ManageRulesModal } from './modals/ManageRulesModal';
|
||||
import { MergeUnusedPayees } from './modals/MergeUnusedPayees';
|
||||
import { Notes } from './modals/Notes';
|
||||
import { PayeeAutocompleteModal } from './modals/PayeeAutocompleteModal';
|
||||
import { PlaidExternalMsg } from './modals/PlaidExternalMsg';
|
||||
import { ReportBalanceMenuModal } from './modals/ReportBalanceMenuModal';
|
||||
import { ReportBudgetMenuModal } from './modals/ReportBudgetMenuModal';
|
||||
import { ReportBudgetSummaryModal } from './modals/ReportBudgetSummaryModal';
|
||||
@@ -216,20 +215,6 @@ export function Modals() {
|
||||
/>
|
||||
);
|
||||
|
||||
case 'plaid-external-msg':
|
||||
return (
|
||||
<PlaidExternalMsg
|
||||
key={name}
|
||||
modalProps={modalProps}
|
||||
onMoveExternal={options.onMoveExternal}
|
||||
onClose={() => {
|
||||
options.onClose?.();
|
||||
send('poll-web-token-stop');
|
||||
}}
|
||||
onSuccess={options.onSuccess}
|
||||
/>
|
||||
);
|
||||
|
||||
case 'gocardless-init':
|
||||
return (
|
||||
<GoCardlessInitialise
|
||||
|
||||
@@ -1,154 +0,0 @@
|
||||
// @ts-strict-ignore
|
||||
import React, { useState, useRef } from 'react';
|
||||
|
||||
import { AnimatedLoading } from '../../icons/AnimatedLoading';
|
||||
import { theme } from '../../style';
|
||||
import { Error } from '../alerts';
|
||||
import { Button } from '../common/Button';
|
||||
import { Modal, ModalButtons } from '../common/Modal';
|
||||
import { Paragraph } from '../common/Paragraph';
|
||||
import { Text } from '../common/Text';
|
||||
import { View } from '../common/View';
|
||||
import { type CommonModalProps } from '../Modals';
|
||||
|
||||
function renderError(error) {
|
||||
return (
|
||||
<Error style={{ alignSelf: 'center' }}>
|
||||
{error === 'timeout'
|
||||
? 'Timed out. Please try again.'
|
||||
: 'An error occurred while linking your account, sorry!'}
|
||||
</Error>
|
||||
);
|
||||
}
|
||||
|
||||
type PlainExternalMsgProps = {
|
||||
modalProps: CommonModalProps;
|
||||
onMoveExternal: () => Promise<{ error; data }>;
|
||||
onSuccess: (data: unknown) => Promise<void>;
|
||||
onClose?: () => void;
|
||||
};
|
||||
|
||||
export function PlaidExternalMsg({
|
||||
modalProps,
|
||||
onMoveExternal,
|
||||
onSuccess,
|
||||
onClose: originalOnClose,
|
||||
}: PlainExternalMsgProps) {
|
||||
const [waiting, setWaiting] = useState(null);
|
||||
const [success, setSuccess] = useState(false);
|
||||
const [error, setError] = useState(null);
|
||||
const data = useRef(null);
|
||||
|
||||
async function onJump() {
|
||||
setError(null);
|
||||
setWaiting('browser');
|
||||
|
||||
const res = await onMoveExternal();
|
||||
if (res.error) {
|
||||
setError(res.error);
|
||||
setWaiting(null);
|
||||
return;
|
||||
}
|
||||
|
||||
data.current = res.data;
|
||||
setWaiting(null);
|
||||
setSuccess(true);
|
||||
}
|
||||
|
||||
function onClose() {
|
||||
originalOnClose?.();
|
||||
modalProps.onClose();
|
||||
}
|
||||
|
||||
async function onContinue() {
|
||||
setWaiting('accounts');
|
||||
await onSuccess(data.current);
|
||||
setWaiting(null);
|
||||
}
|
||||
|
||||
return (
|
||||
<Modal
|
||||
title="Link Your Bank"
|
||||
{...modalProps}
|
||||
onClose={onClose}
|
||||
style={{ flex: 0 }}
|
||||
>
|
||||
{() => (
|
||||
<View>
|
||||
<Paragraph style={{ fontSize: 15 }}>
|
||||
To link your bank account, you will be moved to your browser for
|
||||
enhanced security. Click below and Actual will automatically resume
|
||||
when you have given your bank’s credentials.
|
||||
</Paragraph>
|
||||
{error && renderError(error)}
|
||||
|
||||
{waiting ? (
|
||||
<View style={{ alignItems: 'center', marginTop: 15 }}>
|
||||
<AnimatedLoading
|
||||
color={theme.pageTextDark}
|
||||
style={{ width: 20, height: 20 }}
|
||||
/>
|
||||
<View style={{ marginTop: 10, color: theme.pageText }}>
|
||||
{waiting === 'browser'
|
||||
? 'Waiting on browser...'
|
||||
: waiting === 'accounts'
|
||||
? 'Loading accounts...'
|
||||
: null}
|
||||
</View>
|
||||
</View>
|
||||
) : success ? (
|
||||
<Button
|
||||
type="primary"
|
||||
style={{
|
||||
padding: '10px 0',
|
||||
fontSize: 15,
|
||||
fontWeight: 600,
|
||||
marginTop: 10,
|
||||
}}
|
||||
onClick={onContinue}
|
||||
>
|
||||
Success! Click to continue →
|
||||
</Button>
|
||||
) : (
|
||||
<Button
|
||||
type="primary"
|
||||
style={{
|
||||
padding: '10px 0',
|
||||
fontSize: 15,
|
||||
fontWeight: 600,
|
||||
marginTop: 10,
|
||||
}}
|
||||
onClick={onJump}
|
||||
>
|
||||
Link bank in browser →
|
||||
</Button>
|
||||
)}
|
||||
<div style={{ marginTop: waiting ? 30 : 35 }}>
|
||||
<Text style={{ color: theme.pageText, fontWeight: 600 }}>
|
||||
Why not link it in the app?
|
||||
</Text>
|
||||
</div>
|
||||
<Text
|
||||
style={{
|
||||
marginTop: 10,
|
||||
color: theme.pageText,
|
||||
fontSize: 13,
|
||||
'& a, & a:visited': {
|
||||
color: theme.pageText,
|
||||
},
|
||||
}}
|
||||
>
|
||||
Typing your bank’s username and password is one of the most
|
||||
security-sensitive things you can do, and the browser is the most
|
||||
secure app in the world. Why not use it to make sure your
|
||||
information is safe? [TODO: Link to docs article]
|
||||
</Text>
|
||||
|
||||
<ModalButtons style={{ marginTop: 10 }}>
|
||||
<Button onClick={() => modalProps.onBack()}>Back</Button>
|
||||
</ModalButtons>
|
||||
</View>
|
||||
)}
|
||||
</Modal>
|
||||
);
|
||||
}
|
||||
@@ -1,52 +0,0 @@
|
||||
/* eslint-disable import/no-unused-modules */
|
||||
import { send } from 'loot-core/src/platform/client/fetch';
|
||||
|
||||
function _authorize(pushModal, plaidToken, { onSuccess, onClose }) {
|
||||
pushModal('plaid-external-msg', {
|
||||
onMoveExternal: async () => {
|
||||
const token = await send('create-web-token');
|
||||
let url = 'http://link.actualbudget.com/?token=' + token;
|
||||
// let url = 'http://localhost:8080/?token=' + token;
|
||||
if (plaidToken) {
|
||||
url = url + '&plaidToken=' + plaidToken;
|
||||
}
|
||||
window.Actual?.openURLInBrowser(url);
|
||||
|
||||
const { error, data } = await send('poll-web-token', { token });
|
||||
|
||||
return { error, data };
|
||||
},
|
||||
|
||||
onClose,
|
||||
onSuccess,
|
||||
});
|
||||
}
|
||||
|
||||
export async function authorizeBank(pushModal, { upgradingId } = {}) {
|
||||
_authorize(pushModal, null, {
|
||||
onSuccess: async data => {
|
||||
pushModal('select-linked-accounts', {
|
||||
institution: data.metadata.institution,
|
||||
publicToken: data.publicToken,
|
||||
accounts: data.metadata.accounts,
|
||||
upgradingId,
|
||||
});
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
export async function reauthorizeBank(pushModal, bankId, onSuccess) {
|
||||
const { linkToken } = await send('make-plaid-public-token', {
|
||||
bankId,
|
||||
});
|
||||
|
||||
// We don't do anything with the error right now
|
||||
if (!linkToken) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// When the modal is closed here, always try to re-sync the account
|
||||
// by calling `onSuccess`
|
||||
_authorize(pushModal, linkToken, { onSuccess, onClose: onSuccess });
|
||||
return true;
|
||||
}
|
||||
@@ -59,12 +59,6 @@ type FinanceModals = {
|
||||
targetPayeeId: string;
|
||||
};
|
||||
|
||||
'plaid-external-msg': {
|
||||
onMoveExternal: () => Promise<void>;
|
||||
onClose?: () => void;
|
||||
onSuccess: (data: unknown) => Promise<void>;
|
||||
};
|
||||
|
||||
'gocardless-init': {
|
||||
onSuccess: () => void;
|
||||
};
|
||||
|
||||
@@ -1,59 +0,0 @@
|
||||
// @ts-strict-ignore
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
|
||||
export function generateAccount(balance) {
|
||||
return {
|
||||
account_id: uuidv4(),
|
||||
balances: {
|
||||
available: balance,
|
||||
current: balance,
|
||||
limit: null,
|
||||
},
|
||||
mask: '0000',
|
||||
name: 'Plaid Checking',
|
||||
official_name: 'Plaid Interest Checking',
|
||||
subtype: 'checking',
|
||||
type: 'depository',
|
||||
};
|
||||
}
|
||||
|
||||
export function generateTransaction(
|
||||
acctId,
|
||||
amount,
|
||||
name,
|
||||
date,
|
||||
pending = false,
|
||||
) {
|
||||
return {
|
||||
account_id: acctId,
|
||||
account_owner: null,
|
||||
amount,
|
||||
category: [],
|
||||
category_id: '',
|
||||
date,
|
||||
location: {
|
||||
address: null,
|
||||
city: null,
|
||||
lat: null,
|
||||
lon: null,
|
||||
state: null,
|
||||
store_number: null,
|
||||
zip: null,
|
||||
},
|
||||
name,
|
||||
payment_meta: {
|
||||
by_order_of: null,
|
||||
payee: null,
|
||||
payer: null,
|
||||
payment_method: null,
|
||||
payment_processor: null,
|
||||
ppd_id: null,
|
||||
reason: null,
|
||||
reference_number: null,
|
||||
},
|
||||
pending,
|
||||
pending_transaction_id: null,
|
||||
transaction_id: uuidv4(),
|
||||
transaction_type: 'special',
|
||||
};
|
||||
}
|
||||
@@ -1,46 +1,7 @@
|
||||
// @ts-strict-ignore
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
|
||||
import * as asyncStorage from '../../platform/server/asyncStorage';
|
||||
import { amountToInteger } from '../../shared/util';
|
||||
import * as db from '../db';
|
||||
import { runMutator } from '../mutators';
|
||||
import { post } from '../post';
|
||||
import { getServer } from '../server-config';
|
||||
|
||||
import * as bankSync from './sync';
|
||||
|
||||
export async function handoffPublicToken(institution, publicToken) {
|
||||
const [[, userId], [, key]] = await asyncStorage.multiGet([
|
||||
'user-id',
|
||||
'user-key',
|
||||
]);
|
||||
|
||||
if (institution == null || !institution.institution_id || !institution.name) {
|
||||
throw new Error('Invalid institution object');
|
||||
}
|
||||
|
||||
const id = uuidv4();
|
||||
|
||||
// Make sure to generate an access token first before inserting it
|
||||
// into our local database in case it fails
|
||||
await post(getServer().PLAID_SERVER + '/handoff_public_token', {
|
||||
userId,
|
||||
key,
|
||||
item_id: id,
|
||||
public_token: publicToken,
|
||||
});
|
||||
|
||||
await runMutator(() =>
|
||||
db.insertWithUUID('banks', {
|
||||
id,
|
||||
bank_id: institution.institution_id,
|
||||
name: institution.name,
|
||||
}),
|
||||
);
|
||||
|
||||
return id;
|
||||
}
|
||||
|
||||
export async function findOrCreateBank(institution, requisitionId) {
|
||||
const bank = await db.first(
|
||||
@@ -62,49 +23,3 @@ export async function findOrCreateBank(institution, requisitionId) {
|
||||
|
||||
return bankData;
|
||||
}
|
||||
|
||||
export async function addGoCardlessAccounts(
|
||||
bankId,
|
||||
accountIds,
|
||||
offbudgetIds = [],
|
||||
) {
|
||||
const [[, userId], [, userKey]] = await asyncStorage.multiGet([
|
||||
'user-id',
|
||||
'user-key',
|
||||
]);
|
||||
|
||||
// Get all the available accounts
|
||||
let accounts = await bankSync.getGoCardlessAccounts(userId, userKey, bankId);
|
||||
|
||||
// Only add the selected accounts
|
||||
accounts = accounts.filter(acct => accountIds.includes(acct.account_id));
|
||||
|
||||
return Promise.all(
|
||||
accounts.map(async acct => {
|
||||
const id = await runMutator(async () => {
|
||||
const id = await db.insertAccount({
|
||||
account_id: acct.account_id,
|
||||
name: acct.name,
|
||||
official_name: acct.official_name,
|
||||
balance_current: amountToInteger(acct.balances.current),
|
||||
mask: acct.mask,
|
||||
bank: bankId,
|
||||
offbudget: offbudgetIds.includes(acct.account_id) ? 1 : 0,
|
||||
});
|
||||
|
||||
// Create a transfer payee
|
||||
await db.insertPayee({
|
||||
name: '',
|
||||
transfer_acct: id,
|
||||
});
|
||||
|
||||
return id;
|
||||
});
|
||||
|
||||
// Do an initial sync
|
||||
await bankSync.syncAccount(userId, userKey, id, acct.account_id, bankId);
|
||||
|
||||
return id;
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -41,7 +41,7 @@ async function prepareDatabase() {
|
||||
is_income: 1,
|
||||
});
|
||||
|
||||
const { accounts } = await post(getServer().PLAID_SERVER + '/accounts', {
|
||||
const { accounts } = await post(getServer().GOCARDLESS_SERVER + '/accounts', {
|
||||
client_id: '',
|
||||
group_id: '',
|
||||
item_id: '1',
|
||||
|
||||
@@ -20,9 +20,6 @@ import { title } from './title';
|
||||
import { runRules } from './transaction-rules';
|
||||
import { batchUpdateTransactions } from './transactions';
|
||||
|
||||
// Plaid article about API options:
|
||||
// https://support.plaid.com/customer/en/portal/articles/2612155-transactions-returned-per-request
|
||||
|
||||
function BankSyncError(type: string, code: string) {
|
||||
return { type: 'BankSyncError', category: type, code };
|
||||
}
|
||||
@@ -60,22 +57,6 @@ async function updateAccountBalance(id, balance) {
|
||||
]);
|
||||
}
|
||||
|
||||
export async function getAccounts(userId, userKey, id) {
|
||||
const res = await post(getServer().PLAID_SERVER + '/accounts', {
|
||||
userId,
|
||||
key: userKey,
|
||||
item_id: id,
|
||||
});
|
||||
|
||||
const { accounts } = res;
|
||||
|
||||
accounts.forEach(acct => {
|
||||
acct.balances.current = getAccountBalance(acct);
|
||||
});
|
||||
|
||||
return accounts;
|
||||
}
|
||||
|
||||
export async function getGoCardlessAccounts(userId, userKey, id) {
|
||||
const userToken = await asyncStorage.getItem('user-token');
|
||||
if (!userToken) return;
|
||||
@@ -101,80 +82,6 @@ export async function getGoCardlessAccounts(userId, userKey, id) {
|
||||
return accounts;
|
||||
}
|
||||
|
||||
export function fromPlaid(trans) {
|
||||
return {
|
||||
imported_id: trans.transaction_id,
|
||||
payee_name: trans.name,
|
||||
imported_payee: trans.name,
|
||||
amount: -amountToInteger(trans.amount),
|
||||
date: trans.date,
|
||||
};
|
||||
}
|
||||
|
||||
async function downloadTransactions(
|
||||
userId,
|
||||
userKey,
|
||||
acctId,
|
||||
bankId,
|
||||
since,
|
||||
count?: number,
|
||||
) {
|
||||
let allTransactions = [];
|
||||
let accountBalance = null;
|
||||
const pageSize = 100;
|
||||
let offset = 0;
|
||||
let numDownloaded = 0;
|
||||
|
||||
while (1) {
|
||||
const endDate = monthUtils.currentDay();
|
||||
|
||||
const res = await post(getServer().PLAID_SERVER + '/transactions', {
|
||||
userId,
|
||||
key: userKey,
|
||||
item_id: '' + bankId,
|
||||
account_id: acctId,
|
||||
start_date: since,
|
||||
end_date: endDate,
|
||||
count: pageSize,
|
||||
offset,
|
||||
});
|
||||
|
||||
if (res.error_code) {
|
||||
throw BankSyncError(res.error_type, res.error_code);
|
||||
}
|
||||
|
||||
if (res.transactions.length === 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
numDownloaded += res.transactions.length;
|
||||
|
||||
// Remove pending transactions for now - we will handle them in
|
||||
// the future.
|
||||
allTransactions = allTransactions.concat(
|
||||
res.transactions.filter(t => !t.pending),
|
||||
);
|
||||
accountBalance = getAccountBalance(res.accounts[0]);
|
||||
|
||||
if (
|
||||
numDownloaded === res.total_transactions ||
|
||||
(count != null && allTransactions.length >= count)
|
||||
) {
|
||||
break;
|
||||
}
|
||||
|
||||
offset += pageSize;
|
||||
}
|
||||
|
||||
allTransactions =
|
||||
count != null ? allTransactions.slice(0, count) : allTransactions;
|
||||
|
||||
return {
|
||||
transactions: allTransactions.map(fromPlaid),
|
||||
accountBalance,
|
||||
};
|
||||
}
|
||||
|
||||
async function downloadGoCardlessTransactions(
|
||||
userId,
|
||||
userKey,
|
||||
@@ -745,29 +652,8 @@ export async function syncAccount(
|
||||
startDate,
|
||||
);
|
||||
} else {
|
||||
// Get all transactions since the latest transaction, plus any 5
|
||||
// days before the latest transaction. This gives us a chance to
|
||||
// resolve any transactions that were entered manually.
|
||||
//
|
||||
// TODO: What this really should do is query the last imported_id
|
||||
// and since then
|
||||
let date = monthUtils.subDays(
|
||||
db.fromDateRepr(latestTransaction.date),
|
||||
31,
|
||||
);
|
||||
|
||||
// Never download transactions before the starting date. This was
|
||||
// when the account was added to the system.
|
||||
if (date < startingDate) {
|
||||
date = startingDate;
|
||||
}
|
||||
|
||||
download = await downloadTransactions(
|
||||
userId,
|
||||
userKey,
|
||||
acctId,
|
||||
bankId,
|
||||
date,
|
||||
throw new Error(
|
||||
`Unrecognized bank-sync provider: ${acctRow.account_sync_source}`,
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -567,25 +567,6 @@ handlers['query'] = async function (query) {
|
||||
return aqlQuery(query);
|
||||
};
|
||||
|
||||
handlers['bank-delete'] = async function ({ id }) {
|
||||
const accts = await db.runQuery(
|
||||
'SELECT * FROM accounts WHERE bank = ?',
|
||||
[id],
|
||||
true,
|
||||
);
|
||||
|
||||
await db.delete_('banks', id);
|
||||
await Promise.all(
|
||||
accts.map(async acct => {
|
||||
// TODO: This will not sync across devices because we are bypassing
|
||||
// the "recorded" functions
|
||||
await db.runQuery('DELETE FROM transactions WHERE acct = ?', [acct.id]);
|
||||
await db.delete_('accounts', acct.id);
|
||||
}),
|
||||
);
|
||||
return 'ok';
|
||||
};
|
||||
|
||||
handlers['account-update'] = mutator(async function ({ id, name }) {
|
||||
return withUndo(async () => {
|
||||
await db.update('accounts', { id, name });
|
||||
@@ -720,21 +701,6 @@ handlers['simplefin-accounts-link'] = async function ({
|
||||
return 'ok';
|
||||
};
|
||||
|
||||
handlers['gocardless-accounts-connect'] = async function ({
|
||||
institution,
|
||||
publicToken,
|
||||
accountIds,
|
||||
offbudgetIds,
|
||||
}) {
|
||||
const bankId = await link.handoffPublicToken(institution, publicToken);
|
||||
const ids = await link.addGoCardlessAccounts(
|
||||
bankId,
|
||||
accountIds,
|
||||
offbudgetIds,
|
||||
);
|
||||
return ids;
|
||||
};
|
||||
|
||||
handlers['account-create'] = mutator(async function ({
|
||||
name,
|
||||
balance,
|
||||
@@ -777,8 +743,8 @@ handlers['account-close'] = mutator(async function ({
|
||||
categoryId,
|
||||
forced,
|
||||
}) {
|
||||
// Unlink the account if it's linked. This makes sure to remove it
|
||||
// from Plaid. (This should not be undo-able, as it mutates the
|
||||
// Unlink the account if it's linked. This makes sure to remove it from
|
||||
// bank-sync providers. (This should not be undo-able, as it mutates the
|
||||
// remote server and the user will have to link the account again)
|
||||
await handlers['account-unlink']({ id });
|
||||
|
||||
@@ -878,142 +844,6 @@ handlers['account-move'] = mutator(async function ({ id, targetId }) {
|
||||
|
||||
let stopPolling = false;
|
||||
|
||||
handlers['poll-web-token'] = async function ({ token }) {
|
||||
const [[, userId], [, key]] = await asyncStorage.multiGet([
|
||||
'user-id',
|
||||
'user-key',
|
||||
]);
|
||||
|
||||
const startTime = Date.now();
|
||||
stopPolling = false;
|
||||
|
||||
async function getData(cb) {
|
||||
if (stopPolling) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (Date.now() - startTime >= 1000 * 60 * 10) {
|
||||
cb('timeout');
|
||||
return;
|
||||
}
|
||||
|
||||
const data = await post(
|
||||
getServer().PLAID_SERVER + '/get-web-token-contents',
|
||||
{
|
||||
userId,
|
||||
key,
|
||||
token,
|
||||
},
|
||||
);
|
||||
|
||||
if (data) {
|
||||
if (data.error) {
|
||||
cb('unknown');
|
||||
} else {
|
||||
cb(null, data);
|
||||
}
|
||||
} else {
|
||||
setTimeout(() => getData(cb), 3000);
|
||||
}
|
||||
}
|
||||
|
||||
return new Promise(resolve => {
|
||||
getData((error, data) => {
|
||||
if (error) {
|
||||
resolve({ error });
|
||||
} else {
|
||||
resolve({ data });
|
||||
}
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
handlers['poll-web-token-stop'] = async function () {
|
||||
stopPolling = true;
|
||||
return 'ok';
|
||||
};
|
||||
|
||||
handlers['accounts-sync'] = async function ({ id }) {
|
||||
const [[, userId], [, userKey]] = await asyncStorage.multiGet([
|
||||
'user-id',
|
||||
'user-key',
|
||||
]);
|
||||
let accounts = await db.runQuery(
|
||||
`SELECT a.*, b.id as bankId FROM accounts a
|
||||
LEFT JOIN banks b ON a.bank = b.id
|
||||
WHERE a.tombstone = 0 AND a.closed = 0`,
|
||||
[],
|
||||
true,
|
||||
);
|
||||
|
||||
if (id) {
|
||||
accounts = accounts.filter(acct => acct.id === id);
|
||||
}
|
||||
|
||||
const errors = [];
|
||||
let newTransactions = [];
|
||||
let matchedTransactions = [];
|
||||
let updatedAccounts = [];
|
||||
|
||||
for (let i = 0; i < accounts.length; i++) {
|
||||
const acct = accounts[i];
|
||||
if (acct.bankId) {
|
||||
try {
|
||||
const res = await bankSync.syncAccount(
|
||||
userId,
|
||||
userKey,
|
||||
acct.id,
|
||||
acct.account_id,
|
||||
acct.bankId,
|
||||
);
|
||||
const { added, updated } = res;
|
||||
|
||||
newTransactions = newTransactions.concat(added);
|
||||
matchedTransactions = matchedTransactions.concat(updated);
|
||||
|
||||
if (added.length > 0 || updated.length > 0) {
|
||||
updatedAccounts = updatedAccounts.concat(acct.id);
|
||||
}
|
||||
} catch (err) {
|
||||
if (err.type === 'BankSyncError') {
|
||||
errors.push({
|
||||
type: 'SyncError',
|
||||
accountId: acct.id,
|
||||
message: 'Failed syncing account “' + acct.name + '.”',
|
||||
category: err.category,
|
||||
code: err.code,
|
||||
});
|
||||
} else if (err instanceof PostError && err.reason !== 'internal') {
|
||||
errors.push({
|
||||
accountId: acct.id,
|
||||
message: `Account “${acct.name}” is not linked properly. Please link it again`,
|
||||
});
|
||||
} else {
|
||||
errors.push({
|
||||
accountId: acct.id,
|
||||
message:
|
||||
'There was an internal error. Please get in touch https://actualbudget.org/contact for support.',
|
||||
internal: err.stack,
|
||||
});
|
||||
|
||||
err.message = 'Failed syncing account: ' + err.message;
|
||||
|
||||
captureException(err);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (updatedAccounts.length > 0) {
|
||||
connection.send('sync-event', {
|
||||
type: 'success',
|
||||
tables: ['transactions'],
|
||||
});
|
||||
}
|
||||
|
||||
return { errors, newTransactions, matchedTransactions, updatedAccounts };
|
||||
};
|
||||
|
||||
handlers['secret-set'] = async function ({ name, value }) {
|
||||
const userToken = await asyncStorage.getItem('user-token');
|
||||
|
||||
@@ -1370,25 +1200,6 @@ handlers['account-unlink'] = mutator(async function ({ id }) {
|
||||
return 'ok';
|
||||
});
|
||||
|
||||
handlers['make-plaid-public-token'] = async function ({ bankId }) {
|
||||
const [[, userId], [, userKey]] = await asyncStorage.multiGet([
|
||||
'user-id',
|
||||
'user-key',
|
||||
]);
|
||||
|
||||
const data = await post(getServer().PLAID_SERVER + '/make-public-token', {
|
||||
userId,
|
||||
key: userKey,
|
||||
item_id: '' + bankId,
|
||||
});
|
||||
|
||||
if (data.error_code) {
|
||||
return { error: '', code: data.error_code, type: data.error_type };
|
||||
}
|
||||
|
||||
return { linkToken: data.link_token };
|
||||
};
|
||||
|
||||
handlers['save-global-prefs'] = async function (prefs) {
|
||||
if ('maxMonths' in prefs) {
|
||||
await asyncStorage.setItem('max-months', '' + prefs.maxMonths);
|
||||
|
||||
@@ -4,7 +4,6 @@ type ServerConfig = {
|
||||
BASE_SERVER: string;
|
||||
SYNC_SERVER: string;
|
||||
SIGNUP_SERVER: string;
|
||||
PLAID_SERVER: string;
|
||||
GOCARDLESS_SERVER: string;
|
||||
SIMPLEFIN_SERVER: string;
|
||||
};
|
||||
@@ -32,7 +31,6 @@ export function getServer(url?: string): ServerConfig | null {
|
||||
BASE_SERVER: url,
|
||||
SYNC_SERVER: joinURL(url, '/sync'),
|
||||
SIGNUP_SERVER: joinURL(url, '/account'),
|
||||
PLAID_SERVER: joinURL(url, '/plaid'),
|
||||
GOCARDLESS_SERVER: joinURL(url, '/gocardless'),
|
||||
SIMPLEFIN_SERVER: joinURL(url, '/simplefin'),
|
||||
};
|
||||
|
||||
@@ -78,34 +78,11 @@ handlers['/sync/sync'] = async (data: Uint8Array): Promise<Uint8Array> => {
|
||||
return responsePb.serializeBinary();
|
||||
};
|
||||
|
||||
handlers['/plaid/handoff_public_token'] = () => {
|
||||
// Do nothing
|
||||
};
|
||||
|
||||
handlers['/plaid/accounts'] = () => {
|
||||
handlers['/gocardless/accounts'] = () => {
|
||||
// Ignore the parameters and just return the accounts.
|
||||
return { accounts: currentMockData.accounts };
|
||||
};
|
||||
|
||||
handlers['/plaid/transactions'] = ({
|
||||
account_id,
|
||||
start_date,
|
||||
end_date,
|
||||
count,
|
||||
offset,
|
||||
}) => {
|
||||
const accounts = currentMockData.accounts;
|
||||
const transactions = currentMockData.transactions[account_id].filter(
|
||||
t => t.date >= start_date && t.date <= end_date,
|
||||
);
|
||||
|
||||
return {
|
||||
accounts: accounts.filter(acct => acct.account_id === account_id),
|
||||
transactions: transactions.slice(offset, offset + count),
|
||||
total_transactions: transactions.length,
|
||||
};
|
||||
};
|
||||
|
||||
export const filterMockData = func => {
|
||||
const copied = JSON.parse(JSON.stringify(defaultMockData));
|
||||
currentMockData = func(copied);
|
||||
|
||||
@@ -139,8 +139,6 @@ export interface ServerHandlers {
|
||||
|
||||
query: (query) => Promise<{ data; dependencies }>;
|
||||
|
||||
'bank-delete': (arg: { id }) => Promise<unknown>;
|
||||
|
||||
'account-update': (arg: { id; name }) => Promise<unknown>;
|
||||
|
||||
'accounts-get': () => Promise<AccountEntity[]>;
|
||||
@@ -160,13 +158,6 @@ export interface ServerHandlers {
|
||||
upgradingId;
|
||||
}) => Promise<'ok'>;
|
||||
|
||||
'gocardless-accounts-connect': (arg: {
|
||||
institution;
|
||||
publicToken;
|
||||
accountIds;
|
||||
offbudgetIds;
|
||||
}) => Promise<unknown>;
|
||||
|
||||
'account-create': (arg: {
|
||||
name: string;
|
||||
balance?: number;
|
||||
@@ -185,17 +176,6 @@ export interface ServerHandlers {
|
||||
|
||||
'account-move': (arg: { id; targetId }) => Promise<unknown>;
|
||||
|
||||
'poll-web-token': (arg: { token }) => Promise<unknown>;
|
||||
|
||||
'poll-web-token-stop': () => Promise<'ok'>;
|
||||
|
||||
'accounts-sync': (arg: { id? }) => Promise<{
|
||||
errors: unknown;
|
||||
newTransactions: unknown;
|
||||
matchedTransactions: unknown;
|
||||
updatedAccounts: unknown;
|
||||
}>;
|
||||
|
||||
'secret-set': (arg: { name: string; value: string }) => Promise<null>;
|
||||
'secret-check': (arg: string) => Promise<string | { error?: string }>;
|
||||
|
||||
@@ -247,10 +227,6 @@ export interface ServerHandlers {
|
||||
|
||||
'account-unlink': (arg: { id }) => Promise<'ok'>;
|
||||
|
||||
'make-plaid-public-token': (arg: {
|
||||
bankId;
|
||||
}) => Promise<{ error: ''; code; type } | { linkToken }>;
|
||||
|
||||
'save-global-prefs': (prefs) => Promise<'ok'>;
|
||||
|
||||
'load-global-prefs': () => Promise<GlobalPrefs>;
|
||||
|
||||
6
upcoming-release-notes/2616.md
Normal file
6
upcoming-release-notes/2616.md
Normal file
@@ -0,0 +1,6 @@
|
||||
---
|
||||
category: Maintenance
|
||||
authors: [MatissJanis]
|
||||
---
|
||||
|
||||
Delete old Plaid integration that is no longer working.
|
||||
Reference in New Issue
Block a user