Как проверить статус платежа QIWI: API, SDK и Webhooks

Гайд по проверке статуса транзакций в QIWI: разбираем Bill Payments API, официальные SDK и настройку вебхуков для надежной интеграции.

Бесплатная консультация юриста

+7 (800) 301-07-30

Реклама jurik.ru

Интеграция платежных систем — ключевой этап разработки любого коммерческого проекта. QIWI предоставляет мощные инструменты для приема платежей, но для надежной работы системы необходимо корректно реализовать проверку статуса транзакций. В этой статье подробно разберем, как проверить запрос к QIWI (статус счета/платежа) с использованием современного Bill Payments API, официальных SDK и механизма уведомлений (Webhooks).

Основные способы проверки статуса

Существует три основных подхода к получению информации о состоянии платежа:

  1. Прямой запрос к API (Pull-метод): Ваш сервер инициирует запрос к QIWI для получения актуального статуса счета по его идентификатору (billId).
  2. Официальные SDK: Использование библиотек для .NET, Python, PHP, Node.js, Go, Java, которые инкапсулируют работу с HTTP-запросами и парсингом ответов.
  3. Уведомления (Webhooks / Push-метод): QIWI сам отправляет POST-запрос на ваш сервер при изменении статуса счета. Это наиболее эффективный способ для обновления статуса заказа в реальном времени.

Проверка через Bill Payments API (REST)

Современный протокол QIWI для выставления счетов — Bill Payments API. Устаревший REST-протокол (примеры 2013 года с w.qiwi.com/api/v2/prv/...) не рекомендуется для новых интеграций.

HTTP-запрос на получение информации о счете

GET https://api.qiwi.com/partner/bill/v1/bills/{billId}
Authorization: Bearer <SECRET_KEY>
Accept: application/json
  • billId — уникальный идентификатор счета в вашей системе (передавался при создании счета).
  • SECRET_KEY — секретный ключ API (выдается в личном кабинете разработчика developer.qiwi.com).

Пример успешного ответа (JSON)

{
 "billId": "cc961e8d-d4d6-4f02-b737-2297e5139211",
 "amount": {
 "value": "100.00",
 "currency": "RUB"
 },
 "status": {
 "value": "PAID",
 "changedDateTime": "2026-01-10T15:30:00+03:00"
 },
 "customer": {
 "phone": "###",
 "email": "user@example.com",
 "account": "user_12345"
 },
 "comment": "Оплата заказа №123",
 "creationDateTime": "2026-01-10T15:25:00+03:00",
 "expirationDateTime": "2026-01-12T15:25:00+03:00",
 "payUrl": "https://qiwi.com/payment/form/99... "
}

Ключевое поле: status.value

В уведомлениях и ответе API статус помещается в поле operation.status.value (в старых версиях) или status.value (в Bill Payments API). Основные значения:

  • WAITING — счет создан, ожидает оплаты.
  • PAID — счет оплачен (конечный успешный статус).
  • REJECTED, оплата отклонена (например, не прошла 3-D Secure).
  • EXPIRED — срок действия счета истек.
  • CANCELLED — счет отменен мерчантом через API.
Важно: Всегда опирайтесь на статус PAID для выдачи товара/услуги. Статус WAITING не гарантирует поступление средств.

Использование официальных SDK (Рекомендуемый способ)

Ручная сборка HTTP-запросов чревата ошибками. QIWI предоставляет SDK для основных языков. Репозитории доступны на GitHub: github.com/QIWI-API.

Пример для C# (.NET) — Bill Payments .NET SDK

Основано на типичных вопросах с Stack Overflow (использование BillPaymentsClient).

using Qiwi.BillPayments.Client;
using Qiwi.BillPayments.Model;
using System;

public class QiwiService
{
 private readonly BillPaymentsClient _client;

 public QiwiService(string secretKey)
 {
 // Инициализация клиента
 _client = BillPaymentsClientFactory.Create(secretKey);
 }

 // Создание счета (для контекста)
 public async Task CreateBillAsync(decimal amount, string billId, string comment)
 {
 var billInfo = new CreateBillInfo
 {
 BillId = billId, // Ваш уникальный ID (Guid)
 Amount = new MoneyAmount
 {
 ValueDecimal = amount,
 CurrencyEnum = CurrencyEnum.Rub
 },
 Comment = comment,
 ExpirationDateTime = DateTime.Now.AddDays(2),
 Customer = new Customer
 {
 Account = "user_123", // ID пользователя в вашей системе
 Email = "user@mail.ru",
 Phone = "###"
 }
 };
 return await _client.CreateBillAsync(billInfo);
 }

 // ПРОВЕРКА СТАТУСА СЧЕТА (GetBillInfo)
 public async Task CheckBillStatusAsync(string billId)
 {
 try
 {
 // Метод GetBillInfoAsync выполняет GET запрос к /partner/bill/v1/bills/{billId}
 BillResponse response = await _client.GetBillInfoAsync(billId);
 return response;
 }
 catch (Exception ex)
 {
 // Логирование ошибки (неверный ключ, не найден счет, сеть)
 Console.WriteLine($"Ошибка проверки счета {billId}: {ex.Message}");
 throw;
 }
 }
}

// Использование:
// var service = new QiwiService("ВАШ_SECRET_KEY");
// var bill = await service.CheckBillStatusAsync("cc961e8d-d4d6-4f02-b737-2297e5139211");
// if (bill.Status.Value == BillStatus.Paid) { /* Выдача товара */ }

Пример для Python — qiwi-bill-payments-sdk

Решает проблему «получаю только WAITING», так как SDK корректно парсит ответ.

