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

P2P Enable adding deposit account as payment method #6638

Merged
merged 2 commits into from
Dec 8, 2021
Merged
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
6 changes: 6 additions & 0 deletions src/CONST.js
Original file line number Diff line number Diff line change
Expand Up @@ -432,6 +432,12 @@ const CONST = {
OTHER: 'other',
},

PAYMENT_METHODS: {
PAYPAL: 'payPalMe',
DEBIT_CARD: 'debitCard',
BANK_ACCOUNT: 'bankAccount',
},

IOU: {
// Note: These payment types are used when building IOU reportAction message values in the server and should
// not be changed.
Expand Down
1 change: 1 addition & 0 deletions src/ROUTES.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ export default {
SETTINGS_PAYMENTS: 'settings/payments',
SETTINGS_ADD_PAYPAL_ME: 'settings/payments/add-paypal-me',
SETTINGS_ADD_DEBIT_CARD: 'settings/payments/add-debit-card',
SETTINGS_ADD_BANK_ACCOUNT: 'settings/payments/add-bank-account',
SETTINGS_ADD_LOGIN: 'settings/addlogin/:type',
getSettingsAddLoginRoute: type => `settings/addlogin/${type}`,
NEW_GROUP: 'new/group',
Expand Down
71 changes: 71 additions & 0 deletions src/components/AddPaymentMethodMenu.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
import React from 'react';
import PropTypes from 'prop-types';
import {withOnyx} from 'react-native-onyx';
import Popover from './Popover';
import MenuItem from './MenuItem';
import * as Expensicons from './Icon/Expensicons';
import withLocalize, {withLocalizePropTypes} from './withLocalize';
import styles from '../styles/styles';
import compose from '../libs/compose';
import ONYXKEYS from '../ONYXKEYS';
import CONST from '../CONST';

const propTypes = {
isVisible: PropTypes.bool.isRequired,
onClose: PropTypes.func.isRequired,
anchorPosition: PropTypes.shape({
top: PropTypes.number,
left: PropTypes.number,
}),
Comment on lines +14 to +19
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nab: prop-type doc


/** Username for PayPal.Me */
payPalMeUsername: PropTypes.string,

...withLocalizePropTypes,
};

const defaultProps = {
anchorPosition: {},
payPalMeUsername: '',
};

const AddPaymentMethodMenu = props => (
<Popover
isVisible={props.isVisible}
onClose={props.onClose}
anchorPosition={props.anchorPosition}
>
<MenuItem
title={props.translate('common.bankAccount')}
icon={Expensicons.Bank}
onPress={() => props.onItemSelected(CONST.PAYMENT_METHODS.BANK_ACCOUNT)}
wrapperStyle={styles.pr15}
/>
<MenuItem
title={props.translate('common.debitCard')}
icon={Expensicons.CreditCard}
onPress={() => props.onItemSelected(CONST.PAYMENT_METHODS.DEBIT_CARD)}
wrapperStyle={styles.pr15}
/>
{!props.payPalMeUsername && (
<MenuItem
title={props.translate('common.payPalMe')}
icon={Expensicons.PayPal}
onPress={() => props.onItemSelected(CONST.PAYMENT_METHODS.PAYPAL)}
wrapperStyle={styles.pr15}
/>
)}
</Popover>
);

AddPaymentMethodMenu.propTypes = propTypes;
AddPaymentMethodMenu.defaultProps = defaultProps;

export default compose(
withLocalize,
withOnyx({
payPalMeUsername: {
key: ONYXKEYS.NVP_PAYPAL_ME_ADDRESS,
},
}),
)(AddPaymentMethodMenu);
40 changes: 38 additions & 2 deletions src/components/AddPlaidBankAccount.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import PlaidLink from './PlaidLink';
import * as BankAccounts from '../libs/actions/BankAccounts';
import ONYXKEYS from '../ONYXKEYS';
import styles from '../styles/styles';
import canFocusInputOnScreenFocus from '../libs/canFocusInputOnScreenFocus';
import themeColors from '../styles/themes/default';
import compose from '../libs/compose';
import withLocalize, {withLocalizePropTypes} from './withLocalize';
Expand All @@ -21,10 +22,9 @@ import * as ReimbursementAccountUtils from '../libs/ReimbursementAccountUtils';
import ReimbursementAccountForm from '../pages/ReimbursementAccount/ReimbursementAccountForm';
import getBankIcon from './Icon/BankIcons';
import Icon from './Icon';
import ExpensiTextInput from './ExpensiTextInput';

const propTypes = {
...withLocalizePropTypes,

/** Plaid SDK token to use to initialize the widget */
plaidLinkToken: PropTypes.string,

Expand Down Expand Up @@ -78,6 +78,11 @@ const propTypes = {

/** During the OAuth flow we need to use the plaidLink token that we initially connected with */
plaidLinkOAuthToken: PropTypes.string,

/** Should we require a password to create a bank account? */
isPasswordRequired: PropTypes.bool,

...withLocalizePropTypes,
};

const defaultProps = {
Expand All @@ -90,6 +95,7 @@ const defaultProps = {
text: '',
receivedRedirectURI: null,
plaidLinkOAuthToken: '',
isPasswordRequired: false,
};

class AddPlaidBankAccount extends React.Component {
Expand All @@ -102,10 +108,14 @@ class AddPlaidBankAccount extends React.Component {
this.state = {
selectedIndex: undefined,
institution: {},
password: '',
};

this.getErrors = () => ReimbursementAccountUtils.getErrors(this.props);
this.clearError = inputKey => ReimbursementAccountUtils.clearError(this.props, inputKey);
this.getErrorText = inputKey => ReimbursementAccountUtils.getErrorText(this.props, {
password: 'passwordForm.error.incorrectLoginOrPassword',
}, inputKey);
}

componentDidMount() {
Expand All @@ -119,6 +129,10 @@ class AddPlaidBankAccount extends React.Component {
BankAccounts.fetchPlaidLinkToken();
}

componentWillUnmount() {
BankAccounts.setBankAccountFormValidationErrors({});
}

/**
* Get list of bank accounts
*
Expand Down Expand Up @@ -149,6 +163,11 @@ class AddPlaidBankAccount extends React.Component {
if (_.isUndefined(this.state.selectedIndex)) {
errors.selectedBank = true;
}

if (this.props.isPasswordRequired && _.isEmpty(this.state.password)) {
errors.password = true;
}

BankAccounts.setBankAccountFormValidationErrors(errors);
return _.size(errors) === 0;
}
Expand All @@ -165,6 +184,7 @@ class AddPlaidBankAccount extends React.Component {
bankName,
account,
plaidLinkToken: this.getPlaidLinkToken(),
password: this.state.password,
});
}

Expand Down Expand Up @@ -233,6 +253,22 @@ class AddPlaidBankAccount extends React.Component {
hasError={this.getErrors().selectedBank}
/>
</View>
{!_.isUndefined(this.state.selectedIndex) && this.props.isPasswordRequired && (
<View style={[styles.mb5]}>
<ExpensiTextInput
label={this.props.translate('addPersonalBankAccountPage.enterPassword')}
secureTextEntry
value={this.state.password}
autoCompleteType="password"
textContentType="password"
autoCapitalize="none"
autoFocus={canFocusInputOnScreenFocus()}
onChangeText={text => this.setState({password: text})}
errorText={this.getErrorText('password')}
hasError={this.getErrors().password}
/>
</View>
)}
</ReimbursementAccountForm>
)}
</>
Expand Down
2 changes: 2 additions & 0 deletions src/languages/en.js
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@ export default {
more: 'More',
debitCard: 'Debit card',
payPalMe: 'PayPal.me',
bankAccount: 'Bank account',
},
attachmentPicker: {
cameraPermissionRequired: 'Camera permission required',
Expand Down Expand Up @@ -475,6 +476,7 @@ export default {
},
},
addPersonalBankAccountPage: {
enterPassword: 'Enter Expensify password',
alreadyAdded: 'This account has already been added.',
chooseAccountLabel: 'Account',
},
Expand Down
2 changes: 2 additions & 0 deletions src/languages/es.js
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@ export default {
more: 'Más',
debitCard: 'Tarjeta de débito',
payPalMe: 'PayPal.me',
bankAccount: 'Cuenta bancaria',
},
attachmentPicker: {
cameraPermissionRequired: 'Se necesita permiso para usar la cámara',
Expand Down Expand Up @@ -475,6 +476,7 @@ export default {
},
},
addPersonalBankAccountPage: {
enterPassword: 'Escribe tu contraseña de Expensify',
alreadyAdded: 'Esta cuenta ya ha sido agregada.',
chooseAccountLabel: 'Cuenta',
},
Expand Down
4 changes: 4 additions & 0 deletions src/libs/Navigation/AppNavigator/ModalStackNavigators.js
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,10 @@ const SettingsModalStackNavigator = createModalStackNavigator([
Component: SettingsAddDebitCardPage,
name: 'Settings_Add_Debit_Card',
},
{
Component: AddPersonalBankAccountPage,
name: 'Settings_Add_Bank_Account',
},
{
Component: WorkspaceInitialPage,
name: 'Workspace_Initial',
Expand Down
4 changes: 4 additions & 0 deletions src/libs/Navigation/linkingConfig.js
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,10 @@ export default {
path: ROUTES.SETTINGS_ADD_DEBIT_CARD,
exact: true,
},
Settings_Add_Bank_Account: {
path: ROUTES.SETTINGS_ADD_BANK_ACCOUNT,
exact: true,
},
Settings_Profile: {
path: ROUTES.SETTINGS_PROFILE,
exact: true,
Expand Down
22 changes: 19 additions & 3 deletions src/libs/actions/BankAccounts.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,12 @@
import _ from 'underscore';
import Onyx from 'react-native-onyx';
import CONST from '../../CONST';
import * as API from '../API';
import * as Plaid from './Plaid';
import * as ReimbursementAccount from './ReimbursementAccount';
import Navigation from '../Navigation/Navigation';
import ONYXKEYS from '../../ONYXKEYS';
import * as PaymentMethods from './PaymentMethods';

export {
setupWithdrawalAccount,
Expand Down Expand Up @@ -41,6 +46,7 @@ export {
* @param {String} plaidLinkToken
*/
function addPersonalBankAccount(account, password, plaidLinkToken) {
Onyx.merge(ONYXKEYS.REIMBURSEMENT_ACCOUNT, {loading: true});
const unmaskedAccount = _.find(Plaid.getPlaidBankAccounts(), bankAccount => (
bankAccount.plaidAccountID === account.plaidAccountID
));
Expand Down Expand Up @@ -71,12 +77,22 @@ function addPersonalBankAccount(account, password, plaidLinkToken) {
}),
})
.then((response) => {
if (response.jsonCode !== 200) {
alert('There was a problem adding this bank account.');
if (response.jsonCode === 200) {
PaymentMethods.getPaymentMethods()
.then(() => {
Navigation.goBack();
Onyx.merge(ONYXKEYS.REIMBURSEMENT_ACCOUNT, {loading: false});
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we want success growls here?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Dang yea that would probably be a nice touch. It's pretty strange that we just kick you back to the payments page now that I think about it. But it works for now.

});
return;
}

alert('Bank account added successfully.');
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});
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Failure growl here

});
}

Expand Down
3 changes: 3 additions & 0 deletions src/pages/AddPersonalBankAccountPage.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ const AddPersonalBankAccountPage = props => (
<HeaderWithCloseButton
title={props.translate('bankAccount.addBankAccount')}
onCloseButtonPress={Navigation.dismissModal}
shouldShowBackButton
onBackButtonPress={() => Navigation.goBack()}
/>
<AddPlaidBankAccount
onSubmit={({account, password, plaidLinkToken}) => {
Expand All @@ -35,6 +37,7 @@ const AddPersonalBankAccountPage = props => (
onExitPlaid={Navigation.dismissModal}
receivedRedirectURI={getPlaidOAuthReceivedRedirectURI()}
plaidLinkOAuthToken={props.plaidLinkToken}
isPasswordRequired
/>
</ScreenWrapper>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ class ReimbursementAccountForm extends React.Component {
}}
message={this.props.reimbursementAccount.errorModalMessage}
isMessageHtml={this.props.reimbursementAccount.isErrorModalMessageHtml}
isLoading={this.props.reimbursementAccount.loading}
/>
</ScrollView>
);
Expand Down
Loading