Skip to content

Commit

Permalink
Fix: Allow separators in bank account number validation
Browse files Browse the repository at this point in the history
  • Loading branch information
iamsamgibbs committed Oct 25, 2024
1 parent be36a75 commit a31cdd6
Show file tree
Hide file tree
Showing 3 changed files with 91 additions and 40 deletions.
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
import { defineMessages } from 'react-intl';

export const IBAN_REGEX =
/^([A-Z]{2}[ +\\-]?[0-9]{2})(?=(?:[ +\\-]?[A-Z0-9]){9,30}$)((?:[ +\\-]?[A-Z0-9]{3,5}){2,7})([ +\\-]?[A-Z0-9]{1,3})?$/;
export const ALPHANUMERIC_WITH_SEPARATORS_REGEX = /^[A-Za-z0-9 -]*$/;

export const BIC_REGEX =
// eslint-disable-next-line max-len
/^([a-zA-Z]{4})([a-zA-Z]{2})(([2-9a-zA-Z]{1})([0-9a-np-zA-NP-Z]{1}))((([0-9a-wy-zA-WY-Z]{1})([0-9a-zA-Z]{2}))|([xX]{3})?)$/;
export const SEPARATOR_REGEX = /[ -]/g;

// Strip any separators before comparing against this regex
// 1. Two uppercase letters at the start (for the country code).
// 2. Two digits immediately following (for the checksum).
// 3. 11–30 alphanumeric characters after that, allowing for a total length of 15–34 characters.
export const IBAN_REGEX = /^[A-Z]{2}[0-9]{2}[A-Z0-9]{11,30}$/;

export const displayName =
'v5.pages.UserCryptoToFiatPage.partials.BankDetailsForm';
Expand Down Expand Up @@ -80,14 +83,21 @@ export const BANK_DETAILS_FORM_MSG = defineMessages({
id: `${displayName}.routingNumberLabel`,
defaultMessage: 'Routing number',
},
alphanumeric: {
id: `${displayName}.alphanumeric`,
defaultMessage: 'Please enter only letters and numbers',
},
});

export const BANK_DETAILS_FORM_FIELD_VALUE_LENGTHS = {
accountNumber: {
min: 8,
min: 6,
max: 17,
},
routingNumber: {
length: 9,
},
swift: {
lengths: [8, 11],
},
};
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@ import { capitalizeFirstLetter } from '~utils/strings.ts';
import {
BANK_DETAILS_FORM_FIELD_VALUE_LENGTHS,
BANK_DETAILS_FORM_MSG,
BIC_REGEX,
ALPHANUMERIC_WITH_SEPARATORS_REGEX,
SEPARATOR_REGEX,
IBAN_REGEX,
} from './constants.ts';

Expand All @@ -23,7 +24,10 @@ export enum BankDetailsFields {
ROUTING_NUMBER = 'routingNumber',
}

const { accountNumber, routingNumber } = BANK_DETAILS_FORM_FIELD_VALUE_LENGTHS;
const { accountNumber, routingNumber, swift } =
BANK_DETAILS_FORM_FIELD_VALUE_LENGTHS;

const stripSeparators = (value: string) => value.replace(SEPARATOR_REGEX, '');

