Обработка платёжных уведомлений
Уведомления посылаются сервером платёжной системы на Адрес обратного вызова, указанный в настройках приложения, методом POST
в кодировке UTF-8.
Чтобы исключить возможность подделки, уведомление подписывается секретным ключом, известным только владельцу приложения и платежной системе, (подробнее в разделе Проверка подписи).
Разработчик должен реализовать обработку уведомлений и вернуть результат обработки в случае успеха или описание ошибки в случае неуспеха (см. подробнее в разделе Формат ответа в случае ошибки). Ответ должен быть отправлен в течение 10 секунд, иначе соединение будет разорвано, а попытка отправки уведомления будет предпринята ещё раз через некоторое время.
Важно! Ответ должен быть в формате JSON в кодировке UTF-8.
Структура уведомления
Набор полей зависит от типа уведомления. Однако уведомление любого типа всегда содержит следующие поля:
notification_type
string
Тип уведомления. Возможные значения:
- •
get_item
— получение информации о товаре; - •
order_status_change
— изменение статуса заказа; - •
get_subscription
— получение информации о подписке; - •
subscription_status_change
— изменение статуса подписки.
app_id
integer
Идентификатор приложения.
user_id
integer
Идентификатор пользователя, сделавшего заказ.
receiver_id
integer
Идентификатор получателя заказа (в данный момент совпадает с user_id
, но в будущем может отличаться).
order_id/subscription_id
integer/integer
Идентификатор заказа/подписки.
version
string
Параметр передаётся начиная с версии платежного API 5.132. Используемая версия платёжного API.
sig
string
Подпись уведомления (подробнее в разделе Проверка подписи).
Версионирование уведомлений
В настройках приложения, в разделе Платежи доступно переключение версий платёжных уведомлений.
Начиная с версии 5.132 становятся доступны новые типы уведомлений — об отменах платежей, а также добавляется параметр version
, опивающий версию используемого API.
Проверка подписи
Параметр sig
равен md5 от конкатенации пар имя параметров=значение параметра, расположенных в порядке возрастания имени параметра (по алфавиту) и секретной подписи api_secret
, указанной в настройках вашего приложения.
При несовпадении подписей необходимо выдать в ответе ошибку 10, «Несовпадение вычисленной и переданной подписи» (см. Формат ответа в случае ошибки).
Пример формирования подписи с секретным ключом W7kVvxVxZ4:
sig = md5("name1=value1name2=value2W7kVvxVxZ4")
Формат ответа в случае ошибки
Если при обработке уведомления произошла ошибка, необходимо отправить ответ в следующем формате:
{
"error": {
"error_code": код ошибки,
"error_msg": описание ошибки,
"critical": критичность ошибки
}
}
Поля объекта error
error_code
integer
Числовой код ошибки (см. ниже).
error_msg
string
Описание ошибки в текстовом виде для чтения человеком (обязательно для error_code >= 100
).
critical
boolean
Критичность ошибки. Возможные значения:
- •
true
, если повторение уведомления с такими же параметрами приведёт к такой же ошибке (например, указанного товара не существует). Уведомление не будет отправляться повторно, пользователь получит ошибку. - •
false
, если ошибка временная, и уведомление может быть обработано позже (например, временная ошибка записи в базу данных). Уведомление будет отправлено через некоторое время, пользователь будет ждать ответа.
Числовые коды ошибок
Код ошибки | Критичность | Описание ошибки в текстовом виде |
---|---|---|
1 | true /false | Общая ошибка. |
2 | false | Временная ошибка базы данных. |
10 | true | Несовпадение вычисленной и переданной подписи. |
11 | true | Параметры запроса не соответствуют спецификации; в запросе нет необходимых полей; другие ошибки целостности запроса. |
20 | true | Товара не существует. |
21 | true | Товара нет в наличии. |
22 | true | Пользователя не существует. |
100-999 | Ошибки с номерами 100-999 задаются приложением, при возврате таких ошибок обязательно должно присутствовать текстовое описание ошибки. |
Пример обработчика уведомлений на языке PHP
<?php
header("Content-Type: application/json; encoding=utf-8");
$secret_key = 'hiUl8U4F9q3BcbAl28va'; // Защищённый ключ приложения
$input = $_POST;
// Проверка подписи
$sig = $input['sig'];
unset($input['sig']);
ksort($input);
$str = '';
foreach ($input as $k => $v) {
$str .= $k.'='.$v;
}
if ($sig != md5($str.$secret_key)) {
$response['error'] = array(
'error_code' => 10,
'error_msg' => 'Несовпадение вычисленной и переданной подписи запроса.',
'critical' => true
);
} else {
// Подпись правильная
switch ($input['notification_type']) {
case 'get_item':
// Получение информации о товаре
$item = $input['item']; // наименование товара
if ($item == 'item1') {
$response['response'] = array(
'item_id' => 25,
'title' => '300 золотых монет',
'photo_url' => 'http://somesite/images/coin.jpg',
'price' => 5
);
} elseif ($item == 'item2') {
$response['response'] = array(
'item_id' => 27,
'title' => '500 золотых монет',
'photo_url' => 'http://somesite/images/coin.jpg',
'price' => 10
);
} else {
$response['error'] = array(
'error_code' => 20,
'error_msg' => 'Товара не существует.',
'critical' => true
);
}
break;
case 'get_item_test':
// Получение информации о товаре в тестовом режиме
$item = $input['item'];
if ($item == 'item1') {
$response['response'] = array(
'item_id' => 125,
'title' => '300 золотых монет (тестовый режим)',
'photo_url' => 'http://somesite/images/coin.jpg',
'price' => 5
);
} elseif ($item == 'item2') {
$response['response'] = array(
'item_id' => 127,
'title' => '500 золотых монет (тестовый режим)',
'photo_url' => 'http://somesite/images/coin.jpg',
'price' => 10
);
} else {
$response['error'] = array(
'error_code' => 20,
'error_msg' => 'Товара не существует.',
'critical' => true
);
}
break;
case 'order_status_change':
// Изменение статуса заказа
if ($input['status'] == 'chargeable') {
$order_id = intval($input['order_id']);
// Код проверки товара, включая его стоимость
$app_order_id = 1; // Получающийся у вас идентификатор заказа.
$response['response'] = array(
'order_id' => $order_id,
'app_order_id' => $app_order_id,
);
} else {
$response['error'] = array(
'error_code' => 100,
'error_msg' => 'Передано непонятно что вместо chargeable.',
'critical' => true
);
}
break;
case 'order_status_change_test':
// Изменение статуса заказа в тестовом режиме
if ($input['status'] == 'chargeable') {
$order_id = intval($input['order_id']);
$app_order_id = 1; // Тут фактического заказа может не быть - тестовый режим.
$response['response'] = array(
'order_id' => $order_id,
'app_order_id' => $app_order_id,
);
} else {
$response['error'] = array(
'error_code' => 100,
'error_msg' => 'Передано непонятно что вместо chargeable.',
'critical' => true
);
}
break;
}
}
echo json_encode($response);
?>