# QRNBUEx
[](https://d.tulz.dev/opensource/qr-nbu-ex)
[](https://hex.pm/packages/qr_nbu_ex)
[](https://hexdocs.pm/qr_nbu_ex)
[](LICENSE.md)
**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
- ✅ **All Three NBU QR Code Versions**: V001 (EPC compatible), V002 (Base64URL), V003 (Extended)
- ✅ **Full Validation**: IBAN checksum, tax IDs (EDRPOU/ITIN), amounts, dates
- ✅ **Character Encoding**: UTF-8 and CP1251 (Windows-1251) support
- ✅ **Type Safety**: Custom types with compile-time validation
- ✅ **Comprehensive Testing**: 452 tests with 90.15% coverage
- ✅ **Rich Documentation**: Inline docs with examples and type specs
- ✅ **NBU Compliant**: Implements NBU Resolution No. 97 (August 19, 2025)
## Installation
Add `qr_nbu_ex` to your list of dependencies in `mix.exs`:
```elixir
def deps do
[
{:qr_nbu_ex, "~> 0.2.1"},
{:decimal, "~> 2.0"}
]
end
```
## Quick Start
### Simple Payment (V001 - Plain Text)
```elixir
{: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)
```elixir
{: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)
```elixir
{: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: "Оплата до 31.12.2024",
invoice_validity: ~N[2024-12-31 23:59:59],
invoice_creation: ~N[2024-01-09 10:00:00],
field_lock: 0x0001 # Lock specific fields
})
```
## 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)
- **recipient**: Recipient name (1-70 characters)
- **iban**: Ukrainian IBAN (`UA` + 27 digits with valid checksum)
- **recipient_code**: Tax ID - EDRPOU (8 digits) or ITIN (10 digits)
- **purpose**: Payment purpose (1-140 characters)
### Optional Common Fields
- **amount**: Payment amount as `Decimal.t()` (e.g., `Decimal.new("100.50")`)
- **function**: Function code (`:uct`, `:ict`, `:xct`) - default: `:uct`
- **encoding**: Character encoding (`:utf8`, `:cp1251`) - default: `:utf8`
### V002/V003 Additional Fields
- **reference**: Payment reference number (max 35 characters)
### V003 Exclusive Fields
- **unique_recipient_id**: Unique recipient identifier (max 35 characters)
- **category_purpose**: ISO 20022 category (format: `CCCC/PPPP`, e.g., `"SUPP/REGU"`)
- **display**: Display text for QR scanner (max 140 characters)
- **field_lock**: Field lock bitmap integer `0x0000-0xFFFF`
- **invoice_validity**: Invoice expiration as `NaiveDateTime.t()`
- **invoice_creation**: Invoice creation timestamp as `NaiveDateTime.t()`
- **digital_signature**: Digital signature (max 1000 characters)
## Validation
The library automatically validates all fields:
```elixir
# 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:
```elixir
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)
```elixir
{:ok, qr} = QRNBU.generate(:v002, %{
# ... fields ...
purpose: "Оплата товарів українською",
encoding: :utf8
})
```
### CP1251 (Windows-1251)
```elixir
{:ok, qr} = QRNBU.generate(:v002, %{
# ... fields ...
purpose: "Оплата товарів",
encoding: :cp1251
})
```
## Testing
The library includes comprehensive testing:
```bash
# Run all tests
mix test
# Run tests with coverage
mix test --cover
# Current coverage: 90.15% (452 tests)
```
## Documentation
Full documentation is available on [HexDocs](https://hexdocs.pm/qr_nbu_ex).
Generate local documentation:
```bash
mix docs
open doc/index.html
```
## References
- [NBU Resolution No. 97 (August 19, 2025)](https://bank.gov.ua/)
- [EPC QR Code Guidelines v3.0](https://www.europeanpaymentscouncil.eu/)
- [ISO 20022 External Code Sets](https://www.iso20022.org/)
## License
MIT License - see [LICENSE.md](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
## Support
- 📖 [Documentation](https://hexdocs.pm/qr_nbu_ex)
- 🐛 [Issue Tracker](https://github.com/yourusername/qr_nbu_ex/issues)
- 💬 [Discussions](https://github.com/yourusername/qr_nbu_ex/discussions)