Skip to content

Вебхуки

Arcanum Pay отправляет HTTP-уведомления (вебхуки) на URL мерчанта при изменении статуса операций. Вебхуки отправляются для четырёх типов операций: депозиты, выплаты, возвраты и чарджбеки.

Общие сведения

  • Метод: POST
  • Content-Type: application/json
  • Таймаут: 20 секунд
  • Каждый вебхук содержит поле signature для верификации подлинности запроса

Важно: URL для вебхуков настраивается отдельно для каждого типа операции в настройках мерчанта.

Формат вебхуков

Вебхук отправляется при изменении статуса депозита на URL, указанный в callbackUrl при создании депозита.

Тело запроса

ПолеТипОписание
operationIdstringUUID операции
operationTypestringВсегда "deposit"
statusnumber1 — Approved, 2 — Declined, 3 — Processing
amountstringСумма операции (например, "100.00")
currencystringВалюта операции
paymentMethodstringМетод оплаты (например, "card", "sbp")
merchantOperationIdstring | nullИдентификатор операции на стороне мерчанта (если был передан при создании)
createdAtstringДата создания операции в формате ISO 8601
confirmedAtstring | nullДата подтверждения операции (null, если ещё не подтверждена)
receivedAmountstringСумма за вычетом комиссии мерчанта (amount - merchantFee). Присутствует при финальном статусе (Approved, Declined)
signaturestringHMAC-SHA256 подпись для верификации

Пример: депозит в статусе Processing

json
{
  "operationId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
  "operationType": "deposit",
  "status": 3,
  "amount": "100.00",
  "currency": "RUB",
  "paymentMethod": "card",
  "merchantOperationId": "merch-op-001",
  "createdAt": "2026-05-28T12:00:00.000Z",
  "confirmedAt": null,
  "signature": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
}

Пример: депозит в статусе Approved

json
{
  "operationId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
  "operationType": "deposit",
  "status": 1,
  "amount": "100.00",
  "currency": "RUB",
  "paymentMethod": "card",
  "merchantOperationId": "merch-op-001",
  "createdAt": "2026-05-28T12:00:00.000Z",
  "confirmedAt": "2026-05-28T12:05:00.000Z",
  "receivedAmount": "95.00",
  "signature": "a4d2f8c1e6b3a5d7f9e0c2b4a6d8f0e1c3b5a7d9f1e3c5b7a9d1f3e5c7b9a1"
}

Верификация подписи

Каждый вебхук содержит поле signature — HMAC-SHA256 подпись тела запроса. Мерчант должен проверять подпись для подтверждения подлинности вебхука.

Алгоритм верификации

  1. Извлеките значение поля signature из полученного JSON
  2. Удалите поле signature из объекта
  3. Отсортируйте оставшиеся ключи объекта по алфавиту
  4. Сериализуйте объект в JSON-строку с отсортированными ключами
  5. Вычислите HMAC-SHA256 от полученной строки, используя секретный ключ мерчанта
  6. Сравните результат (в HEX) с полученной подписью

Важно: для депозитов используется секретный ключ депозитов, для выплат — секретный ключ выплат. Для возвратов и чарджбеков используется секретный ключ депозитов.

Выбор секретного ключа

Тип операцииСекретный ключ
depositDeposit Secret Key
withdrawalWithdrawal Secret Key
refundDeposit Secret Key
chargebackDeposit Secret Key

Пример реализации

javascript
const crypto = require('crypto');

function verifyWebhook(payload, secretKey) {
  const { signature, ...data } = payload;

  const sortedJson = JSON.stringify(data, Object.keys(data).sort());

  const expectedSignature = crypto
    .createHmac('sha256', secretKey)
    .update(sortedJson)
    .digest('hex');

  return signature === expectedSignature;
}

// Использование
app.post('/webhook', (req, res) => {
  const payload = req.body;
  const secretKey = 'your-deposit-secret-key'; // или withdrawal secret key

  if (!verifyWebhook(payload, secretKey)) {
    return res.status(401).json({ error: 'Invalid signature' });
  }

  // Обработка вебхука
  console.log('Операция:', payload.operationType);
  console.log('Статус:', payload.status);

  res.status(200).json({ ok: true });
});
python
import hmac
import hashlib
import json

def verify_webhook(payload: dict, secret_key: str) -> bool:
    signature = payload.pop('signature')

    sorted_json = json.dumps(payload, sort_keys=True)

    expected_signature = hmac.new(
        secret_key.encode(),
        sorted_json.encode(),
        hashlib.sha256
    ).hexdigest()

    return hmac.compare_digest(signature, expected_signature)

# Использование (Flask)
@app.route('/webhook', methods=['POST'])
def handle_webhook():
    payload = request.get_json()
    secret_key = 'your-deposit-secret-key'

    if not verify_webhook(payload, secret_key):
        return jsonify({'error': 'Invalid signature'}), 401

    # Обработка вебхука
    print(f"Операция: {payload['operationType']}")
    print(f"Статус: {payload['status']}")

    return jsonify({'ok': True}), 200
php
function verifyWebhook(array $payload, string $secretKey): bool
{
    $signature = $payload['signature'];
    unset($payload['signature']);

    ksort($payload);
    $sortedJson = json_encode($payload, JSON_UNESCAPED_UNICODE);

    $expectedSignature = hash_hmac('sha256', $sortedJson, $secretKey);

    return hash_equals($signature, $expectedSignature);
}

// Использование
$payload = json_decode(file_get_contents('php://input'), true);
$secretKey = 'your-deposit-secret-key';

if (!verifyWebhook($payload, $secretKey)) {
    http_response_code(401);
    echo json_encode(['error' => 'Invalid signature']);
    exit;
}

// Обработка вебхука
echo json_encode(['ok' => true]);