CNPJ Alfanumérico: O Guia Completo Para Programadores Prepararem Seus Sistemas

CNPJ alfanumérico chega em julho de 2026. Veja datas, prazos, combinações e como validar o novo CNPJ. Guia completo para programadores adaptarem bancos de dados, APIs e sistemas.

Publicado

Durante décadas, o CNPJ (Cadastro Nacional da Pessoa Jurídica) foi composto apenas por números. Isso está prestes a mudar.

A Receita Federal vai adotar o chamado CNPJ alfanumérico, um modelo que passa a permitir letras e números na composição do identificador das empresas.

Para o usuário comum, parece uma mudança pequena. Mas para programadores, DBAs e equipes de TI, o impacto é grande.

Neste guia você vai entender os motivos da mudança, as datas oficiais, os prazos, quanto aumenta a capacidade de registros e, principalmente, como validar o novo CNPJ do jeito certo.

O que é o CNPJ alfanumérico?

O novo modelo mantém os 14 caracteres do CNPJ atual, mas passa a aceitar letras e números em parte da composição.

Exemplo fictício:

12.ABC.345/0001-XY

A estrutura básica continua a mesma:

  • 12 caracteres de base (que poderão ser letras ou números)
  • 2 dígitos verificadores numéricos no final

Ou seja, o CNPJ deixa de ser exclusivamente numérico e passa a ser um identificador alfanumérico.

Por que a Receita Federal está fazendo essa mudança?

O principal motivo é o esgotamento das combinações numéricas.

Com o crescimento do empreendedorismo, a expansão do MEI (Microempreendedor Individual) e o aumento das startups, o número de empresas abertas no Brasil cresceu de forma acelerada.

O modelo numérico tradicional tem uma capacidade limitada. Ao permitir letras, a quantidade de combinações cresce de forma exponencial, garantindo capacidade para as próximas décadas.

Quando entra em vigor o CNPJ alfanumérico?

A mudança já tem cronograma oficial.

A Receita Federal publicou a IN RFB nº 2.229/2024 em 15 de outubro de 2024, com vigência a partir de 25 de outubro de 2024. Já a implementação prática do novo modelo está prevista para julho de 2026.

Segundo a Receita, o CNPJ alfanumérico será atribuído apenas a novas inscrições a partir de julho de 2026.

Um detalhe importante: a transição será progressiva. Nem toda nova inscrição vai receber letras já no primeiro dia, pois a implantação acontecerá de forma gradual.

Os CNPJs antigos vão mudar?

Não.

Esse é um dos pontos mais importantes.

Os CNPJs já existentes continuam exatamente como estão. Não haverá conversão em massa, recadastro obrigatório, troca automática de número nem alteração dos dígitos verificadores atuais.

Na prática, o Brasil vai conviver com dois formatos ao mesmo tempo:

  • CNPJ antigo, só com números
  • CNPJ novo, com letras e números

Por isso, os sistemas precisam estar preparados para aceitar os dois formatos simultaneamente.

Qual é o prazo real para adaptar os sistemas?

Para software houses, ERPs, emissores fiscais, marketplaces, bancos, fintechs e integradores, o prazo prático é direto:

os sistemas precisam estar prontos antes de julho de 2026.

Resumindo:

  • prazo legal da mudança: julho de 2026
  • prazo técnico recomendado: o quanto antes
  • prazo máximo seguro: antes de julho de 2026

E tem um detalhe extra: no ecossistema fiscal, cada documento eletrônico pode ter seu cronograma técnico próprio de homologação e produção, por meio de Notas Técnicas. Não basta adaptar só o cadastro interno. É preciso acompanhar também NF-e, NFC-e, CT-e, MDF-e e outros documentos.

De quantas combinações para quantas combinações o CNPJ passa?

Aqui vale a explicação simples.

No modelo numérico, a raiz do CNPJ trabalha com cerca de 100 milhões de combinações.

Com a adoção de letras e números, esse universo sobe para quase 3 trilhões de combinações.

Para quem gosta da conta técnica:

  • modelo numérico: 10⁸ = 100.000.000
  • modelo alfanumérico: 36⁸ = 2.821.109.907.456

Ou seja, salta de 100 milhões para aproximadamente 2,82 trilhões de combinações. É por isso que a mudança faz sentido: evitar o esgotamento da numeração.

O impacto para desenvolvedores é maior do que parece

Muitos sistemas brasileiros foram criados assumindo que o CNPJ sempre seria um número.

Esse é o principal problema.

Durante anos, muitos sistemas armazenaram CNPJs como:

  • Integer
  • BigInt
  • Numeric
  • Long
  • Double

Isso já era uma prática inadequada. Agora, passa a ser um erro crítico.

O jeito certo de tratar um CNPJ

O CNPJ nunca deve ser tratado como número.

O correto é tratá-lo como texto.

Correto

VARCHAR(14) CHAR(14) STRING

Incorreto

INT BIGINT FLOAT DOUBLE DECIMAL

O CNPJ é um identificador, não um valor matemático. Ninguém soma, multiplica ou divide um CNPJ. Portanto, ele deve ser armazenado como texto.

Sistemas que precisarão de revisão

A mudança afeta praticamente qualquer software que lide com dados empresariais:

  • ERPs
  • CRMs
  • Sistemas fiscais e contábeis
  • Plataformas de e-commerce e marketplaces
  • Gateways de pagamento
  • Softwares de emissão de notas fiscais
  • Sistemas bancários
  • APIs de integração
  • Sistemas governamentais
  • Aplicativos mobile

Mesmo sistemas internos de pequenas empresas podem precisar de atualização.

Bancos de dados: um dos pontos mais críticos

Uma revisão completa deve incluir:

Estrutura das tabelas: campos como cnpj BIGINT ou cnpj NUMERIC(14) precisarão virar cnpj VARCHAR(14).

Índices: alguns índices podem precisar ser recriados.

Procedures e funções: tudo que assume valores numéricos deve ser revisado.

Triggers: as triggers de validação também precisam ser verificadas.

Como validar o novo CNPJ do jeito certo

Esse é o ponto em que muitos sistemas vão quebrar se forem adaptados pela metade.

A validação correta passa por 4 etapas:

1. Normalizar a entrada

Remova a máscara e padronize para maiúsculas.

12.ABC.345/01DE-35 vira 12ABC34501DE35

2. Validar o formato

