export const isValidPhone = (value: string) => /^[0-9]{10}$/.test(value.replace(/\D/g, '')); export const isValidOtp = (value: string) => /^[0-9]{6}$/.test(value); /** * Single source of truth for how many digits an MPIN has. The backend accepts * 4 or 6; this app standardises on 4, so the keypad, the validators and the * stored-profile handling all read from here rather than each hard-coding it. */ export const MPIN_LENGTH = 4; /** * The backend refuses an all-same-digit MPIN. That rule is checked after * decryption server-side, so it comes back as a `VALIDATION_ERROR` with a * message rather than a field map — cheaper and clearer to catch it here * before spending a round trip. */ export const isValidMpin = (value: string) => new RegExp(`^[0-9]{${MPIN_LENGTH}}$`).test(value) && !isRepeatedDigits(value); export const isRepeatedDigits = (value: string) => value.length > 0 && /^(\d)\1*$/.test(value); /** * A straight run up or down — 1234, 6789, 4321. The backend refuses these * when an MPIN is SET, in the same message-only way as repeated digits. */ export const isSequentialDigits = (value: string) => { if (value.length < 2 || !/^[0-9]+$/.test(value)) { return false; } const step = Number(value[1]) - Number(value[0]); if (step !== 1 && step !== -1) { return false; } for (let index = 1; index < value.length; index += 1) { if (Number(value[index]) - Number(value[index - 1]) !== step) { return false; } } return true; }; /** * The stricter check for CHOOSING an MPIN — creation and reset. * * Deliberately separate from `isValidMpin`, which sign-in also uses: an MPIN * like 1234 set before the backend began refusing runs still has to be * enterable at sign-in, or its owner is locked out of their own profile. */ export const isAcceptableNewMpin = (value: string) => isValidMpin(value) && !isSequentialDigits(value); /** Constraints as the API documents them, not as any one bank formats them. */ export const isValidAccountNumber = (value: string) => /^[0-9]{5,20}$/.test(value); /** * An IFSC is always 11 characters. Every IFSC input caps at this and the * validator requires exactly this many, so the two cannot drift apart. */ export const IFSC_LENGTH = 11; export const isValidIfsc = (value: string) => new RegExp(`^[A-Za-z0-9]{${IFSC_LENGTH}}$`).test(value); export const isValidMmid = (value: string) => /^[0-9]{7}$/.test(value); export const isValidNickname = (value: string) => value.length <= 60; export const isValidAmount = (value: string) => { const amount = Number(value); return Number.isFinite(amount) && amount > 0; };