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

  1. Отримайте shop_id та секретний ключ у налаштуваннях мерчанта (розділ «Інформація про касу»).
  2. Створіть рахунок через POST /api/v2/invoices/create і перенаправте користувача на payment_url з відповіді.
  3. Вкажіть Webhook URL у налаштуваннях каси — система надішле POST-повідомлення після успішної оплати. Перевірте підпис і оновіть статус замовлення.

Swagger API Документація

Для зручної роботи з API ми надали інтерактивну Swagger документацію, яка дозволяє переглядати всі доступні API endpoints, бачити параметри запитів та приклади відповідей, тестувати API прямо в браузері та імпортувати специфікацію в Postman та інші інструменти.

Swagger документація:

https://aifo.pro/docs/swagger

Swagger документація містить всю інформацію про API endpoints, які описані нижче, але в більш зручному та інтерактивному форматі. Рекомендуємо використовувати Swagger для розробки та тестування інтеграцій.

JSON специфікація доступна за адресою: https://aifo.pro/docs/swagger/spec

Основний API створення рахунку

Для web-каси і Telegram-каси використовуйте єдиний endpoint створення рахунку. У відповіді система поверне системний invoice_id і готове посилання payment_url.

Endpoint:

POST https://aifo.pro/api/v2/invoices/create
Параметр Примітка Тип
shop_idID каси з кабінету мерчанта.Обов'язковий.
amountСума рядком: 100.50. Можна замінити на amount_minor.Обов'язковий.
amount_minorСума в мінімальних одиницях: 10050 = 100.50 UAH. Має пріоритет над amount.Необов'язковий.
external_idВаш унікальний ID замовлення: рядок, UUID або число. Макс. 191 символ. Допустимі символи: літери, цифри, ._:@#-.Обов'язковий.
signature, timestamp, nonceHMAC SHA-256 авторизація. Передаються у тілі POST-запиту або як HTTP-заголовки X-AIFO-Signature, X-AIFO-Timestamp, X-AIFO-Nonce.Обов'язковий.
descriptionКоментар до платежу.Необов'язковий.
lifetime_minutesЧас життя рахунку у хвилинах, від 0 до 43200. Необов'язково.Необов'язковий.
idempotency_keyКлюч ідемпотентності: повторний запит з тим самим nonce поверне початкову відповідь (впродовж 10 хв).Необов'язковий.

HMAC canonical string: METHOD\nPATH\nTIMESTAMP\nNONCE\nSORTED_PARAMS. Параметри сортуються за ключем (ksort) і кодуються через http_build_query. Поля авторизації (signature, timestamp, nonce) виключаються з SORTED_PARAMS. Часове вікно: ±300 секунд. Nonce: 8–128 символів [a-zA-Z0-9._:-].

Приклад генерації HMAC-підпису (PHP):

PHP
function buildHmacSignature(
    string $method,
    string $path,
    int    $timestamp,
    string $nonce,
    array  $params,
    string $secretKey
): string {
    // Remove auth fields from params before signing
    $authFields = ['sign', 'signature', 'timestamp', 'nonce', 'idempotency_key',
                   'aifo_signature', 'aifo_timestamp', 'aifo_nonce'];
    foreach ($authFields as $f) {
        unset($params[$f]);
    }

    ksort($params); // sort by key — required
    $canonical = http_build_query($params, '', '&');

    $base = $method . "\n" . $path . "\n" . $timestamp . "\n" . $nonce . "\n" . $canonical;
    return hash_hmac('sha256', $base, $secretKey);
}

// -------- Create Invoice example --------
$secretKey  = 'YOUR_SECRET_KEY'; // from merchant panel → secret key
$shopId     = 123;               // your shop_id
$externalId = 'ORDER-456';       // your unique order ID
$amount     = '100.50';          // invoice amount

$timestamp  = time();
$nonce      = bin2hex(random_bytes(16)); // unique per request, 8-128 chars

