CNPJ ALFANUMÉRICO – Calculo do DV e Validação

O Cadastro Nacional da Pessoa Jurídica (CNPJ) foi criado pela Lei nº 4.595, de 31 de dezembro de 1964, e entrou em uso em 01 de Janeiro de 1965. Essa lei estabeleceu a necessidade de um registro único para identificar as empresas no Brasil, facilitando a sua administração tributária e a fiscalização por parte do governo. Desde então, o CNPJ se tornou essencial para a formalização e funcionamento das empresas no país.
Sua estrutura por composta por 14 dígitos 00.000.000/1111-22 onde temos os 8 primeiros dígitos representando o número do Cadastro Geral do Contribuinte que é também o número base de registro da empresa, seguido de 4 dígitos que representam o número da filial, iniciando em 0001 e indo sequencialmente até 9999 e para fins de validação, são acrescidos 2 dígitos finais conhecidos como DV que são calculados seguindo um algoritmo que explicaremos mais adiante.
Essa estrutura comporta cerca de 99 milhões de registros, já que a regra exclui alguns números, e como é um número único, e mantido mesmo após a extinção da empresa essa quantidade está prestes a se esgotar. Para solucionar o problema, a Receita Federal do Brasil, publicou no site https://www.gov.br/receitafederal/ uma nota oficial, explicando sobre a mudança, e também sobre a nova estrutura escolhida.
A mudança entra em vigor em JULHO/2026 pode parecer distante, porém, como se trata de uma mudança que vai afetar a forma de operação de todos os sistemas informáticos que lidam com dados de empresas, é necessário se preparar com muita antecedência e realizar testes e validações, e para ajudar os desenvolvedores que vão precisar fazer as novas validações, vamos explicar a estrutura e o algoritmo necessário para as novas regras de validação e um exemplo de código que executa o serviço em 2 linguagens de grande abrangência e que pode facilmente ser migrado para outras.
Em comparação a tipologia atual e a é composta somente por dígitos numéricos, e a nova identificação passa a ser alfanumérica, em termos da quantidade de registros, hoje é possível ter (10^8)-1 registros base o que dá 99999999 registros, porém existem alguns números reservados. Pela nova regra é possível ter (36^8)-1 registros já que além dos valores numéricos é possivel usar também as letras maiúsculas do alfabeto, o que dá cerca de 2,821109907×10 ≃ 2821109907000 (Dois trilhões, oitocentos e vinte e um bilhões, cento e nove milhões, novecentos e sete mil) mantendo os registros reservados, o que é cerca de 28 mil vezes mais registros.
Para o número máximo de filiais, atualmente são 9999, e pela nova estrutura, passam a ser 1.679.616 (um milhão, seiscentos e setenta e nove mil, seiscentos e dezesseis) filiais para cada empresa. Resumidamente é virtualmente ilimitado, e essa nova regra deve facilitar a vida dos novos empreendedores.
Como pode ser visto na imagem, os dígitos verificadores permanecem numéricos. Na regra atual, cada um dos primeiros 12 dígitos é multiplicado por um valor que varia de 2 a 9 da direita para a esquerda, depois é somado o produto dessas multiplicações e o resultado é dividido por 11, pega-se o resto da divisão e subtrai-se de 11 e esse valor corresponde ao primeiro dígito verificador. Para o segundo dígito, são usados os 13 dígitos e o processo é repetido, e desse modo obtemos o segundo dígito. Este algoritmo é amplamente utilizado e conhecido como MÓDULO 11.
Para evitar disrupturas no sistema, o calculo permanece o mesmo, e os CNPJ atuais continuam todos válidos. O que muda é que os dígitos numéricos também serão incluídos e passam a ter um valor. Como todas as letras são maiúsculas, optou-se pela utilização do valor decimal correspondente ao HexaDecimal da tabela ASCII, subtraído do valor 48. desse modo temos: A=17, B=18, C=19, D=20, E=21, F=22, G=23, H=24, I=25, J=26, K=27, L=28, M=29, N=30, O=31, P=32, Q=33, R=34, S=35, T=36, U=37, V=38, W=39, X=40, Y=41, Z=42.
Para a total adaptação ao novo modelo, você deve preparar os seus sistemas para permitirem a digitação dos algarismos alfanuméricos, preparar os bancos de dados para comportarem o novo formato, o que não é muito diferente do atual, já que a estrutura original a indicação é salvar os campos separados, sendo um campo de 8 posições para o inicio, e um campo de 4 posições para a filial, e que o dv seja calculado e/ou salvo em um terceiro campo.
A escolha pelo novo formato alfanumérico, foi preferida, pois mantém as mascaras atuais de 14 digitos, e as recomendações de armazenamento como campos de STRING, aumentar o número de dígitos, causaria impactos maiores, além de permitir pouco acréscimo de registro e uma sobrevida intermediária, o uso do alfanumérico permitiu um crescimento exponencial da estrutura, com pouco impacto significativo.
Para exemplificar o novo cáculo do DV vamos usar o CNPJ AB.CNPJ.24/NOVO, e iremos aplicar o passo a passo dos calculos abaixo, usando os valores acima, temos:
175 + 184 + 193 + 302 + 329 + 268 + 27 + 46 + 305 + 314 + 383 + 312 = 85 + 72 + 57 + 60 + 288 + 208 + 14 + 24 + 150 + 124 + 114 + 62 = 1258 / 11 = 114,36
=> 114 * 11 = 1254 – 1258 = -4 + 11 = 7
1 DV = 7 – refazendo o calculo acrescentendo o algarismo 7 temos
AB.CNPJ.24/NOVO-7 => 176 + 185 + 194 + 303 + 322 + 269 + 28 + 47 + 306 + 315 + 384 + 313 + 7*2 = 102 + 90 + 76 + 90 + 64 + 234 + 16 + 28 + 180 + 155 + 152 + 93 + 14 = 1294 / 11 = 117,63
=> 117 * 11 = 1287 – 1294 = -7 + 11 = 4
2 DV = 4 finalizando temos: AB.CNPJ.24/NOVO-74
Agora, vamos dar 2 exemplos de funções em PHP sendo uma para dados os 12 dígitos iniciais, ela devolver os 2 dígitos do DV, e uma segunda que dados os 14 digitos do CNPJ ela devolve se é ou não um número válido:
function calculaDvCnpj($cnpj = '00000000000000') { $cnpj = (string)$cnpj; if (empty($cnpj)) { throw new Exception('CNPJ VAZIO'); } if (strlen($cnpj) != 14 && strlen($cnpj) != 12) { throw new Exception('CNPJ DEVE SER INFORMADO COM 12 ou 14 POSIÇÕES '); } $cnpj = substr($cnpj, 0, 12); $cnpj = strtoupper($cnpj); if ( $cnpj == '000000000000' || $cnpj == '111111111111' || $cnpj == '222222222222' || $cnpj == '333333333333' || $cnpj == '444444444444' || $cnpj == '555555555555' || $cnpj == '666666666666' || $cnpj == '777777777777' || $cnpj == '888888888888' || $cnpj == '999999999999' ) { throw new Exception('CNPJ INFORMADO É INVALIDO, OU CONTÉM DIGITOS NÃO PERMITIDOS'); } $j = 5; $soma1 = 0; for ($i = 0; $i < 12; $i++) { $j = $j == 1 ? 9 : $j; $soma1 += ((hexdec(bin2hex($cnpj[$i])) - 48) * $j); $j--; } $digito1 = $soma1 % 11 < 2 ? 0 : 11 - $soma1 % 11; $cnpj = $cnpj.$digito1; $j = 6; $soma1 = 0; for ($i = 0; $i < 13; $i++) { $j = $j == 1 ? 9 : $j; $soma1 += ((hexdec(bin2hex($cnpj[$i])) - 48) * $j); $j--; } $digito2 = $soma1 % 11 < 2 ? 0 : 11 - $soma1 % 11; return("$digito1$digito2"); if (substr($cnpj, 12) == "$digito1" . "$digito2") { return true; } else { return false; } } function validaCnpj($cnpj = '00000000000000') { $cnpj = (string)$cnpj; if (empty($cnpj)) { return false; } if (strlen($cnpj) != 14) { return false; } $cnpj = strtoupper($cnpj); if ( $cnpj == '00000000000000' || $cnpj == '11111111111111' || $cnpj == '22222222222222' || $cnpj == '33333333333333' || $cnpj == '44444444444444' || $cnpj == '55555555555555' || $cnpj == '66666666666666' || $cnpj == '77777777777777' || $cnpj == '88888888888888' || $cnpj == '99999999999999' ) { return false; } $j = 5; $k = 6; $soma1 = 0; $soma2 = 0; for ($i = 0; $i < 13; $i++) { $j = $j == 1 ? 9 : $j; $k = $k == 1 ? 9 : $k; $soma2 += ((hexdec(bin2hex($cnpj[$i])) - 48) * $k); if ($i < 12) { $soma1 += ((hexdec(bin2hex($cnpj[$i])) - 48) * $j); } $k--; $j--; } $digito1 = $soma1 % 11 < 2 ? 0 : 11 - $soma1 % 11; $digito2 = $soma2 % 11 < 2 ? 0 : 11 - $soma2 % 11; if (substr($cnpj, 12) == "$digito1" . "$digito2") { return true; } else { return false; } }
para testar este códigos usamos o bloco abaixo, utilizando o cnpj passado no exemplo AB.CNP.J24/NOVO-74
try { echo calculaDvCnpj('ABCNPJ24NOVO'); $cn = calculaDvCnpj('ABCNPJ24NOVO'); } catch (Exception $e) { echo 'Caught exception: ', $e->getMessage(), "\n"; die(); } echo $cn . '<br>'; if (validaCnpj('ABCNPJ24NOVO74')) { echo 'CNPJ VÁLIDO'; } else { echo 'INVÁLIDO'; }
Para facilitar um pouco mais, também preparei a versão JavaScript desse mesmo algoritmo, segue o código:
function convertCharToNumber(char) { const charCode = char.charCodeAt(0); if (char >= '0' && char <= '9') return parseInt(char, 10); if (char >= 'A' && char <= 'Z') return charCode - 48; throw new Error('Caractere inválido no CNPJ'); } function calculaDvCnpj(cnpj = '00000000000000') { cnpj = String(cnpj).toUpperCase(); if (!cnpj) { throw new Error('CNPJ VAZIO'); } if (cnpj.length !== 14 && cnpj.length !== 12) { throw new Error('CNPJ DEVE SER INFORMADO COM 12 ou 14 POSIÇÕES'); } cnpj = cnpj.substring(0, 12); const invalidCnpjs = [ '000000000000', '111111111111', '222222222222', '333333333333', '444444444444', '555555555555', '666666666666', '777777777777', '888888888888', '999999999999', 'AAAAAAAAAAAA' ]; if (invalidCnpjs.includes(cnpj)) { throw new Error('CNPJ INFORMADO É INVALIDO, OU CONTÉM DIGITOS NÃO PERMITIDOS'); } let j = 5; let soma1 = 0; for (let i = 0; i < 12; i++) { j = j === 1 ? 9 : j; soma1 += convertCharToNumber(cnpj[i]) * j; j--; } const digito1 = soma1 % 11 < 2 ? 0 : 11 - (soma1 % 11); cnpj = cnpj + digito1; j = 6; soma1 = 0; for (let i = 0; i < 13; i++) { j = j === 1 ? 9 : j; soma1 += convertCharToNumber(cnpj[i]) * j; j--; } const digito2 = soma1 % 11 < 2 ? 0 : 11 - (soma1 % 11); return `${digito1}${digito2}`; } function validaCnpj(cnpj = '00000000000000') { cnpj = String(cnpj).toUpperCase(); if (!cnpj) { return false; } if (cnpj.length !== 14) { return false; } const invalidCnpjs = [ '00000000000000', '11111111111111', '22222222222222', '33333333333333', '44444444444444', '55555555555555', '66666666666666', '77777777777777', '88888888888888', '99999999999999', 'AAAAAAAAAAAAAA' ]; if (invalidCnpjs.includes(cnpj)) { return false; } let j = 5, k = 6; let soma1 = 0, soma2 = 0; for (let i = 0; i < 13; i++) { j = j === 1 ? 9 : j; k = k === 1 ? 9 : k; soma2 += convertCharToNumber(cnpj[i]) * k; if (i < 12) { soma1 += convertCharToNumber(cnpj[i]) * j; } k--; j--; } const digito1 = soma1 % 11 < 2 ? 0 : 11 - (soma1 % 11); const digito2 = soma2 % 11 < 2 ? 0 : 11 - (soma2 % 11); return cnpj.slice(12) === `${digito1}${digito2}`; }
E para testar o script acima usamos os mesmos valores do exemplo em PHP
alert(calculaDvCnpj('ABCNPJ24NOVO')); if(validaCnpj('ABCNPJ24NOVO74')) { alert('ok') } else { alert('erro'); }
Espero poder ter ajudado na sua implementação e se tiver alguma duvida, é só deixar ai nos comentários.