O formato passa a aceitar 12 posições alfanuméricas e 2 dígitos verificadores numéricos:

(regex)

^[A-Z0-9]{12}[0-9]{2}$

Mas atenção: regex sozinho não valida CNPJ. Ele só verifica se o formato parece correto.

3. Calcular o dígito verificador (DV)

O DV continua usando módulo 11, mas com uma adaptação importante: para cada caractere, use o valor ASCII e subtraia 48

Assim:

  • 0 a 9 continuam valendo 0 a 9
  • A = 65 - 48 = 17
  • B = 66 - 48 = 18
  • C = 67 - 48 = 19
  • Z = 90 - 48 = 42

Agora vamos adotar um CNPJ como exemplo:

AB.C12.345.0001 (os 12 primeiros caracteres)

Pesos do 1º DV:

  • A = 17 * 5 = 85
  • B = 18 * 4 = 72
  • C = 19 * 3 = 57
  • 1 = 1 * 2 = 2
  • 2 = 2 * 9 = 18
  • 3 = 3 * 8 = 24
  • 4 = 4 * 7 = 28
  • 5 = 5 * 6 = 30
  • 0 = 0 * 5 = 0
  • 0 = 0 * 4 = 0
  • 0 = 0 * 3 = 0
  • 1 = 1 * 2 = 2

Somando tudo temos: 318

Divide por 11, temos : 28,90. Pega o 28 e calcula a sobra da divisão: 10

A regra do módulo 11:

  • Se for resto 0 ou 1, o dígito é 0
  • Se for de 2 a 10, o dígito é 11 – resto.

No nosso caso fica 11 – 10 = 1

Então 1 é o primeiro dígito

Pesos do 2º DV:

  • A = 17 * 6 = 102
  • B = 18 * 5 = 90
  • C = 19 * 4 = 76
  • 1 = 1 * 3 = 3
  • 2 = 2 * 2 = 4
  • 3 = 3 * 9 = 27
  • 4 = 4 * 8 = 32
  • 5 = 5 * 7 = 35
  • 0 = 0 * 6 = 0
  • 0 = 0 * 5 = 0
  • 0 = 0 * 4 = 0
  • 1 = 1 * 3 = 3
  • (Primeiro DV) 1 = 1 * 2 = 2

Soma desse item: 374

Divide por 11, ficando 34 com resto zero. Então seguindo a regra fica com o segundo dígito 0.

Então ficamos com o CNPJ assim:

ABC12345/0001-10

4. Comparar com os dois últimos caracteres

Compare os dois dígitos calculados com os dois últimos caracteres do CNPJ informado. Se baterem, o CNPJ é válido.

Funções de validação em várias linguagens

Abaixo você encontra exemplos de validação do CNPJ alfanumérico nas principais linguagens, incluindo opções modernas e legadas.

Ver código em C
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <stdbool.h>

bool validar_novo_cnpj(const char* cnpj) {
    char cnpj_limpo[15];
    int idx = 0;
    
    for (int i = 0; cnpj[i] != '\0' && idx < 14; i++) {
        if (isalnum((unsigned char)cnpj[i])) {
            cnpj_limpo[idx++] = toupper((unsigned char)cnpj[i]);
        }
    }
    cnpj_limpo[idx] = '\0';
    
    if (idx != 14) return false;
    if (!isdigit((unsigned char)cnpj_limpo[12]) || !isdigit((unsigned char)cnpj_limpo[13])) return false;
    
    int pesos_dv1[] = {5, 4, 3, 2, 9, 8, 7, 6, 5, 4, 3, 2};
    int pesos_dv2[] = {6, 5, 4, 3, 2, 9, 8, 7, 6, 5, 4, 3, 2};
    
    int soma_dv1 = 0;
    for (int i = 0; i < 12; i++) {
        soma_dv1 += ((int)cnpj_limpo[i] - 48) * pesos_dv1[i];
    }
    int resto_dv1 = soma_dv1 % 11;
    int dv1 = (resto_dv1 < 2) ? 0 : 11 - resto_dv1;
    
    int soma_dv2 = 0;
    for (int i = 0; i < 12; i++) {
        soma_dv2 += ((int)cnpj_limpo[i] - 48) * pesos_dv2[i];
    }
    soma_dv2 += dv1 * pesos_dv2[12];
    int resto_dv2 = soma_dv2 % 11;
    int dv2 = (resto_dv2 < 2) ? 0 : 11 - resto_dv2;
    
    return (cnpj_limpo[12] - '0') == dv1 && (cnpj_limpo[13] - '0') == dv2;
}
Ver código em C#
using System;
using System.Text.RegularExpressions;

public static class CnpjValidator
{
    public static bool ValidarNovoCnpj(string cnpj)
    {
        if (string.IsNullOrWhiteSpace(cnpj)) return false;
        
        string cnpjLimpo = Regex.Replace(cnpj, "[^A-Za-z0-9]", "").ToUpper();
        
        if (cnpjLimpo.Length != 14) return false;
        if (!char.IsDigit(cnpjLimpo[12]) || !char.IsDigit(cnpjLimpo[13])) return false;
        
        int[] pesosDv1 = { 5, 4, 3, 2, 9, 8, 7, 6, 5, 4, 3, 2 };
        int[] pesosDv2 = { 6, 5, 4, 3, 2, 9, 8, 7, 6, 5, 4, 3, 2 };
        
        int somaDv1 = 0;
        for (int i = 0; i < 12; i++)
        {
            somaDv1 += ((int)cnpjLimpo[i] - 48) * pesosDv1[i];
        }
        int restoDv1 = somaDv1 % 11;
        int dv1 = restoDv1 < 2 ? 0 : 11 - restoDv1;
        
        int somaDv2 = 0;
        for (int i = 0; i < 12; i++)
        {
            somaDv2 += ((int)cnpjLimpo[i] - 48) * pesosDv2[i];
        }
        somaDv2 += dv1 * pesosDv2[12];
        int restoDv2 = somaDv2 % 11;
        int dv2 = restoDv2 < 2 ? 0 : 11 - restoDv2;
        
        return (cnpjLimpo[12] - '0') == dv1 && (cnpjLimpo[13] - '0') == dv2;
    }
}
Ver código em C++
#pragma once
#include <string>
#include <algorithm>
#include <cctype>
#include <vector>

