View Source QRNBU (NBU payment QR v0.3.3)

NBU (National Bank of Ukraine) QR Code generator library.

This library provides a comprehensive API for generating NBU-compliant QR codes for payment systems in Ukraine. It supports all three official NBU QR code versions:

  • V001: Plain text format with CRLF line endings (EPC QR code compatible)
  • V002: Base64URL encoded format with 13 fields
  • V003: Extended Base64URL format with 17 fields (ISO 20022 category purpose)

Quick Start

# Generate a simple V001 QR code
iex> {:ok, qr} = QRNBU.generate(:v001, %{
...>   recipient: "ТОВ Компанія",
...>   iban: "UA213223130000026007233566001",
...>   recipient_code: "12345678",
...>   purpose: "Оплата товарів"
...> })
iex> String.contains?(qr, "BCD")
true

# Generate a V002 QR code with amount
iex> {:ok, qr} = QRNBU.generate(:v002, %{
...>   recipient: "ТОВ Компанія",
...>   iban: "UA213223130000026007233566001",
...>   recipient_code: "12345678",
...>   purpose: "Оплата товарів",
...>   amount: Decimal.new("100.50")
...> })
iex> String.starts_with?(qr, "https://qr.bank.gov.ua/")
true

# Generate a V003 QR code with extended fields
iex> {:ok, qr} = QRNBU.generate(:v003, %{
...>   recipient: "ТОВ Компанія",
...>   iban: "UA213223130000026007233566001",
...>   recipient_code: "12345678",
...>   purpose: "Оплата товарів",
...>   amount: Decimal.new("500.00"),
...>   category_purpose: "SUPP/REGU"
...> })
iex> String.starts_with?(qr, "https://qr.bank.gov.ua/")
true

Version Selection

Choose the appropriate version based on your requirements:

  • Use V001 for compatibility with EPC QR codes and simple payments
  • Use V002 for modern NBU-compliant payments with Base64URL encoding
  • Use V003 for advanced features like ISO 20022 category purpose and field locking

Field Reference

Common Fields (All Versions)

  • :recipient (required) - Recipient name (1-70 characters)
  • :iban (required) - Bank account IBAN (UA + 27 digits)
  • :recipient_code (required) - EDRPOU/IPN/Tax ID (6-35 characters)
  • :purpose (required) - Payment purpose (1-140 characters)
  • :amount (optional) - Payment amount as Decimal.t()
  • :function (optional) - Function code: :uct, :ict, or :xct (default: :uct)
  • :encoding (optional) - Character encoding: :utf8 or :cp1251 (default: :utf8)

V002 Additional Fields

  • :reference (optional) - Payment reference number (max 35 characters)

V003 Additional Fields

  • :reference (optional) - Payment reference number (max 35 characters)
  • :unique_recipient_id (optional) - Unique recipient identifier (max 35 characters)
  • :category_purpose (optional) - ISO 20022 category purpose (format: CCCC/PPPP)
  • :display (optional) - Display text for QR scanner (max 140 characters)
  • :field_lock (optional) - Field lock bitmap 0x0000-0xFFFF (integer)
  • :invoice_validity (optional) - Invoice expiration as NaiveDateTime.t()
  • :invoice_creation (optional) - Invoice creation as NaiveDateTime.t()
  • :digital_signature (optional) - Digital signature string (max 1000 characters)

Error Handling

All functions return {:ok, result} or {:error, reason} tuples.

Common errors:

  • Missing required fields
  • Invalid field formats (IBAN, tax ID, etc.)
  • Invalid character encoding
  • Date/time validation failures

References

  • NBU Resolution No. 97, August 19, 2025
  • EPC QR Code Guidelines v3.0
  • ISO 20022 External Code Sets

Summary

Functions

Detects the version of an NBU QR code string.

Generates an NBU QR code string for the specified version.

Generates an NBU QR code string, raising an exception on error.

Validates NBU QR code data without generating the QR string.

