QRNBUEx

Hex.pm Documentation License

Elixir library for generating NBU-compliant QR codes for Ukrainian payment systems.

Generate QR codes compatible with the National Bank of Ukraine (NBU) standards for money transfers to Ukrainian IBANs. Compatible with all major Ukrainian banking apps including PrivatBank, Monobank, Sense Bank, Ukrgazbank (EcoBank), PUMB, SportBank, IZIBank, and others.

Features

Installation

Add qr_nbu_ex to your list of dependencies in mix.exs:

def deps do
[
{:qr_nbu_ex, "~> 0.3.5"}
]
end

Amounts are Decimal values. decimal comes in as a dependency of qr_nbu_ex (2.1+ and 3.x are supported), so you don't need to add it yourself.

Quick Start

Simple Payment (V001 - Plain Text)

{:ok, qr_data} = QRNBU.generate(:v001, %{
recipient: "ТОВ Компанія Приклад",
iban: "UA213223130000026007233566001",
recipient_code: "12345678",
purpose: "Оплата за послуги"
})
# Returns plain text QR data compatible with EPC QR codes

Payment with Amount (V002 - Base64URL)

{:ok, qr_url} = QRNBU.generate(:v002, %{
recipient: "ФОП Іваненко І.І.",
iban: "UA213223130000026007233566001",
recipient_code: "1234567890", # ITIN (10 digits for individuals)
purpose: "Оплата товарів згідно рахунку №123",
amount: Decimal.new("1500.50"),
encoding: :utf8
})
# Returns: "https://qr.bank.gov.ua/[base64url_encoded_data]"

Advanced Payment (V003 - Extended Features)

{:ok, qr_url} = QRNBU.generate(:v003, %{
recipient: "ТОВ Інтернет-магазин",
iban: "UA213223130000026007233566001",
recipient_code: "12345678",
purpose: "Оплата замовлення #ORD-2024-001",
amount: Decimal.new("2450.00"),
category_purpose: "SUPP/REGU", # ISO 20022 category
reference: "INV-2024-001",
display: "Оплата протягом 7 днів",
# Must be in the future
invoice_validity: NaiveDateTime.add(NaiveDateTime.utc_now(), 7, :day),
invoice_creation: NaiveDateTime.utc_now(),
# Only the amount stays editable in the payer's banking app
field_lock: QRNBU.FieldLock.preset(:editable_amount)
})

Version Selection Guide

Version Use Case Format Features
V001 Simple payments, EPC compatibility Plain text Basic fields only
V002 Modern payments Base64URL + References
V003 Advanced payments Base64URL + ISO 20022, field locking, timestamps

Field Reference

Required Fields (All Versions)

Optional Common Fields

V002/V003 Additional Fields

V003 Exclusive Fields

Validation

The library automatically validates all fields:

# Invalid IBAN checksum
{:error, message} = QRNBU.generate(:v001, %{
recipient: "Test",
iban: "UA213223130000026007233566002", # Wrong checksum
recipient_code: "12345678",
purpose: "Test"
})
# Returns: {:error, "Invalid IBAN checksum"}
# Invalid tax ID format
{:error, message} = QRNBU.generate(:v002, %{
recipient: "Test",
iban: "UA213223130000026007233566001",
recipient_code: "123", # Too short
purpose: "Test"
})
# Returns: {:error, "Tax ID must be 8 digits (EDRPOU) or 10 digits (ITIN)"}

Error Handling

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

case QRNBU.generate(:v003, payment_data) do
{:ok, qr_url} ->
# Success: use qr_url
Logger.info("Generated QR: #{qr_url}")
{:error, reason} ->
# Handle error
Logger.error("QR generation failed: #{reason}")
end

Character Encoding

UTF-8 (Default)

{:ok, qr} = QRNBU.generate(:v002, %{
# ... fields ...
purpose: "Оплата товарів українською",
encoding: :utf8
})

CP1251 (Windows-1251)

{:ok, qr} = QRNBU.generate(:v002, %{
# ... fields ...
purpose: "Оплата товарів",
encoding: :cp1251
})

QR Code Rendering

The library includes built-in QR code rendering functionality through QRNBU.Renderer:

Render as PNG

# Generate QR data
{:ok, qr_string} = QRNBU.generate(:v001, payment_data)
# Render as PNG binary
{:ok, png_binary} = QRNBU.Renderer.to_png(qr_string)
File.write!("payment.png", png_binary)
# With custom options
{:ok, png} = QRNBU.Renderer.to_png(qr_string,
width: 500,
error_correction: :h # :l, :m, :q, or :h
)

Render as SVG

# Render as SVG string (with default UAH logo)
{:ok, svg_string} = QRNBU.Renderer.to_svg(qr_string)
File.write!("payment.svg", svg_string)
# With custom size
{:ok, svg} = QRNBU.Renderer.to_svg(qr_string, width: 400)
# Without logo
{:ok, svg} = QRNBU.Renderer.to_svg(qr_string, logo: false)
# With custom logo (no white circle background)
{:ok, svg} = QRNBU.Renderer.to_svg(qr_string, logo: "/path/to/custom-logo.svg")
# With inline SVG logo
{:ok, svg} = QRNBU.Renderer.to_svg(qr_string, logo: {:svg, "<svg>...</svg>"})

Display in Terminal

# Display QR code in terminal
:ok = QRNBU.Renderer.to_terminal(qr_string)
# With higher error correction
:ok = QRNBU.Renderer.to_terminal(qr_string, error_correction: :h)

Convenience Functions

# Generate and save as PNG in one step
:ok = QRNBU.Renderer.save_png(:v002, payment_data, "payment.png")
# Generate and save as SVG
:ok = QRNBU.Renderer.save_svg(:v003, payment_data, "payment.svg",
width: 600,
error_correction: :q
)
# Generate and display in terminal
:ok = QRNBU.Renderer.display(:v001, payment_data)

Error Correction Levels

Higher error correction creates larger QR codes but increases resistance to damage.

Testing

The library includes comprehensive testing:

# Run all tests
mix test
# Run tests with coverage
mix test --cover

Documentation

Full documentation is available on HexDocs.

Generate local documentation:

mix docs
open doc/index.html

References

License

MIT License - see LICENSE.md for details.

Contributing

Contributions are welcome! Please:

  1. Fork the repository
  2. Create a feature branch
  3. Add tests for new functionality
  4. Ensure all tests pass: mix test
  5. Submit a pull request

Releasing

Publishing to hex.pm is done by GitHub Actions (.github/workflows/release.yml):

  1. Bump @version in mix.exs and merge to main
  2. Tag that commit v<version> and push the tag:
    git tag v0.3.5 && git push origin v0.3.5
  3. The workflow runs the tests, refuses to publish if the tag does not match @version, then publishes the package and docs with the HEX_API_KEY repository secret

Support