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

Handle Plaid events like secure does #20747

Merged
merged 12 commits into from
Aug 29, 2023
30 changes: 30 additions & 0 deletions src/components/AddPlaidBankAccount.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {withOnyx} from 'react-native-onyx';
import lodashGet from 'lodash/get';
import Log from '../libs/Log';
import PlaidLink from './PlaidLink';
import * as App from '../libs/actions/App';
import * as BankAccounts from '../libs/actions/BankAccounts';
import ONYXKEYS from '../ONYXKEYS';
import styles from '../styles/styles';
Expand All @@ -23,6 +24,9 @@ import CONST from '../CONST';
import KeyboardShortcut from '../libs/KeyboardShortcut';

const propTypes = {
/** If the user has been throttled from Plaid */
isPlaidDisabled: PropTypes.bool,

/** Contains plaid data */
plaidData: plaidDataPropTypes.isRequired,

Expand Down Expand Up @@ -66,6 +70,7 @@ const defaultProps = {
plaidLinkOAuthToken: '',
allowDebit: false,
bankAccountID: 0,
isPlaidDisabled: false,
};

class AddPlaidBankAccount extends React.Component {
Expand Down Expand Up @@ -162,6 +167,14 @@ class AddPlaidBankAccount extends React.Component {
const plaidDataErrorMessage = !_.isEmpty(plaidErrors) ? _.chain(plaidErrors).values().first().value() : '';
const bankName = lodashGet(this.props.plaidData, 'bankName');

if (this.props.isPlaidDisabled) {
return (
<View style={[styles.m5]}>
<Text style={[styles.formError]}>{this.props.translate('bankAccount.error.tooManyAttempts')}</Text>
</View>
);
}

// Plaid Link view
if (!plaidBankAccounts.length) {
return (
Expand All @@ -185,6 +198,20 @@ class AddPlaidBankAccount extends React.Component {
onError={(error) => {
Log.hmmm('[PlaidLink] Error: ', error.message);
}}
onEvent={(event, metadata) => {
// Handle Plaid login errors (will potentially reset plaid token and item depending on the error)
if (event === 'ERROR') {
Log.hmmm('[PlaidLink] Error: ', metadata);
if (this.props.bankAccountID && metadata.error_code) {
BankAccounts.handlePlaidError(this.props.bankAccountID, metadata.error_code, metadata.error_message, metadata.request_id);
}
}

// Limit the number of times a user can submit Plaid credentials
if (event === 'SUBMIT_CREDENTIALS') {
App.handleRestrictedEvent(event);
}
}}
// User prematurely exited the Plaid flow
// eslint-disable-next-line react/jsx-props-no-multi-spaces
onExit={this.props.onExitPlaid}
Expand Down Expand Up @@ -235,5 +262,8 @@ export default compose(
key: ONYXKEYS.PLAID_LINK_TOKEN,
initWithStoredValues: false,
},
isPlaidDisabled: {
key: ONYXKEYS.IS_PLAID_DISABLED,
},
}),
)(AddPlaidBankAccount);
1 change: 1 addition & 0 deletions src/components/PlaidLink/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ function PlaidLink(props) {
},
onEvent: (event, metadata) => {
Log.info('[PlaidLink] Event: ', false, {event, metadata});
props.onEvent(event, metadata);
},
onLoad: () => setIsPlaidLoaded(true),

Expand Down
3 changes: 3 additions & 0 deletions src/components/PlaidLink/index.native.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,9 @@ function PlaidLink(props) {
onSuccess: ({publicToken, metadata}) => {
props.onSuccess({publicToken, metadata});
},
onEvent: (event, metadata) => {
props.onEvent(event, metadata);
},
onExit: (exitError, metadata) => {
Log.info('[PlaidLink] Exit: ', false, {exitError, metadata});
props.onExit();
Expand Down
3 changes: 3 additions & 0 deletions src/components/PlaidLink/plaidLinkPropTypes.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,9 @@ const plaidLinkPropTypes = {
// Callback to execute when the user leaves the Plaid widget flow without entering any information
onExit: PropTypes.func,

// Callback to execute whenever a Plaid event occurs
onEvent: PropTypes.func,

// The redirect URI with an OAuth state ID. Needed to re-initialize the PlaidLink after directing the
// user to their respective bank platform
receivedRedirectURI: PropTypes.string,
Expand Down
2 changes: 1 addition & 1 deletion src/languages/es.js
Original file line number Diff line number Diff line change
Expand Up @@ -930,7 +930,7 @@ export default {
routingAndAccountNumberCannotBeSame: 'El número de ruta y el número de cuenta no pueden ser iguales',
companyType: 'Por favor, selecciona un tipo de compañía válido',
tooManyAttempts:
'Debido a la gran cantidad de intentos de inicio de sesión, esta opción se ha desactivado temporalmente durante 24 horas. Vuelve a intentarlo más tarde o introduce los detalles manualmente.',
'Debido a la gran cantidad de intentos de inicio de sesión, esta opción ha sido desactivada temporalmente durante 24 horas. Por favor, inténtalo de nuevo más tarde.',
address: 'Por favor, introduce una dirección válida',
dob: 'Por favor, selecciona una fecha de nacimiento válida',
age: 'Debe ser mayor de 18 años',
Expand Down
7 changes: 7 additions & 0 deletions src/libs/actions/App.js
Original file line number Diff line number Diff line change
Expand Up @@ -380,6 +380,12 @@ function beginDeepLinkRedirectAfterTransition() {
waitForSignOnTransitionToFinish().then(beginDeepLinkRedirect);
}

function handleRestrictedEvent(eventName) {
API.write('HandleRestrictedEvent', {
eventName,
});
}

export {
setLocale,
setLocaleAndNavigate,
Expand All @@ -389,6 +395,7 @@ export {
openApp,
reconnectApp,
confirmReadyToOpenApp,
handleRestrictedEvent,
beginDeepLinkRedirect,
beginDeepLinkRedirectAfterTransition,
createWorkspaceAndNavigateToIt,
Expand Down
10 changes: 10 additions & 0 deletions src/libs/actions/BankAccounts.js
Original file line number Diff line number Diff line change
Expand Up @@ -401,6 +401,15 @@ function openWorkspaceView() {
API.read('OpenWorkspaceView');
}

function handlePlaidError(bankAccountID, error, error_description, plaidRequestID) {
API.write('BankAccount_HandlePlaidError', {
bankAccountID,
error,
error_description,
plaidRequestID,
});
}

/**
* Set the reimbursement account loading so that it happens right away, instead of when the API command is processed.
*
Expand All @@ -419,6 +428,7 @@ export {
connectBankAccountManually,
connectBankAccountWithPlaid,
deletePaymentBankAccount,
handlePlaidError,
openPersonalBankAccountSetupView,
openReimbursementAccountPage,
updateBeneficialOwnersForBankAccount,
Expand Down
9 changes: 7 additions & 2 deletions src/pages/ReimbursementAccount/BankAccountStep.js
Original file line number Diff line number Diff line change
Expand Up @@ -116,8 +116,13 @@ function BankAccountStep(props) {
<Button
icon={Expensicons.Bank}
text={props.translate('bankAccount.connectOnlineWithPlaid')}
onPress={() => BankAccounts.openPlaidView()}
disabled={props.isPlaidDisabled || !props.user.validated}
onPress={() => {
if (props.isPlaidDisabled || !props.user.validated) {
return;
}
BankAccounts.openPlaidView();
}}
isDisabled={props.isPlaidDisabled || !props.user.validated}
style={[styles.mt4]}
iconStyles={[styles.buttonCTAIcon]}
shouldShowRightIcon
Expand Down
Loading