feat(cnpj): support the alphanumeric CNPJ format#67
Merged
Conversation
Validate the new Receita Federal alphanumeric CNPJ (12 uppercase
alphanumeric positions + 2 numeric check digits) while keeping the
legacy numeric format working.
The check-digit calculation maps each base character via its ASCII code
offset by 48 ('0' -> 0, 'A' -> 17 ... 'Z' -> 42), per the official spec,
and the 2 check digits remain numeric (regex ^[A-Z\d]{12}\d{2}$). Only
the mask characters (. / -) are stripped before validation.
Implementation selected from PR #62 (the only one matching the official
ASCII-48 offset), refined to match the codebase style (module-scoped
regex/weights, a generateChecksum helper mirroring validatePIS) and
covered with the full canonical test set from the Receita Federal
reference implementation, including the discriminating 00000000000191
(valid) and zeroed/lowercase/letter-in-DV rejection cases.
Co-authored-by: Jean Carlos Nesi <jean.nesi@nextfit.com.br>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This was referenced Jun 24, 2026
AZagatti
added a commit
that referenced
this pull request
Jun 24, 2026
Adds alphanumeric CNPJ validation (12 alphanumeric + 2 numeric DV) per the Receita Federal spec, keeping legacy numeric support. Consolidates #59/#60/#62; base implementation from #62 (@JeanNesi). Full canonical test set; 115 tests pass. Co-authored-by: Jean Carlos Nesi <102368879+JeanNesi@users.noreply.github.com> Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Adds support for the new alphanumeric CNPJ format (12 uppercase alphanumeric positions + 2 numeric check digits) from Receita Federal, keeping the legacy numeric format fully working.
Background
This consolidates the three community PRs (#59, #60, #62). I checked each against the official Receita Federal reference implementation (Java/Python/TypeScript). The selected base is #62 (@JeanNesi) — the only one using the correct ASCII offset of 48 (
'0'→0,'A'→17). #59 used offset 55 ('A'→10), which produces wrong check digits; #60 was correct but more verbose (Portuguese identifiers, throw-based port).Implementation
charCodeAt(0) - 48(per spec), check digits stay numeric:^[A-Z\d]{12}\d{2}$..,/,-) are stripped; lowercase letters and any other character are rejected (matches the official spec).generateChecksumhelper that mirrors the existingvalidatePIS, for codebase consistency. The sharedisRepeatedutil is untouched.Tests
Full canonical test set from the official reference, including discriminating cases:
00000000000191→ valid (all-zero base with a real DV), while00000000000000(zeroed) → invalid12.ABc.345/01DE-35), letters in DV positions rejected (0000000000019L), invalid DV, disallowed characters, length checks90.0xx.xxxseries,ABCDEFGHIJKL80,12.ABC.345/01DE-35115 tests pass · Biome lint clean · tsc + build OK.
Co-authored with @JeanNesi (#62). Supersedes #59, #60, #62.
🤖 Generated with Claude Code