from qiwi.bill_payments import BillPaymentsClient
from qiwi.bill_payments.models import MoneyAmount, CurrencyEnum
import asyncio

SECRET_KEY = "ВАШ_SECRET_KEY"
BILL_ID = "ваш_bill_id_здесь"

async def check_status:
 client = BillPaymentsClient(secret_key=SECRET_KEY)
 
 try:
 # Получение информации о счете
 bill = await client.check_bill(bill_id=BILL_ID)
 
 print(f"ID счета: {bill.bill_id}")
 print(f"Сумма: {bill.amount.value} {bill.amount.currency}")
 print(f"Статус: {bill.status.value}") # Enum: WAITING, PAID, REJECTED, EXPIRED
 print(f"Дата изменения: {bill.status.changed_date_time}")
 
 if bill.status.value == "PAID":
 print("Счет оплачен! Можно выдавать товар.")
 elif bill.status.value == "WAITING":
 print("Ожидает оплаты.")
  except Exception as e:
 print(f"Ошибка API: {e}")

asyncio.run(check_status)

Обработка уведомлений (Webhooks) — Best Practice

Опрос API (polling) создает нагрузку и задержки. Правильная архитектура подразумевает настройку Notification URL в личном кабинете QIWI (раздел «Настройки протокола» -> «URL для уведомлений»).

Как это работает:

  1. Пользователь оплачивает счет на стороне QIWI.
  2. QIWI отправляет POST запрос на ваш Notification URL с телом в формате JSON.
  3. Ваш сервер должен ответить 200 OK в течение 30 секунд (иначе QIWI будет повторять попытки).
  4. Вы парсите JSON, проверяете подпись (поле signature) и обновляете статус заказа в БД.

Структура уведомления (Bill Payments API)

{
 "bill": {
 "billId": "cc961e8d-d4d6-4f02-b737-2297e5139211",
 "amount": { "value": "100.00", "currency": "RUB" },
 "status": { "value": "PAID", "changedDateTime": "2026-01-10T15:30:00+03:00" },
 "siteId": "123456",
 "comment": "Заказ №123",
 "creationDateTime": "2026-01-10T15:25:00+03:00",
 "expirationDateTime": "2026-01-12T15:25:00+03:00"
 },
 "version": "1.0",
 "signature": "base64_encoded_hmac_sha256_signature" // Для верификации
}

Проверка подписи (PHP пример)

Обязательный шаг безопасности: убедиться, что уведомление пришло именно от QIWI.

<?php
$secretKey = 'ВАШ_SECRET_KEY';
$input = file_get_contents('php://input');
$data = json_decode($input, true);

if (!$data || !isset($data['signature'])) {
 http_response_code(400);
 exit('Invalid payload');
}

// QIWI отправляет подпись в заголовке X-Api-Signature-SHA256 или в теле (зависит от версии протокола)
// Для Bill Payments API подпись обычно в теле JSON в поле 'signature'
$signature = $data['signature'];
unset($data['signature']);

// Формируем строку для подписи: JSON без поля signature, без пробелов
$checkString = json_encode($data, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE);
$expectedSignature = base64_encode(hash_hmac('sha256', $checkString, $secretKey, true));

if (!hash_equals($expectedSignature, $signature)) {
 http_response_code(401);
 exit('Invalid signature');
}

// Подпись верна, обрабатываем статус
$billId = $data['bill']['billId'];
$status = $data['bill']['status']['value'];

if ($status === 'PAID') {
 // Обновляем заказ в БД
 updateOrderStatus($billId, 'paid');
}

http_response_code(200);
echo 'OK';
?>

Тестирование в Песочнице (Sandbox)

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

  • API Endpoint: https://api.qiwi.com/partner/bill/v1/bills/... (тот же, но ключ от песочницы).
  • Тестовые карты: Используйте номера тестовых карт (например, 4111 1111 1111 1111, любая будущая дата, любой CVC) для симуляции оплаты.
  • Получение токена: В песочнице выдается отдельный SECRET_KEY.

Чек-лист типичных ошибок

Проблема Причина / Решение
Всегда возвращается WAITING Проверяете не тот billId (используете внутренний ID заказа вместо ID счета QIWI). Убедитесь, что сохраняете billId, возвращенный при создании счета.
Ошибка 401 Unauthorized Неверный SECRET_KEY (путаете с Public Key), лишние пробелы в заголовке Authorization: Bearer <key>.
Ошибка 404 Not Found Счета с таким billId не существует (возможно, создавали в песочнице, а ищете в продакшне или наоборот).
Webhook не приходит Не настроен URL в личном кабинете.
Сервер не отвечает 200 OK (проверьте логи nginx/php).
Сервер недоступен извне (нет белого IP, блокирует фаервол).
Используете HTTP вместо HTTPS (QIWI требует HTTPS для продакшена).
Неверная подпись Webhook Считаете подпись от некорректной строки. Для Bill Payments API строка — это JSON всего тела запроса без поля signature, без лишних пробелов (compact JSON).

Проверка запроса к QIWI (статуса платежа) строится на использовании актуального Bill Payments API v1.

  1. Для разовых проверок (например, при нажатии пользователем кнопки «Проверить оплату») используйте метод GET /bills/{billId} через официальный SDK для вашего языка.
  2. Для автоматического обновления статусов в реальном времени настройте Webhooks (уведомления) и обязательно верифицируйте подпись HMAC SHA256.
  3. Всегда тестируйте в Sandbox перед деплоем.

Использование готовых SDK (доступных на GitHub QIWI-API) избавит вас от ручной работы с HTTP-заголовками, сериализацией JSON и парсингом дат, что значительно снизит количество багов в продакшене.