Интеграция API для приема криптоплатежей: счета, подписи и вебхуки

Практическое руководство для разработчика: один подписанный запрос для создания счёта, один webhook-эндпоинт для получения статуса и детали безопасности, которые делают интеграцию надёжной.

Обновлено: 5 мин чтения
Интеграция API для приема криптоплатежей: подписанный POST-запрос создаёт счёт и ответ вебхука

Интеграция API для приема криптоплатежей состоит из двух частей: серверного запроса, который создаёт счёт для каждого заказа и возвращает ссылку на страницу оплаты, и webhook-эндпоинта, который получает подписанные обновления статуса, когда платёж обнаружен и подтверждён в блокчейне. В mistKET обе части — это обычный HTTPS и JSON с подписью HMAC-SHA256, без всякого SDK.

В этом руководстве весь процесс разобран с примерами кода, а затем — детали безопасности и надёжности, которые отличают демо от продакшена.

Архитектура крипто платежного API на одной схеме

Любая интеграция API для приема криптовалюты следует одному и тому же циклу из пяти шагов:

  1. Заказ создан — ваш бэкенд вызывает POST /api/v1/invoices с суммой, валютой, ID заказа и callback URL.
  2. Редирект — вы отправляете клиента на полученный checkout_url.
  3. Оплата — клиент выбирает сеть и платит на хостинговой странице; курс фиксируется на всё время жизни счёта.
  4. Вебхук — mistKET отправляет POST с invoice.paid, invoice.confirmed или событием-исключением на ваш callback URL.
  5. Исполнение — вы проверяете подпись, отмечаете заказ оплаченным и выполняете его.

API-ключ, API-секрет и секрет вебхука выдаются в панели мерчанта после одобрения аккаунта. Полная документация находится в панели в разделе API.

Шаг 1: подпишите и отправьте запрос на создание счёта

Каждый запрос к API содержит три заголовка. Подпись — это hex HMAC-SHA256 от строки {timestamp}.{raw body} с вашим API-секретом:

POST /api/v1/invoices
X-Api-Key: mp_live_…
X-Api-Timestamp: 1760000000
X-Api-Signature: hex(hmac_sha256("{ts}.{body}", secret))

{
  "amount": "184.50",
  "currency": "USD",
  "order_id": "2041",
  "callback_url": "https://yourstore.com/webhooks/mistket"
}

Минимальная версия на PHP выглядит так:

$body = json_encode([
  'amount' => '184.50', 'currency' => 'USD',
  'order_id' => '2041',
  'callback_url' => 'https://yourstore.com/webhooks/mistket',
]);
$ts  = (string) time();
$sig = hash_hmac('sha256', $ts . '.' . $body, $apiSecret);
// send $body with X-Api-Key, X-Api-Timestamp, X-Api-Signature

Подписывайте ровно те байты, которые отправляете. Повторная сериализация JSON после подписи (другие пробелы или порядок ключей) — причина ошибок подписи номер один.

Шаг 2: редирект на страницу оплаты

Успешный вызов возвращает 201 Created с UUID счёта, его статусом (new) и checkout_url. Сохраните UUID вместе с заказом и перенаправьте клиента. Хостинговая страница показывает точную сумму, QR-код, обратный отсчёт и статус в реальном времени и работает на мобильных. Клиент выбирает сеть там же — USDT в TRON, BNB Smart Chain, Ethereum или Arbitrum, а также TRX, BNB и ETH, — если только вы не зафиксировали сеть в API-запросе.

Помимо создания счетов, API позволяет получать их список, запрашивать отдельный счёт и отменять его, что полезно, когда корзина меняется или заказ брошен.

Шаг 3: приём и проверка вебхуков о криптоплатежах

Когда статус меняется, mistKET отправляет JSON на ваш callback URL со следующими заголовками:

ЗаголовокНазначение
X-Mistket-EventНазвание события, например invoice.confirmed
X-Mistket-TimestampUnix-время, используемое в подписи
X-Mistket-Signaturesha256= + HMAC-SHA256 от {timestamp}.{raw body} с секретом вебхука
X-Mistket-DeliveryID доставки, удобен для логирования

Проверка на PHP:

$raw = file_get_contents('php://input');
$ts  = $_SERVER['HTTP_X_MISTKET_TIMESTAMP'] ?? '';
$got = $_SERVER['HTTP_X_MISTKET_SIGNATURE'] ?? '';
$exp = 'sha256=' . hash_hmac('sha256', $ts . '.' . $raw, $webhookSecret);
if (!hash_equals($exp, $got) || abs(time() - (int) $ts) > 300) {
  http_response_code(401); exit;
}
$event = json_decode($raw, true);

Используйте сравнение за постоянное время (hash_equals в PHP, hmac.compare_digest в Python, crypto.timingSafeEqual в Node.js) и отклоняйте устаревшие метки времени, чтобы блокировать повторные отправки. Подробнее о самом примитиве — в статье HMAC в Википедии.

Главное: источник истины — вебхук, а не браузер клиента, вернувшийся на ваш сайт. Никогда не отмечайте заказ оплаченным без валидной подписи.

Пример тела вебхука

Подтверждённый платёж приходит компактным JSON-документом. Чаще всего вам понадобятся событие, ID заказа, статус, а также ожидаемая и полученная суммы:

POST https://yourstore.com/webhooks/mistket
X-Mistket-Event: invoice.confirmed
X-Mistket-Signature: sha256=…

{ "event": "invoice.confirmed", "data": {
  "order_id": "2041", "status": "confirmed",
  "pay_amount": "184.5", "paid_amount": "184.5",
  "pay_currency": "USDT", "network": "TRC20" } }

