From 7c65dc9b83d7e5ece8750eda64495325aeed70b0 Mon Sep 17 00:00:00 2001 From: Nathalie Kuoch Date: Tue, 28 Jun 2022 10:25:05 +0200 Subject: [PATCH 01/12] New APIs OpenPlaidBankLogin and OpenPlaidBankAccountSelector and AddPersonalBankAccount --- src/components/AddPlaidBankAccount.js | 90 ++++++++------- src/languages/en.js | 2 + src/languages/es.js | 2 + src/libs/actions/BankAccounts.js | 109 ++++++++++-------- src/libs/actions/Plaid.js | 104 ++++++----------- .../actions/ReimbursementAccount/errors.js | 17 ++- .../fetchFreePlanVerifiedBankAccount.js | 39 ++----- .../actions/ReimbursementAccount/index.js | 7 +- .../setupWithdrawalAccount.js | 15 +-- src/libs/actions/Wallet.js | 2 +- src/libs/deprecatedAPI.js | 23 ---- src/libs/models/BankAccount.js | 1 - src/pages/AddPersonalBankAccountPage.js | 4 +- .../ReimbursementAccount/ACHContractStep.js | 1 + .../ReimbursementAccount/BankAccountStep.js | 9 +- src/pages/ReimbursementAccount/CompanyStep.js | 1 + .../ReimbursementAccountForm.js | 36 ++++-- .../ReimbursementAccountPage.js | 5 +- .../ReimbursementAccount/RequestorStep.js | 1 + .../ReimbursementAccount/ValidationStep.js | 1 + .../reimbursementAccountPropTypes.js | 3 - src/pages/SetPasswordPage.js | 7 +- .../fetchFreePlanVerifiedBankAccountTest.js | 52 +-------- 23 files changed, 234 insertions(+), 297 deletions(-) diff --git a/src/components/AddPlaidBankAccount.js b/src/components/AddPlaidBankAccount.js index 73fb51e1a929..ddf4a0b0a898 100644 --- a/src/components/AddPlaidBankAccount.js +++ b/src/components/AddPlaidBankAccount.js @@ -23,6 +23,7 @@ import ReimbursementAccountForm from '../pages/ReimbursementAccount/Reimbursemen import getBankIcon from './Icon/BankIcons'; import Icon from './Icon'; import TextInput from './TextInput'; +import FullPageOfflineBlockingView from './FullPageOfflineBlockingView'; const propTypes = { /** Plaid SDK token to use to initialize the widget */ @@ -33,14 +34,8 @@ const propTypes = { /** Whether we are fetching the bank accounts from the API */ loading: PropTypes.bool, - /** Error object */ - error: PropTypes.shape({ - /** Error message */ - message: PropTypes.string, - - /** Error title */ - title: PropTypes.string, - }), + /** Error message */ + error: PropTypes.string, /** List of accounts */ accounts: PropTypes.arrayOf(PropTypes.shape({ @@ -50,9 +45,6 @@ const propTypes = { /** Name of account */ addressName: PropTypes.string, - /** Has this account has already been added? */ - alreadyExists: PropTypes.bool, - /** Is the account a savings account? */ isSavings: PropTypes.bool, @@ -65,6 +57,9 @@ const propTypes = { /** last 4 digits of the account number */ mask: PropTypes.string, })), + + /** Plaid access token, used to then retrieve Assets and Balances */ + plaidAccessToken: PropTypes.string, }), /** Fired when the user exits the Plaid flow */ @@ -85,6 +80,9 @@ const propTypes = { /** Should we require a password to create a bank account? */ isPasswordRequired: PropTypes.bool, + /** Are we adding a withdrawal account? */ + allowDebit: PropTypes.bool, + ...withLocalizePropTypes, }; @@ -92,6 +90,7 @@ const defaultProps = { plaidLinkToken: '', plaidBankAccounts: { loading: false, + error: '', }, onExitPlaid: () => {}, onSubmit: () => {}, @@ -99,6 +98,7 @@ const defaultProps = { receivedRedirectURI: null, plaidLinkOAuthToken: '', isPasswordRequired: false, + allowDebit: false, }; class AddPlaidBankAccount extends React.Component { @@ -130,11 +130,7 @@ class AddPlaidBankAccount extends React.Component { } BankAccounts.clearPlaidBankAccountsAndToken(); - BankAccounts.fetchPlaidLinkToken(); - } - - componentWillUnmount() { - BankAccounts.setBankAccountFormValidationErrors({}); + BankAccounts.openPlaidBankLogin(this.props.allowDebit); } /** @@ -189,6 +185,7 @@ class AddPlaidBankAccount extends React.Component { account, plaidLinkToken: this.getPlaidLinkToken(), password: this.state.password, + plaidAccessToken: lodashGet(this.props, 'plaidBankAccounts.plaidAccessToken'), }); } @@ -202,31 +199,39 @@ class AddPlaidBankAccount extends React.Component { return ( <> - {(!token || this.props.plaidBankAccounts.loading) - && ( - - - - )} - {token && ( - { - Log.info('[PlaidLink] Success!'); - BankAccounts.fetchPlaidBankAccounts(publicToken, metadata.institution.name); - this.setState({institution: metadata.institution}); - }} - onError={(error) => { - Log.hmmm('[PlaidLink] Error: ', error.message); - }} - - // User prematurely exited the Plaid flow - // eslint-disable-next-line react/jsx-props-no-multi-spaces - onExit={this.props.onExitPlaid} - receivedRedirectURI={this.props.receivedRedirectURI} - /> - )} - {accounts.length > 0 && ( + {!accounts.length ? ( + + {(!token || this.props.plaidBankAccounts.loading) + && ( + + + + )} + {this.props.plaidBankAccounts.error && ( + + {this.props.plaidBankAccounts.error} + + )} + {token && ( + { + Log.info('[PlaidLink] Success!'); + BankAccounts.openPlaidBankAccountSelector(publicToken, metadata.institution.name, this.props.allowDebit); + this.setState({institution: metadata.institution}); + }} + onError={(error) => { + Log.hmmm('[PlaidLink] Error: ', error.message); + }} + + // User prematurely exited the Plaid flow + // eslint-disable-next-line react/jsx-props-no-multi-spaces + onExit={this.props.onExitPlaid} + receivedRedirectURI={this.props.receivedRedirectURI} + /> + )} + + ) : ( @@ -297,8 +302,5 @@ export default compose( plaidBankAccounts: { key: ONYXKEYS.PLAID_BANK_ACCOUNTS, }, - reimbursementAccount: { - key: ONYXKEYS.REIMBURSEMENT_ACCOUNT, - }, }), )(AddPlaidBankAccount); diff --git a/src/languages/en.js b/src/languages/en.js index 02bf873c3a9d..625304a2abfe 100755 --- a/src/languages/en.js +++ b/src/languages/en.js @@ -399,6 +399,8 @@ export default { notOwnerOfFund: 'There was an error setting this card as your default payment method.', setDefaultFailure: 'Something went wrong. Please chat with Concierge for further assistance.', }, + addBankAccountSuccess: 'Your bank account has successfully been added.', + addBankAccountFailure: 'And unexpected error occurred while trying to add your bank account. Please try again.', }, transferAmountPage: { transfer: ({amount}) => `Transfer${amount ? ` ${amount}` : ''}`, diff --git a/src/languages/es.js b/src/languages/es.js index d7751c630de9..bf5680915390 100644 --- a/src/languages/es.js +++ b/src/languages/es.js @@ -399,6 +399,8 @@ export default { notOwnerOfFund: 'Ha ocurrido un error al establecer esta tarjeta de crédito como tu método de pago predeterminado.', setDefaultFailure: 'No se ha podido configurar el método de pago.', }, + addBankAccountSuccess: 'Su cuenta bancaria ha sido añadida con éxito.', + addBankAccountFailure: 'Y ocurrió un error inesperado al intentar agregar su cuenta bancaria. Inténtalo de nuevo.', }, transferAmountPage: { transfer: ({amount}) => `Transferir${amount ? ` ${amount}` : ''}`, diff --git a/src/libs/actions/BankAccounts.js b/src/libs/actions/BankAccounts.js index 3e912d100e63..65eac54b35c7 100644 --- a/src/libs/actions/BankAccounts.js +++ b/src/libs/actions/BankAccounts.js @@ -1,15 +1,15 @@ -import _ from 'underscore'; -import Onyx from 'react-native-onyx'; import lodashGet from 'lodash/get'; import CONST from '../../CONST'; import * as DeprecatedAPI from '../deprecatedAPI'; +import * as API from '../API'; import * as Plaid from './Plaid'; import * as ReimbursementAccount from './ReimbursementAccount'; import ONYXKEYS from '../../ONYXKEYS'; -import * as PaymentMethods from './PaymentMethods'; import Growl from '../Growl'; import * as Localize from '../Localize'; import * as store from './ReimbursementAccount/store'; +import requireParameters from '../requireParameters'; +import ROUTES from '../../ROUTES'; export { setupWithdrawalAccount, @@ -18,6 +18,7 @@ export { showBankAccountErrorModal, showBankAccountFormValidationError, setBankAccountFormValidationErrors, + resetReimbursementAccount, resetFreePlanBankAccount, validateBankAccount, hideBankAccountErrors, @@ -28,12 +29,10 @@ export { cancelResetFreePlanBankAccount, } from './ReimbursementAccount'; export { - fetchPlaidBankAccounts, + openPlaidBankAccountSelector, clearPlaidBankAccountsAndToken, - fetchPlaidLinkToken, - getPlaidBankAccounts, + openPlaidBankLogin, getBankName, - getPlaidAccessToken, } from './Plaid'; export { fetchOnfidoToken, @@ -46,57 +45,77 @@ export { * * @param {Object} account * @param {String} password - * @param {String} plaidLinkToken + * @param {String} plaidAccessToken */ -function addPersonalBankAccount(account, password, plaidLinkToken) { - Onyx.merge(ONYXKEYS.REIMBURSEMENT_ACCOUNT, {loading: true}); - const unmaskedAccount = _.find(Plaid.getPlaidBankAccounts(), bankAccount => ( - bankAccount.plaidAccountID === account.plaidAccountID - )); - DeprecatedAPI.BankAccount_Create({ - accountNumber: unmaskedAccount.accountNumber, - addressName: unmaskedAccount.addressName, +function addPersonalBankAccount(account, password, plaidAccessToken) { + const commandName = 'AddPersonalBankAccount'; + + const parameters = { + accountNumber: account.accountNumber, + addressName: account.addressName, allowDebit: false, confirm: false, - isSavings: unmaskedAccount.isSavings, + isSavings: account.isSavings, password, - routingNumber: unmaskedAccount.routingNumber, + routingNumber: account.routingNumber, setupType: 'plaid', additionalData: JSON.stringify({ - useOnFido: false, - policyID: '', - plaidLinkToken, - isInSetup: true, - bankAccountInReview: null, - currentStep: 'AccountOwnerInformationStep', bankName: Plaid.getBankName(), - plaidAccountID: unmaskedAccount.plaidAccountID, - ownershipType: '', + plaidAccountID: account.plaidAccountID, acceptTerms: true, country: 'US', currency: CONST.CURRENCY.USD, fieldsType: 'local', - plaidAccessToken: Plaid.getPlaidAccessToken(), + plaidAccessToken, }), - }) - .then((response) => { - if (response.jsonCode === 200) { - PaymentMethods.getPaymentMethods() - .then(() => { - PaymentMethods.continueSetup(); - Onyx.merge(ONYXKEYS.REIMBURSEMENT_ACCOUNT, {loading: false}); - }); - return; - } + }; + + requireParameters([ + 'accountNumber', + 'addressName', + 'isSavings', + 'password', + 'routingNumber', + ], parameters, commandName); + + const onyxData = { + optimisticData: [ + { + onyxMethod: 'merge', + key: ONYXKEYS.REIMBURSEMENT_ACCOUNT, + value: { + loading: true, + error: '', + }, + }, + ], + successData: [ + { + onyxMethod: 'merge', + key: ONYXKEYS.REIMBURSEMENT_ACCOUNT, + value: { + loading: false, + error: '', + + // Once the bank account has been added, we show a success message with a Continue button. + // When clicking on the button, we should redirect to this route. + successRoute: ROUTES.SETTINGS_PAYMENTS, + }, + }, + ], + failureData: [ + { + onyxMethod: 'merge', + key: ONYXKEYS.REIMBURSEMENT_ACCOUNT, + value: { + loading: false, + error: Localize.translateLocal('paymentsPage.addBankAccountFailure'), + }, + }, + ], + }; - if (response.message === 'Incorrect Expensify password entered') { - ReimbursementAccount.setBankAccountFormValidationErrors({password: true}); - } - Onyx.merge(ONYXKEYS.REIMBURSEMENT_ACCOUNT, {loading: false}); - }) - .catch(() => { - Onyx.merge(ONYXKEYS.REIMBURSEMENT_ACCOUNT, {loading: false}); - }); + API.write(commandName, parameters, onyxData); } /** diff --git a/src/libs/actions/Plaid.js b/src/libs/actions/Plaid.js index 8fceb8187748..3a3f93367acf 100644 --- a/src/libs/actions/Plaid.js +++ b/src/libs/actions/Plaid.js @@ -1,10 +1,7 @@ import Onyx from 'react-native-onyx'; -import Str from 'expensify-common/lib/str'; -import _ from 'underscore'; +import getPlaidLinkTokenParameters from '../getPlaidLinkTokenParameters'; import ONYXKEYS from '../../ONYXKEYS'; -import CONST from '../../CONST'; -import * as DeprecatedAPI from '../deprecatedAPI'; -import Growl from '../Growl'; +import * as API from '../API'; import * as Localize from '../Localize'; /** @@ -12,15 +9,12 @@ import * as Localize from '../Localize'; * * @private */ -let plaidBankAccounts = []; let bankName = ''; -let plaidAccessToken = ''; /** * We clear these out of storage once we are done with them so the user must re-enter Plaid credentials upon returning. */ function clearPlaidBankAccountsAndToken() { - plaidBankAccounts = []; bankName = ''; Onyx.set(ONYXKEYS.PLAID_BANK_ACCOUNTS, {}); Onyx.set(ONYXKEYS.PLAID_LINK_TOKEN, null); @@ -28,72 +22,52 @@ function clearPlaidBankAccountsAndToken() { /** * Gets the Plaid Link token used to initialize the Plaid SDK + * @param {Boolean} allowDebit */ -function fetchPlaidLinkToken() { - DeprecatedAPI.Plaid_GetLinkToken() - .then((response) => { - if (response.jsonCode !== 200) { - return; - } - - Onyx.merge(ONYXKEYS.PLAID_LINK_TOKEN, response.linkToken); - }); +function openPlaidBankLogin(allowDebit) { + const params = getPlaidLinkTokenParameters(); + params.allowDebit = allowDebit; + API.read('OpenPlaidBankLogin', params); } /** * @param {String} publicToken * @param {String} bank + * @param {Boolean} allowDebit */ -function fetchPlaidBankAccounts(publicToken, bank) { +function openPlaidBankAccountSelector(publicToken, bank, allowDebit) { bankName = bank; - Onyx.merge(ONYXKEYS.PLAID_BANK_ACCOUNTS, {loading: true}); - DeprecatedAPI.BankAccount_Get({ + API.read('OpenPlaidBankAccountSelector', { publicToken, - allowDebit: false, + allowDebit, bank, - }) - .then((response) => { - if (response.jsonCode === 666 && response.title === CONST.BANK_ACCOUNT.PLAID.ERROR.TOO_MANY_ATTEMPTS) { - Onyx.merge(ONYXKEYS.REIMBURSEMENT_ACCOUNT, {isPlaidDisabled: true}); - } - - plaidAccessToken = response.plaidAccessToken; - - // Filter out any accounts that already exist since they cannot be used again. - plaidBankAccounts = _.filter(response.accounts, account => !account.alreadyExists); - - if (plaidBankAccounts.length === 0) { - Growl.error(Localize.translateLocal('bankAccount.error.noBankAccountAvailable')); - } - - Onyx.merge(ONYXKEYS.PLAID_BANK_ACCOUNTS, { - error: { - title: response.title, - message: response.message, - }, + }, { + optimisticData: [{ + onyxMethod: 'merge', + key: ONYXKEYS.PLAID_BANK_ACCOUNTS, + value: { + loading: true, + error: '', + }, + }], + successData: [{ + onyxMethod: 'merge', + key: ONYXKEYS.PLAID_BANK_ACCOUNTS, + value: { loading: false, - accounts: _.map(plaidBankAccounts, account => ({ - ...account, - accountNumber: Str.maskPAN(account.accountNumber), - })), - bankName, - }); - }); -} - -/** - * @returns {String} - */ -function getPlaidAccessToken() { - return plaidAccessToken; -} - -/** - * @returns {Array} - */ -function getPlaidBankAccounts() { - return plaidBankAccounts; + error: '', + }, + }], + failureData: [{ + onyxMethod: 'merge', + key: ONYXKEYS.PLAID_BANK_ACCOUNTS, + value: { + loading: false, + error: Localize.translateLocal('bankAccount.error.noBankAccountAvailable'), + }, + }], + }); } /** @@ -105,9 +79,7 @@ function getBankName() { export { clearPlaidBankAccountsAndToken, - fetchPlaidBankAccounts, - fetchPlaidLinkToken, - getPlaidAccessToken, - getPlaidBankAccounts, + openPlaidBankAccountSelector, + openPlaidBankLogin, getBankName, }; diff --git a/src/libs/actions/ReimbursementAccount/errors.js b/src/libs/actions/ReimbursementAccount/errors.js index ffec84f8cc0c..bad8b2717a9d 100644 --- a/src/libs/actions/ReimbursementAccount/errors.js +++ b/src/libs/actions/ReimbursementAccount/errors.js @@ -4,11 +4,11 @@ import ONYXKEYS from '../../../ONYXKEYS'; /** * Show error modal and optionally a specific error message * - * @param {String} errorModalMessage The error message to be displayed in the modal's body. - * @param {Boolean} isErrorModalMessageHtml if @errorModalMessage is in html format or not + * @param {String} error The error message to be displayed in the form. + * @param {Boolean} isErrorHtml if @errorModalMessage is in html format or not */ -function showBankAccountErrorModal(errorModalMessage = null, isErrorModalMessageHtml = false) { - Onyx.merge(ONYXKEYS.REIMBURSEMENT_ACCOUNT, {errorModalMessage, isErrorModalMessageHtml}); +function showBankAccountErrorModal(error = null, isErrorHtml = false) { + Onyx.merge(ONYXKEYS.REIMBURSEMENT_ACCOUNT, {error, isErrorHtml}); } /** @@ -22,6 +22,14 @@ function setBankAccountFormValidationErrors(errors) { Onyx.merge(ONYXKEYS.REIMBURSEMENT_ACCOUNT, {errors}); } +/** + * Clear validation messages from reimbursement account + */ +function resetReimbursementAccount() { + setBankAccountFormValidationErrors({}); + Onyx.merge(ONYXKEYS.REIMBURSEMENT_ACCOUNT, {successRoute: null}); +} + /** * Set the current error message. * @@ -35,4 +43,5 @@ export { showBankAccountErrorModal, setBankAccountFormValidationErrors, showBankAccountFormValidationError, + resetReimbursementAccount, }; diff --git a/src/libs/actions/ReimbursementAccount/fetchFreePlanVerifiedBankAccount.js b/src/libs/actions/ReimbursementAccount/fetchFreePlanVerifiedBankAccount.js index 927340ea6204..de3d8db7731f 100644 --- a/src/libs/actions/ReimbursementAccount/fetchFreePlanVerifiedBankAccount.js +++ b/src/libs/actions/ReimbursementAccount/fetchFreePlanVerifiedBankAccount.js @@ -56,7 +56,6 @@ function fetchNameValuePairsAndBankAccount() { const failedValidationAttempts = lodashGet(nameValuePairs, failedValidationAttemptsName, 0); const maxAttemptsReached = failedValidationAttempts > CONST.BANK_ACCOUNT.VERIFICATION_MAX_ATTEMPTS; - const kycVerificationsMigration = lodashGet(nameValuePairs, CONST.NVP.KYC_MIGRATION, ''); const throttledDate = lodashGet(nameValuePairs, CONST.NVP.ACH_DATA_THROTTLED, ''); const bankAccountJSON = _.find(bankAccountList, account => ( account.bankAccountID === bankAccountID @@ -67,7 +66,6 @@ function fetchNameValuePairsAndBankAccount() { return { maxAttemptsReached, - kycVerificationsMigration, throttledDate, bankAccount, isPlaidDisabled, @@ -79,28 +77,14 @@ function fetchNameValuePairsAndBankAccount() { }); } -/** - * @param {BankAccount} bankAccount - * @param {String} kycVerificationsMigration - * @returns {Boolean} - */ -function getHasTriedToUpgrade(bankAccount, kycVerificationsMigration) { - if (!bankAccount) { - return false; - } - - return bankAccount.getDateSigned() > (kycVerificationsMigration || '2020-01-13'); -} - /** * @param {String} stepToOpen * @param {String} stepFromStorage * @param {Object} achData * @param {BankAccount} bankAccount - * @param {Boolean} hasTriedToUpgrade * @returns {String} */ -function getCurrentStep(stepToOpen, stepFromStorage, achData, bankAccount, hasTriedToUpgrade) { +function getCurrentStep(stepToOpen, stepFromStorage, achData, bankAccount) { // If we are providing a stepToOpen via a deep link then we will always navigate to that step. This // should be used with caution as it is possible to drop a user into a flow they can't complete e.g. // if we drop the user into the CompanyStep, but they have no accountNumber or routing Number in @@ -133,7 +117,7 @@ function getCurrentStep(stepToOpen, stepFromStorage, achData, bankAccount, hasTr } if (bankAccount.needsToPassLatestChecks()) { - return hasTriedToUpgrade ? CONST.BANK_ACCOUNT.STEP.VALIDATION : CONST.BANK_ACCOUNT.STEP.COMPANY; + return CONST.BANK_ACCOUNT.STEP.COMPANY; } return CONST.BANK_ACCOUNT.STEP.ENABLE; @@ -141,10 +125,9 @@ function getCurrentStep(stepToOpen, stepFromStorage, achData, bankAccount, hasTr /** * @param {BankAccount} bankAccount - * @param {Boolean} hasTriedToUpgrade * @returns {Boolean} */ -function getIsBankAccountInReview(bankAccount, hasTriedToUpgrade) { +function getIsBankAccountInReview(bankAccount) { if (!bankAccount) { return false; } @@ -153,26 +136,21 @@ function getIsBankAccountInReview(bankAccount, hasTriedToUpgrade) { return true; } - if (bankAccount.isOpen() && bankAccount.needsToPassLatestChecks()) { - return hasTriedToUpgrade; - } - return false; } /** * @param {BankAccount} bankAccount - * @param {Boolean} hasTriedToUpgrade * @param {String} subStep * @returns {Object} */ -function buildACHData(bankAccount, hasTriedToUpgrade, subStep) { +function buildACHData(bankAccount, subStep) { return { ...(bankAccount ? bankAccount.toACHData() : {}), useOnfido: true, policyID: store.getReimbursementAccountWorkspaceID() || '', isInSetup: !bankAccount || bankAccount.isInSetup(), - bankAccountInReview: getIsBankAccountInReview(bankAccount, hasTriedToUpgrade), + bankAccountInReview: getIsBankAccountInReview(bankAccount), domainLimit: 0, subStep: bankAccount && bankAccount.isInSetup() ? CONST.BANK_ACCOUNT.SETUP_TYPE.MANUAL @@ -196,14 +174,13 @@ function fetchFreePlanVerifiedBankAccount(stepToOpen, localBankAccountState) { // Fetch the various NVPs we need to show any initial errors and the bank account itself fetchNameValuePairsAndBankAccount() .then(({ - bankAccount, kycVerificationsMigration, throttledDate, maxAttemptsReached, isPlaidDisabled, + bankAccount, throttledDate, maxAttemptsReached, isPlaidDisabled, }) => { // If we already have a substep stored locally then we will add that to the new achData const subStep = lodashGet(store.getReimbursementAccountInSetup(), 'subStep', ''); - const hasTriedToUpgrade = getHasTriedToUpgrade(bankAccount, kycVerificationsMigration); - const achData = buildACHData(bankAccount, hasTriedToUpgrade, subStep); + const achData = buildACHData(bankAccount, subStep); const stepFromStorage = store.getReimbursementAccountInSetup().currentStep; - const currentStep = getCurrentStep(stepToOpen, stepFromStorage, achData, bankAccount, hasTriedToUpgrade); + const currentStep = getCurrentStep(stepToOpen, stepFromStorage, achData, bankAccount); navigation.goToWithdrawalAccountSetupStep(currentStep, achData); Onyx.merge(ONYXKEYS.REIMBURSEMENT_ACCOUNT, { diff --git a/src/libs/actions/ReimbursementAccount/index.js b/src/libs/actions/ReimbursementAccount/index.js index 7397ab51234b..a37051c29230 100644 --- a/src/libs/actions/ReimbursementAccount/index.js +++ b/src/libs/actions/ReimbursementAccount/index.js @@ -7,7 +7,12 @@ import resetFreePlanBankAccount from './resetFreePlanBankAccount'; import deleteFromBankAccountList from './deleteFromBankAccountList'; export {goToWithdrawalAccountSetupStep} from './navigation'; -export {showBankAccountErrorModal, setBankAccountFormValidationErrors, showBankAccountFormValidationError} from './errors'; +export { + showBankAccountErrorModal, + setBankAccountFormValidationErrors, + resetReimbursementAccount, + showBankAccountFormValidationError, +} from './errors'; /** * Set the current sub step in first step of adding withdrawal bank account diff --git a/src/libs/actions/ReimbursementAccount/setupWithdrawalAccount.js b/src/libs/actions/ReimbursementAccount/setupWithdrawalAccount.js index b67e4c319482..97a2499376c5 100644 --- a/src/libs/actions/ReimbursementAccount/setupWithdrawalAccount.js +++ b/src/libs/actions/ReimbursementAccount/setupWithdrawalAccount.js @@ -3,7 +3,6 @@ import Onyx from 'react-native-onyx'; import lodashGet from 'lodash/get'; import Log from '../../Log'; import BankAccount from '../../models/BankAccount'; -import * as Plaid from '../Plaid'; import CONST from '../../../CONST'; import ONYXKEYS from '../../../ONYXKEYS'; import * as store from './store'; @@ -33,10 +32,7 @@ function getBankAccountListAndGoToValidateStep(updatedACHData) { }); const bankAccount = new BankAccount(bankAccountJSON); const achData = bankAccount.toACHData(); - const needsToPassLatestChecks = achData.state === BankAccount.STATE.OPEN - && achData.needsToPassLatestChecks; - achData.bankAccountInReview = needsToPassLatestChecks - || achData.state === BankAccount.STATE.VERIFYING; + achData.bankAccountInReview = achData.state === BankAccount.STATE.VERIFYING; navigation.goToWithdrawalAccountSetupStep(CONST.BANK_ACCOUNT.STEP.VALIDATION, achData); Onyx.merge(ONYXKEYS.REIMBURSEMENT_ACCOUNT, {loading: false}); @@ -136,13 +132,6 @@ function mergeParamsWithLocalACHData(data) { : CONST.BANK_ACCOUNT.SETUP_TYPE.MANUAL; } - // If we are setting up a Plaid account replace the accountNumber with the unmasked number - if (data.plaidAccountID) { - const unmaskedAccount = _.find(Plaid.getPlaidBankAccounts(), bankAccount => ( - bankAccount.plaidAccountID === data.plaidAccountID - )); - updatedACHData.accountNumber = unmaskedAccount.accountNumber; - } return updatedACHData; } @@ -204,7 +193,7 @@ function mergeParamsWithLocalACHData(data) { * @param {Array} [params.beneficialOwners] */ function setupWithdrawalAccount(params) { - Onyx.merge(ONYXKEYS.REIMBURSEMENT_ACCOUNT, {loading: true, errorModalMessage: '', errors: null}); + Onyx.merge(ONYXKEYS.REIMBURSEMENT_ACCOUNT, {loading: true, error: '', errors: null}); const updatedACHData = mergeParamsWithLocalACHData(params); DeprecatedAPI.BankAccount_SetupWithdrawal(updatedACHData) .then((response) => { diff --git a/src/libs/actions/Wallet.js b/src/libs/actions/Wallet.js index 6f2e02cd06a1..65dda4570f48 100644 --- a/src/libs/actions/Wallet.js +++ b/src/libs/actions/Wallet.js @@ -158,8 +158,8 @@ function buildIdologyError(idologyErrors) { * * Possible steps: * - * - OnfidoStep - Creates an identity check by calling Onfido's API (via Web-Secure) with data returned from the SDK * - AdditionalDetailsStep - Validates a user's provided details against a series of checks + * - OnfidoStep - Creates an identity check by calling Onfido's API (via Web-Secure) with data returned from the SDK * - TermsStep - Ensures that a user has agreed to all of the terms and conditions * * The API will always return the updated userWallet in the response as a convenience so we can avoid calling diff --git a/src/libs/deprecatedAPI.js b/src/libs/deprecatedAPI.js index 67333741f0a7..27ea0801fc1a 100644 --- a/src/libs/deprecatedAPI.js +++ b/src/libs/deprecatedAPI.js @@ -1,5 +1,4 @@ import _ from 'underscore'; -import getPlaidLinkTokenParameters from './getPlaidLinkTokenParameters'; import isViaExpensifyCashNative from './isViaExpensifyCashNative'; import requireParameters from './requireParameters'; import * as Request from './Request'; @@ -591,13 +590,6 @@ function Wallet_GetOnfidoSDKToken(firstName, lastName, dob) { }, CONST.NETWORK.METHOD.POST, true); } -/** - * @returns {Promise} - */ -function Plaid_GetLinkToken() { - return Network.post('Plaid_GetLinkToken', getPlaidLinkTokenParameters(), CONST.NETWORK.METHOD.POST, true); -} - /** * @param {Object} parameters * @param {String} parameters.currentStep @@ -613,19 +605,6 @@ function Wallet_Activate(parameters) { return Network.post(commandName, parameters, CONST.NETWORK.METHOD.POST, true); } -/** - * @param {Object} parameters - * @param {String} parameters.publicToken - * @param {Boolean} parameters.allowDebit - * @param {String} parameters.bank - * @returns {Promise} - */ -function BankAccount_Get(parameters) { - const commandName = 'BankAccount_Get'; - requireParameters(['publicToken', 'allowDebit', 'bank'], parameters, commandName); - return Network.post(commandName, parameters, CONST.NETWORK.METHOD.POST, true); -} - /** * @param {Object} parameters * @param {Object[]} parameters.employees @@ -921,7 +900,6 @@ function GetStatementPDF(parameters) { export { AddBillingCard, BankAccount_Create, - BankAccount_Get, BankAccount_SetupWithdrawal, BankAccount_Validate, ChangePassword, @@ -947,7 +925,6 @@ export { PayWithWallet, PersonalDetails_GetForEmails, PersonalDetails_Update, - Plaid_GetLinkToken, Policy_Employees_Merge, Push_Authenticate, RejectTransaction, diff --git a/src/libs/models/BankAccount.js b/src/libs/models/BankAccount.js index 88343d448174..204a24ad07c2 100644 --- a/src/libs/models/BankAccount.js +++ b/src/libs/models/BankAccount.js @@ -268,7 +268,6 @@ class BankAccount { bankAccountID: this.getID(), state: this.getState(), validateCodeExpectedDate: this.getValidateCodeExpectedDate(), - needsToPassLatestChecks: this.needsToPassLatestChecks(), needsToUpgrade: this.needsToUpgrade(), }, this.getAdditionalData()); } diff --git a/src/pages/AddPersonalBankAccountPage.js b/src/pages/AddPersonalBankAccountPage.js index 43d5d3b3077f..f3eb518e20cc 100644 --- a/src/pages/AddPersonalBankAccountPage.js +++ b/src/pages/AddPersonalBankAccountPage.js @@ -33,8 +33,8 @@ const AddPersonalBankAccountPage = props => ( onBackButtonPress={Navigation.goBack} /> { - BankAccounts.addPersonalBankAccount(account, password, plaidLinkToken); + onSubmit={(params) => { + BankAccounts.addPersonalBankAccount(params.account, params.password, params.plaidAccessToken); }} onExitPlaid={Navigation.goBack} receivedRedirectURI={getPlaidOAuthReceivedRedirectURI()} diff --git a/src/pages/ReimbursementAccount/ACHContractStep.js b/src/pages/ReimbursementAccount/ACHContractStep.js index 20c9d5f663ec..ae5fc4932460 100644 --- a/src/pages/ReimbursementAccount/ACHContractStep.js +++ b/src/pages/ReimbursementAccount/ACHContractStep.js @@ -177,6 +177,7 @@ class ACHContractStep extends React.Component { shouldShowBackButton /> diff --git a/src/pages/ReimbursementAccount/BankAccountStep.js b/src/pages/ReimbursementAccount/BankAccountStep.js index 46417c8e8c96..ed22efcfcbe7 100644 --- a/src/pages/ReimbursementAccount/BankAccountStep.js +++ b/src/pages/ReimbursementAccount/BankAccountStep.js @@ -155,7 +155,7 @@ class BankAccountStep extends React.Component { setupType: CONST.BANK_ACCOUNT.SETUP_TYPE.PLAID, // Params passed via the Plaid callback when an account is selected - plaidAccessToken: params.plaidLinkToken, + plaidAccessToken: params.plaidAccessToken, accountNumber: params.account.accountNumber, routingNumber: params.account.routingNumber, plaidAccountID: params.account.plaidAccountID, @@ -226,9 +226,9 @@ class BankAccountStep extends React.Component { success extraLarge /> - {this.props.isPlaidDisabled && ( + {this.props.error && ( - {this.props.translate('bankAccount.error.tooManyAttempts')} + {this.props.error} )} BankAccounts.setBankAccountSubStep(null)} receivedRedirectURI={this.props.receivedRedirectURI} plaidLinkOAuthToken={this.props.plaidLinkOAuthToken} + allowDebit /> )} {subStep === CONST.BANK_ACCOUNT.SETUP_TYPE.MANUAL && ( diff --git a/src/pages/ReimbursementAccount/CompanyStep.js b/src/pages/ReimbursementAccount/CompanyStep.js index be29bef4f19d..21aa54fdf57d 100644 --- a/src/pages/ReimbursementAccount/CompanyStep.js +++ b/src/pages/ReimbursementAccount/CompanyStep.js @@ -201,6 +201,7 @@ class CompanyStep extends React.Component { onCloseButtonPress={Navigation.dismissModal} /> {this.props.translate('companyStep.subtitle')} diff --git a/src/pages/ReimbursementAccount/ReimbursementAccountForm.js b/src/pages/ReimbursementAccount/ReimbursementAccountForm.js index 04b8b6033c03..9816a9ff3370 100644 --- a/src/pages/ReimbursementAccount/ReimbursementAccountForm.js +++ b/src/pages/ReimbursementAccount/ReimbursementAccountForm.js @@ -4,7 +4,6 @@ import React from 'react'; import PropTypes from 'prop-types'; import {View} from 'react-native'; import {withOnyx} from 'react-native-onyx'; - import styles from '../../styles/styles'; import withLocalize, {withLocalizePropTypes} from '../../components/withLocalize'; import reimbursementAccountPropTypes from './reimbursementAccountPropTypes'; @@ -13,9 +12,13 @@ import ONYXKEYS from '../../ONYXKEYS'; import FormAlertWithSubmitButton from '../../components/FormAlertWithSubmitButton'; import CONST from '../../CONST'; import FormScrollView from '../../components/FormScrollView'; +import Button from '../../components/Button'; +import Navigation from '../../libs/Navigation/Navigation'; +import * as BankAccounts from '../../libs/actions/BankAccounts'; +import Text from '../../components/Text'; const propTypes = { - /** ACH data for the withdrawal account actively being set up */ + /** Data for the bank account actively being set up */ reimbursementAccount: reimbursementAccountPropTypes, /** Called when the form is submitted */ @@ -29,11 +32,30 @@ const defaultProps = { }; class ReimbursementAccountForm extends React.Component { + componentWillUnmount() { + BankAccounts.resetReimbursementAccount(); + } + render() { - const isErrorVisible = _.size(lodashGet(this.props, 'reimbursementAccount.errors', {})) > 0 - || lodashGet(this.props, 'reimbursementAccount.errorModalMessage', '').length > 0 + const successRoute = lodashGet(this.props, 'reimbursementAccount.successRoute'); + if (successRoute) { + return ( + <> + + {this.props.translate('paymentsPage.addBankAccountSuccess')} + + +