Validar CNPJ alfanumérico no Laravel com Rule customizada
Crie uma Rule customizada no Laravel 10/11 que valida o novo CNPJ alfanumérico com o algoritmo oficial (ASCII−48, pesos e módulo 11) e use-a no FormRequest.
Validar CNPJ alfanumérico no Laravel ficou simples: você cria uma Rule customizada que aplica o algoritmo oficial (ASCII−48, pesos e módulo 11) e a usa como ['cnpj' => ['required', new CnpjAlfanumerico]] no seu FormRequest ou validator. Este tutorial mostra a classe completa, o uso e a mensagem de erro em português, prontos para Laravel 10 e 11.
O que muda no CNPJ alfanumérico
A partir de julho de 2026 (Instrução Normativa RFB nº 2.229/2024), o CNPJ passa a aceitar letras maiúsculas A–Z nas 12 primeiras posições (raiz + ordem), mantendo as 14 posições e a máscara XX.XXX.XXX/XXXX-DD. Os dois dígitos verificadores continuam numéricos, e os CNPJs numéricos atuais seguem válidos — seu sistema precisa aceitar os dois formatos. Veja o panorama completo no nosso guia de implementação do CNPJ alfanumérico.
Validando CNPJ alfanumérico em Laravel
Desde o Laravel 10 a forma recomendada de criar regras é implementar a interface Illuminate\Contracts\Validation\ValidationRule, que tem um único método validate() e recebe um Closure $fail para reportar o erro. Gere o esqueleto com php artisan make:rule CnpjAlfanumerico e cole a lógica golden abaixo. Toda a aritmética (normalização, pesos e módulo 11) fica encapsulada na própria Rule.
<?php
namespace App\Rules;
use Closure;
use Illuminate\Contracts\Validation\ValidationRule;
class CnpjAlfanumerico implements ValidationRule
{
private const PESO1 = [5, 4, 3, 2, 9, 8, 7, 6, 5, 4, 3, 2];
private const PESO2 = [6, 5, 4, 3, 2, 9, 8, 7, 6, 5, 4, 3, 2];
public function validate(string $attribute, mixed $value, Closure $fail): void
{
if (! is_string($value) || ! $this->valido($value)) {
$fail('O :attribute informado não é um CNPJ válido.');
}
}
private function valido(string $cnpj): bool
{
$c = $this->normalizar($cnpj);
// 12 posições alfanuméricas (A-Z, 0-9) + 2 dígitos verificadores
if (! preg_match('/^[A-Z0-9]{12}[0-9]{2}$/', $c)) {
return false;
}
if (preg_match('/^0{12}/', $c)) { // base zerada
return false;
}
return $this->calcularDv(substr($c, 0, 12)) === substr($c, 12);
}
private function normalizar(string $s): string
{
return preg_replace('/[^A-Z0-9]/', '', strtoupper($s));
}
private function digito(string $base, array $pesos): int
{
$soma = 0;
for ($i = 0; $i < strlen($base); $i++) {
$soma += (ord($base[$i]) - 48) * $pesos[$i];
}
$resto = $soma % 11;
return $resto < 2 ? 0 : 11 - $resto;
}
private function calcularDv(string $base12): string
{
$d1 = $this->digito($base12, self::PESO1);
$d2 = $this->digito($base12 . $d1, self::PESO2);
return $d1 . $d2;
}
}Observe que normalizar() remove a máscara e força maiúsculas, então a Rule aceita tanto 12.ABC.345/01DE-35 quanto 12abc34501de35. A mesma lógica isolada em funções aparece no nosso tutorial de como validar CNPJ alfanumérico em PHP puro, caso você queira reaproveitá-la fora do Laravel.
Calculando os dígitos verificadores
O coração da Rule são os métodos digito() e calcularDv(). Para cada DV, somamos (ord($char) - 48) * peso posição a posição, tiramos o resto da divisão por 11 e aplicamos a regra resto < 2 ? 0 : 11 - resto. O 1º DV usa os 12 caracteres da base com os pesos PESO1; o 2º DV usa a base já com o 1º DV anexado (13 caracteres) e os pesos PESO2. Como os DV resultantes são sempre dígitos 0–9, a comparação final é uma simples igualdade de strings.
Usando a Rule no FormRequest e no validator
Com a classe pronta, é só referenciá-la como uma instância no array de regras. No FormRequest:
<?php
namespace App\Http\Requests;
use App\Rules\CnpjAlfanumerico;
use Illuminate\Foundation\Http\FormRequest;
class StoreEmpresaRequest extends FormRequest
{
public function rules(): array
{
return [
'cnpj' => ['required', 'string', new CnpjAlfanumerico],
];
}
public function messages(): array
{
return [
'cnpj.required' => 'Informe o CNPJ da empresa.',
];
}
}Ou diretamente no Validator::make() de um controller:
<?php
use App\Rules\CnpjAlfanumerico;
use Illuminate\Support\Facades\Validator;
$validator = Validator::make($request->all(), [
'cnpj' => ['required', new CnpjAlfanumerico],
]);
if ($validator->fails()) {
return back()->withErrors($validator)->withInput();
}Como testar
- Crie a Rule com
php artisan make:rule CnpjAlfanumericoe cole a lógica golden. - Adicione
new CnpjAlfanumericoàs regras do seu FormRequest ou validator. - Escreva um teste Pest/PHPUnit cobrindo casos válidos, inválidos e o CNPJ numérico clássico.
- Gere massa de teste com o nosso gerador de CNPJ alfanumérico e confira cada número no validador de CNPJ alfanumérico.
Use estes vetores verificados nas suas asserções:
| CNPJ | Tipo | Resultado |
|---|---|---|
12.ABC.345/01DE-35 | Alfanumérico (filial) | Válido |
XP.TO1.234/0001-20 | Alfanumérico (matriz) | Válido |
9F.8E7.D6C/0001-36 | Alfanumérico (matriz) | Válido |
A1.B2C.3D4/E5F6-68 | Alfanumérico (filial) | Válido |
11.222.333/0001-81 | Numérico clássico | Válido |
12.ABC.345/01DE-34 | DV incorreto | Inválido |
Perguntas frequentes
Devo usar ValidationRule ou a antiga interface Rule?
Use Illuminate\Contracts\Validation\ValidationRule com o método validate(). Ela é a forma recomendada no Laravel 10 e 11 e substitui a antiga Illuminate\Contracts\Validation\Rule (com passes() e message()), que está obsoleta. Se você ainda está num projeto Laravel 9, a interface antiga funciona com a mesma lógica de cálculo.
A Rule aceita CNPJ numérico antigo também?
Sim. O algoritmo é o mesmo módulo 11 para os dois formatos — os números são apenas um caso particular em que todos os caracteres têm valor 0–9. Por isso 11.222.333/0001-81 passa na mesma Rule que valida 12.ABC.345/01DE-35, sem condicionais extras.
Como customizar a mensagem de erro em português?
A própria Rule chama $fail('O :attribute informado não é um CNPJ válido.'), e o Laravel substitui :attribute pelo nome do campo. Você pode traduzir o atributo em lang/pt-br/validation.php (chave attributes) para exibir "CNPJ" em vez de "cnpj". Para a mensagem de campo obrigatório, use o método messages() do FormRequest com a chave cnpj.required.
Fontes: Receita Federal (IN RFB 2.229/2024) e nota técnica do Serpro sobre o CNPJ alfanumérico.
Ofertas em destaque
Ver todas →
Amazon
-27%
Hellmann's Maionese Light 500g
Mercado Livre
Kit 10 Pares Meia Invisível Unissex Soquete Algodão Selene 10 Branco Original Soquete Cano Curto G ( 39 A 44 )
Shopee
-60%
Brow Rise Gel Para Sobrancelhas By Ruby Rose Linha Rosa HB-E2503 Hot Sale
Shopee
-56%
Calça Pantalona Lanzinha Feminina Cintura Alta Com Elastico De e Bolso Outono / Inverno Promoção
Amazon
-93%
Tramontina Ducha Elétrica 3 Temperaturas 5500 W 127 V Chuveiro Branco
Amazon
Leite em Pó Ninho Integral Instantâneo Sachê 975g
Mercado Livre
Perfume Lattafa Bade'e Al Oud For Glory Edp Spray 100 Ml
Amazon
-14%
Lenços Umedecidos Huggies Rosto e Corpo Limpeza 4 x 48 Un
Shopee
-33%