inline bool validar_novo_cnpj(const std::string& cnpj) {
    std::string cnpj_limpo;
    
    for (char c : cnpj) {
        if (std::isalnum(static_cast<unsigned char>(c))) {
            cnpj_limpo += std::toupper(static_cast<unsigned char>(c));
        }
    }
    
    if (cnpj_limpo.length() != 14) return false;
    if (!std::isdigit(cnpj_limpo[12]) || !std::isdigit(cnpj_limpo[13])) return false;
    
    const int pesos_dv1[] = {5, 4, 3, 2, 9, 8, 7, 6, 5, 4, 3, 2};
    const int pesos_dv2[] = {6, 5, 4, 3, 2, 9, 8, 7, 6, 5, 4, 3, 2};
    
    int soma_dv1 = 0;
    for (int i = 0; i < 12; ++i) {
        soma_dv1 += (static_cast<int>(cnpj_limpo[i]) - 48) * pesos_dv1[i];
    }
    int resto_dv1 = soma_dv1 % 11;
    int dv1 = (resto_dv1 < 2) ? 0 : 11 - resto_dv1;
    
    int soma_dv2 = 0;
    for (int i = 0; i < 12; ++i) {
        soma_dv2 += (static_cast<int>(cnpj_limpo[i]) - 48) * pesos_dv2[i];
    }
    soma_dv2 += dv1 * pesos_dv2[12];
    int resto_dv2 = soma_dv2 % 11;
    int dv2 = (resto_dv2 < 2) ? 0 : 11 - resto_dv2;
    
    return (cnpj_limpo[12] - '0') == dv1 && (cnpj_limpo[13] - '0') == dv2;
}
Ver código em Clipper / Harbour
FUNCTION ValidarNovoCnpj( cCnpj )
    LOCAL cCnpjLimpo := ""
    LOCAL cChar := ""
    LOCAL nI := 0
    LOCAL nValor := 0
    LOCAL nSomaDv1 := 0, nRestoDv1 := 0, nDv1 := 0
    LOCAL nSomaDv2 := 0, nRestoDv2 := 0, nDv2 := 0
    
    LOCAL aPesosDv1 := { 5, 4, 3, 2, 9, 8, 7, 6, 5, 4, 3, 2 }
    LOCAL aPesosDv2 := { 6, 5, 4, 3, 2, 9, 8, 7, 6, 5, 4, 3, 2 }

    cCnpj := Upper( cCnpj )

    FOR nI := 1 TO Len( cCnpj )
        cChar := SubStr( cCnpj, nI, 1 )
        IF ( cChar >= "0" .AND. cChar <= "9" ) .OR. ( cChar >= "A" .AND. cChar <= "Z" ) cCnpjLimpo += cChar ENDIF NEXT IF Len( cCnpjLimpo ) != 14 RETURN .F. ENDIF IF .NOT. ( SubStr( cCnpjLimpo, 13, 1 ) >= "0" .AND. SubStr( cCnpjLimpo, 13, 1 ) <= "9" ) .OR. ; .NOT. ( SubStr( cCnpjLimpo, 14, 1 ) >= "0" .AND. SubStr( cCnpjLimpo, 14, 1 ) <= "9" )
        RETURN .F.
    ENDIF

    FOR nI := 1 TO 12
        nValor := Asc( SubStr( cCnpjLimpo, nI, 1 ) ) - 48
        nSomaDv1 += nValor * aPesosDv1[ nI ]
    NEXT

    nRestoDv1 := nSomaDv1 % 11
    nDv1 := If( nRestoDv1 < 2, 0, 11 - nRestoDv1 )

    FOR nI := 1 TO 12
        nValor := Asc( SubStr( cCnpjLimpo, nI, 1 ) ) - 48
        nSomaDv2 += nValor * aPesosDv2[ nI ]
    NEXT
    nSomaDv2 += nDv1 * aPesosDv2[ 13 ]

    nRestoDv2 := nSomaDv2 % 11
    nDv2 := If( nRestoDv2 < 2, 0, 11 - nRestoDv2 )

    RETURN ( Val( SubStr( cCnpjLimpo, 13, 1 ) ) == nDv1 .AND. ;
             Val( SubStr( cCnpjLimpo, 14, 1 ) ) == nDv2 )
