🇧🇷 CNPJ validation in Go

Adds support for alphanumeric CNPJ (July/2026)

+39 -26
+10 -3
README.md
··· 1 1 # Go CNPJ [![Tests](https://github.com/cuducos/go-cnpj/workflows/Tests/badge.svg)]()[![GoDoc](https://godoc.org/github.com/cuducos/go-cnpj?status.svg)](https://godoc.org/github.com/cuducos/go-cnpj)![Go version](https://img.shields.io/github/go-mod/go-version/cuducos/go-cnpj) 2 2 3 - A Go module to validate CNPJ numbers (Brazilian companies unique identifier for the Federal Revenue). 3 + A Go module to validate CNPJ numbers (Brazilian companies' unique identifier for the Federal Revenue). 4 + 5 + > [!IMPORTANT] 6 + > Starting in July 2026 [the CNPJ number will be alphanumeric](https://www.gov.br/receitafederal/pt-br/acesso-a-informacao/acoes-e-programas/programas-e-atividades/cnpj-alfanumerico). This package **already supports the new format**. If you **do not** want to support the new format, tag this package to [`v0.1.1`](https://github.com/cuducos/go-cnpj/releases/tag/v0.1.1). 4 7 5 8 ```go 6 9 package main ··· 12 15 // these return true 13 16 cnpj.IsValid("11222333000181") 14 17 cnpj.IsValid("11.222.333/0001-81") 18 + cnpj.IsValid("12.ABC.345/01DE-35") 19 + cnpj.IsValid("12ABC34501DE35") 15 20 16 21 // these return false 17 22 cnpj.IsValid("11.111.111/1111-11") ··· 19 24 cnpj.IsValid("AB.CDE.FGH/IJKL-MN") 20 25 cnpj.IsValid("123") 21 26 22 - // this returns 11111111111111 27 + // these return 11111111111111 and 12ABC34501DE35 23 28 cnpj.Unmask("11.111.111/1111-11") 29 + cnpj.Unmask("12.ABC.345/01DE-35") 24 30 25 - // this returns 11.111.111/1111-11 31 + // these return 11.111.111/1111-11 and 12.ABC.345/01DE-35 26 32 cnpj.Mask("11111111111111") 33 + cnpj.Mask("12ABC34501DE35") 27 34 } 28 35 ``` 29 36
+14 -20
cnpj.go
··· 3 3 import ( 4 4 "fmt" 5 5 "regexp" 6 - "strconv" 7 - "strings" 8 6 ) 9 7 10 - func checksum(ds []int64, ref []int64) int64 { 11 - var s int64 8 + func checksum(ds []int32, ref []int32) int32 { 9 + var s int32 12 10 for i, n := range ref { 13 11 s += n * ds[i] 14 12 } ··· 19 17 return 11 - r 20 18 } 21 19 22 - //IsValid checks whether CNPJ number is valid or not 20 + // IsValid checks whether CNPJ number is valid or not 23 21 func IsValid(n string) bool { 24 22 u := Unmask(n) 25 23 if len(u) != 14 { 26 24 return false 27 25 } 28 26 29 - ds := make([]int64, 14) 30 - for i, v := range strings.Split(u, "") { 31 - c, err := strconv.ParseInt(v, 10, 32) 32 - if err != nil { 33 - return false 34 - } 35 - ds[i] = c 27 + ds := make([]int32, 14) 28 + for i, v := range []rune(u) { 29 + ds[i] = int32(v) - 48 // ASCII value minus 48 so "0" becomes 0 and so on 36 30 } 37 31 38 - r1 := []int64{5, 4, 3, 2, 9, 8, 7, 6, 5, 4, 3, 2} 39 - r2 := []int64{6, 5, 4, 3, 2, 9, 8, 7, 6, 5, 4, 3, 2} 32 + r1 := []int32{5, 4, 3, 2, 9, 8, 7, 6, 5, 4, 3, 2} 33 + r2 := []int32{6, 5, 4, 3, 2, 9, 8, 7, 6, 5, 4, 3, 2} 40 34 return checksum(ds, r1) == ds[12] && checksum(ds, r2) == ds[13] 41 35 } 42 36 43 - //Mask returns the CNPJ number formatted 37 + // Mask returns the CNPJ number formatted 44 38 func Mask(n string) string { 45 39 u := Unmask(n) 46 40 if len(u) != 14 { ··· 49 43 return fmt.Sprintf("%s.%s.%s/%s-%s", u[:2], u[2:5], u[5:8], u[8:12], u[12:]) 50 44 } 51 45 52 - //Unmask removes any non-digit (numeric) from the CNPJ number 46 + // Unmask removes any non-digit (numeric) from the CNPJ number 53 47 func Unmask(n string) string { 54 - return regexp.MustCompile(`\D`).ReplaceAllString(n, "") 48 + return regexp.MustCompile(`[^0-9A-Z]`).ReplaceAllString(n, "") 55 49 } 56 50 57 - //Base returns the first 8 digits of the CNPJ number 51 + // Base returns the first 8 digits of the CNPJ number 58 52 func Base(n string) string { 59 53 if !IsValid(n) { 60 54 return "" ··· 63 57 return Unmask(n)[0:8] 64 58 } 65 59 66 - //Order returns the 9th, 10th, 11th and 12th digits of the CNPJ number. 60 + // Order returns the 9th, 10th, 11th and 12th digits of the CNPJ number. 67 61 func Order(n string) string { 68 62 if !IsValid(n) { 69 63 return "" ··· 72 66 return Unmask(n)[8:12] 73 67 } 74 68 75 - //CheckDigit returns the last 2 digits of the CNPJ number. 69 + // CheckDigit returns the last 2 digits of the CNPJ number. 76 70 func CheckDigit(n string) string { 77 71 if !IsValid(n) { 78 72 return ""
+15 -3
cnpj_test.go
··· 13 13 {"11111111111111", "11.111.111/1111-11"}, 14 14 {"123456", "123456"}, 15 15 {"11223344556677889900", "11223344556677889900"}, 16 + {"12ABC34501DE35", "12.ABC.345/01DE-35"}, 16 17 } { 17 18 if got := Mask(tc.cnpj); tc.expected != got { 18 19 t.Errorf("Mask(\"%s\") = %v; expected %s", tc.cnpj, got, tc.expected) ··· 21 22 } 22 23 23 24 func TestUnmask(t *testing.T) { 24 - if got := Unmask("11.111.111/1111-11"); "11111111111111" != got { 25 - t.Errorf("Unmask(\"11.111.111/1111-11\") = %v; want 11111111111111", got) 25 + for _, tc := range []struct { 26 + cnpj string 27 + expected string 28 + }{ 29 + {"11.111.111/1111-11", "11111111111111"}, 30 + {"12.ABC.345/01DE-35", "12ABC34501DE35"}, 31 + } { 32 + if got := Unmask(tc.cnpj); tc.expected != got { 33 + t.Errorf("Unmask(\"%s\") = %v; want %s", tc.cnpj, got, tc.expected) 34 + } 26 35 } 27 36 } 28 37 ··· 35 44 {"11.222.333/0001-81", true}, 36 45 {"123", false}, 37 46 {"11.111.111/1111-11", false}, 47 + {"12.ABC.345/01DE-35", true}, 48 + {"12ABC34501DE35", true}, 49 + {"12ABC34501DE42", false}, 38 50 {"12.345.678 9012-34", false}, 39 51 {"AB.CDE.FGH/IJKL-MN", false}, 40 52 } { 41 53 if got := IsValid(tc.cnpj); tc.expected != got { 42 - t.Errorf("IsValid(%v) = %v; expected %v", tc.cnpj, got, tc.expected) 54 + t.Errorf("IsValid(\"%v\") = %v; expected %v", tc.cnpj, got, tc.expected) 43 55 } 44 56 } 45 57 }