Payment Docs
Основы

Статус заказа и webhook-уведомления

Получайте результаты оплаты через подписанные webhook-уведомления или запрашивайте заказ по ID

Узнать результат оплаты можно двумя способами:

  1. Webhook-уведомления (рекомендуется): при каждой смене статуса мы отправляем подписанный POST-запрос на ваш сервер.
  2. Опрос: вы запрашиваете заказ по ID. Используйте его как резервный, а не основной канал.

Webhook-уведомления

Куда отправляются уведомления

Адрес для webhook-уведомлений определяется в следующем порядке:

  1. callbackUrl, переданный при создании заказа: применяется только к этому заказу;
  2. URL для webhook-уведомлений из настроек мерчанта (попросите вашего менеджера его указать).

Ваш эндпоинт должен принимать POST-запросы с телом в формате JSON и отвечать кодом HTTP 200.

Формат уведомления

POST {callbackUrl}
Content-Type: application/json
X-Signature: HMAC-SHA512(тело запроса, API-токен) в формате hex

Тело запроса — это объект заказа:

{
  "id": "a1b2c3d4-e5f6-7g8h-i9j0-k1l2m3n4o5p6",
  "status": "COMPLETED",
  "purpose": "Payment for order #1234",
  "amount": "1000.5",
  "commission": "1.5",
  "received": "999.0",
  "currency": "RUB",
  "paymentType": "SBP",
  "shopId": "a1b2c3d4-e5f6-7g8h-i9j0-k1l2m3n4o5p6",
  "terminalId": "a1b2c3d4-e5f6-7g8h-i9j0-k1l2m3n4o5p6",
  "merchantId": "a1b2c3d4-e5f6-7g8h-i9j0-k1l2m3n4o5p6",
  "externalId": "order_1234",
  "externalUserId": "user_987",
  "paymentLink": "https://example.com/payment?id=a1b2c3d4-e5f6-7g8h-i9j0-k1l2m3n4o5p6",
  "successUrl": "https://example.com/success",
  "payedAt": "2023-03-21T12:34:56Z",
  "updatedAt": "2023-03-21T12:34:56Z",
  "createdAt": "2023-03-21T12:34:56Z"
}

Проверка подписи

Каждое уведомление содержит заголовок X-Signature: HMAC-SHA512-хэш исходного тела запроса в формате hex, вычисленный с вашим API-токеном в качестве секрета. Отклоняйте запросы, подпись которых не совпадает.

import crypto from "crypto";

// Ваш API-токен используется как секрет для HMAC
const apiToken = process.env.API_TOKEN;

app.post("/webhooks/payments", (req, res) => {
  const signature = req.headers["x-signature"];
  const body = JSON.stringify(req.body);

  // Считаем HMAC-SHA512 от тела запроса и сравниваем в HEX
  const expected = crypto.createHmac("sha512", apiToken).update(body).digest("hex");

  if (expected !== signature) {
    return res.status(403).send("Invalid signature");
  }

  // Подпись верна — обрабатываем уведомление идемпотентно
  const data = req.body;
  if (data.status === "COMPLETED") {
    console.log(`Заказ ${data.externalId} оплачен`);
  }

  res.status(200).send("OK");
});

Рекомендации по работе с webhook-уведомлениями

  • Всегда отвечайте кодом HTTP 200, чтобы подтвердить получение. Если ваш сервер не отвечает, мы повторяем отправку уведомления.
  • Проверяйте заголовок X-Signature перед обработкой тела запроса.
  • Обрабатывайте уведомления идемпотентно: одно и то же уведомление может быть доставлено несколько раз.
  • Сопоставляйте уведомление с вашими записями по externalId, а не по сумме.
  • Обрабатывайте все возможные значения статусов, включая неуспешные.

Если на тот же эндпоинт приходят и уведомления по выплатам, различайте их по заголовку X-Type: PAYOUT_UPDATE, см. Webhook-уведомления по выплатам.

Запрос заказа по ID

GET /v1/orders/{orderId}

orderId — это id из ответа на создание заказа.

curl -X GET "https://api.riopay.online/v1/orders/a1b2c3d4-e5f6-7g8h-i9j0-k1l2m3n4o5p6" \
  -H "X-Api-Token: YOUR_API_TOKEN"

В ответе возвращается объект заказа.

Опрашивайте статус умеренно, например раз в несколько минут для заказов, по которым не пришло webhook-уведомление. Частый опрос не ускоряет оплату.

Статусы заказа

СтатусФинальныйОписание
CREATEDНетЗаказ создан, платёжная ссылка ещё не открыта
PENDINGНетОжидает оплаты
COMPLETEDДаПлатёж успешно завершён, средства получены
FAILEDУсловноОшибка оплаты
CANCELEDУсловноЗаказ отменён системой
EXPIREDУсловноСрок действия заказа истёк до оплаты
BLOCKEDУсловноТранзакция заблокирована банком
REFUNDДаПлатёж возвращён плательщику. Устанавливается после COMPLETED
CHARGEBACKДаПлатёж оспорен плательщиком и отменён банком. Устанавливается после COMPLETED

FAILED, CANCELED, EXPIRED и BLOCKED не являются строго финальными. Если банк позже подтверждает платёж из-за ошибки на своей стороне, заказ переходит в COMPLETED, и webhook-уведомление об этом отправляется автоматически. Будьте готовы получить COMPLETED по заказу, который вы уже отметили как неуспешный.

Только COMPLETED подтверждает, что средства поступили. Завершённый заказ может позже перейти в REFUND или CHARGEBACK, когда деньги возвращаются плательщику: обрабатывайте оба статуса как отмену платежа. FAILED, CANCELED, EXPIRED и BLOCKED — неуспешные исходы, но не строго финальные: если банк позже подтверждает платёж из-за ошибки на своей стороне, заказ переходит в COMPLETED, и вы автоматически получаете webhook-уведомление. Не используйте неуспешный заказ для повторной попытки: создайте новый.

Некоторые поля могут быть null в зависимости от стадии обработки. Актуальные схемы запросов и ответов всегда доступны в Swagger-документации (ссылка в верхнем меню).

Следующие шаги

На этой странице