Types

@type qr_data() :: map()
@type qr_string() :: String.t()
@type version() :: :v001 | :v002 | :v003

Functions

Link to this function

detect_version(qr_string)

View Source
@spec detect_version(qr_string()) :: {:ok, version()} | {:error, String.t()}

Detects the version of an NBU QR code string.

Analyzes the QR code format to determine which version it represents.

Returns

  • {:ok, :v001} - Plain text format with BCD marker
  • {:ok, :v002} - Base64URL with NBU URL prefix
  • {:ok, :v003} - Base64URL with NBU URL prefix (distinguished by field count)
  • {:error, reason} - Unable to detect version

Examples

iex> v001_str = String.duplicate(" ", 23) <> "\r\nBCD\r\n001\r\n"
iex> QRNBU.detect_version(v001_str)
{:ok, :v001}

iex> QRNBU.detect_version("invalid")
{:error, "Unable to detect QR code version"}
@spec generate(version(), qr_data()) :: {:ok, qr_string()} | {:error, String.t()}

Generates an NBU QR code string for the specified version.

Parameters

  • version - QR code version: :v001, :v002, or :v003
  • data - Map containing QR code fields (see module documentation)

Returns

  • {:ok, qr_string} - Successfully generated QR code string
  • {:error, reason} - Validation or encoding error

Examples

iex> {:ok, qr} = QRNBU.generate(:v001, %{
...>   recipient: "ТОВ Компанія",
...>   iban: "UA213223130000026007233566001",
...>   recipient_code: "12345678",
...>   purpose: "Оплата товарів"
...> })
iex> is_binary(qr)
true

iex> {:ok, qr} = QRNBU.generate(:v002, %{
...>   recipient: "ТОВ Компанія",
...>   iban: "UA213223130000026007233566001",
...>   recipient_code: "12345678",
...>   purpose: "Оплата товарів",
...>   amount: Decimal.new("100.50"),
...>   reference: "INV-001"
...> })
iex> String.starts_with?(qr, "https://qr.bank.gov.ua/")
true

iex> QRNBU.generate(:invalid_version, %{})
{:error, "Invalid version: :invalid_version. Must be :v001, :v002, or :v003"}

iex> {:error, reason} = QRNBU.generate(:v001, %{})
iex> is_binary(reason)
true
Link to this function

generate!(version, data)

View Source
@spec generate!(version(), qr_data()) :: qr_string()

Generates an NBU QR code string, raising an exception on error.

Same as generate/2 but raises RuntimeError instead of returning an error tuple.

Examples

iex> qr = QRNBU.generate!(:v001, %{
...>   recipient: "ТОВ Компанія",
...>   iban: "UA213223130000026007233566001",
...>   recipient_code: "12345678",
...>   purpose: "Оплата товарів"
...> })
iex> is_binary(qr) and String.contains?(qr, "BCD")
true
@spec validate(version(), qr_data()) :: :ok | {:error, String.t()}

Validates NBU QR code data without generating the QR string.

Useful for pre-validation before QR code generation.

Parameters

  • version - QR code version: :v001, :v002, or :v003
  • data - Map containing QR code fields

Returns

  • :ok - Data is valid for the specified version
  • {:error, reason} - Validation error

Examples

iex> QRNBU.validate(:v001, %{
...>   recipient: "ТОВ Компанія",
...>   iban: "UA213223130000026007233566001",
...>   recipient_code: "12345678",
...>   purpose: "Оплата товарів"
...> })
:ok

iex> QRNBU.validate(:v001, %{})
{:error, "Missing required field: recipient"}

iex> QRNBU.validate(:v003, %{
...>   recipient: "ТОВ Компанія",
...>   iban: "UA213223130000026007233566001",
...>   recipient_code: "12345678",
...>   purpose: "Оплата товарів",
...>   field_lock: -1
...> })
{:error, "Field lock must be between 0 and 65535"}