
Generate NBU-compliant payment QR codes with validated IBAN/amount/purpose, customizable appearance, deep-link bank buttons, editable-field controls, ISO20022 category support, and PNG/bitmap rendering.
Платіжні QR-коди НБУ для оплати за IBAN (формат 003, Постанова №97). Платник сканує код у застосунку свого банку — і отримувач, IBAN, ЄДРПОУ, сума та призначення платежу заповнюються самі. А кнопки «Оплатити через …» відкривають той самий платіж одразу в застосунку конкретного банку.
Вимоги: Kotlin 2.3+; Android 5.0+ (API 21), JVM 11+ або iOS (arm64, симулятор arm64).
dependencies {
implementation("io.github.skules777:nbu-qr-code:1.0.0")
}У KMP-проєкті — у commonMain.dependencies { … }.
Для Swift-проєктів є nbu-qr-code-swift.
Цей QR-код згенеровано ось цим кодом:
val payment = NBUQRPayment(
recipient = "Петренко Роман Петрович",
iban = "UA906543210000000260323012024",
amount = BigDecimal("63"),
recipientCode = "40121425",
purpose = "За каву",
)
val payload = NBUQRPayload(payment)
val bitmap = NBUQRImageRenderer(accentColor = NBUQRColor.NBU_GREEN).bitmap(payload)
val monobank = payload.deepLinkIntent(NBUQRBank.MONOBANK)Дані — з офіційного прикладу Постанови (P2P-переказ). bitmap(payload) — для
Android; на JVM — bufferedImage(payload), на iOS — uiImage(payload), на будь-якій
платформі — pngBytes(payload) (PNG).
Сума — NBUQRDecimal: BigDecimal на JVM і Android, NSDecimalNumber на iOS.
У спільному коді — "63.50".toNBUQRDecimal().
У QR-коді — посилання https://qr.bank.gov.ua/…. Що відбувається далі,
залежить від того, чим його відкрити:
Сканер у застосунку банку — реквізити заповнюються автоматично. Саме так радить НБУ:
ВАЖЛИВО! Для швидкої оплати наступного разу скануйте QR-код із зображенням знаку гривні відразу із платіжного застосунку.
Камера iPhone відкриває застосунок банку, який обробляв такий QR-код останнім.
Перехід за посиланням на iPhone відкриває один зі встановлених банківських застосунків — дати платнику вибір iOS не дозволяє.
Тому, щоб платник сам обрав банк, покажіть поруч із QR-кодом кнопки «Оплатити через …» — див. Банки.
QR-код з кодом старту застосунку конкретного банку (напр. monobank) бібліотека свідомо не генерує: його опрацює лише застосунок цього банку, сканер будь-якого іншого його не прочитає. А знак ₴ на коді обіцяє платнику, що підійде будь-який банк, — тож такий QR лише вводить в оману.
QR-код сканують застосунки банків з офіційного переліку НБУ.
Кнопки «Оплатити через …» (deep link) відкривають платіж одразу в застосунку конкретного банку:
Якщо на платформі прямого способу відкрити застосунок банку немає, посилання —
null.
// Android
val buttons = NBUQRBank.ALL.mapNotNull { bank ->
payload.deepLinkIntent(bank)
?.takeIf { it.resolveActivity(packageManager) != null } // лише встановлені
?.let { bank.name to it }
}
// iOS (KMP) і бекенд
payload.iosDeepLink(NBUQRBank.MONOBANK) // https://mbnk.app/qr/…
payload.androidDeepLink(NBUQRBank.MONOBANK) // intent://qr.bank.gov.ua/…;package=com.ftband.mono;endandroidDeepLink — Android Intent URI, який Chrome відкриває прямо з вебсторінки.
resolveActivity бачив застосунки банків, оголосіть їхні
пакети в <queries> маніфесту — готовий список є в
маніфесті демо-застосунку.abank24://,
vostok://…), а не https-адресою. Щоб приховати кнопки невстановлених
застосунків через canOpenURL, додайте схеми банків у
LSApplicationQueriesSchemes свого Info.plist.Банк, якого немає в списку, додається одним рядком:
val bank = NBUQRBank(
name = "Example Bank",
iosAppStartUrl = "https://bank.example/qr/",
androidPackage = "com.example.bank",
)
payload.iosDeepLink(bank) // https://bank.example/qr/<payload>NBUQRImageRenderer() // чорний
NBUQRImageRenderer(accentColor = NBUQRColor.NBU_GREEN) // зелений НБУ, як на bank.gov.ua
NBUQRImageRenderer(accentColor = NBUQRColor.fromHex("#1A237E")!!) // колір вашого брендуКолір отримують знак ₴ і центри трьох кутових квадратів. Надто світлий колір рендер відхилить, щоб код лишився читабельним.
category = "MP2P/MP2B" // код категорії / цілі за ISO 20022
category = "" // за замовчуванням: у QR-код потрапляє OTHR/OTHR («інше / інше»)Порожнім поле 10 бібліотека не лишає: постанова позначає його обовʼязковим, а Приват24 код із порожнім полем відхиляє. Деталі — у docs/NBU-COMPLIANCE.md.
Які поля платник може змінити у своєму застосунку перед оплатою:
editableFields = null // за замовчуванням: рішення за банком
editableFields = emptySet() // змінити нічого не можна
editableFields = setOf(NBUQREditableField.AMOUNT) // лише суму
editableFields = setOf(NBUQREditableField.AMOUNT, NBUQREditableField.PURPOSE) // суму й призначенняval issues = payment.validate() // List<NBUQRValidationIssue>
// [NBUQRValidationIssue(field = IBAN, problem = InvalidChecksum)]Кожна проблема прив'язана до поля, тож її легко підсвітити у формі. Порожній
список гарантує, що NBUQRPayload(payment) не кине помилку.
NBUQRException.InvalidPayment(issues) — той самий список, що й validate().NBUQRException.QRDataTooLong — дані не вміщуються в QR-код (deep link при цьому
працює).NBUQRImageException.DataTooShort / DataTooLong — даних замало чи забагато
для QR-коду за вимогами НБУ; помилка каже, на скільки байтів.Тексти помилок (message) — англійською, для логів і розробників. Для
користувача покажіть власний текст за типом помилки.
@Composable
fun PaymentQR(orderId: String, amount: BigDecimal) {
val context = LocalContext.current
val payment = remember(orderId, amount) {
NBUQRPayment(
function = NBUQRPaymentFunction.XCT,
recipient = "ФОП Коваленко Ірина Олександрівна",
iban = "UA906543210000000260323012024",
amount = amount,
recipientCode = "40121425",
reference = orderId,
purpose = "Оплата замовлення №$orderId",
// Reference — єдиний ключ, що звʼязує переказ із замовленням,
// тож усе інше блокуємо від редагування платником.
editableFields = emptySet(),
)
}
// Рендер — CPU-робота, тож не в головному потоці.
val state by produceState<Result<Pair<NBUQRPayload, ImageBitmap>>?>(null, payment) {
value = withContext(Dispatchers.Default) {
runCatching {
val payload = NBUQRPayload(payment)
payload to NBUQRImageRenderer().bitmap(payload).asImageBitmap()
}
}
}
Column(horizontalAlignment = Alignment.CenterHorizontally) {
state?.onSuccess { (payload, image) ->
Image(
bitmap = image,
contentDescription = "Платіжний QR-код",
filterQuality = FilterQuality.None, // модулі лишаються чіткими
modifier = Modifier.size(260.dp),
)
for (bank in listOf(NBUQRBank.PRIVAT_BANK, NBUQRBank.MONOBANK)) {
val intent = payload.deepLinkIntent(bank) ?: continue
Button(onClick = { context.startActivity(intent) }) {
Text("Оплатити через ${bank.name}")
}
}
}?.onFailure {
Text("Не вдалося згенерувати QR: ${it.message}", color = MaterialTheme.colorScheme.error)
} ?: CircularProgressIndicator()
}
}sample/ — застосунок на Compose Multiplatform для Android та iOS: форма
платежу з валідацією, QR-код і кнопки «Оплатити через …» для встановлених
банківських застосунків.
На JVM бібліотека працює без Android і без дисплея (headless):
val png: ByteArray = NBUQRImageRenderer().pngBytes(NBUQRPayload(payment))З Java — так само: new NBUQRPayload(payment), NBUQRImages.bufferedImage(renderer, payload).
Як бібліотека виконує вимоги Постанови №97 і що показала перевірка в банківських застосунках — у docs/NBU-COMPLIANCE.md.
MIT. QR-кодувальник — на основі QR Code generator Project Nayuki (MIT), див. NOTICE.
Платіжні QR-коди НБУ для оплати за IBAN (формат 003, Постанова №97). Платник сканує код у застосунку свого банку — і отримувач, IBAN, ЄДРПОУ, сума та призначення платежу заповнюються самі. А кнопки «Оплатити через …» відкривають той самий платіж одразу в застосунку конкретного банку.
Вимоги: Kotlin 2.3+; Android 5.0+ (API 21), JVM 11+ або iOS (arm64, симулятор arm64).
dependencies {
implementation("io.github.skules777:nbu-qr-code:1.0.0")
}У KMP-проєкті — у commonMain.dependencies { … }.
Для Swift-проєктів є nbu-qr-code-swift.
Цей QR-код згенеровано ось цим кодом:
val payment = NBUQRPayment(
recipient = "Петренко Роман Петрович",
iban = "UA906543210000000260323012024",
amount = BigDecimal("63"),
recipientCode = "40121425",
purpose = "За каву",
)
val payload = NBUQRPayload(payment)
val bitmap = NBUQRImageRenderer(accentColor = NBUQRColor.NBU_GREEN).bitmap(payload)
val monobank = payload.deepLinkIntent(NBUQRBank.MONOBANK)Дані — з офіційного прикладу Постанови (P2P-переказ). bitmap(payload) — для
Android; на JVM — bufferedImage(payload), на iOS — uiImage(payload), на будь-якій
платформі — pngBytes(payload) (PNG).
Сума — NBUQRDecimal: BigDecimal на JVM і Android, NSDecimalNumber на iOS.
У спільному коді — "63.50".toNBUQRDecimal().
У QR-коді — посилання https://qr.bank.gov.ua/…. Що відбувається далі,
залежить від того, чим його відкрити:
Сканер у застосунку банку — реквізити заповнюються автоматично. Саме так радить НБУ:
ВАЖЛИВО! Для швидкої оплати наступного разу скануйте QR-код із зображенням знаку гривні відразу із платіжного застосунку.
Камера iPhone відкриває застосунок банку, який обробляв такий QR-код останнім.
Перехід за посиланням на iPhone відкриває один зі встановлених банківських застосунків — дати платнику вибір iOS не дозволяє.
Тому, щоб платник сам обрав банк, покажіть поруч із QR-кодом кнопки «Оплатити через …» — див. Банки.
QR-код з кодом старту застосунку конкретного банку (напр. monobank) бібліотека свідомо не генерує: його опрацює лише застосунок цього банку, сканер будь-якого іншого його не прочитає. А знак ₴ на коді обіцяє платнику, що підійде будь-який банк, — тож такий QR лише вводить в оману.
QR-код сканують застосунки банків з офіційного переліку НБУ.
Кнопки «Оплатити через …» (deep link) відкривають платіж одразу в застосунку конкретного банку:
Якщо на платформі прямого способу відкрити застосунок банку немає, посилання —
null.
// Android
val buttons = NBUQRBank.ALL.mapNotNull { bank ->
payload.deepLinkIntent(bank)
?.takeIf { it.resolveActivity(packageManager) != null } // лише встановлені
?.let { bank.name to it }
}
// iOS (KMP) і бекенд
payload.iosDeepLink(NBUQRBank.MONOBANK) // https://mbnk.app/qr/…
payload.androidDeepLink(NBUQRBank.MONOBANK) // intent://qr.bank.gov.ua/…;package=com.ftband.mono;endandroidDeepLink — Android Intent URI, який Chrome відкриває прямо з вебсторінки.
resolveActivity бачив застосунки банків, оголосіть їхні
пакети в <queries> маніфесту — готовий список є в
маніфесті демо-застосунку.abank24://,
vostok://…), а не https-адресою. Щоб приховати кнопки невстановлених
застосунків через canOpenURL, додайте схеми банків у
LSApplicationQueriesSchemes свого Info.plist.Банк, якого немає в списку, додається одним рядком:
val bank = NBUQRBank(
name = "Example Bank",
iosAppStartUrl = "https://bank.example/qr/",
androidPackage = "com.example.bank",
)
payload.iosDeepLink(bank) // https://bank.example/qr/<payload>NBUQRImageRenderer() // чорний
NBUQRImageRenderer(accentColor = NBUQRColor.NBU_GREEN) // зелений НБУ, як на bank.gov.ua
NBUQRImageRenderer(accentColor = NBUQRColor.fromHex("#1A237E")!!) // колір вашого брендуКолір отримують знак ₴ і центри трьох кутових квадратів. Надто світлий колір рендер відхилить, щоб код лишився читабельним.
category = "MP2P/MP2B" // код категорії / цілі за ISO 20022
category = "" // за замовчуванням: у QR-код потрапляє OTHR/OTHR («інше / інше»)Порожнім поле 10 бібліотека не лишає: постанова позначає його обовʼязковим, а Приват24 код із порожнім полем відхиляє. Деталі — у docs/NBU-COMPLIANCE.md.
Які поля платник може змінити у своєму застосунку перед оплатою:
editableFields = null // за замовчуванням: рішення за банком
editableFields = emptySet() // змінити нічого не можна
editableFields = setOf(NBUQREditableField.AMOUNT) // лише суму
editableFields = setOf(NBUQREditableField.AMOUNT, NBUQREditableField.PURPOSE) // суму й призначенняval issues = payment.validate() // List<NBUQRValidationIssue>
// [NBUQRValidationIssue(field = IBAN, problem = InvalidChecksum)]Кожна проблема прив'язана до поля, тож її легко підсвітити у формі. Порожній
список гарантує, що NBUQRPayload(payment) не кине помилку.
NBUQRException.InvalidPayment(issues) — той самий список, що й validate().NBUQRException.QRDataTooLong — дані не вміщуються в QR-код (deep link при цьому
працює).NBUQRImageException.DataTooShort / DataTooLong — даних замало чи забагато
для QR-коду за вимогами НБУ; помилка каже, на скільки байтів.Тексти помилок (message) — англійською, для логів і розробників. Для
користувача покажіть власний текст за типом помилки.
@Composable
fun PaymentQR(orderId: String, amount: BigDecimal) {
val context = LocalContext.current
val payment = remember(orderId, amount) {
NBUQRPayment(
function = NBUQRPaymentFunction.XCT,
recipient = "ФОП Коваленко Ірина Олександрівна",
iban = "UA906543210000000260323012024",
amount = amount,
recipientCode = "40121425",
reference = orderId,
purpose = "Оплата замовлення №$orderId",
// Reference — єдиний ключ, що звʼязує переказ із замовленням,
// тож усе інше блокуємо від редагування платником.
editableFields = emptySet(),
)
}
// Рендер — CPU-робота, тож не в головному потоці.
val state by produceState<Result<Pair<NBUQRPayload, ImageBitmap>>?>(null, payment) {
value = withContext(Dispatchers.Default) {
runCatching {
val payload = NBUQRPayload(payment)
payload to NBUQRImageRenderer().bitmap(payload).asImageBitmap()
}
}
}
Column(horizontalAlignment = Alignment.CenterHorizontally) {
state?.onSuccess { (payload, image) ->
Image(
bitmap = image,
contentDescription = "Платіжний QR-код",
filterQuality = FilterQuality.None, // модулі лишаються чіткими
modifier = Modifier.size(260.dp),
)
for (bank in listOf(NBUQRBank.PRIVAT_BANK, NBUQRBank.MONOBANK)) {
val intent = payload.deepLinkIntent(bank) ?: continue
Button(onClick = { context.startActivity(intent) }) {
Text("Оплатити через ${bank.name}")
}
}
}?.onFailure {
Text("Не вдалося згенерувати QR: ${it.message}", color = MaterialTheme.colorScheme.error)
} ?: CircularProgressIndicator()
}
}sample/ — застосунок на Compose Multiplatform для Android та iOS: форма
платежу з валідацією, QR-код і кнопки «Оплатити через …» для встановлених
банківських застосунків.
На JVM бібліотека працює без Android і без дисплея (headless):
val png: ByteArray = NBUQRImageRenderer().pngBytes(NBUQRPayload(payment))З Java — так само: new NBUQRPayload(payment), NBUQRImages.bufferedImage(renderer, payload).
Як бібліотека виконує вимоги Постанови №97 і що показала перевірка в банківських застосунках — у docs/NBU-COMPLIANCE.md.
MIT. QR-кодувальник — на основі QR Code generator Project Nayuki (MIT), див. NOTICE.