Перейти до основного вмісту

HTML форми

Один зі способів додати оплату криптовалютою на сайт, в інтернет-магазин або в бота. Покупець натискає кнопку, відкривається платіжна сторінка з рахунком.

Форма надсилається лише методом POST на https://dash.bitsby.app/invoices/form і містить два поля:

ПолеЩо в ньому
database64url від JSON із полями рахунку
signatureHMAC-SHA256 від рядка data, 64 знаки hex

Одночасно у проєкта може бути не більше ніж 50 неоплачених рахунків, створених у цей спосіб.

Приклад форми

<form method="post" action="https://dash.bitsby.app/invoices/form" target="_blank">
<input type="hidden" name="data" value="eyJwcm9qZWN0SWQiOiJhMWIyYzNkNC01ZTZm...">
<input type="hidden" name="signature" value="f77d6da1be35bd2900e0bfed9f202b04...">
<button type="submit">Pay</button>
</form>

Обидва значення підставляє сервер магазину під час рендерингу сторінки. Секрет у розмітку не потрапляє.

Атрибут target="_blank" необов’язковий: із ним кошик залишається відкритим у вихідній вкладці.

Як збирається контейнер

  1. Зібрати JSON із полями рахунку.
  2. Закодувати його в base64url. Це рядок data.
  3. Обчислити signature = HMAC-SHA256(data, formSecret).

Підписується рядок data цілком, тому порядок ключів у JSON, відступи та спосіб екранування юнікоду не мають значення. Стандартний base64 приймач теж приймає, з падингом або без.

Перезбирати data після підпису не можна: змінився хоч байт — підпис обчислюється заново.

Як обчислити підпис і готові приклади чотирма мовами — у розділі «Підпис HTML-форми».

Ключі всередині data

КлючОбов’язковийЩо в ньому
projectIdтакID проєкту з налаштувань в особистому кабінеті, uuid
amountFiatтакСума від 1 до 100000, не більше ніж два знаки після крапки. Рядком "10.50" або числом 10.5
currencyFiatтакВалюта фіату: USD, EUR або RUB
timeToPayтакСтрок оплати в годинах: 0.5, 1, 3, 6, 12. Рядком або числом
descriptionніОпис для покупця, до 1000 знаків
serviceDataніІдентифікатор замовлення на боці магазину, до 1000 знаків. Покупцеві не показується, але видно його у вихідному коді форми — не передавайте тут нічого чутливого
outputніerrors — показувати подробиці помилок заповнення

Необов’язковий ключ можна не класти в JSON — це те саме, що порожній рядок. Порядок ключів будь-який, незнайомі ключі приймач не читає.

Значення — рядки або числа JSON. Логічні значення, масиви та вкладені об’єкти значеннями полів не вважаються й надходять як порожній рядок.

Приклад вмісту data:

{
"projectId": "a1b2c3d4-5e6f-4a7b-8c9d-0e1f2a3b4c5d",
"amountFiat": 10.5,
"currencyFiat": "USD",
"timeToPay": 1,
"description": "Order #7, delivery",
"serviceData": "order-7"
}

Тут суму та строк оплати передано числами, рядком вони теж приймаються. Необов’язковий output не задано зовсім.

Формат суми

Лише цифри та крапка, не більше ніж два знаки після крапки: 10, 10.00, 1000.50. Пробіли з країв обрізаються.

Будь-який інший формат відхиляється — розділювачі тисяч, кома замість крапки, експоненційний запис, знак перед числом, символ валюти. Округлення немає: третій знак після крапки не відкидається, а дає відмову.

Секрет форми

Секрет лежить у налаштуваннях проєкту в особистому кабінеті, рядок «Секрет форми». Його видає сервер під час створення проєкту. Задати своє значення не можна, секрет можна лише перевипустити — наприклад, якщо його скомпрометовано.

Секрет має залишатися на сервері магазину. Якщо він потрапить в HTML вітрини, підпис втратить сенс.

Секрет форми й секрет вебхука — різні ключі, змішувати їх не можна.

Ідентифікатор замовлення

Заповнюйте serviceData завжди: кладіть туди ідентифікатор замовлення на своєму боці. Значення повертається у сповіщенні про оплату — за ним магазин знаходить замовлення.

Заповнений serviceData захищає від дублів. Повторне надсилання форми та подвійний клік по кнопці не створюють другий рахунок: покупця спрямує на вже наявний неоплачений. Збіг шукається за полями замовлення — проєктом, сумою, валютою, описом і serviceData, а не за байтами data.

Значення має бути унікальним для кожного замовлення. За однакового значення другий покупець потрапить на неоплачений рахунок першого.

Порожній serviceData позбавляє цього захисту: розпізнати повтор нічим, кожне надсилання створює новий рахунок. Дублі витрачають ліміт у 50 неоплачених рахунків, а після оплати магазину нічим пов’язати платіж із замовленням.

Помилки й режим налагодження

Форма проходить дві перевірки: спочатку підпис і контейнер, потім заповнення полів.

Що сталосяЩо покаже сервіс
Підпис не зійшовся, контейнер не читається, проєкт чужий або не існуєЗагальна помилка, без причини
Підпис зійшовся, але поле заповнено неправильноЗагальна помилка. З "output":"errors" — деталізація помилок
Обидві перевірки пройденоПлатіжна сторінка з рахунком

На першій перевірці причина не називається ніколи й за жодних налаштувань.

Пара "output":"errors" у JSON вмикає подробиці другої перевірки: сервіс назве поле, перелічить допустимі значення й повідомить, якщо досягнуто стелю в 50 неоплачених рахунків. Ключ живе всередині підписаного контейнера, тому підставити його ззовні не можна.

На час налаштування форми кладіть "output":"errors" у JSON, після налаштування приберіть ключ і перезберіть контейнер.

Якщо не сходиться сам підпис, звірте свої data і підпис з еталонним набором у розділі «Підпис HTML-форми».

Що далі

Рахунок, створений формою, далі обробляється так само, як рахунок з API або особистого кабінету: