Fiatside

Developers

Вебхуки

Вебхук повідомляє, що замовлення змінило стан. Він не замінює читання замовлення: це сповіщення, а не джерело істини.

Опублікований контракт, ще не відкритий

Вебхуки наразі не надсилаються. Їхня форма, підпис і політика повторних спроб визначені та опубліковані тут, щоб ваш приймач можна було написати та протестувати заздалегідь.

01

Події

Кожна подія має однакову оболонку: id, type, timestamp і об'єкт data. Підписуйтеся на типи, які ви обробляєте, і мовчки ігноруйте решту — нові типи будуть додані, і приймач, який падає на невідомому типі, ламає сам себе.

Події
ТипЩо це означає
order.createdЗамовлення створено, курс зафіксовано. Адресу для депозиту призначено.
order.deposit_detectedВхідна транзакція видна в мережі до підтвердження. Використовуйте цю подію, щоб заспокоїти користувача, але ніколи не для видачі чогось.
order.confirmingЛічильник підтверджень зростає. Випромінюється на кожному кроці, а не на кожному блоці.
order.deposit_confirmedДосягнуто кількості підтверджень, необхідної мережею.
order.underpaidОтримана сума нижча за очікувану поза межами допустимого відхилення. Замовлення не втрачено: воно чекає на вибір між поповненням, продовженням з отриманою сумою або поверненням коштів.
order.overpaidОтримана сума перевищує очікувану. Початкова сума залишається за зафіксованим курсом; надлишок обробляється окремо.
order.payout_sentПереказ відправлено через платіжну систему. Увага: відправлено не означає отримано — решта затримки залежить від банку бенефіціара.
order.completedРозрахунок підтверджено платіжною системою. Кінцевий стан.
order.payout_failedПлатіжна система відхилила або повернула переказ із зазначенням причини. Автоматичний повтор не виконується при банківському поверненні: причина майже завжди в деталях.
order.refundedКошти повернуто лише на адресу відправника, за вирахуванням мережевих комісій.
02

Корисне навантаження

Постійна оболонка, незалежно від типу.

Прикладjson
{
  "id": "evt_01J9ZQF3K8N2M4X7",
  "type": "order.payout_sent",
  "created": 1788356981,
  "data": {
    "reference": "K7Q4-M2XB",
    "state": "PAYOUT_SENT",
    "railId": "sepa_instant",
    "netMinor": 91228,
    "currency": "EUR",
    "sentAt": "2026-09-02T12:21:44Z"
  }
}
03

Підпис

Кожна доставка містить заголовок підпису: мітку часу та HMAC-SHA256, обчислений за цією міткою часу, крапкою та НЕОБРОБЛЕНИМ тілом запиту.

Fiatside-Signaturehttp
Fiatside-Signature: t=<timestamp unix>,v1=<hex hmac-sha256>
Перевірка, Node.jstypescript
import { createHmac, timingSafeEqual } from 'node:crypto'

// IMPORTANT : le corps doit etre le corps BRUT, avant tout parsing JSON.
// Re-serialiser l'objet change l'ordre des cles et invalide la signature.
export function verify(rawBody: string, header: string, secret: string): boolean {
  const parts = Object.fromEntries(header.split(',').map((p) => p.split('=') as [string, string]))
  const timestamp = Number(parts['t'])
  const signature = parts['v1']
  if (!timestamp || !signature) return false

  // Rejeu : une signature valide capturee hier ne doit pas etre rejouable aujourd'hui.
  if (Math.abs(Date.now() / 1000 - timestamp) > 300) return false

  const expected = createHmac('sha256', secret).update(`${timestamp}.${rawBody}`).digest('hex')
  const a = Buffer.from(expected, 'hex')
  const b = Buffer.from(signature, 'hex')
  // Comparaison a temps constant : un === laisse fuiter la signature octet par octet.
  return a.length === b.length && timingSafeEqual(a, b)
}
  • Підписуйте необроблене тіло до будь-якого парсингу. Повторна серіалізація JSON-об'єкта змінює порядок ключів і пробіли: підпис більше не збігатиметься, і ви довго шукатимете причину.
  • Порівнюйте за постійний час. Порівняння через === зупиняється на першому відмінному байті, що дозволяє атакувальнику виміряти очікуваний підпис байт за байтом.
  • Відхиляйте за межами вікна допуску в 300 секунд. Без цієї перевірки дійсний запит, захоплений учора, залишається відтворюваним сьогодні.
  • Відповідайте 200 до обробки. Помістіть подію у свою чергу та обробіть її після: тривала обробка спричиняє тайм-аут і, отже, повторну спробу, навіть якщо ви отримали подію.
04

Повторні спроби та порядок

Будь-який статус, відмінний від 2xx, або відсутність відповіді взагалі, спричиняє повторну спробу за наведеним нижче розкладом.

Повторні спроби та порядок
СпробаЗатримка
1негайно
230 s
32 min
410 min
51 h
66 h
  • Порядок прибуття не гарантується. Подія виплати може прибути до події підтвердження, яка логічно передує їй: довіряйте стану замовлення, а не порядку отримання.
  • Та сама подія може надійти двічі. Зберігайте ідентифікатор події та ігноруйте вже оброблений ідентифікатор: ідемпотентність на вашому боці — це ваша відповідальність, і це легко зробити.
  • Ніколи не довіряйте сумам у корисному навантаженні для зарахування чогось на вашому боці. Перечитайте замовлення через API: корисне навантаження каже, що щось сталося, API каже точно що.
  • Після останньої спроби подія відкидається і залишається видимою у вашому журналі подій. Ви можете відтворити її вручну.