Skip to content

Instantly share code, notes, and snippets.

@SamuelHiroyuki
Last active October 19, 2020 19:00
Show Gist options
  • Save SamuelHiroyuki/6a993b940fe738d6e89f8595ef75acc1 to your computer and use it in GitHub Desktop.
Save SamuelHiroyuki/6a993b940fe738d6e89f8595ef75acc1 to your computer and use it in GitHub Desktop.
A validator for Brazilian CPF and CNPJ documents
// This validator does not check the formatting of the documents.
// Documents can be passed with or without formatting characters.
const processes = {
cpf: {
length: 11,
validator: [11, 10, 9, 8, 7, 6, 5, 4, 3, 2],
validationRegex: (d1, d2) => `([0-9]{9}${d1}${d2})`,
format: [/(\d{3})(\d{3})(\d{3})(\d{2})/, "$1.$2.$3-$4"],
},
cnpj: {
length: 14,
validator: [6, 5, 4, 3, 2, 9, 8, 7, 6, 5, 4, 3, 2],
validationRegex: (d1, d2) => `([0-9]{12}${d1}${d2})`,
format: [/(\d{2})(\d{3})(\d{3})(\d{4})(\d{2})/, "$1.$2.$3/$4-$5"],
},
}
const convertCheckNumber = (checkNumber) => {
const convertedDigit = (checkNumber * 10) % 11;
return convertedDigit > 9 ? 0 : convertedDigit;
}
export function validateDocument({ document = "", type = "" }) {
// Strip all non-numeric characters
const sanitizedDocument = document.replace(/[^\d]+/g, '');
if (!processes[type]) {
const typesLength = Object.keys(processes).map(process => processes[process].length);
const maxLength = typesLength.find(length => sanitizedDocument.length <= length);
if (!maxLength) {
return {
isValid: false,
type: "none",
value: sanitizedDocument,
formattedValue: sanitizedDocument,
};
}
type = Object.keys(processes).find(process => processes[process].length === maxLength);
}
// Retrieve unique process values based on type
const { validator, validationRegex, length, format } = processes[type];
// CPF and CNPJ cannot be a sequence of equal numbers, e.g. 111.111.111-11 and 11.111.111/1111-11
const isInvalidDocument = /^(\d)\1+$/.test(sanitizedDocument);
if (isInvalidDocument || sanitizedDocument.length !== length) {
return {
isValid: false,
type,
value: sanitizedDocument,
formattedValue: sanitizedDocument,
};
}
const [firstCheckNumber, secondCheckNumber] = validator.reduce(([firstCN, secondCN], value, index) => [
firstCN + (sanitizedDocument[index - 1] || 0) * value,
secondCN + sanitizedDocument[index] * value,
], [0, 0]);
const firstDigit = convertCheckNumber(firstCheckNumber);
const secondDigit = convertCheckNumber(secondCheckNumber);
const checkLastDigits = new RegExp(validationRegex(firstDigit, secondDigit));
return {
isValid: checkLastDigits.test(sanitizedDocument),
type,
value: sanitizedDocument,
formattedValue: sanitizedDocument.replace(...format),
};
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment