Generate, parse and validate Pix "copia e cola" payloads (BR Code), the text behind every Pix QR code.
- Static payloads (key, optional amount, txid, description)
- Dynamic payloads (URL provided by the receiver's bank)
- Parsing into a struct, with CRC16 check
- Key validation and normalization (CPF, CNPJ including alphanumeric, phone, e-mail, random key/EVP)
- Zero runtime dependencies
def deps do
[
{:pix_brcode, "~> 0.2.0"}
]
endPixBrcode.encode(%{
key: "pix@bcb.gov.br",
merchant_name: "Fulano de Tal",
merchant_city: "Brasília",
amount: 1050, # integer cents, or the string "10.50"
txid: "ORDER42", # optional, defaults to "***"
description: "Thanks" # optional
})
#=> {:ok, "00020126...6304XXXX"}-
merchant_name(max 25 chars) andmerchant_city(max 15 chars) have their accents removed ("Brasília"becomes"Brasilia"). Longer values return an error; they are never truncated. -
amountnever goes through floats: pass integer cents (1050) or a string with exactly two decimals ("10.50"). -
keyis written in the canonical format of the Central Bank's DICT. Formatted input is accepted, so you can pass what the user typed:Type Input Written as CPF "123.456.789-09""12345678909"CNPJ "11.222.333/0001-81","12.ABC.345/01DE-35""11222333000181","12ABC34501DE35"Phone "+55 (11) 98765-4321","(11) 98765-4321","5511987654321""+5511987654321"E-mail " Fulano@Exemplo.com ""fulano@exemplo.com"EVP uppercase UUID lowercase UUID CPF and CNPJ must have valid check digits. An 11-digit number such as
"11987654321"is read as a CPF; for a phone number without+55, passkey_type: :phone(also:cpf,:cnpj,:email,:evp).
PixBrcode.encode_dynamic(%{
url: "pix.example.com/qr/v2/9d36b84f", # without https://
merchant_name: "Fulano de Tal",
merchant_city: "Brasilia"
})PixBrcode.decode("00020126580014br.gov.bcb.pix0136123e4567-e12b-12d1-a456-4266554400005204000053039865802BR5913Fulano de Tal6008BRASILIA62070503***63041D3D")
#=> {:ok,
#=> %PixBrcode.Payload{
#=> type: :static,
#=> key: "123e4567-e12b-12d1-a456-426655440000",
#=> merchant_name: "Fulano de Tal",
#=> merchant_city: "BRASILIA",
#=> txid: "***",
#=> amount: nil, description: nil, url: nil
#=> }}
PixBrcode.valid?("not a pix")
#=> falsedecode/1 checks the CRC, the structure, the br.gov.bcb.pix GUI and the key format,
and returns the key in canonical form. Check digits are not verified when decoding, since
the CRC already guarantees the payload is intact. Leading/trailing whitespace is ignored.
All functions return {:ok, result} or {:error, reason}:
| Reason | When |
|---|---|
:missing_required_fields |
a required field is missing or is not a string |
:invalid_key |
key does not match any DICT format, or CPF/CNPJ has wrong check digits |
:invalid_key_type |
:key_type is not one of :cpf, :cnpj, :phone, :email, :evp |
:invalid_merchant_name / :invalid_merchant_city |
empty, too long, or not printable ASCII after removing accents |
:invalid_description |
not a string, or not printable ASCII after removing accents |
:invalid_amount |
not positive integer cents nor a "10.50" string, or over 13 characters |
:invalid_txid |
not "***" nor 1–25 letters/digits |
:invalid_url |
empty or contains a protocol (https://) |
{:too_long, id} |
field id would exceed 99 characters |
:invalid_crc / :invalid_tlv / :invalid_format / :invalid_gui / :missing_key |
when decoding |
Generating the image is out of scope, so any QR code library works. With
eqrcode:
{:ok, payload} = PixBrcode.encode(%{key: "pix@bcb.gov.br", merchant_name: "Fulano", merchant_city: "Brasilia"})
svg = payload |> EQRCode.encode() |> EQRCode.svg()Based on the Central Bank of Brazil's Manual do BR Code (EMV QRCPS-MPM) and the key formats of the DICT API and the Manual Operacional do DICT (v8.5, alphanumeric CNPJ).
MIT — Igor Giamoniano.