Ver código em COBOL
       IDENTIFICATION DIVISION.
       PROGRAM-ID. VALIDACNPJ.
       
       DATA DIVISION.
       WORKING-STORAGE SECTION.
       01  WS-CNPJ-ENTRADA     PIC X(20) VALUE "123A5678901200".
       01  WS-CNPJ-LIMPO       PIC X(14).
       01  WS-CNPJ-AUX.
           05 WS-CHAR          PIC X OCCURS 14 TIMES.
       01  WS-VALORES.
           05 WS-VAL          PIC 9(3) OCCURS 14 TIMES.
       
       01  WS-IND              PIC 9(2).
       01  WS-IND-LIMPO        PIC 9(2) VALUE 1.
       01  WS-ASCII            PIC 9(3).
       
       01  WS-PESOS-DV1.
           05 FILLER PIC 9 VALUE 5. 05 FILLER PIC 9 VALUE 4.
           05 FILLER PIC 9 VALUE 3. 05 FILLER PIC 9 VALUE 2.
           05 FILLER PIC 9 VALUE 9. 05 FILLER PIC 9 VALUE 8.
           05 FILLER PIC 9 VALUE 7. 05 FILLER PIC 9 VALUE 6.
           05 FILLER PIC 9 VALUE 5. 05 FILLER PIC 9 VALUE 4.
           05 FILLER PIC 9 VALUE 3. 05 FILLER PIC 9 VALUE 2.
       01  FILLER REDEFINES WS-PESOS-DV1.
           05 WS-P1            PIC 9 OCCURS 12 TIMES.
           
       01  WS-PESOS-DV2.
           05 FILLER PIC 9 VALUE 6. 05 FILLER PIC 9 VALUE 5.
           05 FILLER PIC 9 VALUE 4. 05 FILLER PIC 9 VALUE 3.
           05 FILLER PIC 9 VALUE 2. 05 FILLER PIC 9 VALUE 9.
           05 FILLER PIC 9 VALUE 8. 05 FILLER PIC 9 VALUE 7.
           05 FILLER PIC 9 VALUE 6. 05 FILLER PIC 9 VALUE 5.
           05 FILLER PIC 9 VALUE 4. 05 FILLER PIC 9 VALUE 3.
           05 FILLER PIC 9 VALUE 2.
       01  FILLER REDEFINES WS-PESOS-DV2.
           05 WS-P2            PIC 9 OCCURS 13 TIMES.

       01  WS-SOMA             PIC 9(6) VALUE 0.
       01  WS-RESTO            PIC 9(4) VALUE 0.
       01  WS-DV1-CALC         PIC 9(1) VALUE 0.
       01  WS-DV2-CALC         PIC 9(1) VALUE 0.
       01  WS-VALIDO           PIC X(3) VALUE "NAO".

       PROCEDURE DIVISION.
       MAIN-PROCEDURE.
           INSPECT WS-CNPJ-ENTRADA CONVERTING "abcdefghijklmnopqrstuvwxyz" 
                                           TO "ABCDEFGHIJKLMNOPQRSTUVWXYZ".
           MOVE 1 TO WS-IND-LIMPO.
           PERFORM VARYING WS-IND FROM 1 BY 1 UNTIL WS-IND > 20
               IF (WS-CNPJ-ENTRADA(WS-IND:1) >= "0" AND <= "9") OR (WS-CNPJ-ENTRADA(WS-IND:1) >= "A" AND <= "Z")
                  IF WS-IND-LIMPO <= 14 MOVE WS-CNPJ-ENTRADA(WS-IND:1) TO WS-CHAR(WS-IND-LIMPO) ADD 1 TO WS-IND-LIMPO END-IF END-IF END-PERFORM. PERFORM VARYING WS-IND FROM 1 BY 1 UNTIL WS-IND > 14
               COMPUTE WS-ASCII = FUNCTION ORD(WS-CHAR(WS-IND))
               COMPUTE WS-VAL(WS-IND) = WS-ASCII - 49
           END-PERFORM.

           MOVE 0 TO WS-SOMA.
           PERFORM VARYING WS-IND FROM 1 BY 1 UNTIL WS-IND > 12
               COMPUTE WS-SOMA = WS-SOMA + (WS-VAL(WS-IND) * WS-P1(WS-IND))
           END-PERFORM.
           DIVIDE WS-SOMA BY 11 GIVING WS-SOMA REMAINDER WS-RESTO.
           IF WS-RESTO < 2 MOVE 0 TO WS-DV1-CALC ELSE COMPUTE WS-DV1-CALC = 11 - WS-RESTO END-IF. MOVE 0 TO WS-SOMA. PERFORM VARYING WS-IND FROM 1 BY 1 UNTIL WS-IND > 12
               COMPUTE WS-SOMA = WS-SOMA + (WS-VAL(WS-IND) * WS-P2(WS-IND))
           END-PERFORM.
           COMPUTE WS-SOMA = WS-SOMA + (WS-DV1-CALC * WS-P2(13)).
           DIVIDE WS-SOMA BY 11 GIVING WS-SOMA REMAINDER WS-RESTO.
           IF WS-RESTO < 2
               MOVE 0 TO WS-DV2-CALC
           ELSE
               COMPUTE WS-DV2-CALC = 11 - WS-RESTO
           END-IF.

           IF WS-VAL(13) = WS-DV1-CALC AND WS-VAL(14) = WS-DV2-CALC
               MOVE "SIM" TO WS-VALIDO
           END-IF.
           
           DISPLAY "CNPJ VALIDO? " WS-VALIDO.
           STOP RUN.
Ver código em Delphi
unit ValidarCnpj;

interface

uses
  System.SysUtils, System.RegularExpressions;

function ValidarNovoCnpj(const Cnpj: string): Boolean;

implementation

function ValidarNovoCnpj(const Cnpj: string): Boolean;
var
  CnpjLimpo: string;
  PesosDv1: array[0..11] of Integer;
  PesosDv2: array[0..12] of Integer;
  I, SomaDv1, RestoDv1, Dv1, SomaDv2, RestoDv2, Dv2: Integer;
begin
  Result := False;
  CnpjLimpo := TRegEx.Replace(Cnpj, '[^A-Za-z0-9]', '').ToUpper;

  if Length(CnpjLimpo) <> 14 then Exit;
  if not (CnpjLimpo[13].IsDigit and CnpjLimpo[14].IsDigit) then Exit;

  PesosDv1[0] := 5; PesosDv1[1] := 4; PesosDv1[2] := 3; PesosDv1[3] := 2;
  PesosDv1[4] := 9; PesosDv1[5] := 8; PesosDv1[6] := 7; PesosDv1[7] := 6;
  PesosDv1[8] := 5; PesosDv1[9] := 4; PesosDv1[10] := 3; PesosDv1[11] := 2;

  PesosDv2[0] := 6; PesosDv2[1] := 5; PesosDv2[2] := 4; PesosDv2[3] := 3;
  PesosDv2[4] := 2; PesosDv2[5] := 9; PesosDv2[6] := 8; PesosDv2[7] := 7;
  PesosDv2[8] := 6; PesosDv2[9] := 5; PesosDv2[10] := 4; PesosDv2[11] := 3;
  PesosDv2[12] := 2;

  SomaDv1 := 0;
  for I := 0 to 11 do
    SomaDv1 := SomaDv1 + (Ord(CnpjLimpo[I + 1]) - 48) * PesosDv1[I];
  
  RestoDv1 := SomaDv1 mod 11;
  if RestoDv1 < 2 then Dv1 := 0 else Dv1 := 11 - RestoDv1;

  SomaDv2 := 0;
  for I := 0 to 11 do
    SomaDv2 := SomaDv2 + (Ord(CnpjLimpo[I + 1]) - 48) * PesosDv2[I];
  
  SomaDv2 := SomaDv2 + (Dv1 * PesosDv2[12]);
  RestoDv2 := SomaDv2 mod 11;
  if RestoDv2 < 2 then Dv2 := 0 else Dv2 := 11 - RestoDv2;

  Result := (Ord(CnpjLimpo[13]) - 48 = Dv1) and (Ord(CnpjLimpo[14]) - 48 = Dv2);
end;

