Appearance
Вебхуки
Arcanum Pay отправляет HTTP-уведомления (вебхуки) на URL мерчанта при изменении статуса операций. Вебхуки отправляются для четырёх типов операций: депозиты, выплаты, возвраты и чарджбеки.
Общие сведения
- Метод:
POST - Content-Type:
application/json - Таймаут: 20 секунд
- Каждый вебхук содержит поле
signatureдля верификации подлинности запроса
Важно: URL для вебхуков настраивается отдельно для каждого типа операции в настройках мерчанта.
Формат вебхуков
Вебхук отправляется при изменении статуса депозита на URL, указанный в callbackUrl при создании депозита.
Тело запроса
| Поле | Тип | Описание |
|---|---|---|
operationId | string | UUID операции |
operationType | string | Всегда "deposit" |
status | number | 1 — Approved, 2 — Declined, 3 — Processing |
amount | string | Сумма операции (например, "100.00") |
currency | string | Валюта операции |
paymentMethod | string | Метод оплаты (например, "card", "sbp") |
merchantOperationId | string | null | Идентификатор операции на стороне мерчанта (если был передан при создании) |
createdAt | string | Дата создания операции в формате ISO 8601 |
confirmedAt | string | null | Дата подтверждения операции (null, если ещё не подтверждена) |
receivedAmount | string | Сумма за вычетом комиссии мерчанта (amount - merchantFee). Присутствует при финальном статусе (Approved, Declined) |
signature | string | HMAC-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 подпись тела запроса. Мерчант должен проверять подпись для подтверждения подлинности вебхука.
Алгоритм верификации
- Извлеките значение поля
signatureиз полученного JSON - Удалите поле
signatureиз объекта - Отсортируйте оставшиеся ключи объекта по алфавиту
- Сериализуйте объект в JSON-строку с отсортированными ключами
- Вычислите HMAC-SHA256 от полученной строки, используя секретный ключ мерчанта
- Сравните результат (в HEX) с полученной подписью
Важно: для депозитов используется секретный ключ депозитов, для выплат — секретный ключ выплат. Для возвратов и чарджбеков используется секретный ключ депозитов.
Выбор секретного ключа
| Тип операции | Секретный ключ |
|---|---|
deposit | Deposit Secret Key |
withdrawal | Withdrawal Secret Key |
refund | Deposit Secret Key |
chargeback | Deposit 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}), 200php
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]);