/** * Validates a CNPJ (Cadastro Nacional da Pessoa Jurídica) * Checks format and verifies check digits */ export function validateCNPJ(cnpj: string): boolean { // Remove non-numeric characters const cleaned = cnpj.replace(/\D/g, '') // Must have exactly 14 digits if (cleaned.length !== 14) { return false } // All same digits are invalid if (/^(\d)\1{13}$/.test(cleaned)) { return false } // Validate first check digit let sum = 0 let position = 5 for (let i = 0; i < 8; i++) { sum += parseInt(cleaned[i]) * position position -= 1 } let remainder = sum % 11 const firstDigit = remainder < 2 ? 0 : 11 - remainder if (parseInt(cleaned[8]) !== firstDigit) { return false } // Validate second check digit sum = 0 position = 9 for (let i = 0; i < 9; i++) { sum += parseInt(cleaned[i]) * position position -= 1 } remainder = sum % 11 const secondDigit = remainder < 2 ? 0 : 11 - remainder if (parseInt(cleaned[9]) !== secondDigit) { return false } return true } /** * Formats a CNPJ string to the standard format: 00.000.000/0000-00 * Accepts both formatted and unformatted input */ export function formatCNPJ(value: string): string { const cleaned = value.replace(/\D/g, '').slice(0, 14) if (cleaned.length === 0) return '' if (cleaned.length <= 2) return cleaned if (cleaned.length <= 5) return `${cleaned.slice(0, 2)}.${cleaned.slice(2)}` if (cleaned.length <= 8) return `${cleaned.slice(0, 2)}.${cleaned.slice(2, 5)}.${cleaned.slice(5)}` if (cleaned.length <= 12) return `${cleaned.slice(0, 2)}.${cleaned.slice(2, 5)}.${cleaned.slice(5, 8)}/${cleaned.slice(8)}` return `${cleaned.slice(0, 2)}.${cleaned.slice(2, 5)}.${cleaned.slice(5, 8)}/${cleaned.slice(8, 12)}-${cleaned.slice(12)}` } /** * Removes CNPJ formatting, returning only digits */ export function removeCNPJFormatting(cnpj: string): string { return cnpj.replace(/\D/g, '') }