end.
Ver código em Elixir
defmodule CnpjValidator do
  def validar_novo_cnpj(cnpj) when is_binary(cnpj) do
    cnpj_limpo = String.replace(cnpj, ~r/[^A-Za-z0-9]/, "") |> String.upcase()

    if String.length(cnpj_limpo) == 14 and String.match?(String.slice(cnpj_limpo, 12..13), ~r/^\d{2}$/) do
      chars = String.to_charlist(cnpj_limpo)
      
      pesos_dv1 = [5, 4, 3, 2, 9, 8, 7, 6, 5, 4, 3, 2]
      pesos_dv2 = [6, 5, 4, 3, 2, 9, 8, 7, 6, 5, 4, 3, 2]

      soma_dv1 = Enum.zip(Enum.take(chars, 12), pesos_dv1)
                 |> Enum.reduce(0, fn {char, peso}, acc -> acc + (char - 48) * peso end)
      resto_dv1 = rem(soma_dv1, 11)
      dv1 = if resto_dv1 < 2, do: 0, else: 11 - resto_dv1 soma_dv2 = Enum.zip(Enum.take(chars, 12), pesos_dv2) |> Enum.reduce(0, fn {char, peso}, acc -> acc + (char - 48) * peso end)
      soma_dv2 = soma_dv2 + (dv1 * Enum.at(pesos_dv2, 12))
      resto_dv2 = rem(soma_dv2, 11)
      dv2 = if resto_dv2 < 2, do: 0, else: 11 - resto_dv2

      Enum.at(chars, 12) - 48 == dv1 and Enum.at(chars, 13) - 48 == dv2
    else
      false
    end
  end
end
Ver código em Go
package cnpj

import (
    "regexp"
    "strings"
)

func ValidarNovoCnpj(cnpj string) bool {
    reg := regexp.MustCompile("[^A-Za-z0-9]")
    cnpjLimpo := strings.ToUpper(reg.ReplaceAllString(cnpj, ""))

    if len(cnpjLimpo) != 14 {
        return false
    }

    if cnpjLimpo[12] < '0' || cnpjLimpo[12] > '9' || cnpjLimpo[13] < '0' || cnpjLimpo[13] > '9' {
        return false
    }

    pesosDv1 := []int{5, 4, 3, 2, 9, 8, 7, 6, 5, 4, 3, 2}
    pesosDv2 := []int{6, 5, 4, 3, 2, 9, 8, 7, 6, 5, 4, 3, 2}

    somaDv1 := 0
    for i := 0; i < 12; i++ { somaDv1 += (int(cnpjLimpo[i]) - 48) * pesosDv1[i] } restoDv1 := somaDv1 % 11 dv1 := 0 if restoDv1 >= 2 {
        dv1 = 11 - restoDv1
    }

    somaDv2 := 0
    for i := 0; i < 12; i++ { somaDv2 += (int(cnpjLimpo[i]) - 48) * pesosDv2[i] } somaDv2 += dv1 * pesosDv2[12] restoDv2 := somaDv2 % 11 dv2 := 0 if restoDv2 >= 2 {
        dv2 = 11 - restoDv2
    }

    return int(cnpjLimpo[12]-'0') == dv1 && int(cnpjLimpo[13]-'0') == dv2
}
Ver código em Java
public class CnpjValidator {
    public static boolean validarNovoCnpj(String cnpj) {
        if (cnpj == null) return false;
        String cnpjLimpo = cnpj.replaceAll("[^A-Za-z0-9]", "").toUpperCase();
        
        if (cnpjLimpo.length() != 14) return false;
        if (!cnpjLimpo.substring(12).matches("\\d{2}")) return false;
        
        int[] pesosDv1 = {5, 4, 3, 2, 9, 8, 7, 6, 5, 4, 3, 2};
        int[] pesosDv2 = {6, 5, 4, 3, 2, 9, 8, 7, 6, 5, 4, 3, 2};
        
        int somaDv1 = 0;
        for (int i = 0; i < 12; i++) {
            somaDv1 += ((int) cnpjLimpo.charAt(i) - 48) * pesosDv1[i];
        }
        int restoDv1 = somaDv1 % 11;
        int dv1 = restoDv1 < 2 ? 0 : 11 - restoDv1;
        
        int somaDv2 = 0;
        for (int i = 0; i < 12; i++) {
            somaDv2 += ((int) cnpjLimpo.charAt(i) - 48) * pesosDv2[i];
        }
        somaDv2 += dv1 * pesosDv2[12];
        int restoDv2 = somaDv2 % 11;
        int dv2 = restoDv2 < 2 ? 0 : 11 - restoDv2;
        
        return Character.getNumericValue(cnpjLimpo.charAt(12)) == dv1 && 
               Character.getNumericValue(cnpjLimpo.charAt(13)) == dv2;
    }
}
Ver código em JavaScript
function validarNovoCnpj(cnpj) {
    if (typeof cnpj !== 'string') return false;
    const cnpjLimpo = cnpj.replace(/[^A-Za-z0-9]/g, '').toUpperCase();
    
    if (cnpjLimpo.length !== 14) return false;
    if (!/^\d{2}$/.test(cnpjLimpo.slice(-2))) return false;
    
    const pesosDv1 = [5, 4, 3, 2, 9, 8, 7, 6, 5, 4, 3, 2];
    const pesosDv2 = [6, 5, 4, 3, 2, 9, 8, 7, 6, 5, 4, 3, 2];
    
    let somaDv1 = 0;
    for (let i = 0; i < 12; i++) {
        somaDv1 += (cnpjLimpo.charCodeAt(i) - 48) * pesosDv1[i];
    }
    const restoDv1 = somaDv1 % 11;
    const dv1 = restoDv1 < 2 ? 0 : 11 - restoDv1;
    
    let somaDv2 = 0;
    for (let i = 0; i < 12; i++) {
        somaDv2 += (cnpjLimpo.charCodeAt(i) - 48) * pesosDv2[i];
    }
    somaDv2 += dv1 * pesosDv2[12];
    const restoDv2 = somaDv2 % 11;
    const dv2 = restoDv2 < 2 ? 0 : 11 - restoDv2;
    
    return parseInt(cnpjLimpo[12], 10) === dv1 && parseInt(cnpjLimpo[13], 10) === dv2;
}
Ver código em Kotlin
object CnpjValidator {
    fun validarNovoCnpj(cnpj: String): Boolean {
        val cnpjLimpo = cnpj.replace(Regex("[^A-Za-z0-9]"), "").uppercase()
        
        if (cnpjLimpo.length != 14) return false;
        if (!cnpjLimpo.takeLast(2).all { it.isDigit() }) return false
        
        val pesosDv1 = intArrayOf(5, 4, 3, 2, 9, 8, 7, 6, 5, 4, 3, 2)
        val pesosDv2 = intArrayOf(6, 5, 4, 3, 2, 9, 8, 7, 6, 5, 4, 3, 2)
        
        var somaDv1 = 0
        for (i in 0 until 12) {
            somaDv1 += (cnpjLimpo[i].code - 48) * pesosDv1[i]
        }
        val restoDv1 = somaDv1 % 11
        val dv1 = if (restoDv1 < 2) 0 else 11 - restoDv1
        
        var somaDv2 = 0
        for (i in 0 until 12) {
            somaDv2 += (cnpjLimpo[i].code - 48) * pesosDv2[i]
        }
        somaDv2 += dv1 * pesosDv2[12]
        val restoDv2 = somaDv2 % 11
        val dv2 = if (restoDv2 < 2) 0 else 11 - restoDv2
        
        return (cnpjLimpo[12] - '0') == dv1 && (cnpjLimpo[13] - '0') == dv2
    }
}
Ver código em Node.js
function validarNovoCnpj(cnpj) {
    if (typeof cnpj !== 'string') return false;
    const cnpjLimpo = cnpj.replace(/[^A-Za-z0-9]/g, '').toUpperCase();
    
    if (cnpjLimpo.length !== 14) return false;
    if (!/^\d{2}$/.test(cnpjLimpo.slice(-2))) return false;
    
    const pesosDv1 = [5, 4, 3, 2, 9, 8, 7, 6, 5, 4, 3, 2];
    const pesosDv2 = [6, 5, 4, 3, 2, 9, 8, 7, 6, 5, 4, 3, 2];
    
    let somaDv1 = 0;
    for (let i = 0; i < 12; i++) {
        somaDv1 += (cnpjLimpo.charCodeAt(i) - 48) * pesosDv1[i];
    }
    const restoDv1 = somaDv1 % 11;
    const dv1 = restoDv1 < 2 ? 0 : 11 - restoDv1;
    
    let somaDv2 = 0;
    for (let i = 0; i < 12; i++) {
        somaDv2 += (cnpjLimpo.charCodeAt(i) - 48) * pesosDv2[i];
    }
    somaDv2 += dv1 * pesosDv2[12];
    const restoDv2 = somaDv2 % 11;
    const dv2 = restoDv2 < 2 ? 0 : 11 - restoDv2;
    
    return parseInt(cnpjLimpo[12], 10) === dv1 && parseInt(cnpjLimpo[13], 10) === dv2;
}

