README.md

# QRNBUEx
[![Build Status](https://d.tulz.dev/api/badges/opensource/qr-nbu-ex/status.svg)](https://d.tulz.dev/opensource/qr-nbu-ex)
[![Hex.pm](https://img.shields.io/hexpm/v/qr_nbu_ex.svg)](https://hex.pm/packages/qr_nbu_ex)
[![Documentation](https://img.shields.io/badge/docs-hexpm-blue.svg)](https://hexdocs.pm/qr_nbu_ex)
[![License](https://img.shields.io/hexpm/l/qr_nbu_ex.svg)](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**: 495 tests with 90.35% coverage
- ✅ **QR Code Rendering**: Built-in PNG, SVG, and terminal output support
- ✅ **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
})
```

## QR Code Rendering

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

### Render as PNG

```elixir
# 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

```elixir
# Render as SVG string
{: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)
```

### Display in Terminal

```elixir
# 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

```elixir
# 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

- **`:l`** (Low) - ~7% error recovery
- **`:m`** (Medium) - ~15% error recovery (default)
- **`:q`** (Quartile) - ~25% error recovery
- **`:h`** (High) - ~30% error recovery

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

## Testing

The library includes comprehensive testing:

```bash
# Run all tests
mix test

# Run tests with coverage
mix test --cover

# Current coverage: 90.35% (495 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)