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

feat: add enable wallet button #29573

Merged
merged 28 commits into from
Oct 19, 2023
Merged
Show file tree
Hide file tree
Changes from 27 commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
af05114
feat: add enable wallet button
samh-nl Oct 13, 2023
b28682a
fix: disable when offline
samh-nl Oct 13, 2023
4beaad4
Merge branch 'main' into feat/issue-29320
samh-nl Oct 14, 2023
49ac0b5
feat: continue kyc flow
samh-nl Oct 15, 2023
a381ece
feat: language update
samh-nl Oct 16, 2023
029b55d
feat: store source initiating kyc flow
samh-nl Oct 16, 2023
1493c32
feat: update button text
samh-nl Oct 16, 2023
d0744c4
feat: upgrade to gold wallet requires bank account
samh-nl Oct 16, 2023
924f3f5
fix: navigate to wallet upon upgrading
samh-nl Oct 16, 2023
52de510
Merge branch 'main' into feat/issue-29320
samh-nl Oct 16, 2023
acfef2a
fix: updated ts type
samh-nl Oct 16, 2023
2617784
fix: satisfy linter
samh-nl Oct 16, 2023
9687887
Merge branch 'main' into feat/issue-29320
samh-nl Oct 17, 2023
25bd8ae
refactor: consistent wallet activation check
samh-nl Oct 17, 2023
9037c85
fix: rely on continue button press
samh-nl Oct 17, 2023
68ef8c6
refactor: improve naming
samh-nl Oct 17, 2023
c943353
fix: require bank account to be linked wallet
samh-nl Oct 17, 2023
2c00b82
Revert "fix: require bank account to be linked wallet"
samh-nl Oct 17, 2023
a342a05
Update src/components/KYCWall/kycWallPropTypes.js
samh-nl Oct 18, 2023
660904c
Merge branch 'main' into feat/issue-29320
samh-nl Oct 18, 2023
4af2029
refactor: update continueSetup due to ts migration
samh-nl Oct 18, 2023
bb13297
Update src/CONST.ts
samh-nl Oct 18, 2023
41a6974
Merge branch 'main' into feat/issue-29320
samh-nl Oct 19, 2023
0bd8f47
refactor: specify fallback route in updated method
samh-nl Oct 19, 2023
15c561d
refactor: remove duplicate export
samh-nl Oct 19, 2023
4385242
refactor: satisfy linter
samh-nl Oct 19, 2023
3dc1a79
fix: add pending delete check
samh-nl Oct 19, 2023
d6e71ea
fix: make not required
samh-nl Oct 19, 2023
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.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1069,6 +1069,12 @@ const CONST = {
},
},

KYC_WALL_SOURCE: {
REPORT: 'REPORT', // The user attempted to pay a money request
ENABLE_WALLET: 'ENABLE_WALLET', // The user clicked on the `Enable wallet` button on the Wallet page
TRANSFER_BALANCE: 'TRANSFER_BALANCE', // The user attempted to transfer their wallet balance to their bank account or debit card
},