module.exports = { validarNovoCnpj };
Ver código em PHP
<?php

function validarNovoCnpj(string $cnpj): bool {
    $cnpjLimpo = strtoupper(preg_replace('/[^A-Za-z0-9]/', '', $cnpj));
    
    if (strlen($cnpjLimpo) !== 14) return false;
    if (!ctype_digit(substr($cnpjLimpo, -2))) return false;
    
    $pesosDv1 = [5, 4, 3, 2, 9, 8, 7, 6, 5, 4, 3, 2];
    $pesosDv2 = [6, 5, 4, 3, 2, 9, 8, 7, 6, 5, 4, 3, 2];
    
    $somaDv1 = 0;
    for ($i = 0; $i < 12; $i++) {
        $somaDv1 += (ord($cnpjLimpo[$i]) - 48) * $pesosDv1[$i];
    }
    $restoDv1 = $somaDv1 % 11;
    $dv1 = $restoDv1 < 2 ? 0 : 11 - $restoDv1;
    
    $somaDv2 = 0;
    for ($i = 0; $i < 12; $i++) {
        $somaDv2 += (ord($cnpjLimpo[$i]) - 48) * $pesosDv2[$i];
    }
    $somaDv2 += $dv1 * $pesosDv2[12];
    $restoDv2 = $somaDv2 % 11;
    $dv2 = $restoDv2 < 2 ? 0 : 11 - $restoDv2;
    
    return (int)$cnpjLimpo[12] === $dv1 && (int)$cnpjLimpo[13] === $dv2;
}
Ver código em Python
import re

def validar_novo_cnpj(cnpj: str) -> bool:
    cnpj_limpo = re.sub(r'[^A-Za-z0-9]', '', cnpj).upper()
    
    if len(cnpj_limpo) != 14 or not cnpj_limpo[-2:].isdigit():
        return False
    
    pesos_dv1 = [5, 4, 3, 2, 9, 8, 7, 6, 5, 4, 3, 2]
    pesos_dv2 = [6, 5, 4, 3, 2, 9, 8, 7, 6, 5, 4, 3, 2]
    
    soma_dv1 = sum((ord(cnpj_limpo[i]) - 48) * pesos_dv1[i] for i in range(12))
    resto_dv1 = soma_dv1 % 11
    dv1 = 0 if resto_dv1 < 2 else 11 - resto_dv1
    
    soma_dv2 = sum((ord(cnpj_limpo[i]) - 48) * pesos_dv2[i] for i in range(12))
    soma_dv2 += dv1 * pesos_dv2[12]
    resto_dv2 = soma_dv2 % 11
    dv2 = 0 if resto_dv2 < 2 else 11 - resto_dv2
    
    return int(cnpj_limpo[12]) == dv1 and int(cnpj_limpo[13]) == dv2
Ver código em Ruby
def validar_novo_cnpj(cnpj)
  cnpj_limpo = cnpj.gsub(/[^A-Za-z0-9]/, '').upcase
  
  return false if cnpj_limpo.length != 14
  return false unless cnpj_limpo[-2..-1].match?(/\A\d+\z/)
  
  pesos_dv1 = [5, 4, 3, 2, 9, 8, 7, 6, 5, 4, 3, 2]
  pesos_dv2 = [6, 5, 4, 3, 2, 9, 8, 7, 6, 5, 4, 3, 2]
  
  soma_dv1 = 0
  12.times { |i| soma_dv1 += (cnpj_limpo[i].ord - 48) * pesos_dv1[i] }
  resto_dv1 = soma_dv1 % 11
  dv1 = resto_dv1 < 2 ? 0 : 11 - resto_dv1
  
  soma_dv2 = 0
  12.times { |i| soma_dv2 += (cnpj_limpo[i].ord - 48) * pesos_dv2[i] }
  soma_dv2 += dv1 * pesos_dv2[12]
  resto_dv2 = soma_dv2 % 11
  dv2 = resto_dv2 < 2 ? 0 : 11 - resto_dv2
  
  cnpj_limpo[12].to_i == dv1 && cnpj_limpo[13].to_i == dv2
