Условия использования
Для подключения на свой сайт сервиса приема переводов для физических лиц p2p необходимо иметь QIWI Кошелек со статусом идентификации «Основной» или «Профессиональный». Если Ваш кошелек имеет статус «Анонимный» – пройдите идентификацию удобным для вас способом. Для получения «Основного» статуса достаточно указать паспортные данные, для получения «Профессионального» статуса необходимо пройти очную идентификацию.
Рекомендуем получить «Профессиональный» статус, т.к. такой статус имеет повышенные лимиты на остаток на балансе, сумму платежей и переводов в месяц, максимальную сумму одной операции – подробнее про лимиты.
Рекомендуем ознакомиться с частыми вопросами по нашему сервису, а также с информацией о том, как избежать блокировки кошелька.
Активация p2p
- Авторизуйтесь на p2p.qiwi.com
- Убедитесь, что вам доступно выставление счетов – в форме Выставить счет заполните поле Сумма и нажмите на кнопку. Ниже должна появиться ссылка на счет и кнопка Скопировать ссылку.
Поздравляем! Вы можете приступить к интеграции.
Персонализация формы
Вы можете настроить персонализированную форму оплаты – изменить свое имя на название магазина и настроить цвет фона и кнопок. Перейдите в личном кабинете в раздел Форма приема переводов, нажмите на кнопку Настроить, произведите настройку и нажмите на кнопку Сохранить.
При использовании API или SDK Вам необходимо будет передавать в параметр customFields переменную themeCode
, значение которой у вас отображается в личном кабинете – подробнее на шаге 4.
Обратите внимание, что значение themeCode
индивидуально для разных кошельков.
Процесс интеграции через SDK
Ознакомьтесь с нашей документацией.
Шаг 1. Подготовка среды разработки
-
Выберите SDK для вашего языка программирования и перейдите в репозиторий на GitHub:
//Установка с помощью npm
$ npm install @qiwi/bill-payments-node-js-sdk --save
//Установка с помощью composer
$ composer require qiwi/bill-payments-php-sdk
//Установка с помощью maven
<dependency>
<groupId>com.qiwi</groupId>
<artifactId>bill-payments-java-sdk</artifactId>
<version>1.5.0</version>
</dependency>
//Установка с помощью Nuget
nuget install Qiwi.BillPayments
- Ознакомьтесь с файлом README.md
-
Установите SDK >
Обратите внимание, что в зависимости от выбранного вами SDK потребуется установка Composer, Apache Maven или NuGet.
Шаг 2. Создание секретного ключа
-
Перейдите на вкладку API.
-
Нажмите на кнопку Настроить и придумайте название, по которому потом сможете найти нужный API-ключ.
Рекомендуем подключить уведомления об оплате, отметив чекбокс Использовать эту пару ключей для серверных уведомлений об изменении статусов счетов. После настройки серверных уведомлений вы сможете узнавать, когда выставленные счета оплачены, и автоматически реагировать на оплату счетов (пополнять баланс, отгружать товар, давать доступ к контенту). Подробнее об уведомлениях – в документации.
В поле URL сервера для уведомлений укажите адрес вашего сервера для обработки уведомлений об оплате.
-
Нажмите Создать и получите ключи авторизации.
-
Скопируйте себе секретный ключ (в оранжевом блоке), нажав на ссылку Скопировать в буфер. Рекомендуем не закрывать данное окно и не нажимать на кнопку Дальше, пока не настроите авторизацию.
Шаг 3. Авторизация
const QiwiBillPaymentsAPI = require('@qiwi/bill-payments-node-js-sdk');
const SECRET_KEY = 'eyJ2ZXJzaW9uIjoicmVzdF92MyIsImRhdGEiOnsibWVyY2hhbnRfaWQiOjUyNjgxMiwiYXBpX3VzZXJfaWQiOjcxNjI2MTk3LCJzZWNyZXQiOiJmZjBiZmJiM2UxYzc0MjY3YjIyZDIzOGYzMDBkNDhlYjhiNTnONPININONPN090MTg5Z**********************=';
const qiwiApi = new QiwiBillPaymentsAPI(SECRET_KEY);
<?php
const SECRET_KEY = 'eyJ2ZXJzaW9uIjoicmVzdF92MyIsImRhdGEiOnsibWVyY2hhbnRfaWQiOjUyNjgxMiwiYXBpX3VzZXJfaWQiOjcxNjI2MTk3LCJzZWNyZXQiOiJmZjBiZmJiM2UxYzc0MjY3YjIyZDIzOGYzMDBkNDhlYjhiNTnONPININONPN090MTg5Z**********************=';
$billPayments = new Qiwi\Api\BillPayments(SECRET_KEY);
?>
String secretKey = "eyJ2ZXJzaW9uIjoicmVzdF92MyIsImRhdGEiOnsibWVyY2hhbnRfaWQiOjUyNjgxMiwiYXBpX3VzZXJfaWQiOjcxNjI2MTk3LCJzZWNyZXQiOiJmZjBiZmJiM2UxYzc0MjY3YjIyZDIzOGYzMDBkNDhlYjhiNTnONPININONPN090MTg5Z**********************=";
BillPaymentClient client = BillPaymentClientFactory.createDefault(secretKey);
var client = BillPaymentClientFactory.Create(
secretKey: "eyJ2ZXJzaW9uIjoicmVzdF92MyIsImRhdGEiOnsibWVyY2hhbnRfaWQiOjUyNjgxMiwiYXBpX3VzZXJfaWQiOjcxNjI2MTk3LCJzZWNyZXQiOiJmZjBiZmJiM2UxYzc0MjY3YjIyZDIzOGYzMDBkNDhlYjhiNTnONPININONPN090MTg5Z**********************="
);
В разрабатываемом коде заведите константу SECRET_KEY и присвойте ей значение секретного ключа, вставив его из буфера обмена комбинацией клавиш Ctrl+V.
Проверьте, что ключ скопирован полностью, в конце должен быть знак "равно" =
.
Раздел документации с примерами авторизации находится здесь.
Если возвращается ошибка авторизации HTTP/1.1 401 Unauthorized
, это значит что-то не так с секретным ключом. Выпустите новый ключ и скопируйте его полностью, в конце должен быть знак "равно" =
.
Шаг 4. Выставление счета
Реализуйте вызов метода createBill
.
Пример генерации
billId
<?php
/** @var \Qiwi\Api\BillPayments $billPayments */
$billId = $billPayments->generateId();
//e9b47ee9-b2f9-4b45-9438-52370670e2a6
?>
const billId = qiwiApi.generateId();
//e9b47ee9-b2f9-4b45-9438-52370670e2a6
Примеры генерации
expirationDateTime
. Входной параметр - сколько дней счёт будет доступен, по умолчанию 45 дней. Метод возвращает строку в формате ISO 8601 UTC±0:00
<?php
//now: 2021-01-28T17:16:58.033Z
/** @var \Qiwi\Api\BillPayments $billPayments */
$lifetime = $billPayments->getLifetimeByDay(1);
//2021-01-29T17:16:58.033Z
?>
<?php
//now: 2021-01-28T17:16:58.033Z
/** @var \Qiwi\Api\BillPayments $billPayments */
$lifetime = $billPayments->getLifetimeByDay(0.5);
//2021-01-29T05:16:58.033Z
?>
//now: 2021-01-28T17:16:58.033Z
const lifetime = qiwiApi.getLifetimeByDay(1);
//2021-01-29T17:16:58.033Z
//now: 2021-01-28T17:16:58.033Z
const lifetime = qiwiApi.getLifetimeByDay(0.5);
//2021-01-29T05:16:58.033Z
Запрос создания счета
const billId = 'cc961e8d-d4d6-4f02-b737-2297e51fb48e';
const fields = {
amount: 1.00,
currency: 'RUB',
comment: 'Hello world',
expirationDateTime: '2020-08-28T19:44:07',
email: 'example@mail.org',
account : 'client4563',
customFields : {themeCode: 'кодСтиля'},
successUrl: 'http://test.ru/'
};
qiwiApi.createBill( billId, fields ).then( data => {
//do with data
});
<?php
$billId = 'cc961e8d-d4d6-4f02-b737-2297e51fb48e';
$customFields = ['themeCode' => 'кодСтиля'];
$fields = [
'amount' => 1.00,
'currency' => 'RUB',
'comment' => 'Hello world',
'expirationDateTime' => '2020-08-28T19:44:07+03:00',
'email' => 'example@mail.org',
'account' => 'client4563',
'successUrl' => 'http://test.ru/',
'customFields' => $customFields,
];
/** @var \Qiwi\Api\BillPayments $billPayments */
$response = $billPayments->createBill($billId, $fields);
print_r($response);
?>
CreateBillInfo billInfo = new CreateBillInfo(
UUID.randomUUID().toString(),
new MoneyAmount(
BigDecimal.valueOf(199.90),
Currency.getInstance("RUB")
),
"Hello world",
ZonedDateTime.now().plusDays(45),
new Customer(
"example@mail.org",
UUID.randomUUID().toString(),
"79123456789"
),
"http://merchant.ru/success"
);
BillResponse response = client.createBill(billInfo);
client.CreateBill(
info: new CreateBillInfo
{
BillId = Guid.NewGuid().ToString(),
Amount = new MoneyAmount
{
ValueDecimal = 199.9m,
CurrencyEnum = CurrencyEnum.Rub
},
Comment = "Hello world",
ExpirationDateTime = DateTime.Now.AddDays(45),
Customer = new Customer
{
Email = "example@mail.org",
Account = Guid.NewGuid().ToString(),
Phone = "79123456789"
},
SuccessUrl = new Uri("http://merchant.ru/success")
CustomFields: new CustomFields
{
ThemeCode = "Ivan-VbRVlW-YGL"
}
},
);
curl --location --request PUT 'https://api.qiwi.com/partner/bill/v1/bills/cc961e8d-d4d6-4f02-b737-2297e51fb48e' \
--header 'content-type: application/json' \
--header 'accept: application/json' \
--header 'Authorization: Bearer eyJ2ZXJzaW9uIjoicmVzdF92MyIsImRhdGEiOnsibWVyY2hhbnRfaWQiOjIwNDIsImFwaV91c2VyX2lkIjo1NjYwMzk3Miwic2VjcmV0IjoiQjIwODlDNkI5Q0NDNTdCNDQzNGHJK43JFJDK595FJFJMjlCRkFFRDM5OE***********************' \
--data-raw '{
"amount": {
"currency": "RUB",
"value": "1.00"
},
"comment": "Text comment",
"expirationDateTime": "2025-12-10T09:02:00+03:00",
"customer": {
"phone": "78710009999",
"email": "test@tester.com",
"account": "454678"
},
"customFields" : {
"paySourcesFilter":"qw",
"themeCode": "Yvan-YKaSh",
"yourParam1": "64728940",
"yourParam2": "order 678"
}
}'
Обязательные параметры:
-
billId – сгенерированный вами идентификатор счета. Он должен быть уникальным и генерироваться на вашей стороне любым способом. Идентификатором может быть любая уникальная последовательность букв или цифр, также разрешено использование символов подчеркивания (
_
) и тире (–
).В некоторых SDK предусмотрен вспомогательный метод
generateId
, позволяющий генерировать уникальные идентификаторы. - amount – информация о сумме перевода:
- amount.value – сумма счёта;
- amount.currency – валюта счёта:
- RUB;
- KZT;
-
expirationDateTime – срок оплаты счёта в формате
ГГГГ-ММ-ДДTчч:мм:сс±чч:мм
, например,2020-11-05T11:27:41+03:00
.В некоторых SDK предусмотрен вспомогательный метод
getLifetimeByDay
, позволяющий генерировать дату.
Рекомендуем использовать дополнительные параметры:
- comment – комментарий к платежу
- customFields.paySourcesFilter – настроить способы оплаты счёта. При открытии формы будут отображаться только указанные способы перевода (один или несколько), если они доступны.
Возможные значения:
- qw – QIWI Кошелек;
- card – банковская карта.
- customFields.themeCode – применить настроенную персонализацию платежной формы. Значение themeCode вы можете найти в личном кабинете в разделе Форма приема переводов в сером блоке – см. скриншот. Обратите внимание, что значение themeCode индивидуально для разных кошельков.
Необязательные параметры для выставления счета:
- customer – информация о пользователе с вашего сайта, обычно используется если на вашем сайте есть профиль пользователя:
- customer.phone – номер телефона пользователя в международном формате;
- customer.email – e-mail пользователя;
- customer.account – идентификатор пользователя в вашей системе;
- customFields – дополнительные данные, необходимые вашей системе, передаются на ваше усмотрение, например:
- customFields.yourParam1 - любое значение, которое вы хотите получать в уведомлении или при запросе информации о счёте.
Ответ
{
"siteId": "270305",
"billId": "cc961e8d-d4d6-4f02-b737-2297e51fb48e",
"amount": {
"currency": "RUB",
"value": "1.00"
},
"status": {
"value": "WAITING",
"changedDateTime": "2020-07-28T19:44:07.855+03:00"
},
"comment": "Hello world",
"customFields": {
"themeCode": "кодСтиля",
},
"creationDateTime": "2020-07-28T19:44:07.855+03:00",
"expirationDateTime": "2020-08-28T19:44:07.855+03:00",
"payUrl": "https://oplata.qiwi.com/form/?invoice_uid=bb773791-9bd9-42c1-b8fc-3358cd108422&successUrl=http%3A%2F%2Ftest.ru%2F"
}
Array
(
[siteId] => 270305
[billId] => cc961e8d-d4d6-4f02-b737-2297e51fb48e
[amount] => Array
(
[currency] => RUB
[value] => 1.00
)
[status] => Array
(
[value] => WAITING
[changedDateTime] => 2020-07-28T19:44:38.855+03:00
)
[comment] => Hello world
[customFields] => Array
(
[themeCode] => кодТемы
)
[creationDateTime] => 2020-07-28T19:44:07.855+03:00
[expirationDateTime] => 2020-08-28T19:44:07.855+03:00
[payUrl] => https://oplata.qiwi.com/form/?invoice_uid=bb773791-9bd9-42c1-b8fc-3358cd108422&successUrl=http%3A%2F%2Ftest.ru%2F
)
{
"siteId": "270304",
"billId": "cc961e8d-d4d6-4f02-b737-2297e51fb48e",
"amount": {
"value": "199.90",
"currency": "RUB"
},
"status": {
"value": "WAITING",
"changedDateTime": "2020-08-28T19:44:51.407Z"
},
"comment": "Hello world",
"customer": {
"email": "example@mail.org",
"account": "040c3bb8-b207-4ecc-9ff9-90168d3bc34f",
"phone": "79123456789"
},
"creationDateTime": "2020-07-28T19:49:07.407Z",
"expirationDateTime": "2020-09-10T19:49:07.407Z",
"payUrl": "https://oplata.qiwi.com/form/?invoice_uid=c77a9051-1467-416b-991e-c25f06c61168&successUrl=http%3A%2F%2Fmerchant.ru%2Fsuccess",
"customFields": {
"apiClient": "java_sdk",
"apiClientVersion": "1.0.0"
}
}
new BillResponse
{
SiteId = "270304",
BillId = "cc961e8d-d4d6-4f02-b737-2297e51fb48e",
Amount = new MoneyAmount
{
ValueString = "199.90",
CurrencyString = "RUB"
},
Status = new ResponseStatus
{
ValueString: "WAITING",
ChangedDateTime: BillPaymentsUtils.ParseDate("2020-07-28T19:44:07+03:00")
},
Comment = "Hello world",
Customer = new Customer
{
Email = "example@mail.org",
Account = "040c3bb8-b207-4ecc-9ff9-90168d3bc34f",
Phone = "79123456789"
},
CreationDateTime = BillPaymentsUtils.ParseDate("2020-07-28T19:44:07+03:00"),
ExpirationDateTime = BillPaymentsUtils.ParseDate("2020-09-10T19:44:07+03:00"),
PayUrl = new Uri("https://oplata.qiwi.com/form/?invoice_uid=c77a9051-1467-416b-991e-c25f06c61168&successUrl=http%3A%2F%2Fmerchant.ru%2Fsuccess"),
CustomFields = new CustomFields
{
ApiClient = "dotnet_sdk",
ApiClientVersion = "0.1.0",
ThemeCode = "Ivan-VbRVlW-YGL"
}
};
{
"siteId": "9hh4jb-00",
"billId": "cc961e8d-d4d6-4f02-b737-2297e51fb48e",
"amount": {
"currency": "RUB",
"value": "1.00"
},
"status": {
"value": "WAITING",
"changedDateTime": "2021-01-18T14:22:56.672+03:00"
},
"customer": {
"phone": "78710009999",
"email": "test@tester.com",
"account": "454678"
},
"customFields": {
"paySourcesFilter": "qw",
"themeCode": "Yvan-YKaSh",
"yourParam1": "64728940",
"yourParam2": "order 678"
},
"comment": "Text comment",
"creationDateTime": "2021-01-18T14:22:56.672+03:00",
"expirationDateTime": "2025-12-10T09:02:00+03:00",
"payUrl": "https://oplata.qiwi.com/form/?invoice_uid=aa0fa2bb-5452-47ca-9190-cd9c1a73718f"
}
В ответе на запрос выставления счета возвращаются следующие поля:
- siteId – ваш идентификатор в системе p2p QIWI;
- billId – идентификатор счёта, указанный при его создании;
- amount – информация о сумме перевода:
- amount.value – сумма счёта;
- amount.currency – валюта счёта, RUB или KZT;
- status – данные о статусе счёта:
- status.value – текущий статус счёта;
- status.changedDateTime – дата обновления статуса.
Формат даты:ГГГГ-ММ-ДДTчч:мм:сс±чч:мм
- customer – информация о пользователе с вашего сайта, обычно используется если на вашем сайте есть профиль пользователя:
- customer.phone – номер телефона пользователя в международном формате;
- customer.email – e-mail пользователя;
- customer.account – идентификатор пользователя в вашей системе;
- customFields – объект строковых дополнительных параметров:
- customFields.paySourcesFilter – доступные способы оплаты счёта:
- qw – QIWI Кошелек;
- card – банковская карта;
- customFields.paySourcesFilter – доступные способы оплаты счёта:
- customFields.themeCode – код темы платежной формы;
- comment – комментарий к платежу;
- creationDateTime – системная дата создания счета.
Формат даты:ГГГГ-ММ-ДДTчч:мм:сс±чч:мм
; - expirationDateTime – срок действия созданной формы для перевода.
Формат даты:ГГГГ-ММ-ДДTчч:мм:сс±чч:мм
; - payUrl – ссылка на форму оплаты счета.
Если на запрос создания счёта вернулась ошибка - проверьте формат переданных в запрос параметров. Если не получается разобраться самостоятельно - опишите ситуацию и отправьте нам traceId
из ответа в Telegram-бот.
Для тестирования и отладки сервиса рекомендуем выставлять и оплачивать счета суммой 1 рубль.
Раздел документации с примерами выставления счетов находится здесь.
Шаг 5. Установка дополнительных параметров к ссылке на счёт
https://oplata.qiwi.com/form/?invoice_uid=aa0fa2bb-5452-47ca-9190-cd9c1a73718f&successUrl=qiwi.com&paySource=qw
При выставлении счета через API в ответе приходит payUrl, содержащий ссылку на форму. К данной ссылке можно добавить следующие параметры:
- successUrl – ссылка на страницу вашего сайта для редиректа после успешной оплаты счета;
- paySource – установить рекомендуемый способ оплаты, при открытии формы сразу будет выбран указанный способ перевода.
Возможные значения:
- qw - QIWI Кошелек;
- card - банковская карта;
- mobile - платеж со счета мобильного телефона.
Шаг 6. Редирект пользователя на платежную форму
Пример получения URL оплаты по счету
<?php
/** @var array $bill */
/** @var \Qiwi\Api\BillPayments $billPayments */
$payUrl = $billPayments->getPayUrl($bill, 'http://test.ru/');
// https://oplata.qiwi.com/form/?invoice_uid=d875277b-6f0f-445d-8a83-f62c7c07be77&successUrl=http%3A%2F%2Ftest.ru%2F
?>
Реализуйте на вашем сайте перенаправление пользователя на платежную форму по ссылке, полученной в ответе на запрос выставления счёта в параметре payUrl
или по ссылке с дополнительными параметрами, сформированной на шаге 5.
Добавьте реферальные ссылки для платежей с сайта. Полная ссылка подтвердит его реальность и позволит избежать проблем с блокировкой кошелька.
Пример передачи реферальной ссылки
<?php header("Referrer-Policy: no-referrer-when-downgrade"); ?>
Для того, чтобы в новых версиях браузеров передавался полный referer при переходе, необходимо выставить дополнительный заголовок Referrer-Policy
в ответе сервера.
Это можно сделать для всего сайта или отдельно на той странице, где отрисовывается ссылка на форму оплаты.
Значение заголовка должно быть no-referrer-when-downgrade
.
Шаг 7. Получение серверных уведомлений об оплате счетов или опрос статуса счета - что выбрать?
Сервис уведомлений позволяет понять, когда и по какому счету произошла оплата. Вам не нужно каждый раз отправлять запросы в QIWI, можно подключить сервис и получать уведомления автоматически. Но стоит обратить внимание на то, что уведомление может быть отправлено несколько раз даже в случае успешного ответа от вашего сервиса, поэтому это необходимо учитывать при разработке бизнес-логики отгрузки товара или услуги на вашей стороне.
Формат уведомлений описан в документации.
Данные приходят в теле запроса (body). Данные запроса хранятся в формате JSON.
Так как для отладки уведомлений тестовый контур не предусмотрен, рекомендуем выставлять счета на 1 рубль и оплачивать их самостоятельно.
Сервис уведомлений не является обязательным для интеграции, вы можете реализовать более простой вариант с опросом статуса счета. В этом случае перейдите сразу к шагу 11.
Условия интеграции с API уведомлений
Если же вы пока не можете определиться, тогда ознакомьтесь с условиями обслуживания интеграции с API уведомлений:
-
Мы устанавливаем на своей стороне таймаут на установление соединения – 2 секунды, и таймаут на получение ответа – также 2 секунды. Поэтому не рекомендуем внедрять долго выполняющуюся логику или логику, связанную с ожиданием (waiter, sleep), в процесс обработки уведомления.
Рекомендуем на стороне вашего сервера организовывать очередь и складывать в нее входящие запросы от QIWI с изменением статусов счетов, после чего отвечать QIWI на уведомление успешным статусом.
Обработку уведомлений в очереди и свою бизнес-логику стоит проводить в потоках, отдельных от приема уведомлений, чтобы успевать отвечать нам вовремя.
- Если в течение двух секунд мы не получаем ответ, то считаем отправку уведомления неуспешной, и будем его повторять в течение суток. Успешно доставленным уведомлением считается уведомление, на которое получен ответ со статусом 2ХХ ОК.
- Эта же механика работает, если ваш сервер для приема уведомлений недоступен. Мы будем пытаться отправить уведомление в течение суток, после чего данное уведомление получить будет невозможно.
-
Мы не можем гарантировать exactly once доставку уведомления, т.е. вам может прийти более одного уведомления об одной и той же успешной оплате счёта.
Чтобы обработать такие ситуации, вам необходимо проверять на своей стороне, присылали ли мы уже уведомление по оплате данного счета и была ли произведена отгрузка товара/услуги по этому счету вашему клиенту.
- В связи с неравномерностью нагрузки задержка уведомлений может быть до 1 дня (а в случае нештатной ситуации – до суток).
Если вы выберете механизм уведомлений для интеграции и получения сообщений об успешных платежах, мы все же рекомендуем использовать в дополнение к нему опрос статуса счета.
Шаг 8*. Настройка серверных уведомлений
Пример уведомления
POST /qiwi-notify.php HTTP/1.1
Accept: application/json
Content-type: application/json
X-Api-Signature-SHA256: J4WNfNZd***V5mv2w=
Host: server.ru
{ "bill":
{
"siteId":"23044",
"billId":"1519892138404fhr7i272a2",
"amount":{
"value":"100",
"currency":"RUB"
},
"status":{
"value":"PAID",
"datetime":"2018-03-01T11:16:12"
},
"customer":{},
"customFields":{},
"creationDateTime":"2018-03-01T11:15:39",
"expirationDateTime":"2018-04-01T11:15:39+03:00"
},
"version":"1"
}
Пример получения уведомления
<?php
$log_file = fopen(__DIR__ . '/qiwi.txt', 'a+');
fwrite($log_file, print_r(json_decode(file_get_contents('php://input')), true).PHP_EOL);
fwrite($log_file, print_r(getallheaders(), true).PHP_EOL);
fclose($log_file);
?>
Уведомление представляет собой входящий POST-запрос.
Тело запроса содержит JSON-сериализованные данные счета (кодировка UTF-8).
-
Проверьте, что IP-адреса, с которых QIWI отправляет уведомления, находятся в списке разрешенных:
- 79.142.16.0/20
- 195.189.100.0/22
- 91.232.230.0/23
- 91.213.51.0/24
- Убедитесь, что адрес сервера для приема уведомлений, указанный при создании ключей в личном кабинете, доступен из внешней сети (не только с вашего компьютера).
- Проверьте что ожидаете POST-запрос, для начала попробуйте логировать уведомления в файл на сервере.
- После успешного получения уведомления на стороне вашего сервера организуйте очередь и складывайте в нее входящие запросы от QIWI с изменением статусов счетов.
Если не приходят уведомления по счету:
- Проверьте заголовки. По стандарту они регистронезависимы и ваш клиент может их изменить – смотрите документацию вашего клиента.
-
Попробуйте отправить себе уведомление, заменив в примере
https://test.com/notif.php
на URL, который вы указали при создании ключей для получения уведомлений.Пример:
curl --location --request POST 'https://test.com/notif.php' --header 'Accept: application/json' --header 'Content-type: application/json' --header 'Cookie: laravel_session=qePlTipWuv7zuRn0WTp9StDVFv2nAREe30gte7zk' --data-raw '{"bill":{"siteId":"2304","billId":"151989213804fhr7i272a2","amount":{"value":"1","currency":"RUB"},"status":{"value":"PAID","datetime":"2020-07-28T11:16:12"},"customer":{},"customFields":{},"creationDateTime":"2020-07-28T11:15:39","expirationDateTime":"2020-08-28T11:15:39+03:00"},"version":"1"}'
- Если вы используете сторонние сервисы, создающие готовые эндпоинты для приема запросов по https (например, Google Apps Scripts), убедитесь, что можно отправлять запросы в ваш сервис без дополнительной авторизации (в том числе анонимным пользователям).
-
Если вы не используете облачные сервисы для приема уведомлений вроде amazon или heroku, а настраивали свой вебсервер и ssl в нем самостоятельно, проверьте ssl сертификат.
Если ssl сертификат для установления соединения по https выпущен не общеизвестным доверенным центром сертификации (например, Comodo, Verisign, Thawte и т.п.), проверьте, что ваш сервер при установке соединения отправляет полную цепочку сертификатов, включая доверенный корневой центр сертификации в начале цепочки.
Убедиться в том, что сервер присылает полную цепочку сертификатов, можно при помощи утилиты командной строки
openssl
, пример есть здесь.Полную цепочку сертификатов вы можете загрузить на сайте центра сертификации, выпустившего ваш сертификат.
- Если не получается разобраться самостоятельно – отправьте нам ссылку на счет или номер счета в Telegram-бот.
Шаг 9*. Проверка подписи уведомлений
Настоятельно рекомендуем проверять уведомления на подлинность, т.к. мы не несем ответственность, если будут приходить поддельные уведомления от мошенников.
Формат уведомлений описан в документации.
Данные приходят в теле запроса (body). Данные запроса хранятся в формате JSON.
Для проверки подлинности используется механизм цифровой подписи. Подпись уведомления отправляется в HTTP-заголовке X-Api-Signature-SHA256
. Для формирования подписи используется механизм проверки целостности HMAC с хэш-функцией SHA256.
Пример
const validSignatureFromNotificationServer =
'07e0ebb10916d97760c196034105d010607a6c6b7d72bfa1c3451448ac484a3b';
const notificationData = {
bill: {
siteId: '270304',
billId: 'cc961e8d-d4d6-4f02-b737-2297e51fb48e',
amount: {value: 1, currency: 'RUB'},
status: {value: 'PAID'}
},
version: '3'
};
const merchantSecret = 'eyJ2ZXJzaW9uIjoicmVzdF92MyIsImRhdGEiOnsibWVyY2hhbnRfaWQiOjUyNjgxMiwiYXBpX3VzZXJfaWQiOjcxNjI2MTk3LCJzZWNyZXQiOiJmZjBiZmJiM2UxYzc0MjY3YjIyZDIzOGYzMDBkNDhlYjhiNTnONPININONPN090MTg5Z**********************';
qiwiApi.checkNotificationSignature(
validSignatureFromNotificationServer, notificationData, merchantSecret
); // true
<?php
$validSignatureFromNotificationServer = '07e0ebb10916d97760c196034105d010607a6c6b7d72bfa1c3451448ac484a3b';
$notificationData = [
'bill' => [
'siteId' => '270304',
'billId' => 'cc961e8d-d4d6-4f02-b737-2297e51fb48e',
'amount' => ['value' => 1, 'currency' => 'RUB'],
'status' => ['value' => 'PAID']
],
'version' => '3'
];
$merchantSecret = 'eyJ2ZXJzaW9uIjoicmVzdF92MyIsImRhdGEiOnsibWVyY2hhbnRfaWQiOjUyNjgxMiwiYXBpX3VzZXJfaWQiOjcxNjI2MTk3LCJzZWNyZXQiOiJmZjBiZmJiM2UxYzc0MjY3YjIyZDIzOGYzMDBkNDhlYjhiNTnONPININONPN090MTg5Z**********************';
/** @var \Qiwi\Api\BillPayments $billPayments */
$billPayments->checkNotificationSignature(
$validSignatureFromNotificationServer, $notificationData, $merchantSecret
); // true
?>
String merchantSecret = "eyJ2ZXJzaW9uIjoicmVzdF92MyIsImRhdGEiOnsibWVyY2hhbnRfaWQiOjUyNjgxMiwiYXBpX3VzZXJfaWQiOjcxNjI2MTk3LCJzZWNyZXQiOiJmZjBiZmJiM2UxYzc0MjY3YjIyZDIzOGYzMDBkNDhlYjhiNTnONPININONPN090MTg5Z**********************";
Notification notification = new Notification(
new Bill(
"270304",
"cc961e8d-d4d6-4f02-b737-2297e51fb48e",
new MoneyAmount(
BigDecimal.ONE,
Currency.getInstance("RUB")
),
BillStatus.PAID
),
"3"
);
String validSignature = "07e0ebb10916d97760c196034105d010607a6c6b7d72bfa1c3451448ac484a3b";
BillPaymentsUtils.checkNotificationSignature(validSignature, notification, merchantSecret); //true
Assert.IsTrue(
condition: BillPaymentsUtils.CheckNotificationSignature(
validSignature: "07e0ebb10916d97760c196034105d010607a6c6b7d72bfa1c3451448ac484a3b",
notification: new Notification
{
Bill = new Bill
{
SiteId = "270304",
BillId = "cc961e8d-d4d6-4f02-b737-2297e51fb48e",
Amount = new MoneyAmount
{
ValueDecimal = 1m,
CurrencyEnum = CurrencyEnum.Rub
},
Status = new BillStatus
{
ValueEnum = BillStatusEnum.Paid
}
),
Version = "1"
},
merchantSecret: "eyJ2ZXJzaW9uIjoicmVzdF92MyIsImRhdGEiOnsibWVyY2hhbnRfaWQiOjUyNjgxMiwiYXBpX3VzZXJfaWQiOjcxNjI2MTk3LCJzZWNyZXQiOiJmZjBiZmJiM2UxYzc0MjY3YjIyZDIzOGYzMDBkNDhlYjhiNTnONPININONPN090MTg5Z**********************"
)
);
Алгоритм проверки подписи:
-
Объединить значения следующих параметров уведомления в одну строку с разделителем
|
:invoice_parameters = {amount.currency}|{amount.value}|{billId}|{siteId}|{status.value}
где
{*}
– значение параметра. Все значения при проверке подписи должны трактоваться как строки. -
Вычислить HMAC-хэш c алгоритмом хэширования SHA256:
hash = HMAС(SHA256, invoice_parameters, secret_key)
где:
secret_key
– ключ функции;invoice_parameters
– строка из п.1. -
Сравнить значение заголовка X-Api-Signature-SHA256 с результатом из п.2.
В разрабатываемом коде заведите константу merchantSecret
и присвойте ей значение секретного ключа, который выпустили в личном кабинете. Значение должно быть то же самое, что в константе SECRET_KEY на шаге авторизации.
Проверьте, что ключ скопирован полностью, в конце должен быть знак "равно" =
.
Шаг 10*. Обработка уведомлений - отправка 200 OK
HTTP/1.1 200 OK
Content-Type: application/json
{
"error":"0"
}
После проверки уведомлений на подлинность отправьте QIWI ответ на уведомление успешным статусом.
Если вы наблюдаете задержки в получении уведомлений более 10 минут (увеличился баланс вашего кошелька или написал клиент, что оплатил, а уведомление так и не пришло) – рекомендуем в дополнение переключиться на поллинг статусов счетов – см. следующий шаг.
Шаг 11*. Проверка статуса перевода по счету
Пример
const billId = 'cc961e8d-d4d6-4f02-b737-2297e51fb48e';
qiwiApi.getBillInfo(billId).then( data => {
//do with data
});
<?php
$billId = '4fa5cb2a-942e-4e67-b552-ff10c71d6f8d';
/** @var \Qiwi\Api\BillPayments $billPayments */
$response = $billPayments->getBillInfo($billId);
print_r($response);
?>
String billId = "fcb40a23-6733-4cf3-bacf-8e425fd1fc71";
BillResponse response = client.getBillInfo(billId);
client.GetBillInfo(
billId: "fcb40a23-6733-4cf3-bacf-8e425fd1fc71"
);
curl --location --request GET 'https://api.qiwi.com/partner/bill/v1/bills/cc961e8d-d4d6-4f02-b737-2297e51fb48e' \
--header 'accept: application/json' \
--header 'Authorization: Bearer eyJ2ZXJzaW9uIjoicmVzdF92MyIsImRhdGEiOnsibWVyY2hhbnRfaWQiOjIwNDIsImFwaV91c2VyX2lkIjo1NjYwMzk3Miwic2VjcmV0IjoiQjIwODlDNkI5Q0NDNTdCNDQzNGHJK43JFJDK595FJFJMjlCRkFFRDM5OE***********************'
Ответ
{
"siteId": "270305",
"billId": "cc961e8d-d4d6-4f02-b737-2297e51fb48e",
"amount": {
"currency": "RUB",
"value": "200.34"
},
"status": {
"value": "WAITING",
"changedDateTime": "2020-07-29T19:31:06.846+03:00"
},
"comment": "test",
"creationDateTime": "2020-07-28T19:31:06.846+03:00",
"expirationDateTime": "2020-08-8T19:31:06.846+03:00",
"payUrl": "https://oplata.qiwi.com/form/?invoice_uid=ee3ad91d-cfb8-4dbf-8449-b6859fdfec3c"
}
Array
(
[siteId] => 270305
[billId] => 4fa5cb2a-942e-4e67-b552-ff10c71d6f8d
[amount] => Array
(
[currency] => RUB
[value] => 200.34
)
[status] => Array
(
[value] => WAITING
[changedDateTime] => 2018-07-12T10:31:06.846+03:00
)
[comment] => test
[creationDateTime] => 2020-07-28T19:31:06.846+03:00
[expirationDateTime] => 2020-08-28T19:31:06.846+03:00
[payUrl] => https://oplata.qiwi.com/form/?invoice_uid=ee3ad91d-cfb8-4dbf-8449-b6859fdfec3c
)
{
"siteId": "270304",
"billId": "fcb40a23-6733-4cf3-bacf-8e425fd1fc71",
"amount": {
"value": "199.90",
"currency": "RUB"
},
"status": {
"value": "WAITING",
"changedDateTime": "2020-07-28T16:03:09.062Z"
},
"comment": "test",
"customer": {
"email": "example@mail.org",
"account": "349d5978-bccc-4e10-be7e-3ca0808237b7",
"phone": "79123456789"
},
"creationDateTime": "2020-07-28T16:03:09.062Z",
"expirationDateTime": "2020-08-28T16:03:08.668Z",
"payUrl": "https://oplata.qiwi.com/form/?invoice_uid=b77618b4-746c-485f-8bb8-fff43ddef114",
"customFields": {
"apiClient": "java_sdk",
"apiClientVersion": "1.0.0"
}
}
new BillResponse
{
SiteId = "270304",
BillId = "fcb40a23-6733-4cf3-bacf-8e425fd1fc71",
Amount = new MoneyAmount
{
ValueString = "199.90",
CurrencyString = "RUB"
},
Status = new ResponseStatus
{
ValueString: "WAITING",
ChangedDateTime: BillPaymentsUtils.ParseDate("2020-07-28T16:03:09+03:00")
},
Comment = "test",
Customer = new Customer
{
Email = "example@mail.org",
Account = "349d5978-bccc-4e10-be7e-3ca0808237b7",
Phone = "79123456789"
},
CreationDateTime = BillPaymentsUtils.ParseDate("2020-07-28T16:03:09+03:00"),
ExpirationDateTime = BillPaymentsUtils.ParseDate("2020-08-28T16:03:08+03:00"),
PayUrl = new Uri("https://oplata.qiwi.com/form/?invoice_uid=b77618b4-746c-485f-8bb8-fff43ddef114"),
CustomFields = new CustomFields
{
ApiClient = "dotnet_sdk",
ApiClientVersion = "0.1.0"
}
};
{
"siteId": "9hh4jb-00",
"billId": "cc961e8d-d4d6-4f02-b737-2297e51fb48e",
"amount": {
"currency": "RUB",
"value": "1.00"
},
"status": {
"value": "WAITING",
"changedDateTime": "2021-01-18T14:22:56.672+03:00"
},
"customer": {
"email": "test@tester.com",
"phone": "78710009999",
"account": "454678"
},
"customFields": {
"paySourcesFilter": "qw",
"themeCode": "Yvan-YKaSh",
"yourParam1": "64728940",
"yourParam2": "order 678"
},
"comment": "Text comment",
"creationDateTime": "2021-01-18T14:22:56.672+03:00",
"expirationDateTime": "2025-12-10T09:02:00+03:00",
"payUrl": "https://oplata.qiwi.com/form/?invoice_uid=aa0fa2bb-5452-47ca-9190-cd9c1a73718f"
}
Так как в случае недоступности вашего сервера или сбоя на нашей стороне уведомления могут быть доставлены с задержкой, поэтому рекомендуем использовать метод проверки статуса оплаты счета как дополнение к механизму обработки уведомлений.
Также данный метод можно использовать как самостоятельный, т.е. после выставления счета раз в какой-то промежуток времени опрашивать статус этого счета. Этот метод проще интеграции с сервисом уведомлений.
Раздел документации с примерами проверки статуса оплаты счета находится здесь.
Реализуйте вызов метода getBillInfo
. В параметрах необходимо передать идентификатор счета в вашей системе billId
, в результате будет получен ответ со статусом счета.
Шаг 12. Бизнес-логика
Счет в своем жизненном цикле проходит следующие статусы оплаты:
Статус | Описание | Комментарий |
---|---|---|
WAITING | Счет выставлен, ожидает оплаты | Нефинальный, ожидание оплаты или истечения срока действия |
PAID | Счет оплачен | Финальный (измениться не может) |
REJECTED | Счет отклонен | Финальный (измениться не может) |
EXPIRED | Время жизни счета истекло. Счет не оплачен | Финальный (измениться не может) |
Опираясь на статус счета, полученный в уведомлении или при помощи поллинга статуса, доработайте бизнес-логику вашего сайта.
Например:
- счет выставлен – появляется заказ в личном кабинете вашего пользователя,
- счет оплачен – обновляется статус заказа и, например, высылается письмо на почту пользователя и выполняется отгрузка товара или услуги.
Поздравляем, интеграция с QIWI для получения платежей закончена!
Дополнительно. Шаг 13. Отмена неоплаченных счетов
Пример
const billId = '60418b7e-1e95-4ac0-936e-0b98d7a7fdae';
qiwiApi.cancelBill(billId).then( data => {
//do with data
});
<?php
$billId = '60418b7e-1e95-4ac0-936e-0b98d7a7fdae';
/** @var \Qiwi\Api\BillPayments $billPayments */
$response = $billPayments->cancelBill($billId);
print_r($response);
?>
String billId = "fcb40a23-6733-4cf3-bacf-8e425fd1fc71";
BillResponse response = client.cancelBill(billId);
client.CancelBill(
billId: "fcb40a23-6733-4cf3-bacf-8e425fd1fc71"
);
curl --location --request POST 'https://api.qiwi.com/partner/bill/v1/bills/cc961e8d-d4d6-4f02-b737-2297e51fb48e/reject' \
--header 'content-type: application/json' \
--header 'accept: application/json' \
--header 'Authorization: Bearer eyJ2ZXJzaW9uIjoicmVzdF92MyIsImRhdGEiOnsibWVyY2hhbnRfaWQiOjIwNDIsImFwaV91c2VyX2lkIjo1NjYwMzk3Miwic2VjcmV0IjoiQjIwODlDNkI5Q0NDNTdCNDQzNGHJK43JFJDK595FJFJMjlCRkFFRDM5OE***********************' \
--data-raw ''
Ответ
{
"siteId": "270305",
"billId": "60418b7e-1e95-4ac0-936e-0b98d7a7fdae",
"amount": {
"currency": "RUB",
"value": "200.34"
},
"status": {
"value": "REJECTED",
"changedDateTime": "2018-07-12T10:32:17.595+03:00"
},
"comment": "test",
"creationDateTime": "2018-07-12T10:32:17.481+03:00",
"expirationDateTime": "2018-08-26T10:32:17.481+03:00",
"payUrl": "https://oplata.qiwi.com/form/?invoice_uid=a3fe62b2-9962-4d9d-9025-0766fb492546"
}
Array
(
[siteId] => 270305
[billId] => 60418b7e-1e95-4ac0-936e-0b98d7a7fdae
[amount] => Array
(
[currency] => RUB
[value] => 200.34
)
[status] => Array
(
[value] => REJECTED
[changedDateTime] => 2018-07-12T10:32:17.595+03:00
)
[comment] => test
[creationDateTime] => 2018-07-12T10:32:17.481+03:00
[expirationDateTime] => 2018-08-26T10:32:17.481+03:00
[payUrl] => https://oplata.qiwi.com/form/?invoice_uid=a3fe62b2-9962-4d9d-9025-0766fb492546
)
{
"siteId": "270304",
"billId": "fcb40a23-6733-4cf3-bacf-8e425fd1fc71",
"amount": {
"value": "199.90",
"currency": "RUB"
},
"status": {
"value": "REJECTED",
"changedDateTime": "2018-11-03T16:03:09.062Z"
},
"comment": "test",
"customer": {
"email": "example@mail.org",
"account": "349d5978-bccc-4e10-be7e-3ca0808237b7",
"phone": "79123456789"
},
"creationDateTime": "2018-11-03T16:03:09.062Z",
"expirationDateTime": "2018-12-18T16:03:08.668Z",
"payUrl": "https://oplata.qiwi.com/form/?invoice_uid=b77618b4-746c-485f-8bb8-fff43ddef114",
"customFields": {
"apiClient": "java_sdk",
"apiClientVersion": "1.0.0"
}
}
new BillResponse
{
SiteId = "270304",
BillId = "fcb40a23-6733-4cf3-bacf-8e425fd1fc71",
Amount = new MoneyAmount
{
ValueString = "199.90",
CurrencyString = "RUB"
},
Status = new ResponseStatus
{
ValueString: "REJECTED",
ChangedDateTime: BillPaymentsUtils.ParseDate("2018-11-03T16:03:09+03:00")
},
Comment = "test",
Customer = new Customer
{
Email = "example@mail.org",
Account = "349d5978-bccc-4e10-be7e-3ca0808237b7",
Phone = "79123456789"
},
CreationDateTime = BillPaymentsUtils.ParseDate("2018-11-03T16:03:09+03:00"),
ExpirationDateTime = BillPaymentsUtils.ParseDate("2018-12-18T16:03:08+03:00"),
PayUrl = new Uri("https://oplata.qiwi.com/form/?invoice_uid=b77618b4-746c-485f-8bb8-fff43ddef114"),
CustomFields = new CustomFields
{
ApiClient = "dotnet_sdk",
ApiClientVersion = "0.1.0"
}
};
{
"siteId": "9hh4jb-00",
"billId": "cc961e8d-d4d6-4f02-b737-2297e51fb48e",
"amount": {
"currency": "RUB",
"value": "1.00"
},
"status": {
"value": "REJECTED",
"changedDateTime": "2021-01-18T14:36:17.65+03:00"
},
"customer": {
"email": "test@tester.com",
"phone": "78710009999",
"account": "454678"
},
"customFields": {
"paySourcesFilter": "qw",
"themeCode": "Yvan-YKaSh",
"yourParam1": "64728940",
"yourParam2": "order 678"
},
"comment": "Text comment",
"creationDateTime": "2021-01-18T14:22:56.672+03:00",
"expirationDateTime": "2025-12-10T09:02:00+03:00",
"payUrl": "https://oplata.qiwi.com/form/?invoice_uid=aa0fa2bb-5452-47ca-9190-cd9c1a73718f"
}
Вы можете отменять неоплаченные счета самостоятельно, используя метод cancelBill.
Раздел документации с примерами запросов для отмены неоплаченных счетов находится здесь.