Brasilex
Elixir library for Brazilian utilities and helpers.
[](https://hex.pm/packages/brasilex) [](https://hexdocs.pm/brasilex) [](https://github.com/stlucasgarcia/brasilex/blob/main/LICENSE)Features
Boleto (Bank Slip)
- Validate boleto linha digitável (typeable line) and barcode
- Parse boleto data into structured format
- Banking Boletos - Bank collection boletos
- Linha digitável: 47 digits
- Barcode: 44 digits
- Convenio Boletos - Utility/tax boletos (starting with "8")
- Linha digitável: 48 digits
- Barcode: 44 digits
State Registration (Inscrição Estadual - IE)
- Validate IE numbers with auto-detection
- Parse IE into structured data with state-specific formatting
- All 27 states supported: AC, AL, AM, AP, BA, CE, DF, ES, GO, MA, MG, MS, MT, PA, PB, PE, PI, PR, RJ, RN, RO, RR, RS, SC, SE, SP, TO
- Multiple state detection - Returns all states that match when algorithms overlap
General
- Full typespec coverage
- Comprehensive test suite
Roadmap
Upcoming features for future releases:
- CPF - Validate, format, and generate CPF numbers
- CNPJ - Validate, format, and generate CNPJ numbers
- CEP - Validate and format postal codes
- Phone - Validate and format Brazilian phone numbers
- PIS/PASEP - Validate social security numbers
- Vehicle Plate - Validate traditional and Mercosul formats
- CNH - Validate driver's license numbers
- RENAVAM - Validate vehicle registration numbers
- Currency - Format BRL currency values
Installation
Add brasilex to your list of dependencies in mix.exs:
def deps do
[
{:brasilex, "~> 0.2.0"}
]
endThen run:
$ mix deps.getUsage
Validate a Boleto
# Returns :ok or {:error, reason}
Brasilex.validate_boleto("00190.00009 01234.567890 12345.678908 1 00000000000000")
#=> :ok
Brasilex.validate_boleto("invalid")
#=> {:error, :invalid_length}
# Bang version raises on error
Brasilex.validate_boleto!("00190.00009 01234.567890 12345.678908 1 00000000000000")
#=> :ok
Brasilex.validate_boleto!("invalid")
#=> ** (Brasilex.ValidationError) Invalid length: wrong number of digitsParse a Boleto
{:ok, boleto} = Brasilex.parse_boleto("00190000090123456789012345678908100000000000000")
boleto.type #=> :banking
boleto.bank_code #=> "001"
boleto.amount #=> Decimal.new("150.00") or nil
boleto.due_date #=> ~D[2020-07-04] or nil
boleto.barcode #=> "00191000000000000001234567890123456789080"
# Bang version raises on error
boleto = Brasilex.parse_boleto!("00190000090123456789012345678908100000000000000")Input Formats
The library accepts both linha digitável and barcode, with or without formatting:
# Linha digitável with dots, spaces, hyphens
Brasilex.validate_boleto("00190.00009 01234.567890 12345.678908 1 00000000000000")
# Linha digitável digits only (47 or 48 digits)
Brasilex.validate_boleto("00190000090123456789012345678908100000000000000")
# Barcode (44 digits)
Brasilex.validate_boleto("23791843400000199003812860000000003000000004")Boleto Types
Banking Boleto (47 digits)
Bank collection boletos used for payments, invoices, etc.
{:ok, boleto} = Brasilex.parse_boleto("23793.38128 60000.000003 00000.000400 1 84340000019900")
boleto.type #=> :banking
boleto.bank_code #=> "237" (e.g., "001" = Banco do Brasil)
boleto.currency_code #=> "9" (BRL)
boleto.amount #=> Decimal.new("199.00") or nil if any amount
boleto.due_date #=> ~D[2020-07-04] or nil if no due date
boleto.free_field #=> "3812860000000003000000004" (25 digits of bank-defined content)Convenio Boleto (48 digits)
Utility bills, taxes, and government collections. First digit is always "8".
{:ok, boleto} = Brasilex.parse_boleto("846700000005 573200481018 150820204176 494672890166")
boleto.type #=> :convenio
boleto.segment #=> "6" (determines validation algorithm)
boleto.amount #=> Decimal.new("573.20") or nil
boleto.company_id #=> "0481018150820204176494672890166"
boleto.free_field #=> Segment-specific contentState Registration (IE)
Validate an IE
# Returns :ok or {:error, reason}
Brasilex.validate_ie("110.042.490.114")
#=> :ok
Brasilex.validate_ie("12345")
#=> {:error, :invalid_length}
# Bang version raises on error
Brasilex.validate_ie!("110.042.490.114")
#=> :okParse an IE
# Returns all states that match the IE
{:ok, [ie]} = Brasilex.parse_ie("110.042.490.114")
ie.state #=> :sp
ie.raw #=> "110042490114"
ie.formatted #=> "110.042.490.114"
# Some IEs are valid for multiple states (shared algorithms)
{:ok, ies} = Brasilex.parse_ie("820000000")
Enum.map(ies, & &1.state)
#=> [:am, :sc, :se]
# Each IE has state-specific formatting
am_ie = Enum.find(ies, & &1.state == :am)
am_ie.formatted #=> "82.000.000-0"
sc_ie = Enum.find(ies, & &1.state == :sc)
sc_ie.formatted #=> "820.000.000"Supported States
All 27 Brazilian states are supported with their specific validation rules:
| State | Digits | Algorithm | Notes |
|---|---|---|---|
| AC | 13 | Mod11 (2 DVs) | Prefix "01" |
| AL | 9 | Mod11 | Prefix "24", type codes 0,3,5,7,8 |
| AM | 9 | Mod11 | Special case when sum < 11 |
| AP | 9 | Mod11 | Prefix "03", special p/d values |
| BA | 8-9 | Mod10/Mod11 | Based on first digit |
| CE | 9 | Mod11 | Weights 9-2 |
| DF | 13 | Mod11 (2 DVs) | Prefix "07" |
| ES | 9 | Mod11 | Weights 9-2 |
| GO | 9 | Mod11 | Prefixes 10, 11, 20-29 |
| MA | 9 | Mod11 | Prefix "12" |
| MG | 13 | Mod10 + Mod11 | 2 check digits |
| MS | 9 | Mod11 | Prefix "28" |
| MT | 11 | Mod11 | Single DV |
| PA | 9 | Mod11 | Prefixes 15, 75-79 |
| PB | 9 | Mod11 | Weights 9-2 |
| PE | 9/14 | Mod11 | eFisco (9) or CACEPE (14) |
| PI | 9 | Mod11 | Weights 9-2 |
| PR | 10 | Mod11 (2 DVs) | Weights 3,2,7,6,5,4,3,2 |
| RJ | 8 | Mod11 | Weights 2,7,6,5,4,3,2 |
| RN | 9-10 | Mod11 | Prefix "20" |
| RO | 9/14 | Mod11 | Legacy (9) or new (14) |
| RR | 9 | Mod9 | Prefix "24" |
| RS | 10 | Mod11 | Single DV |
| SC | 9 | Mod11 | Weights 9-2 |
| SE | 9 | Mod11 | Weights 9-2 |
| SP | 12/13 | Custom | Regular (12) or rural "P" (13) |
| TO | 11 | Mod11 | Type codes 01, 02, 03, 99 |
Error Handling
All functions return {:ok, result} or {:error, reason} tuples:
case Brasilex.validate_boleto(input) do
:ok ->
IO.puts("Valid boleto!")
{:error, :invalid_length} ->
IO.puts("Wrong number of digits (expected 47 or 48)")
{:error, :invalid_format} ->
IO.puts("Invalid characters found")
{:error, :invalid_checksum} ->
IO.puts("General check digit validation failed")
{:error, {:invalid_field_checksum, n}} ->
IO.puts("Field #{n} check digit validation failed")
{:error, :unknown_type} ->
IO.puts("Could not determine boleto type")
endBang Variants
Bang variants raise Brasilex.ValidationError for cleaner pipelines:
try do
boleto = Brasilex.parse_boleto!("invalid")
# Process boleto...
rescue
e in Brasilex.ValidationError ->
IO.puts("Validation failed: #{e.message}")
endResponse Types with Structs
For better type safety and developer experience, Brasilex provides struct definitions:
# Boleto struct with all parsed fields
{:ok, %Brasilex.Boleto{} = boleto} = Brasilex.parse_boleto("...")
# Banking boleto fields
boleto.type #=> :banking
boleto.bank_code #=> String.t()
boleto.currency_code #=> String.t()
boleto.amount #=> Decimal.t() | nil
boleto.due_date #=> Date.t() | nil
boleto.free_field #=> String.t()
boleto.barcode #=> String.t()
# Convenio boleto fields
boleto.type #=> :convenio
boleto.segment #=> String.t()
boleto.amount #=> Decimal.t() | nil
boleto.company_id #=> String.t()
boleto.free_field #=> String.t()
boleto.barcode #=> String.t()
# IE struct with parsed fields
{:ok, [%Brasilex.IE{} = ie]} = Brasilex.parse_ie("110.042.490.114")
ie.state #=> :sp
ie.raw #=> "110042490114"
ie.formatted #=> "110.042.490.114"Available Structs
| Struct | Description |
|---|---|
Brasilex.Boleto | Parsed boleto with all fields (type, amount, dates, etc.) |
Brasilex.IE | Parsed state registration with state, raw, and formatted |
Brasilex.ValidationError | Exception raised by bang functions |
Validation Details
Check Digit Algorithms
Brasilex validates boletos using the standard Brazilian algorithms:
- Banking boletos (47 digits): Module 10 for field check digits, Module 11 for general verifier
- Convenio boletos (48 digits): Module 10 or Module 11 depending on segment identifier
What Gets Validated
- Length: Must be exactly 44, 47, or 48 digits (after removing formatting)
- Format: Must contain only digits (after removing dots, spaces, hyphens)
- Check digits: All field-level and general check digits are verified
- Type detection: Boleto type is determined from digit count and first character
- 44 digits starting with "8" → Convenio barcode
- 44 digits (other) → Banking barcode
- 47 digits → Banking linha digitável
- 48 digits starting with "8" → Convenio linha digitável
Contributing
Bug reports and pull requests are welcome on GitHub at https://github.com/stlucasgarcia/brasilex.
- Fork the repository
-
Create your feature branch (
git checkout -b feature/my-feature) -
Commit your changes (
git commit -am 'Add my feature') -
Push to the branch (
git push origin feature/my-feature) - Create a Pull Request
License
The library is available as open source under the terms of the MIT License.