end
Ver código em Rust
pub fn validar_novo_cnpj(cnpj: &str) -> bool {
    let cnpj_limpo: String = cnpj
        .chars()
        .filter(|c| c.is_alphanumeric())
        .collect::()
        .to_uppercase();

    if cnpj_limpo.len() != 14 {
        return false;
    }

    let chars: Vec = cnpj_limpo.chars().collect();
    
    if !chars[12].is_ascii_digit() || !chars[13].is_ascii_digit() {
        return false;
    }

    let pesos_dv1 = [5, 4, 3, 2, 9, 8, 7, 6, 5, 4, 3, 2];
    let pesos_dv2 = [6, 5, 4, 3, 2, 9, 8, 7, 6, 5, 4, 3, 2];

    let mut soma_dv1 = 0;
    for i in 0..12 {
        soma_dv1 += ((chars[i] as i32) - 48) * pesos_dv1[i];
    }
    let resto_dv1 = soma_dv1 % 11;
    let dv1 = if resto_dv1 < 2 { 0 } else { 11 - resto_dv1 };

    let mut soma_dv2 = 0;
    for i in 0..12 {
        soma_dv2 += ((chars[i] as i32) - 48) * pesos_dv2[i];
    }
    soma_dv2 += dv1 * pesos_dv2[12];
    let resto_dv2 = soma_dv2 % 11;
    let dv2 = if resto_dv2 < 2 { 0 } else { 11 - resto_dv2 };

    (chars[12] as i32 - 48) == dv1 && (chars[13] as i32 - 48) == dv2
}
Ver código em TypeScript
export function validarNovoCnpj(cnpj: string): boolean {
    const cnpjLimpo: string = cnpj.replace(/[^A-Za-z0-9]/g, '').toUpperCase();
    
    if (cnpjLimpo.length !== 14) return false;
    if (!/^\d{2}$/.test(cnpjLimpo.slice(-2))) return false;
    
    const pesosDv1: number[] = [5, 4, 3, 2, 9, 8, 7, 6, 5, 4, 3, 2];
    const pesosDv2: number[] = [6, 5, 4, 3, 2, 9, 8, 7, 6, 5, 4, 3, 2];
    
    let somaDv1 = 0;
    for (let i = 0; i < 12; i++) {
        somaDv1 += (cnpjLimpo.charCodeAt(i) - 48) * pesosDv1[i];
    }
    const restoDv1: number = somaDv1 % 11;
    const dv1: number = restoDv1 < 2 ? 0 : 11 - restoDv1;
    
    let somaDv2 = 0;
    for (let i = 0; i < 12; i++) {
        somaDv2 += (cnpjLimpo.charCodeAt(i) - 48) * pesosDv2[i];
    }
    somaDv2 += dv1 * pesosDv2[12];
    const restoDv2: number = somaDv2 % 11;
    const dv2: number = restoDv2 < 2 ? 0 : 11 - restoDv2;
    
    return parseInt(cnpjLimpo[12], 10) === dv1 && parseInt(cnpjLimpo[13], 10) === dv2;
}
Ver código em VB6 / VBA
Public Function ValidarNovoCnpj(ByVal Cnpj As String) As Boolean
    Dim CnpjLimpo As String
    Dim CharCode As Integer
    Dim I As Integer, Valor As Integer
    Dim SomaDv1 As Long, RestoDv1 As Integer, Dv1 As Integer
    Dim SomaDv2 As Long, RestoDv2 As Integer, Dv2 As Integer
    
    Dim PesosDv1() As Variant
    Dim PesosDv2() As Variant
    
    PesosDv1 = Array(5, 4, 3, 2, 9, 8, 7, 6, 5, 4, 3, 2)
    PesosDv2 = Array(6, 5, 4, 3, 2, 9, 8, 7, 6, 5, 4, 3, 2)
    
    CnpjLimpo = ""
    Cnpj = UCase(Cnpj)
    For I = 1 To Len(Cnpj)
        Dim C As String
        C = Mid(Cnpj, I, 1)
        If (C >= "0" And C <= "9") Or (C >= "A" And C <= "Z") Then
            CnpjLimpo = CnpjLimpo & C
        End If
    Next I
    
    If Len(CnpjLimpo) <> 14 Then
        ValidarNovoCnpj = False
        Exit Function
    End If
    
    If Not (Mid(CnpjLimpo, 13, 1) >= "0" And Mid(CnpjLimpo, 13, 1) <= "9") Or _ Not (Mid(CnpjLimpo, 14, 1) >= "0" And Mid(CnpjLimpo, 14, 1) <= "9") Then
        ValidarNovoCnpj = False
        Exit Function
    End If
    
    SomaDv1 = 0
    For I = 0 To 11
        CharCode = Asc(Mid(CnpjLimpo, I + 1, 1))
        Valor = CharCode - 48
        SomaDv1 = SomaDv1 + (Valor * PesosDv1(I))
    Next I
    
    RestoDv1 = SomaDv1 Mod 11
    If RestoDv1 < 2 Then Dv1 = 0 Else Dv1 = 11 - RestoDv1
    
    SomaDv2 = 0
    For I = 0 To 11
        CharCode = Asc(Mid(CnpjLimpo, I + 1, 1))
        Valor = CharCode - 48
        SomaDv2 = SomaDv2 + (Valor * PesosDv2(I))
    Next I
    SomaDv2 = SomaDv2 + (Dv1 * PesosDv2(12))
    
    RestoDv2 = SomaDv2 Mod 11
    If RestoDv2 < 2 Then Dv2 = 0 Else Dv2 = 11 - RestoDv2
    
    Dim Dv1Informado As Integer
    Dim Dv2Informado As Integer
    Dv1Informado = Val(Mid(CnpjLimpo, 13, 1))
    Dv2Informado = Val(Mid(CnpjLimpo, 14, 1))
    
    ValidarNovoCnpj = (Dv1 = Dv1Informado) And (Dv2 = Dv2Informado)
End Function

[INSIRA AQUI AS FUNÇÕES DE VALIDAÇÃO]

Ruby · Python · PHP · Java · JavaScript · Rust · Go · TypeScript · Elixir · COBOL e outras.