export const validationSchema = object({
[BankDetailsFields.ACCOUNT_OWNER]: string().required(
Expand Down Expand Up @@ -53,8 +57,22 @@ export const validationSchema = object({
}),
)
.matches(
IBAN_REGEX,
ALPHANUMERIC_WITH_SEPARATORS_REGEX,
formatText(BANK_DETAILS_FORM_MSG.alphanumeric),
)
.test(
'is-valid-iban',
formErrorMessage(BANK_DETAILS_FORM_MSG.ibanLabel, 'invalid'),
(value) => {
if (!value) {
return false;
}

// Strip separators before validating
const cleanedValue = stripSeparators(value);

return IBAN_REGEX.test(cleanedValue);
},
),
otherwise: string().notRequired(),
}),
Expand All @@ -67,8 +85,22 @@ export const validationSchema = object({
}),
)
.matches(
BIC_REGEX,
ALPHANUMERIC_WITH_SEPARATORS_REGEX,
formatText(BANK_DETAILS_FORM_MSG.alphanumeric),
)
.test(
'is-valid-swift',
formErrorMessage(BANK_DETAILS_FORM_MSG.swiftLabel, 'invalid'),
(value) => {
if (!value) {
return false;
}

// Strip separators before validating
const cleanedValue = stripSeparators(value);

return swift.lengths.includes(cleanedValue.length);
},
),
otherwise: string().notRequired(),
}),
Expand All @@ -83,30 +115,31 @@ export const validationSchema = object({
}),
)
.matches(
/^[0-9]+$/,
ALPHANUMERIC_WITH_SEPARATORS_REGEX,
formatText(BANK_DETAILS_FORM_MSG.alphanumeric),
)
.test(
'is-valid-account-number',
capitalizeFirstLetter(
formErrorMessage(
BANK_DETAILS_FORM_MSG.accountNumberLabel,
'invalid',
),
{ lowerCaseRemainingLetters: true },
),
)
.min(
accountNumber.min,
formErrorMessage(
BANK_DETAILS_FORM_MSG.accountNumberLabel,
'min',
accountNumber.min,
),
)
.max(
accountNumber.max,
formErrorMessage(
BANK_DETAILS_FORM_MSG.accountNumberLabel,
'max',
accountNumber.max,
),
(value) => {
if (!value) {
return false;
}

// Strip separators before validating
const cleanedValue = stripSeparators(value);

return (
cleanedValue.length >= accountNumber.min &&
cleanedValue.length <= accountNumber.max
);
},
),
otherwise: string().notRequired(),
},
Expand All @@ -122,22 +155,28 @@ export const validationSchema = object({
}),
)
.matches(
/^[0-9]+$/,
ALPHANUMERIC_WITH_SEPARATORS_REGEX,
formatText(BANK_DETAILS_FORM_MSG.alphanumeric),
)
.test(
'is-valid-routing-number',
capitalizeFirstLetter(
formErrorMessage(
BANK_DETAILS_FORM_MSG.routingNumberLabel,
'invalid',
),
{ lowerCaseRemainingLetters: true },
),
)
.length(
routingNumber.length,
formErrorMessage(
BANK_DETAILS_FORM_MSG.routingNumberLabel,
'length',
routingNumber.length,
),
(value) => {
if (!value) {
return false;
}

// Strip separators before validating
const cleanedValue = stripSeparators(value);

return cleanedValue.length === routingNumber.length;
},
),
otherwise: string().notRequired(),
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ import Toast from '~shared/Extensions/Toast/Toast.tsx';
import { type BridgeBankAccount } from '~types/graphql.ts';
import { formatText } from '~utils/intl.ts';

import { SEPARATOR_REGEX } from '../BankDetailsForm/constants.ts';

const displayName = 'v5.pages.UserCryptoToFiatPage.partials.BankDetailsModal';

// eslint-disable-next-line react-refresh/only-export-components
Expand Down Expand Up @@ -101,18 +103,18 @@ export const useBankDetailsFields = ({
currency === CURRENCY_VALUES[SupportedCurrencies.Eur]
? {
// eslint-disable-next-line camelcase
account_number: iban,
bic: swift,
account_number: iban.replace(SEPARATOR_REGEX, ''),
bic: swift.replace(SEPARATOR_REGEX, ''),
country,
}
: undefined,
usAccount:
currency === CURRENCY_VALUES[SupportedCurrencies.Usd]
? {
// eslint-disable-next-line camelcase
account_number: accountNumber,
account_number: accountNumber.replace(SEPARATOR_REGEX, ''),
// eslint-disable-next-line camelcase
routing_number: routingNumber,
routing_number: routingNumber.replace(SEPARATOR_REGEX, ''),
}
: undefined,
address:
Expand Down

0 comments on commit a31cdd6

Please sign in to comment.