CNPJ Completo

CNPJ alfanumérico

O que muda e como adaptar um sistema

O que muda

Desde julho de 2026, a Receita emite CNPJs com letras. O primeiro saiu em 31 de julho, para uma agência do Banco do Brasil: 00.000.000/E08G-12.

Continuam 14 caracteres: as 12 primeiras posições aceitam letras de A a Z, e os 2 dígitos verificadores seguem só algarismos. Os CNPJs já emitidos não mudam, e os dois formatos vão conviver.

O que revisar no sistema

Expressões regulares

Sem pontuação: ^[0-9A-Z]{12}[0-9]{2}$

Com pontuação: ^[0-9A-Z]{2}\.[0-9A-Z]{3}\.[0-9A-Z]{3}/[0-9A-Z]{4}-[0-9]{2}$

Elas conferem só o formato, com as letras em maiúsculas. Os dígitos verificadores precisam de uma das funções abaixo.

Dígitos verificadores

Cada caractere vale o código ASCII menos 48 (A = 17, Z = 42); o resto é a conta de sempre, com pesos de 2 a 9 e o resto da divisão por 11. O validador mostra a conta de qualquer CNPJ.

As funções aceitam o CNPJ com ou sem pontuação, em qualquer caixa, e recusam sequências repetidas. Foram testadas com CNPJs reais e com o exemplo da Receita.

TypeScript e JavaScript

export function cnpjValido(texto: string): boolean {
  const cnpj = texto.toUpperCase().replace(/[^0-9A-Z]/g, "");
  if (!/^[0-9A-Z]{12}[0-9]{2}$/.test(cnpj) || /^(.)\1{13}$/.test(cnpj)) return false;
  const d1 = digito(cnpj.slice(0, 12));
  const d2 = digito(cnpj.slice(0, 12) + d1);
  return cnpj.slice(12) === String(d1) + d2;
}

// Cada caractere vale o código ASCII menos 48; os pesos vão de 2 a 9 da direita para a esquerda.
function digito(base: string): number {
  let soma = 0;
  for (let i = 0; i < base.length; i++) {
    soma += (base.charCodeAt(i) - 48) * (2 + ((base.length - 1 - i) % 8));
  }
  const resto = soma % 11;
  return resto < 2 ? 0 : 11 - resto;
}

Em JavaScript, tire as anotações de tipo (: string, : boolean e : number).

Python

import re


def cnpj_valido(texto: str) -> bool:
    cnpj = re.sub(r"[^0-9A-Z]", "", texto.upper())
    if not re.fullmatch(r"[0-9A-Z]{12}[0-9]{2}", cnpj) or len(set(cnpj)) == 1:
        return False
    d1 = digito(cnpj[:12])
    d2 = digito(cnpj[:12] + str(d1))
    return cnpj[12:] == f"{d1}{d2}"


# Cada caractere vale o código ASCII menos 48; os pesos vão de 2 a 9 da direita para a esquerda.
def digito(base: str) -> int:
    soma = sum((ord(c) - 48) * (2 + (len(base) - 1 - i) % 8) for i, c in enumerate(base))
    resto = soma % 11
    return 0 if resto < 2 else 11 - resto

Java

import java.util.Locale;

public final class Cnpj {
    public static boolean valido(String texto) {
        String cnpj = texto.toUpperCase(Locale.ROOT).replaceAll("[^0-9A-Z]", "");
        if (!cnpj.matches("[0-9A-Z]{12}[0-9]{2}") || cnpj.chars().distinct().count() == 1) return false;
        int d1 = digito(cnpj.substring(0, 12));
        int d2 = digito(cnpj.substring(0, 12) + d1);
        return cnpj.substring(12).equals("" + d1 + d2);
    }

    // Cada caractere vale o código ASCII menos 48; os pesos vão de 2 a 9 da direita para a esquerda.
    private static int digito(String base) {
        int soma = 0;
        for (int i = 0; i < base.length(); i++) {
            soma += (base.charAt(i) - 48) * (2 + (base.length() - 1 - i) % 8);
        }
        int resto = soma % 11;
        return resto < 2 ? 0 : 11 - resto;
    }
}

Go

package cnpj

import (
	"regexp"
	"strconv"
	"strings"
)

var (
	pontuacao = regexp.MustCompile("[^0-9A-Z]")
	formato   = regexp.MustCompile("^[0-9A-Z]{12}[0-9]{2}$")
)

func Valido(texto string) bool {
	cnpj := pontuacao.ReplaceAllString(strings.ToUpper(texto), "")
	if !formato.MatchString(cnpj) || strings.Count(cnpj, cnpj[:1]) == len(cnpj) {
		return false
	}
	d1 := digito(cnpj[:12])
	d2 := digito(cnpj[:12] + strconv.Itoa(d1))
	return cnpj[12:] == strconv.Itoa(d1)+strconv.Itoa(d2)
}

// Cada caractere vale o código ASCII menos 48; os pesos vão de 2 a 9 da direita para a esquerda.
func digito(base string) int {
	soma := 0
	for i := 0; i < len(base); i++ {
		soma += int(base[i]-48) * (2 + (len(base)-1-i)%8)
	}
	resto := soma % 11
	if resto < 2 {
		return 0
	}
	return 11 - resto
}

PostgreSQL

CREATE FUNCTION cnpj_valido(texto text) RETURNS boolean
LANGUAGE plpgsql IMMUTABLE AS $$
DECLARE
  cnpj text := regexp_replace(upper(texto), '[^0-9A-Z]', '', 'g');
  base text;
  soma int;
  resto int;
  dv int[] := '{}';
BEGIN
  IF cnpj !~ '^[0-9A-Z]{12}[0-9]{2}$' OR cnpj ~ '^(.)\1{13}$' THEN
    RETURN false;
  END IF;
  -- Cada caractere vale o código ASCII menos 48; os pesos vão de 2 a 9 da direita para a esquerda.
  FOR k IN 1..2 LOOP
    base := left(cnpj, 11 + k);
    soma := 0;
    FOR i IN 1..length(base) LOOP
      soma := soma + (ascii(substr(base, i, 1)) - 48) * (2 + (length(base) - i) % 8);
    END LOOP;
    resto := soma % 11;
    dv := dv || CASE WHEN resto < 2 THEN 0 ELSE 11 - resto END;
  END LOOP;
  RETURN right(cnpj, 2) = dv[1]::text || dv[2]::text;
END
$$;

Para recusar CNPJ inválido na gravação: ALTER TABLE empresas ADD CONSTRAINT cnpj_ok CHECK (cnpj_valido(cnpj));