O jeito certo e o jeito errado de validar

Jeito certo:

  • tratar CNPJ como string
  • aceitar números e letras
  • remover máscara antes do cálculo
  • usar módulo 11 com ASCII − 48
  • manter compatibilidade com CNPJ antigo e novo
  jQuery : Como obter html de sites externos com php + jQuery

Jeito errado:

  • usar parseInt, Number, BIGINT ou Long
  • validar com regex antiga como ^\d{14}$
  • assumir que CNPJ sempre terá só números
  • ordenar ou comparar CNPJ como número
  • confiar só na máscara visual

APIs precisarão ser revisadas

Muitas APIs ainda usam contratos como:

(json)

{ "cnpj": 12345678000199 }

Esse modelo deve ser evitado. O correto é tratar o CNPJ como string:

(json)

{ "cnpj": "12345678000199" }

Ou, futuramente:

(json)

{ "cnpj": "12ABC3450001XY" }

A mudança impacta APIs REST, GraphQL, SOAP, microsserviços, filas de mensagens e integrações com parceiros.

O erro mais perigoso: converter automaticamente

Muitos sistemas fazem conversões automáticas, como:

Long.parseLong(cnpj) Number(cnpj) int(cnpj)

Quando as letras começarem a aparecer, essas rotinas vão gerar erros. Por isso, é fundamental localizar todas as conversões numéricas relacionadas ao CNPJ.

Ponto importante: a adaptação precisa ser de ponta a ponta

O maior erro será fazer uma adaptação cosmética: aceitar letra no front-end, mas continuar gravando como número no banco, ou manter a validação antiga no back-end, ou quebrar integrações porque a API ainda espera inteiro.

Para funcionar de verdade, a adaptação precisa cobrir todas as camadas:

  • front-end
  • back-end
  • banco de dados
  • APIs
  • mensageria
  • relatórios e BI
  • fiscal
  • integrações externas

Sistemas legados merecem atenção especial

Os sistemas mais antigos provavelmente serão os mais afetados. É comum encontrar comparações numéricas, ordenações numéricas, máscaras fixas, campos limitados, exportações para planilhas e integrações via arquivos TXT.

Uma auditoria completa do código é altamente recomendada.

O impacto nos relatórios e no BI

Ferramentas de Business Intelligence também precisarão de ajustes, pois muitos painéis usam o CNPJ como chave de relacionamento. Quando o campo muda de numérico para textual, alguns relacionamentos podem falhar.

Ferramentas que merecem revisão: Power BI, Tableau, Qlik, Looker e Metabase.

  Mysql lento em alguns clientes

O impacto para startups e empresas de tecnologia

As empresas que se prepararem cedo terão vantagem competitiva, porque:

  • evitam retrabalho futuro
  • reduzem riscos operacionais
  • mantêm conformidade legal
  • evitam falhas em integrações

Para startups, é uma boa oportunidade de modernizar sistemas que já deveriam tratar identificadores como texto.

O que muda para o empreendedor brasileiro?

Embora a alteração seja técnica, seus efeitos alcançam o ambiente de negócios. Os benefícios incluem:

  • maior capacidade de registros empresariais
  • sustentação do crescimento econômico
  • suporte ao aumento do empreendedorismo
  • menor risco de esgotamento da numeração
  • continuidade dos processos de formalização

Em resumo, a mudança ajuda a garantir que o sistema de identificação empresarial acompanhe a evolução da economia digital brasileira.

Checklist para Programadores

  • ✅ Tratar CNPJ como texto
  • ✅ Revisar banco de dados
  • ✅ Atualizar APIs
  • ✅ Verificar validações
  • ✅ Implementar o novo cálculo de DV (módulo 11 com ASCII − 48)
  • ✅ Aceitar CNPJ antigo e novo ao mesmo tempo
  • ✅ Revisar integrações externas
  • ✅ Atualizar documentação
  • ✅ Revisar ETLs e processos de BI
  • ✅ Testar importações e exportações
  • ✅ Revisar sistemas legados
  • ✅ Acompanhar as publicações oficiais da Receita Federal

Conclusão

A chegada do CNPJ alfanumérico é uma das maiores mudanças estruturais em sistemas corporativos brasileiros dos últimos anos.

Para o usuário final, parece pouco. Para o desenvolvedor, exige atenção imediata.

A principal lição é simples: CNPJ não é número. É um identificador.

Sistemas que já seguem essa prática terão poucas adaptações. Já aqueles que dependem de conversões numéricas, validações rígidas ou modelagens antigas poderão enfrentar desafios consideráveis.

O melhor momento para começar a revisão é agora, antes que os primeiros CNPJs alfanuméricos comecem a circular em larga escala.

Perguntas Frequentes (FAQ)

1. Quando o CNPJ alfanumérico entra em vigor?
A implementação prática está prevista para julho de 2026, quando novas inscrições poderão receber letras e números.

2. Os CNPJs antigos vão mudar?
Não. Os CNPJs atuais continuam iguais. O Brasil terá os dois formatos convivendo ao mesmo tempo.

3. Quantas combinações o novo CNPJ permite?
A capacidade salta de cerca de 100 milhões para quase 3 trilhões de combinações.

4. Como validar o novo CNPJ?
Trate como texto, valide o formato, calcule o DV com módulo 11 usando ASCII − 48 e compare com os dois últimos caracteres.

5. O que mais impacta os programadores?
Parar de tratar o CNPJ como número. Ele deve ser string em todas as camadas: banco, back-end, API e front-end.

Fontes

APIs, Backend, Banco de Dados, CNPJ, CNPJ Alfanumérico, Desenvolvimento, ERP, Frontend, Guia para Programadores, Módulo 11, programacao, Receita Federal, Sistemas Empresariais, Software Corporativo, TI, Transição CNPJ 2026, Validação

Apoio

Participe da campanha!

Cafézinho

Quer me pagar um café? Pode usar a chave PIX abaixo

Chave PIX e-mail

[email protected]

Vídeos

Assista e se inscreva em nosso canal!

NEWSLETTER

Cadastre-se gratuitamente e fique por dentro de todas as novidades do blog, como dicas e tutoriais.

Não enviamos spams, fique tranquilo

Deixe um comentário

Este site utiliza o Akismet para reduzir spam. Saiba como seus dados em comentários são processados.