image

Bolsas de estudo DIO PRO para acessar bootcamps ilimitados

Available only:

37 slots
Carlos Souza
Carlos Souza13/04/2026 08:10
Share
CI&T - Do Prompt ao AgenteRecommended for youCI&T - Do Prompt ao Agente

Adaptando sistemas ao CNPJ alfanumérico: um guia prático de validação em TypeScript Resumo

    A Receita Federal definiu a adoção do CNPJ alfanumérico para novas inscrições a partir de julho de 2026, mantendo válidos e inalterados os CNPJs já existentes. O novo identificador continua com 14 posições, mas passa a admitir letras e números nas 12 primeiras posições, enquanto os 2 dígitos verificadores finais permanecem numéricos. Este guia apresenta, de forma prática, o que muda nos sistemas, como funciona o cálculo oficial do dígito verificador e como implementar um validador confiável em TypeScript.

    1. Introdução

    A mudança para o CNPJ alfanumérico foi motivada pelo crescimento contínuo do número de inscrições e pelo risco de esgotamento do espaço numérico do modelo atual. A transição não substitui os CNPJs já emitidos; ela vale apenas para novas inscrições, inclusive podendo alcançar novas filiais de empresas já existentes. Em termos de arquitetura, isso significa que muitos sistemas precisarão deixar de tratar CNPJ como “somente número” e passar a tratá-lo como um identificador alfanumérico com regra específica de validação.

    2. O que muda no formato

    O novo CNPJ mantém a estrutura de 14 caracteres. As 8 primeiras posições continuam representando a raiz do cadastro e as 4 posições seguintes representam a ordem do estabelecimento; ambas passam a poder conter letras e números. Já os 2 últimos caracteres, correspondentes aos dígitos verificadores, continuam sendo numéricos. A Receita também esclarece que o formato alfanumérico se aplica apenas a novas inscrições, sem exigir qualquer alteração dos CNPJs atuais.

    Um efeito importante dessa definição é que validadores antigos, regexes restritivas, máscaras de input, colunas tipadas como numéricas e integrações que assumem “14 dígitos” precisarão ser revisados. Além disso, a área fiscal já recebeu nota técnica conjunta para implementação do CNPJ alfanumérico nos documentos fiscais eletrônicos sob coordenação do ENCAT, o que reforça que a adaptação não é apenas cadastral, mas também de integração.

    3. Regra oficial do dígito verificador

    A Receita Federal manteve a lógica de validação por módulo 11, mas ajustou a forma de converter caracteres em valores numéricos. No modelo alfanumérico, cada caractere deve ser convertido usando seu código ASCII menos 48. Assim, os dígitos 0 a 9 continuam valendo 0 a 9, enquanto as letras maiúsculas passam a valer 17 a 42; por exemplo, A = 17, B = 18 e Z = 42.

    O cálculo ocorre em duas etapas. Para o primeiro dígito verificador, usam-se os 12 primeiros caracteres com os pesos 5, 4, 3, 2, 9, 8, 7, 6, 5, 4, 3, 2. Para o segundo dígito, acrescenta-se o primeiro DV ao final e usam-se os pesos 6, 5, 4, 3, 2, 9, 8, 7, 6, 5, 4, 3, 2. Em ambos os casos, calcula-se o somatório ponderado, tira-se o resto por 11 e aplica-se a regra: se o resto for 0 ou 1, o dígito é 0; caso contrário, o dígito é 11 - resto.

    Uma consequência prática importante é que um único algoritmo bem implementado pode validar tanto o CNPJ numérico atual quanto o novo CNPJ alfanumérico, desde que a aplicação normalize corretamente a entrada e pare de restringir as 12 primeiras posições a dígitos. Isso decorre do fato de que, para caracteres numéricos, a transformação ASCII−48 preserva exatamente os valores já usados no modelo atual. Essa conclusão é uma inferência técnica consistente com a documentação oficial.

    4. Implementação prática em TypeScript

    A seguir está uma implementação enxuta, pronta para uso, pensada para funcionar tanto com CNPJ numérico quanto com CNPJ alfanumérico.

    
    export type CnpjValidationResult = {
    valid: boolean;
    normalized: string;
    formatted?: string;
    reason?: string;
    };
    
    const BASE_LENGTH = 12;
    const TOTAL_LENGTH = 14;
    
    const FIRST_DV_WEIGHTS = [5, 4, 3, 2, 9, 8, 7, 6, 5, 4, 3, 2];
    const SECOND_DV_WEIGHTS = [6, 5, 4, 3, 2, 9, 8, 7, 6, 5, 4, 3, 2];
    
    function normalizeCnpj(value: string): string {
    return value.replace(/[^0-9A-Za-z]/g, "").toUpperCase();
    }
    
    function isBodyChar(char: string): boolean {
    return /^[0-9A-Z]$/.test(char);
    }
    
    function charToDvValue(char: string): number {
    if (!isBodyChar(char)) {
      throw new Error(`Caractere inválido para CNPJ: ${char}`);
    }
    
    // Regra oficial: valor ASCII - 48
    return char.charCodeAt(0) - 48;
    }
    
    function calculateDv(values: number[], weights: number[]): number {
    const sum = values.reduce((acc, value, index) => acc + value * weights[index], 0);
    const remainder = sum % 11;
    return remainder < 2 ? 0 : 11 - remainder;
    }
    
    function computeCheckDigits(base12: string): string {
    const normalizedBase = normalizeCnpj(base12);
    
    if (normalizedBase.length !== BASE_LENGTH) {
      throw new Error(`A base do CNPJ deve ter ${BASE_LENGTH} caracteres.`);
    }
    
    for (const char of normalizedBase) {
      if (!isBodyChar(char)) {
        throw new Error(`Caractere inválido na base do CNPJ: ${char}`);
      }
    }
    
    const firstValues = [...normalizedBase].map(charToDvValue);
    const dv1 = calculateDv(firstValues, FIRST_DV_WEIGHTS);
    
    const secondValues = [...normalizedBase, String(dv1)].map(charToDvValue);
    const dv2 = calculateDv(secondValues, SECOND_DV_WEIGHTS);
    
    return `${dv1}${dv2}`;
    }
    
    export function formatCnpj(cnpj: string): string {
    const normalized = normalizeCnpj(cnpj);
    
    if (normalized.length !== TOTAL_LENGTH) {
      throw new Error("CNPJ deve ter 14 caracteres após normalização.");
    }
    
    return normalized.replace(
      /^(.{2})(.{3})(.{3})(.{4})(.{2})$/,
      "$1.$2.$3/$4-$5"
    );
    }
    
    export function validateCnpj(cnpj: string): CnpjValidationResult {
    const normalized = normalizeCnpj(cnpj);
    
    if (normalized.length !== TOTAL_LENGTH) {
      return {
        valid: false,
        normalized,
        reason: "CNPJ deve ter 14 caracteres após normalização.",
      };
    }
    
    const body = normalized.slice(0, 12);
    const dv = normalized.slice(12);
    
    if (!/^[0-9A-Z]{12}$/.test(body)) {
      return {
        valid: false,
        normalized,
        reason: "As 12 primeiras posições devem ser alfanuméricas.",
      };
    }
    
    if (!/^[0-9]{2}$/.test(dv)) {
      return {
        valid: false,
        normalized,
        reason: "Os 2 dígitos verificadores devem ser numéricos.",
      };
    }
    
    const expectedDv = computeCheckDigits(body);
    
    if (dv !== expectedDv) {
      return {
        valid: false,
        normalized,
        reason: `Dígitos verificadores inválidos. Esperado: ${expectedDv}.`,
      };
    }
    
    return {
      valid: true,
      normalized,
      formatted: formatCnpj(normalized),
    };
    }
    
    

    5. Exemplo de uso

    
    const examples = [
    "12.ABC.345/01DE-35",
    "12345678000195",
    "12ABC34501DE35",
    "12ABC34501DE99",
    ];
    
    for (const value of examples) {
    const result = validateCnpj(value);
    console.log(value, result);
    }
    
    

    No exemplo oficial divulgado pela Receita e pelo Serpro, a base 12ABC34501DE gera os dígitos 35, resultando em 12.ABC.345/01DE-35. Isso é útil como caso de teste de regressão para garantir que a implementação esteja aderente à regra oficial.

    6. Cuidados de migração em sistemas reais

    A primeira mudança recomendada é abandonar qualquer modelagem que trate CNPJ como número. Em banco de dados, o campo deve ser textual; em APIs, contratos devem aceitar caracteres alfanuméricos; em front-end, máscaras e validações precisam permitir letras nas 12 primeiras posições; e em integrações, o CNPJ deve ser transportado como string normalizada. Isso não decorre de uma exigência textual única da Receita, mas é a consequência direta do novo formato oficial e da necessidade de ler e validar inscrições alfanuméricas.

    Também é prudente revisar pelo menos cinco pontos do sistema: persistência, validação, exibição, busca e interoperabilidade. Persistência envolve schema de banco e índices; validação inclui regex, formulários e middlewares; exibição cobre máscaras e relatórios; busca afeta filtros, ordenações e consultas externas; interoperabilidade envolve XML, filas, ERPs, gateways e documentos fiscais. A existência de nota técnica específica para ambientes de DF-e mostra que a adaptação precisa alcançar também os fluxos fiscais.

    7. Estratégia de testes recomendada

    Uma adaptação segura deve incluir testes com três grupos de entradas: CNPJs numéricos válidos, CNPJs alfanuméricos válidos e casos inválidos. Entre os inválidos, convém testar caracteres fora do padrão, tamanho incorreto, letras nos dígitos verificadores, e CNPJs com DVs alterados. O exemplo 12ABC34501DE35 deve ser mantido na suíte como teste positivo obrigatório, por ser reproduzível a partir da documentação oficial.

    Um conjunto mínimo de testes em TypeScript pode ficar assim:

    
    import { describe, expect, it } from "vitest";
    import { validateCnpj, formatCnpj } from "./cnpj";
    
    describe("CNPJ validator", () => {
    it("valida exemplo oficial alfanumérico", () => {
      const result = validateCnpj("12ABC34501DE35");
      expect(result.valid).toBe(true);
      expect(result.formatted).toBe("12.ABC.345/01DE-35");
    });
    
    it("rejeita DV incorreto", () => {
      const result = validateCnpj("12ABC34501DE99");
      expect(result.valid).toBe(false);
    });
    
    it("aceita CNPJ numérico no mesmo algoritmo", () => {
      const result = validateCnpj("12345678000195");
      expect(result.valid).toBeTypeOf("boolean");
    });
    
    it("formata corretamente", () => {
      expect(formatCnpj("12ABC34501DE35")).toBe("12.ABC.345/01DE-35");
    });
    });
    
    

    8. Conclusão

    O CNPJ alfanumérico não exige a troca dos cadastros atuais, mas exige uma revisão séria na forma como sistemas interpretam, validam e armazenam esse identificador. A boa notícia é que a regra continua relativamente simples: o formato muda, mas a lógica central do módulo 11 é preservada, com adaptação baseada em ASCII - 48. Por isso, uma implementação bem estruturada em TypeScript consegue oferecer compatibilidade com o modelo atual e com o novo padrão sem grande complexidade algorítmica.

    Referências

    Receita Federal. CNPJ Alfanumérico.

    Receita Federal. Cálculo do DV do CNPJ Alfanumérico.

    Receita Federal. Perguntas e Respostas: CNPJ Alfanumérico.

    Serpro. Serpro divulga códigos para auxiliar transição rumo ao CNPJ alfanumérico.

    Serpro. Cálculo dos dígitos verificadores de CNPJ alfanumérico.

    Portal NF-e / ENCAT. Nota Técnica Conjunta CNPJ Alfanumérico

    Share
    Recommended for you
    Globant  - Java & Spring Boot AI Developer
    Accenture - Python para Análise e Automação de Dados
    Lupo - Primeiros Passos com Inteligência Artificial
    Comments (0)
    Recommended for youCI&T - Do Prompt ao Agente