Overview
Expensify supports connecting both personal and business bank accounts for sending and receiving payments. This guide covers the complete process of connecting accounts via Plaid or manual entry.Personal Bank Accounts
Opening Setup Flow
Initiate the personal bank account setup with optional parameters for routing.type OpenPersonalBankAccountSetupViewProps = {
/** The reportID of the report to redirect to once the flow is finished */
exitReportID?: string;
/** The policyID of the policy to set the bank account on */
policyID?: string;
/** The source of the bank account */
source?: string;
/** Whether to set up a US bank account */
shouldSetUpUSBankAccount?: boolean;
/** Whether the user is validated */
isUserValidated?: boolean;
};
function openPersonalBankAccountSetupView({
exitReportID,
policyID,
source,
shouldSetUpUSBankAccount = false,
isUserValidated = true
}: OpenPersonalBankAccountSetupViewProps) {
clearInternationalBankAccount().then(() => {
if (exitReportID) {
Onyx.merge(ONYXKEYS.PERSONAL_BANK_ACCOUNT, {exitReportID});
}
if (policyID) {
Onyx.merge(ONYXKEYS.PERSONAL_BANK_ACCOUNT, {policyID});
}
if (source) {
Onyx.merge(ONYXKEYS.PERSONAL_BANK_ACCOUNT, {source});
}
if (!isUserValidated) {
Navigation.navigate(createDynamicRoute(DYNAMIC_ROUTES.ADD_BANK_ACCOUNT_VERIFY_ACCOUNT.path));
return;
}
if (shouldSetUpUSBankAccount) {
Navigation.navigate(ROUTES.SETTINGS_ADD_US_BANK_ACCOUNT);
return;
}
Navigation.navigate(ROUTES.SETTINGS_ADD_BANK_ACCOUNT.getRoute(Navigation.getActiveRoute()));
});
}
Connecting via Plaid
Connect a bank account using Plaid for secure, instant verification.function connectBankAccountWithPlaid(
bankAccountID: number,
selectedPlaidBankAccount: PlaidBankAccount,
policyID: string
) {
const parameters: ConnectBankAccountParams = {
bankAccountID,
routingNumber: selectedPlaidBankAccount.routingNumber,
accountNumber: selectedPlaidBankAccount.accountNumber,
bank: selectedPlaidBankAccount.bankName,
plaidAccountID: selectedPlaidBankAccount.plaidAccountID,
plaidAccessToken: selectedPlaidBankAccount.plaidAccessToken,
plaidMask: selectedPlaidBankAccount.mask,
isSavings: selectedPlaidBankAccount.isSavings,
policyID,
};
API.write(WRITE_COMMANDS.CONNECT_BANK_ACCOUNT_WITH_PLAID, parameters, getVBBADataForOnyx());
}
Manual Bank Account Entry
For cases where Plaid is unavailable, allow manual entry of bank details.function connectBankAccountManually(
bankAccountID: number,
bankAccount: PlaidBankAccount,
policyID: string
) {
const parameters: ConnectBankAccountParams = {
bankAccountID,
routingNumber: bankAccount.routingNumber,
accountNumber: bankAccount.accountNumber,
bank: bankAccount.bankName,
plaidAccountID: bankAccount.plaidAccountID,
plaidAccessToken: bankAccount.plaidAccessToken,
plaidMask: bankAccount.mask,
isSavings: bankAccount.isSavings,
policyID,
};
API.write(WRITE_COMMANDS.CONNECT_BANK_ACCOUNT_MANUALLY, parameters, getVBBADataForOnyx());
}
Adding Personal Bank Account
Complete the process of adding a personal bank account with all required information.function addPersonalBankAccount(
account: Partial<PlaidBankAccount & PersonalBankAccountForm>,
personalPolicyID: string | undefined,
policyID?: string,
source?: string,
lastPaymentMethod?: LastPaymentMethodType | string | undefined,
) {
const parameters: AddPersonalBankAccountParams = {
addressName: account?.setupType === CONST.BANK_ACCOUNT.SETUP_TYPE.MANUAL
? `${account?.legalFirstName} ${account?.legalLastName}`
: account.addressName,
routingNumber: account?.routingNumber,
accountNumber: account?.accountNumber,
isSavings: account.isSavings ?? false,
setupType: account?.setupType,
bank: account?.bankName,
plaidAccountID: account?.plaidAccountID,
plaidAccessToken: account?.plaidAccessToken,
phoneNumber: account?.phoneNumber,
legalFirstName: account?.legalFirstName,
legalLastName: account?.legalLastName,
addressStreet: getFormattedStreet(account?.addressStreet, account?.addressStreet2),
addressCity: account?.addressCity,
addressState: account?.addressState,
addressZip: account?.addressZipCode,
addressCountry: account?.country,
};
if (policyID) {
parameters.policyID = policyID;
}
if (source) {
parameters.source = source;
}
const onyxData: OnyxData<typeof ONYXKEYS.PERSONAL_BANK_ACCOUNT | typeof ONYXKEYS.USER_WALLET | typeof ONYXKEYS.NVP_LAST_PAYMENT_METHOD> = {
optimisticData: [
{
onyxMethod: Onyx.METHOD.MERGE,
key: ONYXKEYS.PERSONAL_BANK_ACCOUNT,
value: {
isLoading: true,
errors: null,
plaidAccountID: account.plaidAccountID,
},
},
],
successData: [
{
onyxMethod: Onyx.METHOD.MERGE,
key: ONYXKEYS.PERSONAL_BANK_ACCOUNT,
value: {
isLoading: false,
errors: null,
shouldShowSuccess: true,
},
},
{
onyxMethod: Onyx.METHOD.MERGE,
key: ONYXKEYS.USER_WALLET,
value: {
currentStep: CONST.WALLET.STEP.ADDITIONAL_DETAILS,
},
},
],
failureData: [
{
onyxMethod: Onyx.METHOD.MERGE,
key: ONYXKEYS.PERSONAL_BANK_ACCOUNT,
value: {
isLoading: false,
errors: getMicroSecondOnyxErrorWithTranslationKey('walletPage.addBankAccountFailure'),
},
},
],
};
API.write(WRITE_COMMANDS.ADD_PERSONAL_BANK_ACCOUNT, parameters, onyxData);
}
Business Bank Accounts (Reimbursement Accounts)
Opening Reimbursement Account Page
Fetch and display data for the reimbursement account setup process.type OpenReimbursementAccountPageActionParams = {
stepToOpen?: ReimbursementAccountStep;
subStep?: ReimbursementAccountSubStep;
localCurrentStep?: ReimbursementAccountStep;
policyID?: string;
bankAccountID?: number;
shouldPreserveDraft?: boolean;
};
function openReimbursementAccountPage({
stepToOpen = '',
subStep = '',
localCurrentStep = '',
policyID,
bankAccountID,
shouldPreserveDraft
}: OpenReimbursementAccountPageActionParams) {
const onyxData: OnyxData<typeof ONYXKEYS.REIMBURSEMENT_ACCOUNT> = {
optimisticData: [
{
onyxMethod: Onyx.METHOD.MERGE,
key: ONYXKEYS.REIMBURSEMENT_ACCOUNT,
value: {
isLoading: true,
},
},
],
successData: [
{
onyxMethod: Onyx.METHOD.MERGE,
key: ONYXKEYS.REIMBURSEMENT_ACCOUNT,
value: {
isLoading: false,
},
},
],
failureData: [
{
onyxMethod: Onyx.METHOD.MERGE,
key: ONYXKEYS.REIMBURSEMENT_ACCOUNT,
value: {
isLoading: false,
},
},
],
};
const parameters: OpenReimbursementAccountPageParams = {
stepToOpen,
subStep,
localCurrentStep,
policyID,
bankAccountID,
shouldPreserveDraft,
};
return API.read(READ_COMMANDS.OPEN_REIMBURSEMENT_ACCOUNT_PAGE, parameters, onyxData);
}
Updating Company Information
Submit business information during the bank account verification process.function updateCompanyInformationForBankAccount(
bankAccountID: number,
params: Partial<CompanyStepProps>,
policyID: string | undefined,
isConfirmPage: boolean
) {
API.write(
WRITE_COMMANDS.UPDATE_COMPANY_INFORMATION_FOR_BANK_ACCOUNT,
{
...params,
bankAccountID,
policyID,
confirm: isConfirmPage,
},
getVBBADataForOnyx(CONST.BANK_ACCOUNT.STEP.COMPANY, isConfirmPage),
);
}
Updating Personal Information
Submit requestor personal details for bank account verification.function updatePersonalInformationForBankAccount(
bankAccountID: number,
params: RequestorStepProps,
policyID: string | undefined,
isConfirmPage: boolean
) {
API.write(
WRITE_COMMANDS.UPDATE_PERSONAL_INFORMATION_FOR_BANK_ACCOUNT,
{
...params,
bankAccountID,
policyID,
confirm: isConfirmPage,
},
getVBBADataForOnyx(CONST.BANK_ACCOUNT.STEP.REQUESTOR, isConfirmPage),
);
}
Adding Beneficial Owners
Submit beneficial owner information for compliance.function updateBeneficialOwnersForBankAccount(
bankAccountID: number,
params: Partial<BeneficialOwnersStepProps>,
policyID: string | undefined
) {
API.write(
WRITE_COMMANDS.UPDATE_BENEFICIAL_OWNERS_FOR_BANK_ACCOUNT,
{
...params,
bankAccountID,
policyID,
},
getVBBADataForOnyx(),
);
}
ACH Contract Acceptance
Accept the ACH terms and conditions to finalize bank account setup.function acceptACHContractForBankAccount(
bankAccountID: number,
params: ACHContractStepProps,
policyID?: string,
lastPaymentMethod?: LastPaymentMethodType | string
) {
const onyxData = getOnyxDataForConnectingVBBAAndLastPaymentMethod(policyID, lastPaymentMethod);
API.write(
WRITE_COMMANDS.ACCEPT_ACH_CONTRACT_FOR_BANK_ACCOUNT,
{
...params,
bankAccountID,
policyID,
},
onyxData,
);
}
Bank Account Validation
Micro-deposit Validation
Validate a bank account using micro-deposit amounts.function validateBankAccount(
bankAccountID: number,
validateCode: string,
policyID?: string
) {
const parameters: ValidateBankAccountWithTransactionsParams = {
bankAccountID,
validateCode,
policyID,
};
const onyxData: OnyxData<typeof ONYXKEYS.REIMBURSEMENT_ACCOUNT> = {
optimisticData: [
{
onyxMethod: Onyx.METHOD.MERGE,
key: ONYXKEYS.REIMBURSEMENT_ACCOUNT,
value: {
isLoading: true,
errors: null,
},
},
],
successData: [
{
onyxMethod: Onyx.METHOD.MERGE,
key: ONYXKEYS.REIMBURSEMENT_ACCOUNT,
value: {
isLoading: false,
},
},
],
failureData: [
{
onyxMethod: Onyx.METHOD.MERGE,
key: ONYXKEYS.REIMBURSEMENT_ACCOUNT,
value: {
isLoading: false,
errors: getMicroSecondOnyxErrorWithTranslationKey('common.genericErrorMessage'),
},
},
],
};
API.write(WRITE_COMMANDS.VALIDATE_BANK_ACCOUNT_WITH_TRANSACTIONS, parameters, onyxData);
}
Identity Verification
Verify identity using Onfido for enhanced security.function verifyIdentityForBankAccount(
bankAccountID: number,
onfidoData: OnfidoDataWithApplicantID,
policyID?: string
) {
const parameters: VerifyIdentityForBankAccountParams = {
bankAccountID,
onfidoData: JSON.stringify(onfidoData),
policyID,
};
API.write(WRITE_COMMANDS.VERIFY_IDENTITY_FOR_BANK_ACCOUNT, parameters, getVBBADataForOnyx());
}
Deleting Bank Accounts
Remove a bank account from the system.function deletePaymentBankAccount(
bankAccountID: number,
personalPolicyID: string | undefined,
lastUsedPaymentMethods?: LastPaymentMethod,
bankAccount?: OnyxEntry<PersonalBankAccount>,
newBankAccountID?: number,
) {
const parameters: DeletePaymentBankAccountParams = {bankAccountID};
const bankAccountFailureData = {
...bankAccount,
errors: getMicroSecondOnyxErrorWithTranslationKey('bankAccount.error.deletePaymentBankAccount'),
pendingAction: null,
};
const onyxData: OnyxData<typeof ONYXKEYS.BANK_ACCOUNT_LIST | typeof ONYXKEYS.NVP_LAST_PAYMENT_METHOD | typeof ONYXKEYS.USER_WALLET> = {
optimisticData: [
{
onyxMethod: Onyx.METHOD.MERGE,
key: `${ONYXKEYS.BANK_ACCOUNT_LIST}`,
value: {[bankAccountID]: {pendingAction: CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE}},
},
],
successData: [
{
onyxMethod: Onyx.METHOD.MERGE,
key: `${ONYXKEYS.BANK_ACCOUNT_LIST}`,
value: {[bankAccountID]: null},
},
],
failureData: [
{
onyxMethod: Onyx.METHOD.MERGE,
key: `${ONYXKEYS.BANK_ACCOUNT_LIST}`,
value: {
[bankAccountID]: bankAccountFailureData,
},
},
],
};
// Handle setting a new default if deleting the current default
if (newBankAccountID) {
const newDefaultPaymentMethodOnyxData = getMakeDefaultPaymentOnyxData(newBankAccountID);
onyxData.optimisticData?.push(
...(newDefaultPaymentMethodOnyxData as Array<OnyxUpdate<typeof ONYXKEYS.BANK_ACCOUNT_LIST | typeof ONYXKEYS.NVP_LAST_PAYMENT_METHOD | typeof ONYXKEYS.USER_WALLET>>),
);
}
API.write(WRITE_COMMANDS.DELETE_PAYMENT_BANK_ACCOUNT, parameters, onyxData);
}
Deleting a bank account that is set as the default payment method will automatically set the next most recent bank account as the default, if available.
Related Resources
Payment Methods
Managing payment methods and defaults
Wallet
Expensify Wallet configuration
