diff --git a/src/libs/CardUtils.ts b/src/libs/CardUtils.ts index d56477c3f148..77aeb8e0ecc3 100644 --- a/src/libs/CardUtils.ts +++ b/src/libs/CardUtils.ts @@ -135,15 +135,26 @@ function maskCard(lastFour = ''): string { * Converts given 'X' to '•' for the entire card string. * * @param cardName - card name with XXXX in the middle. + * @param feed - card feed. * @returns - The masked card string. */ -function maskCardNumber(cardName: string): string { +function maskCardNumber(cardName: string, feed: string | undefined): string { if (!cardName || cardName === '') { return ''; } const hasSpace = /\s/.test(cardName); const maskedString = cardName.replace(/X/g, '•'); - return hasSpace ? cardName : maskedString.replace(/(.{4})/g, '$1 ').trim(); + const isAmexBank = [CONST.COMPANY_CARD.FEED_BANK_NAME.AMEX, CONST.COMPANY_CARD.FEED_BANK_NAME.AMEX_DIRECT].some((value) => value === feed); + + if (hasSpace) { + return cardName; + } + + if (isAmexBank && maskedString.length === 15) { + return maskedString.replace(/(.{4})(.{6})(.{5})/, '$1 $2 $3'); + } + + return maskedString.replace(/(.{4})/g, '$1 ').trim(); } /** diff --git a/src/pages/settings/Wallet/PaymentMethodList.tsx b/src/pages/settings/Wallet/PaymentMethodList.tsx index 6913b166100e..e9280d494857 100644 --- a/src/pages/settings/Wallet/PaymentMethodList.tsx +++ b/src/pages/settings/Wallet/PaymentMethodList.tsx @@ -238,7 +238,7 @@ function PaymentMethodList({ if (!CardUtils.isExpensifyCard(card.cardID)) { assignedCardsGrouped.push({ key: card.cardID.toString(), - title: CardUtils.maskCardNumber(card.cardName ?? ''), + title: CardUtils.maskCardNumber(card.cardName ?? '', card.bank), description: getDescriptionForPolicyDomainCard(card.domainName), shouldShowRightIcon: false, interactive: false, diff --git a/src/pages/workspace/companyCards/WorkspaceCompanyCardDetailsPage.tsx b/src/pages/workspace/companyCards/WorkspaceCompanyCardDetailsPage.tsx index 9fdfa7bec7b3..bee196031bda 100644 --- a/src/pages/workspace/companyCards/WorkspaceCompanyCardDetailsPage.tsx +++ b/src/pages/workspace/companyCards/WorkspaceCompanyCardDetailsPage.tsx @@ -112,7 +112,7 @@ function WorkspaceCompanyCardDetailsPage({route}: WorkspaceCompanyCardDetailsPag /> diff --git a/src/pages/workspace/companyCards/WorkspaceCompanyCardsList.tsx b/src/pages/workspace/companyCards/WorkspaceCompanyCardsList.tsx index 9e9e2bf5208f..75b4f44fc843 100644 --- a/src/pages/workspace/companyCards/WorkspaceCompanyCardsList.tsx +++ b/src/pages/workspace/companyCards/WorkspaceCompanyCardsList.tsx @@ -60,7 +60,7 @@ function WorkspaceCompanyCardsList({cardsList, policyID}: WorkspaceCompanyCardsL > diff --git a/src/pages/workspace/companyCards/assignCard/CardSelectionStep.tsx b/src/pages/workspace/companyCards/assignCard/CardSelectionStep.tsx index f9e552ac5afe..4b07e7a220b8 100644 --- a/src/pages/workspace/companyCards/assignCard/CardSelectionStep.tsx +++ b/src/pages/workspace/companyCards/assignCard/CardSelectionStep.tsx @@ -105,7 +105,7 @@ function CardSelectionStep({feed, policyID}: CardSelectionStepProps) { const cardListOptions = Object.entries(filteredCardList).map(([cardNumber, encryptedCardNumber]) => ({ keyForList: encryptedCardNumber, value: encryptedCardNumber, - text: CardUtils.maskCardNumber(cardNumber), + text: CardUtils.maskCardNumber(cardNumber, feed), isSelected: cardSelected === encryptedCardNumber, leftElement: ( editStep(CONST.COMPANY_CARD.STEP.CARD)} /> diff --git a/tests/unit/CardUtilsTest.ts b/tests/unit/CardUtilsTest.ts index 09a36fcfb562..9d4af5aa3760 100644 --- a/tests/unit/CardUtilsTest.ts +++ b/tests/unit/CardUtilsTest.ts @@ -215,4 +215,41 @@ describe('CardUtils', () => { expect(feedName).toBe(undefined); }); }); + + describe('maskCardNumber', () => { + it("Should return the card number divided into chunks of 4, with 'X' replaced by '•' if it's provided in the '480801XXXXXX2554' format", () => { + const cardNumber = '480801XXXXXX2554'; + const maskedCardNumber = CardUtils.maskCardNumber(cardNumber, CONST.COMPANY_CARD.FEED_BANK_NAME.MASTER_CARD); + expect(maskedCardNumber).toBe('4808 01•• •••• 2554'); + }); + + it('Should return card number without changes if it has empty space', () => { + const cardNumber = 'CREDIT CARD...6607'; + const maskedCardNumber = CardUtils.maskCardNumber(cardNumber, CONST.COMPANY_CARD.FEED_BANK_NAME.CHASE); + expect(maskedCardNumber).toBe(cardNumber); + }); + + it("Should return the Amex direct feed card number divided into 4/6/5 chunks, with 'X' replaced by '•' if it's provided in '211944XXXXX6557' format", () => { + const cardNumber = '211944XXXXX6557'; + const maskedCardNumber = CardUtils.maskCardNumber(cardNumber, CONST.COMPANY_CARD.FEED_BANK_NAME.AMEX_DIRECT); + expect(maskedCardNumber).toBe('2119 44•••• •6557'); + }); + + it("Should return the Amex custom feed card number divided into 4/6/5 chunks, with 'X' replaced by '•' if it's provided in '211944XXXXX6557' format", () => { + const cardNumber = '211944XXXXX6557'; + const maskedCardNumber = CardUtils.maskCardNumber(cardNumber, CONST.COMPANY_CARD.FEED_BANK_NAME.AMEX); + expect(maskedCardNumber).toBe('2119 44•••• •6557'); + }); + + it('Should return masked card number even if undefined feed was provided', () => { + const cardNumber = '480801XXXXXX2554'; + const maskedCardNumber = CardUtils.maskCardNumber(cardNumber, undefined); + expect(maskedCardNumber).toBe('4808 01•• •••• 2554'); + }); + + it('Should return empty string if invalid card name was provided', () => { + const maskedCardNumber = CardUtils.maskCardNumber('', CONST.COMPANY_CARD.FEED_BANK_NAME.MASTER_CARD); + expect(maskedCardNumber).toBe(''); + }); + }); });