Вебхуки и ценовые оповещения
Получайте подписанные HTTP-уведомления, когда цена металла пересекает заданный порог, и проверяйте их подлинность с помощью подписи HMAC-SHA256.
Как работают оповещения и вебхуки
MetalAPI отправляет вебхуки, когда срабатывает ценовое оповещение. Оповещение отслеживает один символ в выбранной вами валюте. Оно срабатывает, когда цена поднимается выше порога или опускается ниже него либо когда она вырастает или падает на заданный процент относительно предыдущего закрытия.
Оповещение срабатывает в момент пересечения: вы получаете уведомление один раз, когда условие становится истинным, и оповещение снова активируется только после того, как условие опять станет ложным. Период ожидания (по умолчанию 60 минут, настраивается от 5 минут до 7 дней) защищает от потока уведомлений, когда цена колеблется около порога.
Оповещения приходят по электронной почте на тарифе Lite и выше или через вебхук (HTTPS-запрос POST на ваш сервер) на тарифе Advanced и выше.
Настройка вебхука
- Откройте раздел Alerts в личном кабинете и создайте оповещение.
- Выберите символ, валюту, условие и порог.
- Выберите доставку через вебхук и укажите URL вашего эндпоинта.
- Скопируйте секрет для подписи вебхуков (он начинается с
whsec_) из личного кабинета в конфигурацию вашего сервера.
URL вебхука должен использовать https:// и указывать на публичный хост. URL, содержащие учётные данные или указывающие на частные или внутренние IP-адреса, отклоняются, а перенаправления не выполняются.
Полезная нагрузка
Каждая доставка — это запрос POST с JSON и следующими заголовками и телом:
POST /webhooks/metalapi HTTP/1.1
Content-Type: application/json
User-Agent: MetalAPI-Webhooks/1.0
X-MetalAPI-Timestamp: 1792765800
X-MetalAPI-Signature: t=1792765800,v1=3b9f0c4e…a71d
{
"event": "alert.triggered",
"id": 18342,
"created": 1792765800,
"alert": {
"id": "7c9e6679-7425-40de-944b-e07fc1f90ae7",
"symbol": "XAU",
"base": "USD",
"condition": "above",
"threshold": 4000,
"note": "Gold above 4k"
},
"price": 4001.25,
"unit": "troy_oz"
}condition принимает одно из значений above, below, change_pct_up или change_pct_down. price — цена, при которой сработало оповещение, в base за unit. id уникален для каждого события, поэтому по нему можно отбрасывать повторные доставки.
Проверка подписи
Прежде чем обрабатывать запрос, всегда проверяйте, что он действительно пришёл от MetalAPI. Заголовок X-MetalAPI-Signature имеет вид t=<unix timestamp>,v1=<signature>. Подпись — это HMAC-SHA256 в шестнадцатеричной кодировке от строки <timestamp>.<raw request body>, вычисленный с вашим секретом для подписи в качестве ключа.
- Вычисляйте HMAC по необработанному телу запроса — до разбора JSON.
- Сравнивайте подписи за постоянное время.
- Отклоняйте метки времени старше пяти минут, чтобы блокировать повторно отправленные запросы.
Пример на Node.js с Express:
import crypto from "node:crypto";
import express from "express";
const app = express();
const SECRET = process.env.METALAPI_WEBHOOK_SECRET;
app.post("/webhooks/metalapi", express.raw({ type: "application/json" }), (req, res) => {
const header = req.get("X-MetalAPI-Signature") ?? "";
const parts = Object.fromEntries(header.split(",").map((p) => p.split("=")));
const body = req.body.toString("utf8");
const expected = crypto.createHmac("sha256", SECRET).update(`${parts.t}.${body}`).digest("hex");
const valid =
typeof parts.v1 === "string" &&
parts.v1.length === expected.length &&
crypto.timingSafeEqual(Buffer.from(parts.v1), Buffer.from(expected));
const fresh = Math.abs(Date.now() / 1000 - Number(parts.t)) < 300;
if (!valid || !fresh) return res.status(400).send("invalid signature");
const event = JSON.parse(body);
console.log(event.alert.symbol, event.alert.condition, event.alert.threshold, event.price);
res.sendStatus(200);
});Проверка на PHP
Та же проверка на чистом PHP:
<?php
$secret = getenv('METALAPI_WEBHOOK_SECRET');
$payload = file_get_contents('php://input');
$header = $_SERVER['HTTP_X_METALAPI_SIGNATURE'] ?? '';
parse_str(str_replace(',', '&', $header), $parts);
$timestamp = (int) ($parts['t'] ?? 0);
$expected = hash_hmac('sha256', $timestamp . '.' . $payload, $secret);
if (!hash_equals($expected, $parts['v1'] ?? '') || abs(time() - $timestamp) > 300) {
http_response_code(400);
exit('invalid signature');
}
$event = json_decode($payload, true);
// Здесь обработайте $event['alert'] и $event['price'].
http_response_code(200);Ответ и повторные попытки
Верните любой статус 2xx в течение 8 секунд. Длительную обработку выполняйте уже после ответа, например в очереди, потому что тайм-ауты и ответы со статусом, отличным от 2xx, считаются неудачными доставками.
Неудачные доставки повторяются с растущими интервалами — всего до трёх попыток в течение первого часа после срабатывания оповещения. Повторная попытка может прийти после доставки, которая завершилась тайм-аутом на нашей стороне, но была успешно обработана на вашей, поэтому сохраняйте id события и делайте обработчик идемпотентным.
Похожие руководства
Готовы начать разработку?
Создайте бесплатный аккаунт, чтобы получить ключ API, или изучите все эндпоинты в справочнике API.