$params = [
    'shop_id'     => $shopId,
    'amount'      => $amount,
    'external_id' => $externalId,
];

$signature = buildHmacSignature('POST', '/api/v2/invoices/create', $timestamp, $nonce, $params, $secretKey);

// Add auth fields to POST body
$postData = array_merge($params, [
    'timestamp' => $timestamp,
    'nonce'     => $nonce,
    'signature' => $signature,
]);

// Send request
$ctx = stream_context_create(['http' => [
    'method'  => 'POST',
    'header'  => 'Content-Type: application/x-www-form-urlencoded',
    'content' => http_build_query($postData),
    'timeout' => 10,
]]);
$response = file_get_contents('https://aifo.pro/api/v2/invoices/create', false, $ctx);
$result   = json_decode($response, true);

if ($result['status'] === 'success') {
    $paymentUrl = $result['data']['payment_url'];
    $invoiceId  = $result['data']['invoice_id'];
    // Redirect the user: header('Location: ' . $paymentUrl);
}

Приклад відповіді:

{
  "status": "success",
  "message": "Invoice created",
  "data": {
    "api_version": "v2",
    "invoice_id": 456,
    "external_id": "ORDER-456",
    "amount": "100.50",
    "amount_minor": 10050,
    "currency": "UAH",
    "status": 0,
    "status_text": "pending",
    "merchant_type": "domain",
    "checkout_endpoint": "/pay",
    "payment_url": "https://aifo.pro/pay?shop_id=123&pay_id=456&amount=100.50&sign=...",
    "created_at": "2024-01-15 10:30:00",
    "expires_at": 1705309800,
    "lifetime_minutes": 60,
    "auth_mode": "hmac",
    "key_status": "active"
  }
}

API перевірки статусу рахунку

Статус рахунку доступний тільки з shop_id і підписом. Це захищає платежі від перегляду лише за одним ID.

GET https://aifo.pro/api/v2/invoices/status?shop_id=123&invoice_id=456&timestamp=...&nonce=...&signature=...
Параметр Примітка Тип
shop_idID каси з кабінету мерчанта.Обов'язковий.
invoice_idСистемний invoice_id з відповіді API при створенні. Можна замінити на external_id.Один з двох.
external_idВаш external_id, переданий при створенні рахунку. Альтернатива invoice_id.Один з двох.
signature, timestamp, nonceHMAC SHA-256 авторизація. Передаються у тілі POST-запиту або як HTTP-заголовки X-AIFO-Signature, X-AIFO-Timestamp, X-AIFO-Nonce.Обов'язковий.

Коди статусів рахунку

Код status_text Опис
0pendingОчікує оплати
1paidУспішно оплачений
2expiredТермін дії минув
3testТестовий платіж (sandbox)

Приклад генерації HMAC-підпису (PHP): (GET)

PHP
$params = [
    'shop_id'    => $shopId,
    'invoice_id' => 456, // or 'external_id' => 'ORDER-456'
];

$timestamp = time();
$nonce     = bin2hex(random_bytes(16));
$signature = buildHmacSignature('GET', '/api/v2/invoices/status', $timestamp, $nonce, $params, $secretKey);

$queryString = http_build_query(array_merge($params, [
    'timestamp' => $timestamp,
    'nonce'     => $nonce,
    'signature' => $signature,
]));

$response = file_get_contents('https://aifo.pro/api/v2/invoices/status?' . $queryString);
$result   = json_decode($response, true);

if ($result['status'] === 'success') {
    $statusText = $result['data']['status_text']; // 'pending', 'paid', 'expired', 'test'
    $isPaid     = ($statusText === 'paid');
}

Коди помилок API

При помилці API повертає JSON з полем error_code. Використовуйте його для обробки конкретних помилок у коді.

