Интеграция платежных систем — ключевой этап разработки любого коммерческого проекта. QIWI предоставляет мощные инструменты для приема платежей, но для надежной работы системы необходимо корректно реализовать проверку статуса транзакций. В этой статье подробно разберем, как проверить запрос к QIWI (статус счета/платежа) с использованием современного Bill Payments API, официальных SDK и механизма уведомлений (Webhooks).
Основные способы проверки статуса
Существует три основных подхода к получению информации о состоянии платежа:
- Прямой запрос к API (Pull-метод): Ваш сервер инициирует запрос к QIWI для получения актуального статуса счета по его идентификатору (
billId). - Официальные SDK: Использование библиотек для .NET, Python, PHP, Node.js, Go, Java, которые инкапсулируют работу с HTTP-запросами и парсингом ответов.
- Уведомления (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.
Использование официальных 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 для уведомлений»).
Как это работает:
- Пользователь оплачивает счет на стороне QIWI.
- QIWI отправляет POST запрос на ваш
Notification URLс телом в формате JSON. - Ваш сервер должен ответить 200 OK в течение 30 секунд (иначе QIWI будет повторять попытки).
- Вы парсите 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.
- Для разовых проверок (например, при нажатии пользователем кнопки «Проверить оплату») используйте метод GET /bills/{billId} через официальный SDK для вашего языка.
- Для автоматического обновления статусов в реальном времени настройте Webhooks (уведомления) и обязательно верифицируйте подпись
HMAC SHA256. - Всегда тестируйте в Sandbox перед деплоем.
Использование готовых SDK (доступных на GitHub QIWI-API) избавит вас от ручной работы с HTTP-заголовками, сериализацией JSON и парсингом дат, что значительно снизит количество багов в продакшене.