aoa
EventsLocationsSpacesPricingIntegrationsAPIBlogHelp
EventsLocationsSpacesPricingIntegrationsAPIBlogHelp
© 2026 AOA
ArtistsVenuesPast eventsCareersGitHub
AboutPrivacySecurityTermsMerchant
aoa
Тарифи
Тарифи

API Reference

  • Вступ
  • Автентифікація
  • Пісочниця
  • Ліміти запитів
  • Помилки
  • Версіонування
  • Події
  • Резервування
  • Оплата

Webhooks

  • Вступ
  • Типи подій
  • Перевірка підпису
  • Make, Zapier, n8n

Webhooks

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

Як переконатися, що запит справді від AOA: підпис AOA-Signature, вікно часу й дедуплікація.

Адреса вашого обробника рано чи пізно стане відомою — вона світиться в логах проксі, в історії браузера, у скриншотах. Підпис — єдине, що відрізняє наш запит від будь-якого іншого. Перевіряйте його завжди, ще до розбору тіла.

HTTP
AOA-Signature: t=1800000000,v1=5f2a9c...64-символьний-hex

t — час підписання в секундах, v1 — HMAC-SHA256 від рядка {t}.{тіло} із вашим секретом. Схема збігається зі Stripe, тож готові приклади звідти підійдуть майже без змін.

Три правила

  • Беріть сире тіло. Підпис рахується від байтів, які прийшли. Якщо фреймворк уже розібрав JSON, а ви серіалізуєте його назад — порядок ключів чи пробіли зміняться, і підпис не зійдеться.
  • Порівнюйте безпечно. timingSafeEqual замість ===: різниця в часі відповіді дозволяє підбирати підпис символ за символом.
  • Перевіряйте вік запиту. Без цього перехоплений колись запит можна відтворити будь-коли — підпис у ньому вічно валідний. Розумне вікно — 5 хвилин.

Node.js

JavaScript
import crypto from 'node:crypto';

const TOLERANCE_SECONDS = 300;

export function verifyAoaSignature(header, rawBody, secret) {
  const parts = header.split(',');
  const timestamp = parts.find((p) => p.startsWith('t='))?.slice(2);
  const signature = parts.find((p) => p.startsWith('v1='))?.slice(3);
  if (!timestamp || !signature) return false;

  // Захист від повторного відтворення старого запиту.
  const age = Math.abs(Math.floor(Date.now() / 1000) - Number(timestamp));
  if (!Number.isFinite(age) || age > TOLERANCE_SECONDS) return false;

  const expected = crypto
    .createHmac('sha256', secret)
    .update(`${timestamp}.${rawBody}`)
    .digest('hex');

  const a = Buffer.from(signature, 'hex');
  const b = Buffer.from(expected, 'hex');
  if (a.length !== b.length) return false;

  return crypto.timingSafeEqual(a, b);
}

Express

JavaScript
// Тіло потрібне СИРИМ: express.json() змінює рядок, і підпис не зійдеться.
app.post(
  '/hooks/aoa',
  express.raw({ type: 'application/json' }),
  (req, res) => {
    const raw = req.body.toString('utf8');

    if (!verifyAoaSignature(req.get('AOA-Signature'), raw, process.env.AOA_WEBHOOK_SECRET)) {
      return res.status(401).end();
    }

    const event = JSON.parse(raw);

    // Відповідаємо одразу — важку роботу робимо після відповіді.
    res.status(200).end();

    void handleEvent(event);
  },
);

Python

Python
import hmac, hashlib, time

TOLERANCE_SECONDS = 300

def verify_aoa_signature(header: str, raw_body: bytes, secret: str) -> bool:
    parts = dict(p.split("=", 1) for p in header.split(",") if "=" in p)
    timestamp, signature = parts.get("t"), parts.get("v1")
    if not timestamp or not signature:
        return False

    if abs(int(time.time()) - int(timestamp)) > TOLERANCE_SECONDS:
        return False

    expected = hmac.new(
        secret.encode(),
        f"{timestamp}.".encode() + raw_body,
        hashlib.sha256,
    ).hexdigest()

    return hmac.compare_digest(signature, expected)

Дедуплікація

Одна подія може прийти двічі — типовий випадок: ви обробили запит, але відповідь не дійшла вчасно, і ми повторили. Заголовок AOA-Delivery-Id при повторі той самий, тож зберігайте оброблені ідентифікатори і пропускайте знайомі.

Тримати їх вічно не треба: ретраї живуть близько девʼяти годин, тож доба зберігання з запасом покриває всі повтори.

Якщо секрет витік

Видаліть адресу і зареєструйте наново — новий секрет видається при створенні. Старий перестає діяти одразу разом із видаленою адресою.

Ми не ходимо за редиректами

Підписаний запит не переходить за 3xx: інакше зміна редиректу на вашому хості пересилала б підпис і дані покупця на чужий сервер. Вказуйте кінцеву адресу одразу.
НазадТипи подійДаліMake, Zapier, n8n