{
  "status": "error",
  "error_code": "invalid_signature",
  "message": "Invalid API signature: timestamp_out_of_window",
  "code": 401
}
error_code HTTP Опис
invalid_shop_id400shop_id не вказаний або некоректний
merchant_not_found404Мерчант з таким shop_id не знайдений
merchant_not_active403Мерчант не активований
merchant_inactive_deactivated403Мерчант деактивований через відсутність активності (30 днів без оплат)
invalid_signature401Невірний або прострочений HMAC-підпис. Перевірте timestamp (вікно ±300 с), nonce та правильність обчислення
invalid_amount400Некоректна сума: має бути позитивним числом, до 2 знаків після коми
invalid_amount_minor400amount_minor має бути позитивним цілим числом
external_id_required400Поле external_id обов'язкове
external_id_too_long400external_id перевищує 191 символ
invalid_external_id400external_id містить неприпустимі символи (допустимі: літери, цифри, ._:@#-)
description_too_long400description перевищує 255 символів
request_blocked429Запит заблокований антифрод-системою (перевищено ліміт або підозріла активність)
invoice_not_found404Рахунок не знайдений
invoice_create_failed500Внутрішня помилка при створенні рахунку

Прямий checkout URL

Для ініціалізації платежу через єдину форму оплати достатньо направити користувача за спеціальною URL-адресою, а також передати ряд обов'язкових параметрів.

URL форми оплати:

https://aifo.pro/pay/?shop_id=ID&pay_id=ORDER_ID&amount=AMOUNT&sign=SIGN

Форма оплати містить усі необхідні дані для проведення платежу. Передається методом GET/POST на адресу.

Параметр Примітка Тип
shop_id ID каси, можна отримати в налаштуваннях проєкту. Обов'язковий.
amount Сума платежу. Обов'язковий.
pay_id Номер рахунку. Може бути ваш ID замовлення (виджет або пряме посилання) або системний invoice_id (посилання з API). Система спочатку шукає інвойс за вашим id (invoice_uid), потім за invoice_id. Обов'язковий.
sign Підпис платежу. Обов'язковий.
desc Коментар до платежу. Необов'язковий.

Підпис для створення платежу формується шляхом обчислення SHA256 || SHA1 || SHA512 || SHA384 || RIPEMD160-хешу. Рекомендується SHA256.

Увага: MD5 застарілий і небезпечний. Використовуйте SHA256 або інші безпечні алгоритми.

Важливо: Для генерації підпису використовуйте секретний ключ (kassa_secretkey), а не публічний ключ. Секретний ключ доступний тільки вам і не повинен публікуватися.

Приклад коду на PHP (підпис):

PHP
$shop_id    = 123;             // your shop ID
$amount     = '100.50';       // payment amount (string)
$secret_key = 'YOUR_SECRET';  // secret key from merchant settings
$order_id   = 'ORDER-456';    // your order ID (string or number)

// SHA-256 (default, recommended)
$sign = hash('sha256', $shop_id . ':' . $amount . ':' . $secret_key . ':' . $order_id);

// SHA-1 (legacy only — not recommended)
// $sign = hash('sha1', $shop_id . ':' . $amount . ':' . $secret_key . ':' . $order_id);

// Build checkout URL:
$payment_url = 'https://aifo.pro/pay/'
    . '?shop_id=' . $shop_id
    . '&pay_id=' . urlencode($order_id)
    . '&amount=' . $amount
    . '&sign=' . $sign;

Перевірка IP

Рекомендуємо перевіряти IP сервера, що надсилає інформацію. Наші IP — 77.83.102.155

Приклад коду на PHP:

PHP
function getIP() {
    if (isset($_SERVER['HTTP_CF_CONNECTING_IP'])) {
        $_SERVER['REMOTE_ADDR'] = $_SERVER['HTTP_CF_CONNECTING_IP'];
    }
    return $_SERVER['REMOTE_ADDR'];
}
$allowed = array('77.83.102.155');
if (!in_array(getIP(), $allowed)) die('hacking attempt!');

Сповіщення про платіж

Після ініціалізації оплати користувач переходить на сторінку чеку, де відбувається відстеження статусу платежу. При отриманні успішного або помилкового статусу користувач перенаправляється на сайт партнера (поля Fail URL / Success URL у налаштуваннях кабінету) з POST-параметрами:

POST-параметри повідомлення

Параметр Примітка
sum Сума платежу.
invoice Номер рахунку.
http_auth_signature Підпис, згенерований у MD5 || SHA1 || SHA256 || SHA512 || SHA384 || RIPEMD160 із секретним ключем.
aifo_event_id Унікальний ID події, наприклад invoice.456.paid.1705309800
aifo_timestamp Unix-час відправки повідомлення
aifo_signature_version v2
aifo_signature HMAC-SHA256 всіх полів payload (крім aifo_signature): ksort + http_build_query

HTTP-заголовки повідомлення (додатково)

Параметр Примітка
X-SignatureПідпис, згенерований у MD5 || SHA1 || SHA256 || SHA512 || SHA384 || RIPEMD160 із секретним ключем.
X-AIFO-SignatureHMAC-SHA256 всіх полів payload (крім aifo_signature): ksort + http_build_query
X-AIFO-TimestampUnix-час відправки повідомлення
X-AIFO-Event-IdУнікальний ID події, наприклад invoice.456.paid.1705309800

Формула legacy-підпису у webhook: hash(sha256, shop_id:sum:secret_key:invoice), де invoice — значення поля invoice з POST (ваш external_id або системний invoice_id).


Приклад обробника платежу

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

Приклад обробника на PHP:

Після перевірки — оновіть статус замовлення у вашій БД. Обов'язково поверніть рядок ok (HTTP 200) — інакше сервер повторить відправку повідомлення.

PHP
<?php
// webhook.php — handler for payment notifications

function getIP() {
    if (isset($_SERVER['HTTP_X_REAL_IP']))        return $_SERVER['HTTP_X_REAL_IP'];
    if (isset($_SERVER['HTTP_CF_CONNECTING_IP'])) return $_SERVER['HTTP_CF_CONNECTING_IP'];
    return $_SERVER['REMOTE_ADDR'];
}

// 1. IP whitelist check (get actual IPs from the "IP Check" section above)
$allowed = array('77.83.102.155');
if (!in_array(getIP(), $allowed)) {
    http_response_code(403);
    die('Forbidden');
}

// 2. Incoming data
$sum        = $_POST['sum']                ?? '';
$invoice    = $_POST['invoice']            ?? '';
$legacySig  = $_POST['http_auth_signature'] ?? '';
$aifoSig    = $_POST['aifo_signature']     ?? '';

$shop_id    = 123;               // your shop_id
$secret_key = 'YOUR_SECRET_KEY'; // secret key from merchant panel

// 3. Verify legacy signature
$expected = hash('sha256', $shop_id . ':' . $sum . ':' . $secret_key . ':' . $invoice);
if (!hash_equals($expected, $legacySig)) {
    http_response_code(400);
    die('Error signature');
}

// 4. (Optional) Verify aifo_signature for extra security
if ($aifoSig !== '') {
    $payload = $_POST;
    unset($payload['aifo_signature']);
    ksort($payload);
    $expectedHmac = hash_hmac('sha256', http_build_query($payload, '', '&'), $secret_key);
    if (!hash_equals($expectedHmac, $aifoSig)) {
        http_response_code(400);
        die('Error HMAC signature');
    }
}

// 5. Check the invoice has not already been paid (prevent duplicate processing)
// $order = DB::find('orders', ['external_id' => $invoice]);
// if ($order && $order->status === 'paid') { echo 'ok'; exit; }

// 6. Update order status in your database
// DB::update('orders', ['status' => 'paid', 'paid_amount' => $sum], ['external_id' => $invoice]);

// IMPORTANT: Return "ok" (HTTP 200) — otherwise the server will retry the notification
echo 'ok';