# BramaPay — швидкий старт для ШІ-агента

Самодостатній посібник для серверної інтеграції магазину. API v2 та протокол вебхуків v2 — окремі контракти. Це не готовий production-застосунок і не підтвердження доступності Live.

## Початкові посібники

- [Швидкий старт](https://docs.bramapay.com/uk/getting-started/quick-start)
- [Перевірка підпису](https://docs.bramapay.com/uk/webhooks/signature-verification)
- [Дедуплікація подій і замовлень](https://docs.bramapay.com/uk/webhooks/deduplication)
- [Створення платіжної сесії v2](https://docs.bramapay.com/uk/checkout/payment-sessions-v2)

## Приклад серверних змінних середовища

Це приклад назв для застосунку магазину, а не налаштування розгортання BramaPay. Залиште секрети порожніми в .env.example та налаштуйте їх безпечне зберігання на сервері. Не вставляйте секрети в ШІ-чат, frontend, репозиторій чи логи. Почніть із локальних прикладів; Test налаштовується окремо, автоматичний перехід на Live заборонений. Цей посібник не дозволяє створювати віддалені сесії, переказувати кошти чи виконувати deploy.

```dotenv
BRAMAPAY_API_ORIGIN=https://api.bramapay.com
BRAMAPAY_TEST_PROJECT_KEY=
BRAMAPAY_WEBHOOK_KEY_ID=
BRAMAPAY_WEBHOOK_SECRET=
```

## Звіряння суми та ідентичності замовлення

Беріть ціну із серверного замовлення. Десяткові значення "25" і "25.00" чисельно рівні: використовуйте точну десяткову арифметику, а не рівність початкових рядків чи JavaScript Number. Звіряйте збережений ціновий контракт: вартість замовлення, payable USD і точна сума токена — різні значення. Не обчислюйте очікувану суму токена самостійно. Після невизначеного результату зберігайте externalId, точне тіло й Idempotency-Key; не обходьте 409 новим ключем.

# Швидкий старт

Створіть **тестову платіжну сесію v2**, відкрийте сторінку оплати й підтвердьте одне тестове замовлення. Потрібен сервер магазину: запити, зберігання секретів і перевірка оплати виконуються на ньому. Це навчальний приклад, а не готовий застосунок. До обробки подій реалізуйте [повну перевірку підпису](https://docs.bramapay.com/uk/webhooks/signature-verification). Повний контракт наведено в [посібнику із сесій](https://docs.bramapay.com/uk/checkout/payment-sessions-v2); [рахунки v1](https://docs.bramapay.com/uk/api-reference/invoices) підтримуються для наявних інтеграцій.

## 1. Підготуйте проєкт

Зареєструйтеся, підтвердьте email, виберіть проєкт і режим **Test**. Зареєструйте підконтрольний вам тестовий гаманець і явно дозвольте USDT у TRON Shasta. Реєстрація гаманця Live не створює реєстрацію Test. Не передавайте приватний ключ чи фразу відновлення.

Задайте `successUrl`, `cancelUrl` і точні `allowedRedirectDomains`. Додайте активну публічну HTTPS-адресу вебхука, збережіть одноразовий `secret` і `keyId`. Реалізуйте перевірку підпису до перевірки з'єднання. Створіть секретний API-ключ Test для цього проєкту та зберігайте його на сервері. Див. [налаштування проєкту](https://docs.bramapay.com/uk/projects/readiness) і [URL повернення](https://docs.bramapay.com/uk/projects/return-urls).

Режим Test має бути доступний. Якщо він недоступний, зверніться до підтримки й перевіряйте обробник на локальних прикладах; не переходьте автоматично на Live. Тестові токени не оплачують реальні замовлення й не поповнюють сервісний баланс Live.

## 2. Створіть одну тестову сесію

**До надсилання** збережіть замовлення, очікувані проєкт і режим, точне тіло запиту й ключ ідемпотентності. Після тайм-ауту з невідомим результатом повторюйте те саме тіло з тим самим ключем. Не обходьте конфлікт новим ключем. Грошові значення передаються десятковими рядками, а не числами з рухомою комою.

```bash
curl --request POST 'https://api.bramapay.com/v2/payment-sessions' \
  --header 'X-Api-Key: YOUR_TEST_PROJECT_SECRET_KEY' \
  --header 'Idempotency-Key: order-demo-1042-attempt-1' \
  --header 'Content-Type: application/json' \
  --data '{"amountUsd":"25.00","currency":"USD","externalId":"order-demo-1042"}'
```

Проєкт і режим визначає ключ. Не передавайте `projectId`, `mode`, ID гаманців чи перевизначення URL повернення. Додаткові поля й варіанти комісії описані в [контракті сесії](https://docs.bramapay.com/uk/checkout/payment-sessions-v2) та [правилах комісії покупця](https://docs.bramapay.com/uk/billing/buyer-fee-pricing).

## 3. Збережіть відповідь

До відкриття оплати зв'яжіть із замовленням ID сесії, проєкт, режим, зовнішній ID замовлення, валюту, суму й незмінний знімок ціни. Нижче **часткова вигадана відповідь**, а не повна схема чи реквізити: домен зарезервований для прикладів, токен доступу несправжній.

```json
{
  "sessionId": "00000000-0000-4000-8000-000000000001",
  "projectId": "prj_00000000000000000000000000000000",
  "externalId": "order-demo-1042",
  "mode": "TEST",
  "paymentMode": "TEST",
  "amountUsd": "25",
  "currency": "USD",
  "status": "OPEN",
  "checkoutUrl": "https://checkout.example/en/checkout/00000000-0000-4000-8000-000000000001#token=EXAMPLE_NOT_A_REAL_CAPABILITY",
  "routes": [],
  "attempts": []
}
```

`routes: []` означає відсутність доступного маршруту. Не вигадуйте його й не просіть переказ. Нові спроби на розміщеній сторінці оплати використовують USDT; запис у реєстрі не гарантує доступності для сесії. Раніше видані спроби зберігають реквізити.

## 4. Відкрийте оплату

Використовуйте повернений `checkoutUrl` без змін. Фрагмент містить токен доступу: виключіть повний URL із журналів та аналітики. Секретний API-ключ не має потрапляти до браузера. Не складайте посилання самостійно й не переносьте токен у рядок запиту.

Покупець вибирає доступну мережу й **один раз** надсилає точну суму на поточну адресу, у вказаному токені та до строку. QR містить лише адресу. Не округлюйте, не розбивайте переказ, не обчислюйте суму токенів самостійно й не просіть доплатити різницю. Зміна маршруту не подовжує строк. Див. [перший тестовий платіж](https://docs.bramapay.com/uk/wallets/first-test-payment).

## 5. Перевірте підтвердження

До обробки JSON виконайте [повну перевірку початкового тіла](https://docs.bramapay.com/uk/webhooks/signature-verification): обмежте розмір, перевірте час у межах 300 секунд, підписані ID і тип події, вибір секрету за ID ключа, точний конверт протоколу v2 та HMAC порівнянням за сталий час. Вимагайте `X-Idempotency-Key === X-Event-Id`. Зберігайте нескомпрометовані старі секрети для наявних доставок. Простір підпису — буквально `vaultless.webhook` (історична назва протоколу, змінювати її не потрібно); версії API та протоколу вебхуків незалежні.

Подія оплати сесії — лише `payment_session.paid`. `integration.test`, `webhook.test`, перехід браузера й подія дочірнього рахунку не дозволяють виконання замовлення сесії. Подія Test не оплачує замовлення Live. Після перевірки підпису та структури отримайте сесію зі свого сервера:

```bash
curl 'https://api.bramapay.com/v2/payment-sessions/SESSION_ID' \
  --header 'X-Api-Key: YOUR_TEST_PROJECT_SECRET_KEY'
```

Вимагайте `status === "PAID"` з авторизованої відповіді та збіг збережених сесії, замовлення, проєкту, режиму, валюти й ціни за початковим контрактом. Порівнюйте десяткові значення точно. За комісії з налаштувань проєкту `amountUsd` зберігає вартість замовлення; явний `BUYER_FEE_V1` має іншу семантику. Не плутайте вартість замовлення, суму USD до сплати й точну суму токенів.

Зберігайте дедуплікацію події та одноразове виконання замовлення в одній транзакції БД, з унікальними обмеженнями для події та замовлення й чергою вихідних завдань. Повертайте 2xx після надійного збереження; тимчасові помилки читання чи БД мають допускати повтор. Див. [підтвердження замовлення](https://docs.bramapay.com/uk/security/payment-verification) і [повторну доставку](https://docs.bramapay.com/uk/webhooks/retries).

## 6. Перед переходом на Live

Налаштуйте окремі гаманці, секрети й вебхуки Live; успішно перевірте **кожну поточну адресу та конфігурацію Live**. Перевірте [готовність проєкту](https://docs.bramapay.com/uk/projects/readiness), маршрути й чинну ставку. Створення нових платежів Live дозволено лише за **B + C > 0**, де B — фактичний сервісний баланс, C — схвалений кредит. Типово C = 0. Уже видані платежі продовжують оброблятися та можуть збільшити борг понад поріг. Для поповнення використовуйте окремі реквізити [Billing](https://docs.bramapay.com/uk/billing/ledger-topups).

На локальних прикладах перевірте неправильні підписи, режим, замовлення й тип події, повтори подій і виконання, відсутність маршрутів, завершення строку та невідомий результат створення. Звичайний 503 не доводить відмови: потрібен перевірений `PAYMENT_CREATION_DECLINED` зі збігом ідентичності, квитанцією відмови, `retryable: false` і `sessionCreated: false`. Збережіть квитанцію.

Таймер не доводить остаточності: своєчасний переказ може підтвердитися пізніше. Використовуйте серверний статус, `monitoringComplete` та `isFinal`. Помилкові, пізні й додаткові перекази потребують [розгляду](https://docs.bramapay.com/uk/invoices/mismatches); не запитуйте гроші повторно автоматично й не обіцяйте повернення. Успішний тест не доводить доступності Live.

---

# Перевірка підпису

Збирайте точні Buffer-байти до JSON middleware, обмежуючи читання 256 KiB. Відхиляйте повторні заголовки підпису за початковим списком HTTP; лише потім приводьте назви до нижнього регістру. Обирайте секрет із довіреної карти за `X-Key-Id`; невідомий ID відхиляйте. До перевірки ID — лише підказка пошуку. Зберігайте старі нескомпрометовані секрети для наявних доставок. Синхронізуйте годинник сервера.

Обов’язкові заголовки: `X-Webhook-Version`, `X-Event-Id`, `X-Delivery-Id`, `X-Event-Type`, `X-Key-Id`, `X-Timestamp`, `X-Attempt`, `X-Idempotency-Key`, `X-Signature-SHA256`. Namespace підпису залишається буквально `vaultless.webhook`. Підписується JSON.stringify упорядкованого масиву нижче: версія — число 2, інші значення envelope — рядки.

Функція передбачає, що HTTP-обробник уже відхилив повторні заголовки, обмежив розмір тіла й обрав довірений секрет.

```ts
import { createHmac, timingSafeEqual } from "node:crypto";

export function verifyBramaPayWebhook(
  rawBody: Buffer,
  headers: Record<string, string | undefined>,
  secret: string,
) {
  const required = (name: string) => {
    const value = headers[name.toLowerCase()];
    if (!value) throw new Error(`Missing ${name}`);
    return value;
  };

  const version = required("x-webhook-version");
  const eventId = required("x-event-id");
  const deliveryId = required("x-delivery-id");
  const eventType = required("x-event-type");
  const timestamp = required("x-timestamp");
  const keyId = required("x-key-id");
  const attempt = required("x-attempt");
  const signature = required("x-signature-sha256");

  if (version !== "2" || required("x-idempotency-key") !== eventId)
    throw new Error("Invalid webhook envelope");
  if (!/^\d+$/.test(timestamp) || Math.abs(Date.now() / 1000 - Number(timestamp)) > 300)
    throw new Error("Stale webhook");
  if (!/^[0-9a-f]{64}$/.test(signature) || !/^[1-9]\d*$/.test(attempt))
    throw new Error("Invalid signature encoding or attempt");

  const input = JSON.stringify([
    "vaultless.webhook",
    2,
    eventId,
    deliveryId,
    eventType,
    timestamp,
    keyId,
    attempt,
    rawBody.toString("utf8"),
  ]);
  const expected = Buffer.from(createHmac("sha256", secret).update(input).digest("hex"), "hex");
  const actual = Buffer.from(signature, "hex");
  if (actual.length !== expected.length || !timingSafeEqual(actual, expected))
    throw new Error("Invalid webhook signature");
  return { eventId, eventType, keyId };
}
```

Розбирайте JSON і перевіряйте схему й підписаний тип події лише після успіху перевірки. Самого підпису недостатньо для видачі: звіряйте сесію, проєкт, режим, замовлення, валюту й ціну з авторизованою PAID-відповіддю. Відхиляйте Test-подію для Live-замовлення. Тест зв’язку може отримати 2xx після перевірки, але не оплачує замовлення. За тимчасового збою читання/зберігання повертайте non-2xx для повтору. Див. [дедуплікацію](https://docs.bramapay.com/uk/webhooks/deduplication) і [сесії](https://docs.bramapay.com/uk/api-reference/payment-sessions).

---

# Дедуплікація подій і замовлень

Повтори й ручна доставка можуть повторити коректну подію. `X-Event-Id` — логічна ідентичність події, `X-Delivery-Id` стосується доставки на endpoint. Дедуплікуйте подію й бізнес-замовлення. Номера спроби чи delivery ID недостатньо.

Після перевірки початкового підпису й авторизованої PAID-відповіді зі звіркою замовлення/проєкту/режиму/валюти/ціни виконайте одну транзакцію БД: вставте унікальний `(merchant_scope, eventId)`, позначте пов’язане замовлення оплаченим під унікальним обмеженням замовлення/сесії й додайте видачу до надійного outbox. Прийнята подія чи оплачене замовлення не створюють повторної видачі. Спершу commit, потім невеликий 2xx. Тимчасовий збій читання/зберігання має допускати повтор.

Не можна видавати замовлення за сесію й повторно за дочірній рахунок. Test-події, treasury-поповнення, тести зв’язку, redirects і записи перевірки не оплачують Live-замовлення.

---

# Створення платіжної сесії v2

Сесія — одне замовлення; спроба створюється лише після вибору маршруту. У кабінеті виберіть проєкт і режим, відкрийте Платіжні запити → Створити. JWT власника дозволяє створення без API-ключа за готовності проєкту. Для інтеграції використовуйте секрет проєкту.

## Створіть одну сесію на замовлення

**До надсилання** збережіть тіло запиту, ID замовлення, очікувані проєкт і режим, а також ключ ідемпотентності. Після тайм-ауту чи втрати відповіді повторюйте той самий запит із тим самим ключем.

```bash
curl --request POST 'https://api.bramapay.com/v2/payment-sessions' \
  --header 'X-Api-Key: YOUR_PROJECT_SECRET_KEY' \
  --header 'Idempotency-Key: order-demo-1042-attempt-1' \
  --header 'Content-Type: application/json' \
  --data '{
    "amountUsd": "25.00",
    "currency": "USD",
    "externalId": "order-demo-1042",
    "displayOrderNumber": "1042",
    "description": "Example service order",
    "expiresInSeconds": 1800
  }'
```

| Поле | Контракт |
|---|---|
| `amountUsd` | Додатний десятковий рядок, щонайбільше шість знаків після крапки. Не число з рухомою комою. |
| `currency` | Лише `USD`. Фіксоване співвідношення обліку стейблкоїнів не є ринковим курсом або гарантією погашення. |
| `externalId` | Обов’язкове поле, 1–200 символів; постійний ідентифікатор замовлення в проєкті й режимі. |
| `displayOrderNumber` | Необов’язковий публічний текст, до 100 кодових точок Unicode. |
| `description` | Необов’язковий публічний текст, до 1000 кодових точок Unicode. Без облікових даних, секретів видачі та персональних даних клієнта. |
| `expiresInSeconds` | Необов’язкове ціле число 1–86400. Типове значення визначається серверною політикою строку рахунку. Отриманий строк фіксований. |

Не передавайте в цей endpoint `projectId`, `mode`, ID гаманців, вибраний спосіб оплати й перевизначення URL повернення. Контекст задають API-ключ і налаштування проєкту. Для явного перенесення комісії використовуйте інший контракт: `baseAmountUsd`, `feePayer: "BUYER"`, `pricingVersion: "BUYER_FEE_V1"`, без `amountUsd`. Не змішуйте ці варіанти.

Успішна відповідь містить `sessionId`, `externalId`, `mode`/`paymentMode`, `amountUsd`, `currency`, `status`, `expiresAt`, `checkoutUrl`, `routes`, `attempts` і, коли застосовно, відомості про ціну та моніторинг. Збережіть зв’язок сесії із замовленням і незмінні цінові параметри. Нова сесія може ще не мати платіжних спроб.

### Обробіть відповідь створення

Після успішної відповіді збережіть `sessionId`, `projectId`, `mode`, `externalId`, `amountUsd`, `currency` та знімок розрахунку комісії в замовленні до переходу на `checkoutUrl`. Нижче неповна вигадана відповідь, а не справжні платіжні реквізити. Можливий `routes: []`; не вигадуйте маршрут і не просіть переказ, якщо доступних способів немає.

> Цей самий приклад уже наведено вище.

## Відкрийте оплату

Спрямуйте покупця на отриманий `checkoutUrl` без змін. У фрагменті URL міститься токен доступу до сесії: не записуйте повний URL у журнали й аналітику. Не створюйте посилання самостійно й не переносьте токен у query-параметри.

Покупець вибирає доступний спосіб, після чого отримує адресу й **точну суму**. QR містить лише адресу. Суму слід надіслати одним переказом у зазначеній мережі до завершення строку. Зміна способу й повтори не подовжують строк. Не округлюйте суму, не розділяйте переказ і не просіть доплатити різницю без перевірки.

## Перевірте webhook і достовірний статус

Подія оплати сесії — `payment_session.paid`; для рахунків v1 використовується `invoice.paid`. `integration.test` і `webhook.test` перевіряють з’єднання, а не оплату. Test-подія не має виконувати замовлення Live.

Перевірте [підпис v2](https://docs.bramapay.com/uk/webhooks/signature-verification) на початковому тілі запиту до його розбору для бізнес-обробки. Порядок підписуваних даних:

```js
JSON.stringify([
  "vaultless.webhook", 2, eventId, deliveryId, eventType,
  timestamp, keyId, attempt, rawBodyAsUtf8String
])
```

Використовуйте HMAC-SHA256, порівняння зі сталим часом, секрет за `X-Key-Id` і перевірку свіжості часу. Версія — число `2`, інші значення конверта — рядки. Перевіряйте `X-Idempotency-Key === X-Event-Id`. Зберігайте старі нескомпрометовані ключі для раніше створених доставок: ротація не перепідписує їх.

### Повний приклад перевірки підпису на Node.js

Передавайте початкові байти HTTP-запиту як `rawBody` до JSON middleware. У Node HTTP збирайте Buffer-фрагменти з обмеженням 256 КіБ; в Express для цього маршруту встановіть `express.raw({type: "application/json", limit: "256kb"})` перед JSON-парсером. Перетворіть назви заголовків на нижній регістр і відхиляйте повторювані заголовки підпису. Вибирайте `secret` зі свого довіреного списку за `x-key-id`; невідомий ключ відхиляйте. До перевірки підпису заголовок — лише підказка для пошуку ключа. Синхронізуйте годинник сервера.

> Ідентичний повний verifier уже наведено вище в розділі перевірки підпису. Використовуйте цю єдину реалізацію.

Лише після успішної перевірки розберіть JSON, перевірте його структуру й тип події, потім запитайте авторизований статус, як показано нижче. За тимчасової помилки читання статусу або сховища повертайте не 2xx, щоб доставка повторилася. Неправильний підпис відхиляйте без обробки. Перевірений тест з’єднання отримує 2xx, але не запускає видачу товару.

Запитуйте статус із сервера тим самим секретним ключем проєкту:

```bash
curl 'https://api.bramapay.com/v2/payment-sessions/SESSION_ID' \
  --header 'X-Api-Key: YOUR_PROJECT_SECRET_KEY'
```

Вимагайте `status === "PAID"`; звірте ID сесії, `externalId`, проєкт, режим, валюту й очікувану вартість за збереженим ціновим контрактом. Порівнюйте десяткові суми точно. За перенесення комісії налаштуванням проєкту `amountUsd` інтеграції зберігає вартість замовлення, а `baseAmountUsd`/`payableAmountUsd` розкривають складові; явний buyer-контракт має іншу семантику. Вартість замовлення, сума з комісією та точна сума токенів — різні величини.

Рекомендована обробка на боці магазину; це псевдокод вашого застосунку, а не методи API BramaPay:

```text
перевірити raw-підпис, час, тип та ідентифікатор події
знайти збережений зв’язок замовлення із сесією й перевірити статус через API
в одній транзакції бази даних:
  вставити (merchant_scope, eventId) з унікальним обмеженням
  якщо подію вже оброблено: нічого не виконувати повторно
  інакше позначити пов’язане замовлення оплаченим і поставити видачу в чергу один раз
повернути невелику відповідь 2xx після надійного збереження
```

Усувайте дублікати і за подією, і за виконанням замовлення. Повернення браузера, пошук транзакції, запис для ручної перевірки й неперевірений клієнтський статус недостатні.

## Маршрути, сторінка оплати та подія оплати

- Посилання на оплату має вигляд `/{locale}/checkout/{sessionId}#token=…`. Сторінка оплати прибирає токен з історії браузера та зберігає його під ключем цієї сесії, щоб перезавантаження та інші вкладки могли його відновити. Якщо сховище браузера заблоковане, для повторного відкриття потрібне початкове повне посилання.
- Публічна сторінка оплати викликає `GET /v2/checkout/:id` і `POST /v2/checkout/:id/attempts` із заголовком `X-Checkout-Token` і тілом на кшталт `{ "asset": "USDT", "network": "TRON" }`, без cookie та авторизації магазину. Серверній інтеграції магазину ці endpoints не потрібні.
- Спроба створюється під час першого вибору маршруту; повторний вибір повертає ті самі адресу, точну суму та строк. Раніше видані спроби лишаються у списку, навіть якщо видача нових маршрутів недоступна.
- Статус сесії не залежить від маршруту, відкритого в покупця: якщо підтвердився маршрут A, а покупець дивиться B, сесія стає `PAID`, а `winningAttemptId` вказує на A.
- Для спроб сесії видачу замовлення виконують за `payment_session.paid`, а не за дочірнім `invoice.paid`. Тіло події містить `sessionId`, `attemptId`, `invoiceId` (спроба-переможець), `externalId`, `projectId`, `mode`, `paymentMode`, `status: "PAID"`, `amountUsd`, `currency`, `amountPaid`, `amountExpected`, `txHash` і `chain`; у Test також тестову мережу та `test: true`. Суми передаються рядками.
- `displayOrderNumber` і `description` показуються покупцю як звичайний текст: HTML і Markdown не інтерпретуються. Без публічного номера замовлення сторінка оплати не підставляє замість нього `externalId`. Ціни в описі не впливають на суму платежу.
- Розділ **Платіжні запити** в кабінеті обмежений вибраними проєктом і режимом та показує спроби, стан моніторингу, спробу-переможця й класифікацію надходжень. Раніше видані рахунки v1 лишаються в історії старих рахунків; їхні посилання та інтеграції продовжують працювати.

## Обробіть помилки та повтори

| Ситуація | Дія інтеграції |
|---|---|
| Тайм-аут, втрата відповіді, загальний 5xx | Результат може бути невідомим. Збережіть початкові тіло й ключ, повторюйте той самий запит. Не створюйте нове замовлення. |
| Конфлікт 409 | Перевірте ключ, тіло, зовнішнє замовлення й готовність проєкту. Не обходьте конфлікт новим ключем. |
| 401 | Перевірте секретний ключ і його проєкт/режим; не публікуйте ключ у діагностиці. |
| 429 | Збільште інтервал та врахуйте доступні відомості про повтор. |
| `PAYMENT_CREATION_DECLINED` | Лише перевірена відповідь із `retryable: false`, `sessionCreated: false`, відповідною ідентичністю запиту та квитанцією відмови підтверджує остаточну відмову. Самого HTTP 503 недостатньо. Збережіть квитанцію. |
| Завершення строку, пізній, зайвий або невідповідний переказ | Не просіть платити повторно автоматично. Перевірте статус і зверніться до підтримки з ID, мережею та хешем транзакції. Автоповернення й постійний процес ручного підтвердження не обіцяються. |

Webhooks можуть доставлятися повторно; успішна доставка не гарантується. За цикл виконується до п’яти спроб. Для помилок, що допускають повтор, інтервали становлять 30 секунд, 5 хвилин, 30 хвилин і 2 години. Інші помилки можуть завершити цикл одразу. Невдалу доставку на активний endpoint можна повторити вручну після паузи; повтор не дозволяє видавати замовлення знову. Див. [повтори доставки](https://docs.bramapay.com/uk/webhooks/retries).

Таймер не визначає остаточність результату. Переказ, включений у блок у межах строку, може підтвердитися пізніше. Використовуйте серверні `status`, `monitoringComplete`, `isFinal`; не обіцяйте час підтвердження й не вважайте кожну відповідь про завершення строку остаточною відсутністю оплати.

API повертає `status` сесії як `OPEN`, `PAID` або `EXPIRED`. Закриття передається окремо в `closedAt`; CLOSED і CONFIRMING — обчислювані фільтри кабінету, а не значення цього поля API. Перший відповідний підтверджений переказ виграє; додаткові й пізні надходження не дозволяють другу видачу. `reviewCandidates` — неприписані перекази на адресу, які можуть належати іншим замовленням. Пошук транзакції не означає ручне підтвердження або повернення коштів.
