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

add “Report virtual card fraud” page and route #28312

Merged
merged 14 commits into from
Oct 10, 2023
1 change: 1 addition & 0 deletions src/ONYXKEYS.ts
Original file line number Diff line number Diff line change
Expand Up @@ -291,6 +291,7 @@ const ONYXKEYS = {
PRIVATE_NOTES_FORM: 'privateNotesForm',
I_KNOW_A_TEACHER_FORM: 'iKnowTeacherForm',
INTRO_SCHOOL_PRINCIPAL_FORM: 'introSchoolPrincipalForm',
REPORT_VIRTUAL_CARD_FRAUD: 'reportVirtualCardFraudForm',
},
} as const;

Expand Down
4 changes: 4 additions & 0 deletions src/ROUTES.ts
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,10 @@ export default {
route: '/settings/wallet/card/:domain',
getRoute: (domain: string) => `/settings/wallet/card/${domain}`,
},
SETTINGS_REPORT_FRAUD: {
route: '/settings/wallet/cards/:domain/report-virtual-fraud',
getRoute: (domain: string) => `/settings/wallet/cards/${domain}/report-virtual-fraud`,
},
SETTINGS_ADD_DEBIT_CARD: 'settings/wallet/add-debit-card',
SETTINGS_ADD_BANK_ACCOUNT: 'settings/wallet/add-bank-account',
SETTINGS_ENABLE_PAYMENTS: 'settings/wallet/enable-payments',
Expand Down
7 changes: 7 additions & 0 deletions src/languages/en.ts
Original file line number Diff line number Diff line change
Expand Up @@ -833,6 +833,7 @@ export default {
availableSpend: 'Remaining spending power',
virtualCardNumber: 'Virtual card number',
physicalCardNumber: 'Physical card number',
reportFraud: 'Report virtual card fraud',
cardDetails: {
cardNumber: 'Virtual card number',
expiration: 'Expiration',
Expand All @@ -842,6 +843,12 @@ export default {
copyCardNumber: 'Copy card number',
},
},
reportFraudPage: {
title: 'Report virtual card fraud',
description: 'If your virtual card details have been stolen or compromised, we’ll permanently deactivate your existing card and provide you with a new virtual card and number.',
deactivateCard: 'Deactivate card',
reportVirtualCardFraud: 'Report virtual card fraud',
},
activateCardPage: {
activateCard: 'Activate card',
pleaseEnterLastFour: 'Please enter the last four digits of your card.',
Expand Down
8 changes: 8 additions & 0 deletions src/languages/es.ts
Original file line number Diff line number Diff line change
Expand Up @@ -829,6 +829,7 @@ export default {
availableSpend: 'Capacidad de gasto restante',
virtualCardNumber: 'Número de la tarjeta virtual',
physicalCardNumber: 'Número de la tarjeta física',
reportFraud: 'Reportar fraude con la tarjeta virtual',
cardDetails: {
cardNumber: 'Número de tarjeta virtual',
expiration: 'Expiración',
Expand All @@ -838,6 +839,13 @@ export default {
copyCardNumber: 'Copiar número de la tarjeta',
},
},
reportFraudPage: {
title: 'Reportar fraude con la tarjeta virtual',
description:
'Si los datos de tu tarjeta virtual han sido robados o se han visto comprometidos, desactivaremos permanentemente la tarjeta actual y le proporcionaremos una tarjeta virtual y un número nuevo.',
deactivateCard: 'Desactivar tarjeta',
reportVirtualCardFraud: 'Reportar fraude con la tarjeta virtual',
},
activateCardPage: {
activateCard: 'Activar tarjeta',
pleaseEnterLastFour: 'Introduce los cuatro últimos dígitos de la tarjeta.',
Expand Down
1 change: 1 addition & 0 deletions src/libs/Navigation/AppNavigator/ModalStackNavigators.js
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,7 @@ const SettingsModalStackNavigator = createModalStackNavigator({
Settings_Lounge_Access: () => require('../../../pages/settings/Profile/LoungeAccessPage').default,
Settings_Wallet: () => require('../../../pages/settings/Wallet/WalletPage').default,
Settings_Wallet_DomainCards: () => require('../../../pages/settings/Wallet/ExpensifyCardPage').default,
Settings_Wallet_ReportVirtualCardFraud: () => require('../../../pages/settings/Wallet/ReportVirtualCardFraudPage').default,
Settings_Wallet_Card_Activate: () => require('../../../pages/settings/Wallet/ActivatePhysicalCardPage').default,
Settings_Wallet_Transfer_Balance: () => require('../../../pages/settings/Wallet/TransferBalancePage').default,
Settings_Wallet_Choose_Transfer_Account: () => require('../../../pages/settings/Wallet/ChooseTransferAccountPage').default,
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 @@ -73,6 +73,10 @@ export default {
path: ROUTES.SETTINGS_WALLET_DOMAINCARDS.route,
exact: true,
},
Settings_Wallet_ReportVirtualCardFraud: {
path: ROUTES.SETTINGS_REPORT_FRAUD.route,
exact: true,
},
Settings_Wallet_EnablePayments: {
path: ROUTES.SETTINGS_ENABLE_PAYMENTS,
exact: true,
Expand Down
43 changes: 42 additions & 1 deletion src/libs/actions/Card.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,47 @@ import Onyx from 'react-native-onyx';
import ONYXKEYS from '../../ONYXKEYS';
import * as API from '../API';

/**
* @param {Number} cardID
*/
function reportVirtualExpensifyCardFraud(cardID) {
API.write(
'ReportVirtualExpensifyCardFraud',
{
cardID,
},
{
optimisticData: [
{
onyxMethod: Onyx.METHOD.MERGE,
key: ONYXKEYS.FORMS.REPORT_VIRTUAL_CARD_FRAUD,
value: {
isLoading: true,
},
},
],
successData: [
{
onyxMethod: Onyx.METHOD.MERGE,
key: ONYXKEYS.FORMS.REPORT_VIRTUAL_CARD_FRAUD,
value: {
isLoading: false,
},
},
],
failureData: [
{
onyxMethod: Onyx.METHOD.MERGE,
key: ONYXKEYS.FORMS.REPORT_VIRTUAL_CARD_FRAUD,
value: {
isLoading: false,
},
},
],
},
);
}

/**
* Activates the physical Expensify card based on the last four digits of the card number
*
Expand Down Expand Up @@ -60,4 +101,4 @@ function clearCardListErrors(cardID) {
Onyx.merge(ONYXKEYS.CARD_LIST, {[cardID]: {errors: null, isLoading: false}});
}

export {activatePhysicalExpensifyCard, clearCardListErrors};
export {reportVirtualExpensifyCardFraud, activatePhysicalExpensifyCard, clearCardListErrors};
10 changes: 9 additions & 1 deletion src/pages/settings/Wallet/ExpensifyCardPage.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import useLocalize from '../../../hooks/useLocalize';
import * as CurrencyUtils from '../../../libs/CurrencyUtils';
import Navigation from '../../../libs/Navigation/Navigation';
import styles from '../../../styles/styles';
import * as Expensicons from '../../../components/Icon/Expensicons';
import * as CardUtils from '../../../libs/CardUtils';
import Button from '../../../components/Button';
import CardDetails from './WalletPage/CardDetails';
Expand Down Expand Up @@ -108,14 +109,21 @@ function ExpensifyCardPage({
}
/>
)}
<MenuItemWithTopDescription
title={translate('cardPage.reportFraud')}
titleStyle={styles.walletCardMenuItem}
icon={Expensicons.Flag}
shouldShowRightIcon
onPress={() => Navigation.navigate(ROUTES.SETTINGS_REPORT_FRAUD.getRoute(domain))}
/>
</>
)}
{!_.isEmpty(physicalCard) && (
<MenuItemWithTopDescription
description={translate('cardPage.physicalCardNumber')}
title={CardUtils.maskCard(physicalCard.lastFourPAN)}
interactive={false}
titleStyle={styles.walletCardNumber}
titleStyle={styles.walletCardMenuItem}
/>
)}
</ScrollView>
Expand Down
104 changes: 104 additions & 0 deletions src/pages/settings/Wallet/ReportVirtualCardFraudPage.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
import React, {useEffect} from 'react';
import _ from 'underscore';
import {View} from 'react-native';
import PropTypes from 'prop-types';
import {withOnyx} from 'react-native-onyx';
import ROUTES from '../../../ROUTES';
import HeaderWithBackButton from '../../../components/HeaderWithBackButton';
import ScreenWrapper from '../../../components/ScreenWrapper';
import Navigation from '../../../libs/Navigation/Navigation';
import styles from '../../../styles/styles';
import Text from '../../../components/Text';
import useLocalize from '../../../hooks/useLocalize';
import * as Card from '../../../libs/actions/Card';
import assignedCardPropTypes from './assignedCardPropTypes';
import * as CardUtils from '../../../libs/CardUtils';
import ONYXKEYS from '../../../ONYXKEYS';
import NotFoundPage from '../../ErrorPage/NotFoundPage';
import usePrevious from '../../../hooks/usePrevious';
import FormAlertWithSubmitButton from '../../../components/FormAlertWithSubmitButton';
import * as ErrorUtils from '../../../libs/ErrorUtils';

const propTypes = {
/* Onyx Props */
formData: PropTypes.shape({
isLoading: PropTypes.bool,
}),
cardList: PropTypes.objectOf(assignedCardPropTypes),
/** The parameters needed to authenticate with a short-lived token are in the URL */
route: PropTypes.shape({
/** Each parameter passed via the URL */
params: PropTypes.shape({
/** Domain string */
domain: PropTypes.string,
}),
}).isRequired,
};

const defaultProps = {
cardList: {},
formData: {},
};

function ReportVirtualCardFraudPage({
route: {
params: {domain},
},
cardList,
formData,
}) {
const {translate} = useLocalize();

const domainCards = CardUtils.getDomainCards(cardList)[domain];
const virtualCard = _.find(domainCards, (card) => card.isVirtual) || {};
const virtualCardError = ErrorUtils.getLatestErrorMessage(virtualCard) || '';

const prevIsLoading = usePrevious(formData.isLoading);

useEffect(() => {
if (!prevIsLoading || formData.isLoading) {
return;
}
if (!_.isEmpty(virtualCard.errors)) {
return;
}

Navigation.navigate(ROUTES.SETTINGS_WALLET_DOMAINCARDS.getRoute(domain));
}, [domain, formData.isLoading, prevIsLoading, virtualCard.errors]);

if (_.isEmpty(virtualCard)) {
return <NotFoundPage />;
}

return (
<ScreenWrapper testID={ReportVirtualCardFraudPage.displayName}>
<HeaderWithBackButton
title={translate('reportFraudPage.title')}
onBackButtonPress={() => Navigation.goBack(ROUTES.SETTINGS_WALLET_DOMAINCARDS.getRoute(domain))}
/>
<View style={[styles.flex1, styles.justifyContentBetween]}>
<Text style={[styles.baseFontStyle, styles.mh5]}>{translate('reportFraudPage.description')}</Text>
<FormAlertWithSubmitButton
isAlertVisible={Boolean(virtualCardError)}
onSubmit={() => Card.reportVirtualExpensifyCardFraud(virtualCard.cardID)}
message={virtualCardError}
isLoading={formData.isLoading}
buttonText={translate('reportFraudPage.deactivateCard')}
/>
</View>
</ScreenWrapper>
);
}

ReportVirtualCardFraudPage.propTypes = propTypes;
ReportVirtualCardFraudPage.defaultProps = defaultProps;
ReportVirtualCardFraudPage.displayName = 'ReportVirtualCardFraudPage';

export default withOnyx({
cardList: {
key: ONYXKEYS.CARD_LIST,
},
formData: {
key: ONYXKEYS.FORMS.REPORT_VIRTUAL_CARD_FRAUD,
},
})(ReportVirtualCardFraudPage);
2 changes: 1 addition & 1 deletion src/styles/styles.js
Original file line number Diff line number Diff line change
Expand Up @@ -3770,7 +3770,7 @@ const styles = (theme) => ({
overflow: 'hidden',
},

walletCardNumber: {
walletCardMenuItem: {
color: theme.text,
fontSize: variables.fontSizeNormal,
},
Expand Down
Loading