Сохраняйте pay_currency и network вместе с заказом. Они пригодятся для бухгалтерии и для общения с поддержкой в будущем.

Проверка в Node.js и Python

Та же проверка на двух других популярных языках:

// Node.js (Express with express.raw for this route)
const exp = 'sha256=' + crypto.createHmac('sha256', secret)
  .update(ts + '.' + rawBody).digest('hex');
const ok = crypto.timingSafeEqual(Buffer.from(exp), Buffer.from(sig));

# Python
exp = 'sha256=' + hmac.new(secret.encode(), f'{ts}.{raw}'.encode(),
                           hashlib.sha256).hexdigest()
ok = hmac.compare_digest(exp, sig)

В обоих случаях обязательно читайте сырое тело запроса. Многие фреймворки автоматически парсят JSON; после парсинга и повторной сериализации байты уже не совпадают с подписью.

Локальное тестирование

Вебхукам нужен публичный HTTPS URL, поэтому локальный ноутбук не может получать их напрямую. Два практичных варианта: заранее развернуть обработчик на staging-сервере или временно открыть локальный порт через туннелирующий инструмент. Создайте небольшие счета с callback на staging, затем изучите логи доставки в панели мерчанта, чтобы увидеть, что именно было отправлено и что ответил ваш сервер.

Постарайтесь во время тестирования хотя бы раз вызвать каждый тип события: paid, confirmed, underpaid и expired. Когда каждая ветка обработчика отработала на реальном вебхуке, в продакшене вас мало что удивит, а вы будете точно знать, как выглядят логи доставки, если что-то пойдёт не так.

Шаг 4: обработка каждого статуса

Тело вебхука содержит ID заказа, статус, ожидаемую сумму (pay_amount), полученную сумму (paid_amount), валюту и сеть. Сопоставьте каждому событию действие:

СобытиеЗначениеТипичное действие
invoice.paidПеревод обнаружен, подтверждения в процессеПоказать «платёж получен, подтверждается»
invoice.confirmedНужное число подтверждений достигнутоОтметить оплаченным и выполнить заказ
invoice.underpaidНиже вашего допускаЖдать доплаты или связаться с клиентом
invoice.overpaidБольше ожидаемогоВыполнить заказ; решить, что делать с разницей
invoice.expiredНет валидного платежа вовремяОсвободить товар, оставить заказ неоплаченным

Допуск недоплаты, частичные платежи, которые в сумме дают итог, и поздние платежи в пределах льготного окна обрабатывает mistKET; ваш код реагирует только на конечный результат.

Надёжность: повторы и идемпотентность

Если ваш эндпоинт недоступен или возвращает ошибку, вебхуки отправляются повторно с нарастающей задержкой в течение нескольких часов. Значит, иногда вы получите одно и то же событие дважды. Сделайте обработчик идемпотентным по ID заказа:

  • Быстро отвечайте 2xx, а тяжёлую работу выполняйте в очереди.
  • Перед исполнением проверяйте, не отмечен ли заказ уже оплаченным.
  • Логируйте ID доставки и событие, чтобы отслеживать проблемы; в панели также есть логи доставки вебхуков.

Чек-лист безопасности

Интеграция API для приема криптоплатежей работает с деньгами, поэтому относитесь к ней как к любому другому платёжному коду:

  • Храните API-секрет и секрет вебхука только на сервере — никогда во фронтенд-коде или мобильных приложениях.
  • Ограничьте доступ к API списком разрешённых IP в панели.
  • Отклоняйте запросы вне короткого окна по метке времени.
  • Настройте срок действия счёта в соответствии с логикой резервирования товара.
  • Храните суммы как строки или decimal, никогда как float.
Совет: перед запуском оплатите небольшой счёт сами, намеренно отправьте неполную сумму, чтобы увидеть сценарий недоплаты, и дайте одному счёту истечь. Вы проверите все ветки меньше чем за час.

Что дальше

Вот и вся интеграция API для приема криптоплатежей: один подписанный запрос, один проверенный вебхук и карта статусов. Тот же процесс с точки зрения не-разработчика описан в статье как принимать криптовалюту на сайте. Делаете бота, а не сайт? Читайте приём криптоплатежей в Telegram-боте. Запросите API-ключи, подав заявку через Telegram @mistnetwork, посмотрите пример в разделе для разработчиков и управляйте ключами через вход для мерчантов.

Частые вопросы

Нужен ли SDK для интеграции крипто платежного API?
С mistKET — нет. Используется обычный HTTPS и JSON; интегрироваться можно на любом языке, который умеет делать HTTP-запросы и считать HMAC-SHA256.
Как аутентифицируются запросы к API mistKET?
Каждый запрос отправляет X-Api-Key, X-Api-Timestamp и X-Api-Signature, где подпись — hex HMAC-SHA256 от '{timestamp}.{raw body}' с вашим API-секретом.
Как проверить вебхук о криптоплатеже?
Пересчитайте HMAC-SHA256 от '{timestamp}.{raw body}' с секретом вебхука, сравните с X-Mistket-Signature за постоянное время и отклоняйте старые метки времени.
Что будет, если мой сервер пропустит вебхук?
Вебхуки повторяются с нарастающей задержкой в течение нескольких часов, а статус счёта можно также получить через API. Сделайте обработчик идемпотентным по ID заказа.
Можно ли задать конкретную сеть через API?
Да. Сеть можно зафиксировать в API-запросе; иначе клиент выбирает её на странице оплаты.
Какие события должны запускать исполнение заказа?
Исполняйте заказ по invoice.confirmed. invoice.paid используйте только для отображения прогресса, а underpaid, overpaid и expired обрабатывайте согласно своей политике.

Похожие статьи