Статус заказа и webhook-уведомления
Получайте результаты оплаты через подписанные webhook-уведомления или запрашивайте заказ по ID
Узнать результат оплаты можно двумя способами:
- Webhook-уведомления (рекомендуется): при каждой смене статуса мы отправляем подписанный
POST-запрос на ваш сервер. - Опрос: вы запрашиваете заказ по ID. Используйте его как резервный, а не основной канал.
Webhook-уведомления
Куда отправляются уведомления
Адрес для webhook-уведомлений определяется в следующем порядке:
callbackUrl, переданный при создании заказа: применяется только к этому заказу;- 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-документации (ссылка в верхнем меню).