OS: {
WINDOWS: 'Windows',
MAC_OS: PLATFORM_OS_MACOS,
Expand Down
39 changes: 31 additions & 8 deletions src/components/KYCWall/BaseKYCWall.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ class KYCWall extends React.Component {

this.continue = this.continue.bind(this);
this.setMenuPosition = this.setMenuPosition.bind(this);
this.selectPaymentMethod = this.selectPaymentMethod.bind(this);
this.anchorRef = React.createRef(null);

this.state = {
Expand All @@ -39,7 +40,6 @@ class KYCWall extends React.Component {
if (this.props.shouldListenForResize) {
this.dimensionsSubscription = Dimensions.addEventListener('change', this.setMenuPosition);
}
Wallet.setKYCWallSourceChatReportID(this.props.chatReportID);
}

componentWillUnmount() {
Expand Down Expand Up @@ -88,6 +88,18 @@ class KYCWall extends React.Component {
});
}

/**
* @param {String} paymentMethod
*/
selectPaymentMethod(paymentMethod) {
this.props.onSelectPaymentMethod(paymentMethod);
if (paymentMethod === CONST.PAYMENT_METHODS.BANK_ACCOUNT) {
Navigation.navigate(this.props.addBankAccountRoute);
Copy link
Contributor

Choose a reason for hiding this comment

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

Coming from #32906, we should have callled BankAccounts.openPersonalBankAccountSetupView(); to ensure that Plaid data is cleared before Plaid view gets rendered.

} else if (paymentMethod === CONST.PAYMENT_METHODS.DEBIT_CARD) {
Navigation.navigate(this.props.addDebitCardRoute);
}
}

/**
* Take the position of the button that calls this method and show the Add Payment method menu when the user has no valid payment method.
* If they do have a valid payment method they are navigated to the "enable payments" route to complete KYC checks.
Expand All @@ -97,6 +109,14 @@ class KYCWall extends React.Component {
* @param {String} iouPaymentType
*/
continue(event, iouPaymentType) {
const currentSource = lodashGet(this.props.walletTerms, 'source', this.props.source);

/**
* Set the source, so we can tailor the process according to how we got here.
* We do not want to set this on mount, as the source can change upon completing the flow, e.g. when upgrading the wallet to Gold.
*/
Wallet.setKYCWallSource(this.props.source, this.props.chatReportID);

if (this.state.shouldShowAddPaymentMenu) {
this.setState({shouldShowAddPaymentMenu: false});
return;
Expand All @@ -111,9 +131,13 @@ class KYCWall extends React.Component {
// Check to see if user has a valid payment method on file and display the add payment popover if they don't
if (
(isExpenseReport && lodashGet(this.props.reimbursementAccount, 'achData.state', '') !== CONST.BANK_ACCOUNT.STATE.OPEN) ||
(!isExpenseReport && !PaymentUtils.hasExpensifyPaymentMethod(paymentCardList, this.props.bankAccountList))
(!isExpenseReport && !PaymentUtils.hasExpensifyPaymentMethod(paymentCardList, this.props.bankAccountList, this.props.shouldIncludeDebitCard))
) {
Log.info('[KYC Wallet] User does not have valid payment method');
if (!this.props.shouldIncludeDebitCard) {
this.selectPaymentMethod(CONST.PAYMENT_METHODS.BANK_ACCOUNT);
return;
}
const clickedElementLocation = getClickedTargetLocation(targetElement);
const position = this.getAnchorPosition(clickedElementLocation);
this.setPositionAddPaymentMenu(position);
Expand All @@ -132,7 +156,7 @@ class KYCWall extends React.Component {
}
}
Log.info('[KYC Wallet] User has valid payment method and passed KYC checks or did not need them');
this.props.onSuccessfulKYC(iouPaymentType);
this.props.onSuccessfulKYC(iouPaymentType, currentSource);
}

render() {
Expand All @@ -149,11 +173,7 @@ class KYCWall extends React.Component {
anchorAlignment={this.props.anchorAlignment}
onItemSelected={(item) => {
this.setState({shouldShowAddPaymentMenu: false});
if (item === CONST.PAYMENT_METHODS.BANK_ACCOUNT) {
Navigation.navigate(this.props.addBankAccountRoute);
} else if (item === CONST.PAYMENT_METHODS.DEBIT_CARD) {
Navigation.navigate(this.props.addDebitCardRoute);
}
this.selectPaymentMethod(item);
}}
/>
{this.props.children(this.continue, this.anchorRef)}
Expand All @@ -169,6 +189,9 @@ export default withOnyx({
userWallet: {
key: ONYXKEYS.USER_WALLET,
},
walletTerms: {
key: ONYXKEYS.WALLET_TERMS,
},
fundList: {
key: ONYXKEYS.FUND_LIST,
},
Expand Down
16 changes: 16 additions & 0 deletions src/components/KYCWall/kycWallPropTypes.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import bankAccountPropTypes from '../bankAccountPropTypes';
import cardPropTypes from '../cardPropTypes';
import iouReportPropTypes from '../../pages/iouReportPropTypes';
import reimbursementAccountPropTypes from '../../pages/ReimbursementAccount/ReimbursementAccountDraftPropTypes';
import walletTermsPropTypes from '../../pages/EnablePayments/walletTermsPropTypes';
import CONST from '../../CONST';

const propTypes = {
Expand All @@ -26,6 +27,12 @@ const propTypes = {
/** The user's wallet */
userWallet: userWalletPropTypes,

/** Information related to the last step of the wallet activation flow */
walletTerms: walletTermsPropTypes,

/** The source that triggered the KYC wall */
source: PropTypes.oneOf(_.values(CONST.KYC_WALL_SOURCE)).isRequired,

/** When the button is opened via an IOU, ID for the chatReport that the IOU is linked to */
chatReportID: PropTypes.string,

Expand All @@ -49,10 +56,17 @@ const propTypes = {
horizontal: PropTypes.oneOf(_.values(CONST.MODAL.ANCHOR_ORIGIN_HORIZONTAL)),
vertical: PropTypes.oneOf(_.values(CONST.MODAL.ANCHOR_ORIGIN_VERTICAL)),
}),

/** Whether the option to add a debit card should be included */
shouldIncludeDebitCard: PropTypes.bool,

/** Callback for when a payment method has been selected */
onSelectPaymentMethod: PropTypes.func,
};

const defaultProps = {
userWallet: {},
walletTerms: {},
shouldListenForResize: false,
isDisabled: false,
chatReportID: '',
Expand All @@ -66,6 +80,8 @@ const defaultProps = {
horizontal: CONST.MODAL.ANCHOR_ORIGIN_HORIZONTAL.LEFT,
vertical: CONST.MODAL.ANCHOR_ORIGIN_VERTICAL.BOTTOM,
},
shouldIncludeDebitCard: true,
onSelectPaymentMethod: () => {},
};

export {propTypes, defaultProps};
1 change: 1 addition & 0 deletions src/components/SettlementButton.js
Original file line number Diff line number Diff line change
Expand Up @@ -207,6 +207,7 @@ function SettlementButton({
addBankAccountRoute={addBankAccountRoute}
addDebitCardRoute={addDebitCardRoute}
isDisabled={isOffline}
source={CONST.KYC_WALL_SOURCE.REPORT}
chatReportID={chatReportID}
iouReport={iouReport}
anchorAlignment={anchorAlignment}
Expand Down
4 changes: 3 additions & 1 deletion src/languages/en.ts
Original file line number Diff line number Diff line change
Expand Up @@ -857,6 +857,8 @@ export default {
receiveMoney: 'Receive money in your local currency',
expensifyWallet: 'Expensify Wallet',
sendAndReceiveMoney: 'Send and receive money from your Expensify Wallet.',
enableWalletToSendAndReceiveMoney: 'Enable your Expensify Wallet to start sending and receiving money with friends!',
enableWallet: 'Enable wallet',
bankAccounts: 'Bank accounts',
addBankAccountToSendAndReceive: 'Add a bank account to send and receive payments directly in the app.',
addBankAccount: 'Add bank account',
Expand Down Expand Up @@ -1219,7 +1221,7 @@ export default {
},
additionalDetailsStep: {
headerTitle: 'Additional details',
helpText: 'We need to confirm the following information before we can process this payment.',
helpText: 'We need to confirm the following information before you can send and receive money from your Wallet.',
helpTextIdologyQuestions: 'We need to ask you just a few more questions to finish validating your identity.',
helpLink: 'Learn more about why we need this.',
legalFirstNameLabel: 'Legal first name',
Expand Down
4 changes: 3 additions & 1 deletion src/languages/es.ts
Original file line number Diff line number Diff line change
Expand Up @@ -853,6 +853,8 @@ export default {
receiveMoney: 'Recibe dinero en tu moneda local',
expensifyWallet: 'Billetera Expensify',
sendAndReceiveMoney: 'Envía y recibe dinero desde tu Billetera Expensify.',
enableWalletToSendAndReceiveMoney: 'Habilita tu Billetera Expensify para comenzar a enviar y recibir dinero con amigos',
enableWallet: 'Habilitar Billetera',
bankAccounts: 'Cuentas bancarias',
addBankAccountToSendAndReceive: 'Añade una cuenta bancaria para enviar y recibir pagos directamente en la aplicación.',
addBankAccount: 'Agregar cuenta bancaria',
Expand Down Expand Up @@ -1238,7 +1240,7 @@ export default {
},
additionalDetailsStep: {
headerTitle: 'Detalles adicionales',
helpText: 'Necesitamos confirmar la siguiente información antes de que podamos procesar el pago.',
helpText: 'Necesitamos confirmar la siguiente información antes de que puedas enviar y recibir dinero desde tu Billetera.',
helpTextIdologyQuestions: 'Tenemos que preguntarte unas preguntas más para terminar de verificar tu identidad',
helpLink: 'Obtén más información sobre por qué necesitamos esto.',
legalFirstNameLabel: 'Primer nombre legal',
Expand Down
6 changes: 3 additions & 3 deletions src/libs/PaymentUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,16 +11,16 @@ type AccountType = BankAccount['accountType'] | Fund['accountType'];
/**
* Check to see if user has either a debit card or personal bank account added
*/
function hasExpensifyPaymentMethod(fundList: Record<string, Fund>, bankAccountList: Record<string, BankAccount>): boolean {
function hasExpensifyPaymentMethod(fundList: Record<string, Fund>, bankAccountList: Record<string, BankAccount>, shouldIncludeDebitCard = true): boolean {
const validBankAccount = Object.values(bankAccountList).some((bankAccountJSON) => {
const bankAccount = new BankAccountModel(bankAccountJSON);
return bankAccount.isDefaultCredit();
return bankAccount.getPendingAction() !== CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE && bankAccount.isDefaultCredit();
});

// Hide any billing cards that are not P2P debit cards for now because you cannot make them your default method, or delete them
const validDebitCard = Object.values(fundList).some((card) => card?.accountData?.additionalData?.isP2PDebitCard ?? false);

return validBankAccount || validDebitCard;
return validBankAccount || (shouldIncludeDebitCard && validDebitCard);
}

function getPaymentMethodDescription(accountType: AccountType, account: BankAccount['accountData'] | Fund['accountData']): string {
Expand Down
2 changes: 1 addition & 1 deletion src/libs/actions/BankAccounts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ function openPersonalBankAccountSetupView(exitReportID: string) {
}

/**
* Whether after adding a bank account we should continue with the KYC flow
* Whether after adding a bank account we should continue with the KYC flow. If so, we must specify the fallback route.
*/
function setPersonalBankAccountContinueKYCOnSuccess(onSuccessFallbackRoute: string) {
Onyx.merge(ONYXKEYS.PERSONAL_BANK_ACCOUNT, {onSuccessFallbackRoute});
Expand Down
9 changes: 5 additions & 4 deletions src/libs/actions/Wallet.js
Original file line number Diff line number Diff line change
Expand Up @@ -69,12 +69,13 @@ function setAdditionalDetailsErrorMessage(additionalErrorMessage) {
}

/**
* Save the ID of the chat whose IOU triggered showing the KYC wall.
* Save the source that triggered the KYC wall and optionally the chat report ID associated with the IOU
*
* @param {String} source
* @param {String} chatReportID
*/
function setKYCWallSourceChatReportID(chatReportID) {
Onyx.merge(ONYXKEYS.WALLET_TERMS, {chatReportID});
function setKYCWallSource(source, chatReportID = '') {
Onyx.merge(ONYXKEYS.WALLET_TERMS, {source, chatReportID});
}

/**
Expand Down Expand Up @@ -333,5 +334,5 @@ export {
updatePersonalDetails,
verifyIdentity,
acceptWalletTerms,
setKYCWallSourceChatReportID,
setKYCWallSource,
};
8 changes: 8 additions & 0 deletions src/libs/models/BankAccount.js
Original file line number Diff line number Diff line change
Expand Up @@ -222,6 +222,14 @@ class BankAccount {
return this.json.accountData.additionalData || {};
}

/**
* Get the pending action of the bank account
* @returns {String}
*/
getPendingAction() {
return lodashGet(this.json, 'pendingAction', '');
}

/**
* Return a map needed to setup a withdrawal account
* @returns {Object}
Expand Down
6 changes: 5 additions & 1 deletion src/pages/AddPersonalBankAccountPage.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import HeaderWithBackButton from '../components/HeaderWithBackButton';
import ScreenWrapper from '../components/ScreenWrapper';
import Navigation from '../libs/Navigation/Navigation';
import * as BankAccounts from '../libs/actions/BankAccounts';
import * as PaymentMethods from '../libs/actions/PaymentMethods';
import withLocalize, {withLocalizePropTypes} from '../components/withLocalize';
import AddPlaidBankAccount from '../components/AddPlaidBankAccount';
import getPlaidOAuthReceivedRedirectURI from '../libs/getPlaidOAuthReceivedRedirectURI';
Expand All @@ -17,7 +18,6 @@ import Form from '../components/Form';
import ROUTES from '../ROUTES';
import * as PlaidDataProps from './ReimbursementAccount/plaidDataPropTypes';
import ConfirmationPage from '../components/ConfirmationPage';
import * as PaymentMethods from '../libs/actions/PaymentMethods';

const propTypes = {
...withLocalizePropTypes,
Expand All @@ -36,6 +36,9 @@ const propTypes = {
/** Any reportID we should redirect to at the end of the flow */
exitReportID: PropTypes.string,

/** Whether we should continue with KYC at the end of the flow */
shouldContinueKYCOnSuccess: PropTypes.bool,

/** Whether the form is loading */
isLoading: PropTypes.bool,

Expand All @@ -52,6 +55,7 @@ const defaultProps = {
isLoading: false,
plaidAccountID: '',
exitReportID: '',
shouldContinueKYCOnSuccess: false,
},
};

Expand Down
13 changes: 11 additions & 2 deletions src/pages/EnablePayments/ActivateStep.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,14 +25,23 @@ const propTypes = {
const defaultProps = {
userWallet: {},
walletTerms: {
source: '',
chatReportID: 0,
},
};

function ActivateStep(props) {
const isActivatedWallet = _.contains([CONST.WALLET.TIER_NAME.GOLD, CONST.WALLET.TIER_NAME.PLATINUM], props.userWallet.tierName);
const animation = isActivatedWallet ? LottieAnimations.Fireworks : LottieAnimations.ReviewingBankInfo;
const continueButtonText = props.walletTerms.chatReportID ? props.translate('activateStep.continueToPayment') : props.translate('activateStep.continueToTransfer');
let continueButtonText = '';

if (props.walletTerms.chatReportID) {
continueButtonText = props.translate('activateStep.continueToPayment');
} else if (props.walletTerms.source === CONST.KYC_WALL_SOURCE.ENABLE_WALLET) {
continueButtonText = props.translate('common.continue');
} else {
continueButtonText = props.translate('activateStep.continueToTransfer');
}

return (
<>
Expand All @@ -43,7 +52,7 @@ function ActivateStep(props) {
description={props.translate(`activateStep.${isActivatedWallet ? 'activated' : 'checkBackLater'}Message`)}
shouldShowButton={isActivatedWallet}
buttonText={continueButtonText}
onButtonPress={PaymentMethods.continueSetup}
onButtonPress={() => PaymentMethods.continueSetup()}
/>
</>
);
Expand Down
5 changes: 5 additions & 0 deletions src/pages/EnablePayments/walletTermsPropTypes.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,15 @@
import PropTypes from 'prop-types';
import _ from 'underscore';
import CONST from '../../CONST';

/** Prop types related to the Terms step of KYC flow */
export default PropTypes.shape({
/** Any error message to show */
errors: PropTypes.objectOf(PropTypes.string),

/** The source that triggered the KYC wall */
source: PropTypes.oneOf(_.values(CONST.KYC_WALL_SOURCE)).isRequired,

/** When the user accepts the Wallet's terms in order to pay an IOU, this is the ID of the chatReport the IOU is linked to */
chatReportID: PropTypes.string,
});
Loading
Loading