Merchant API
version: 1.0.0
Для работы Merchant API необходим публичный адрес вашего бэкенда. Укажите адрес в поле Callback URL в консоли Yandex Split на странице разработчика.
Ваш бэкенд должен проверять подлинность запроса, прежде чем приступать к его обработке.
Важно
Не привязывайте логику к конкретным IP-адресам — они могут измениться. Для проверки подлинности используйте валидацию JWT-токена с публичными ключами.
Аутентификация запроса Яндекса
В теле запроса Яндекс присылает JWT-токен, подписанный по алгоритму ES256.
Токен состоит из трех частей: заголовка (header), полезной нагрузки (payload) и подписи,
конкатенированных через точку: <base64-encoded headers JSON>.<base64-encoded payload JSON>.<signature>.
Раскодированный из base64 заголовок представляет собой JSON со следующей структурой:
{
"alg": "ES256", // алгоритм подписи
"kid": "key-id", // ID ключа, используется при определении публичного ключа
"iat": "1639408318", // UNIX время, когда токен был выпущен
"exp": "1639429918", // UNIX время, когда токен истекает, токен признается не валидным после наступления этого времени
"typ": "JWT" // тип токена
}
Полезная нагрузка также представляет собой JSON, конкретная структура полей которого определяется вызываемым методом. Подпись необходима для проверки валидности JWT-токена.
Важно
Перед десериализацией токена проверьте его валидность.
Алгоритм проверки валидности токена
Для проверки валидности JWT-токена удобнее воспользоваться одной из стандартных библиотек из списка: https://jwt.io/libraries. По возможности воздержитесь от ручной реализации данных проверок.
В общем случае алгоритм проверки JWT-токена с помощью библиотеки включает в себя:
- Проверку валидности подписи JWT-токена с помощью публичных JWK-ключей, размещенных по адресам:
https://sandbox.pay.yandex.uz/api/jwks для тестового окружения и https://pay.yandex.uz/api/jwks
для продакшн-окружения. Публичный ключ, используемый для валидации подписи конкретного JWT-токена,
выбирается на основе требований
algиkidв заголовке самого токена. - Проверку стандартных требований в заголовках JWT-токена:
alg,typ,iat,expи т.д.
Только в случае, если обе проверки прошли успешно, получатель может десериализовать JSON payload JWT-токена. После этого получатель должен дополнительно проверить, что merchantId в payload токена совпадает с ID продавца в Yandex Split. В противном случае токен не может быть использован.
Если любая из проверок завершилась неудачей, токен считается невалидным, а запрос — неаутентифицированным. Такой запрос должен быть отклонен. Код ошибки reasonCode зависит от причины:
TOKEN_EXPIRED— срок действия токена истек;UNAUTHORIZED— не удалось проверить подпись токена;OTHER— другие ошибки валидации.
Примеры ответов:
{
"status": "fail",
"reasonCode": "TOKEN_EXPIRED",
"reason": "JWT token has expired"
}
{
"status": "fail",
"reasonCode": "UNAUTHORIZED",
"reason": "Invalid token signature"
}
{
"status": "fail",
"reasonCode": "OTHER",
"reason": "Invalid merchantId"
}
Кеширование публичных JWK-ключей
Допускается кеширование JWK-ключей, используемых для валидации JWT-токенов, на бэкенде магазина. Время жизни такого кеша необходимо устанавливать таким образом, чтобы это не приводило к ошибкам валидации токенов в случае ротации JWK-ключей на стороне Yandex Split. На практике некоторые библиотеки по умолчанию устанавливают время жизни такого кеша в 10 минут. В случае кеширования JWK-ключей, необходимо реализовать следующие сценарии проверок.
Сценарий А: Успешная валидация токена кешированными JWK-ключами.
- Приходит запрос с токеном, чей
kidнайден среди кешированных JWK-ключей, и время жизни кеша не истекло. - Валидация подписи токена завершается успешно.
- Токен считается валидным.
Сценарий B: Неуспешная валидация токена кешированными JWK-ключами.
- Приходит запрос с токеном, чей
kidнайден среди кешированных JWK-ключей, и время жизни кеша не истекло. - Валидация подписи токена завершается неудачей.
- Токен считается невалидным, запрос отклоняется.
Сценарий C: Валидация токена со сбросом кеша JWK-ключей.
- Приходит запрос с токеном, чей
kidне найден среди кешированных JWK-ключей или время жизни кеша истекло. - Продавец должен сбросить кеш JWK-ключей и запросить весь список активных JWK-ключей заново с соответствующего окружению адреса.
- Валидация продолжается по сценарию А или B.
Как и в случае с валидацией токена, рекомендуется использовать стандартные библиотеки и воздержаться от ручной реализации данных сценариев.
Пример кода валидации JWT-токена
Ниже приведен пример успешной валидации JWT-токена, выпущенного в тестовом окружении.
Для валидации токенов в продакшн-окружении должны использоваться публичные ключи, доступные по адресу https://pay.yandex.uz/api/jwks.
import json
from urllib.request import urlopen
from jose import jwt
YANDEX_JWK_ENDPOINT = "https://sandbox.pay.yandex.uz/api/jwks"
JWT_TOKEN = (
"eyJhbGciOiJFUzI1NiIsImlhdCI6MTY1MDE5Njc1OCwia2lkIjoidGVzdC1rZXkiLCJ0eXAiOiJKV1QifQ.eyJjYXJ0Ijp7Iml0ZW1zIjpbeyJwcm9kdWN"
"0SWQiOiJwMSIsInF1YW50aXR5Ijp7ImNvdW50IjoiMSJ9fV19LCJjdXJyZW5jeUNvZGUiOiJSVUIiLCJtZXJjaGFudElkIjoiMjc2Y2YxZjEtZjhlZC00N"
"GZlLTg5ZTMtNWU0MTEzNDZkYThkIn0.YmQjHlh3ddLWgBexQ3QrwtbgAA3u1TVnBl1qnfMIvToBwinH3uH92KGB15m4NAQXdz5nhkjPZZu7RUStJt40PQ"
)
with urlopen(YANDEX_JWK_ENDPOINT) as response:
public_jwks = json.load(response)
payload = jwt.decode(JWT_TOKEN, public_jwks, algorithms=["ES256"])
print(json.dumps(payload, indent=2))
use Firebase\JWT\JWT;
use Firebase\JWT\JWK;
$sandboxJwk = 'https://sandbox.pay.yandex.uz/api/jwks';
$JWT_TOKEN =
"eyJhbGciOiJFUzI1NiIsImlhdCI6MTY1MDE5Njc1OCwia2lkIjoidGVzdC1rZXkiLCJ0eXAiOiJKV1QifQ.eyJjYXJ0Ijp7Iml0ZW1zIjpbeyJwcm9kdWN"
. "0SWQiOiJwMSIsInF1YW50aXR5Ijp7ImNvdW50IjoiMSJ9fV19LCJjdXJyZW5jeUNvZGUiOiJSVUIiLCJtZXJjaGFudElkIjoiMjc2Y2YxZjEtZjhlZC00N"
. "GZlLTg5ZTMtNWU0MTEzNDZkYThkIn0.YmQjHlh3ddLWgBexQ3QrwtbgAA3u1TVnBl1qnfMIvToBwinH3uH92KGB15m4NAQXdz5nhkjPZZu7RUStJt40PQ";
$client = new GuzzleHttp\Client();
$keysJson = $client->get($sandboxJwk)->getBody();
$keysData = json_decode($keysJson, JSON_OBJECT_AS_ARRAY);
$keys = JWK::parseKeySet($keysData);
$payload = JWT::decode($JWT_TOKEN, $keys);
print_r($payload);
Для работы примера необходимо установить зависимости:
composer require firebase/php-jwt
composer require guzzlehttp/guzzle
Решение проблем c вебхуками
Если нотификации не приходят, проверьте следующие моменты:
-
Неправильный адрес бэкенда
Нотификации могут приходить не туда, куда ожидаете. Указывайте Callback URL без
/v1/webhook— этот путь добавится автоматически, например:Callback URL
Куда придет запрос
https://example.merchant.uzhttps://example.merchant.uz/v1/webhookhttps://example.merchant.uz/v1/webhookhttps://example.merchant.uz/v1/webhook/v1/webhook -
Обработка
Content-TypeУбедитесь, что бэкенд вашего магазина готов принимать сообщения с заголовком
Content-Type: application/octet-stream. -
SSL-сертификат
Система не распознает самоподписанные SSL-сертификаты. Используйте сертификат от доверенного центра сертификации.
-
Настройки брандмауэра
Проверьте, что брандмауэр не блокирует входящие запросы и не обрезает тело запроса.
Endpoints
Specification
Open API
{
"components": {
"schemas": {
"MerchantErrorResponse": {
"properties": {
"reason": {
"description": "Описание причины ошибки.",
"type": "string"
},
"reasonCode": {
"description": "Код ошибки:\n\n- `FORBIDDEN` — заказ существует, но был оплачен не через Yandex Split;\n- `ORDER_NOT_FOUND` — заказ не найден в системе продавца;\n- `ORDER_AMOUNT_MISMATCH` — сумма заказа не совпадает с суммой в системе продавца;\n- `ORDER_DETAILS_MISMATCH` — детали заказа отличаются от данных в системе продавца;\n- `OTHER` — общая ошибка;\n- `UNAUTHORIZED` — не удалось проверить подпись JWT-токена;\n- `TOKEN_EXPIRED` — срок действия JWT-токена истек;\n- `CONFLICT` — данные в нотификации расходятся с состоянием заказа в системе продавца. Например, пришла нотификация об оплате для отмененного заказа.",
"enum": [
"FORBIDDEN",
"ITEM_NOT_FOUND",
"ORDER_NOT_FOUND",
"ORDER_AMOUNT_MISMATCH",
"ORDER_DETAILS_MISMATCH",
"OUT_OF_INVENTORY",
"PICKUP_POINT_NOT_FOUND",
"SHIPPING_DETAILS_MISMATCH",
"OTHER",
"UNAUTHORIZED",
"TOKEN_EXPIRED",
"CONFLICT"
],
"type": "string"
},
"status": {
"default": "fail",
"type": "string"
}
},
"required": [
"reasonCode"
],
"type": "object"
},
"MerchantSuccessResponse": {
"properties": {
"status": {
"default": "success",
"type": "string"
}
},
"type": "object"
},
"MerchantWebhookV1Request": {
"properties": {
"event": {
"description": "Тип события:\n- `ORDER_STATUS_UPDATED` — обновление статуса заказа;\n- `OPERATION_STATUS_UPDATED` — обновление статуса операций списания, возврата или отмены платежа.",
"enum": [
"TRANSACTION_STATUS_UPDATE",
"ORDER_STATUS_UPDATED",
"OPERATION_STATUS_UPDATED",
"SUBSCRIPTION_STATUS_UPDATED"
],
"type": "string"
},
"eventTime": {
"description": "Время события в формате `RFC 3339`: `YYYY-MM-DDThh:mm:ssTZD`.",
"example": "2025-05-26T21:00:36.08847+00:00",
"format": "date-time",
"type": "string"
},
"merchantId": {
"description": "ID (идентификатор) продавца.",
"format": "uuid",
"type": "string"
},
"operation": {
"allOf": [
{
"properties": {
"externalOperationId": {
"description": "Идентификатор операции в системе продавца. Должен быть уникальным.\n\nПередайте этот параметр, чтобы отслеживать конкретную операцию через метод [v1/operations/{external_operation_id}](../yandex-pay-api/operation/merchant_v1_operations-get).",
"type": "string"
},
"operationId": {
"description": "Идентификатор операции.",
"example": "5d32f295-8723-457d-81f9-ab13f17b7bd6",
"format": "uuid",
"type": "string"
},
"operationType": {
"description": "Тип операции. Подробнее о типах операций читайте в разделе [Статусы операций](../../../payments/statuses).",
"enum": [
"AUTHORIZE",
"BIND_CARD",
"REFUND",
"CAPTURE",
"VOID",
"RECURRING",
"PREPAYMENT",
"SUBMIT"
],
"type": "string"
},
"orderId": {
"description": "ID заказа, переданный в [/v1/orders](../yandex-pay-api/order/merchant_v1_orders-post.md) при создании заказа.",
"type": "string"
},
"status": {
"description": "Статус операции. Подробнее о статусах операций читайте в разделе [Статусы операций](../../../payments/statuses).",
"enum": [
"PENDING",
"SUCCESS",
"FAIL"
],
"type": "string"
}
},
"required": [
"operationId",
"operationType",
"orderId",
"status"
],
"type": "object"
}
],
"description": "Информация по операции. Приходит с событием `OPERATION_STATUS_UPDATED`"
},
"order": {
"allOf": [
{
"properties": {
"cartUpdated": {
"description": "Была ли обновлена корзина. Возвращается при оплате баллами.Если флаг имеет значение `true`, получите актуальную корзину.",
"type": "boolean"
},
"orderId": {
"description": "ID заказа, переданный в [/v1/orders](../yandex-pay-api/order/merchant_v1_orders-post.md) при создании заказа.",
"type": "string"
},
"paymentStatus": {
"description": "Статус заказа. Подробнее читайте в разделе [Статус заказа](../../../payments/statuses.md).",
"enum": [
"PENDING",
"AUTHORIZED",
"CAPTURED",
"VOIDED",
"REFUNDED",
"CONFIRMED",
"PARTIALLY_REFUNDED",
"FAILED"
],
"type": "string",
"x-enumDescriptions": {
"AUTHORIZED": "Платеж за заказ авторизован. Средства заблокированы на счету плательщика",
"CAPTURED": "Заказ успешно оплачен. Средства списаны со счета плательщика",
"CONFIRMED": "Заказ успешно оформлен",
"FAILED": "Заказ не был успешно оплачен",
"PARTIALLY_REFUNDED": "Совершён частичный возврат средств за заказ",
"PENDING": "Ожидается оплата",
"REFUNDED": "Совершён возврат средств за заказ",
"VOIDED": "Оплата отменена (voided). Списание средств не производилось"
}
}
},
"required": [
"orderId",
"paymentStatus"
],
"type": "object"
}
],
"description": "Информация по заказу. Приходит с событием `ORDER_STATUS_UPDATED`"
},
"subscription": {
"allOf": [
{
"properties": {
"customerSubscriptionId": {
"description": "ID подписки. Возвращается из SDK при успешном создании подписки. Также можно сохранить подписку при получении первой нотификации по ней. Дальнейшие обновления по этой подписке будут приходить с таким же значением этого поля.",
"format": "uuid",
"type": "string"
},
"nextWriteOff": {
"description": "Дата следующей попытки списания денег по подписке",
"format": "date-time",
"type": "string"
},
"status": {
"description": "Статус подписки",
"enum": [
"NEW",
"ACTIVE",
"CANCELLED",
"EXPIRED"
],
"type": "string"
},
"subscriptionPlanId": {
"description": "ID плана подписки, созданного в личном кабинете или через API.",
"format": "uuid",
"type": "string"
}
},
"required": [
"customerSubscriptionId",
"status",
"subscriptionPlanId"
],
"type": "object"
}
],
"description": "Состояние подписки."
}
},
"required": [
"event",
"eventTime",
"merchantId"
],
"type": "object"
},
"OperationWebhookData": {
"properties": {
"externalOperationId": {
"description": "Идентификатор операции в системе продавца. Должен быть уникальным.\n\nПередайте этот параметр, чтобы отслеживать конкретную операцию через метод [v1/operations/{external_operation_id}](../yandex-pay-api/operation/merchant_v1_operations-get).",
"type": "string"
},
"operationId": {
"description": "Идентификатор операции.",
"example": "5d32f295-8723-457d-81f9-ab13f17b7bd6",
"format": "uuid",
"type": "string"
},
"operationType": {
"description": "Тип операции. Подробнее о типах операций читайте в разделе [Статусы операций](../../../payments/statuses).",
"enum": [
"AUTHORIZE",
"BIND_CARD",
"REFUND",
"CAPTURE",
"VOID",
"RECURRING",
"PREPAYMENT",
"SUBMIT"
],
"type": "string"
},
"orderId": {
"description": "ID заказа, переданный в [/v1/orders](../yandex-pay-api/order/merchant_v1_orders-post.md) при создании заказа.",
"type": "string"
},
"status": {
"description": "Статус операции. Подробнее о статусах операций читайте в разделе [Статусы операций](../../../payments/statuses).",
"enum": [
"PENDING",
"SUCCESS",
"FAIL"
],
"type": "string"
}
},
"required": [
"operationId",
"operationType",
"orderId",
"status"
],
"type": "object"
},
"OrderWebhookData": {
"properties": {
"cartUpdated": {
"description": "Была ли обновлена корзина. Возвращается при оплате баллами.Если флаг имеет значение `true`, получите актуальную корзину.",
"type": "boolean"
},
"orderId": {
"description": "ID заказа, переданный в [/v1/orders](../yandex-pay-api/order/merchant_v1_orders-post.md) при создании заказа.",
"type": "string"
},
"paymentStatus": {
"description": "Статус заказа. Подробнее читайте в разделе [Статус заказа](../../../payments/statuses.md).",
"enum": [
"PENDING",
"AUTHORIZED",
"CAPTURED",
"VOIDED",
"REFUNDED",
"CONFIRMED",
"PARTIALLY_REFUNDED",
"FAILED"
],
"type": "string",
"x-enumDescriptions": {
"AUTHORIZED": "Платеж за заказ авторизован. Средства заблокированы на счету плательщика",
"CAPTURED": "Заказ успешно оплачен. Средства списаны со счета плательщика",
"CONFIRMED": "Заказ успешно оформлен",
"FAILED": "Заказ не был успешно оплачен",
"PARTIALLY_REFUNDED": "Совершён частичный возврат средств за заказ",
"PENDING": "Ожидается оплата",
"REFUNDED": "Совершён возврат средств за заказ",
"VOIDED": "Оплата отменена (voided). Списание средств не производилось"
}
}
},
"required": [
"orderId",
"paymentStatus"
],
"type": "object"
},
"SubscriptionWebhookData": {
"properties": {
"customerSubscriptionId": {
"description": "ID подписки. Возвращается из SDK при успешном создании подписки. Также можно сохранить подписку при получении первой нотификации по ней. Дальнейшие обновления по этой подписке будут приходить с таким же значением этого поля.",
"format": "uuid",
"type": "string"
},
"nextWriteOff": {
"description": "Дата следующей попытки списания денег по подписке",
"format": "date-time",
"type": "string"
},
"status": {
"description": "Статус подписки",
"enum": [
"NEW",
"ACTIVE",
"CANCELLED",
"EXPIRED"
],
"type": "string"
},
"subscriptionPlanId": {
"description": "ID плана подписки, созданного в личном кабинете или через API.",
"format": "uuid",
"type": "string"
}
},
"required": [
"customerSubscriptionId",
"status",
"subscriptionPlanId"
],
"type": "object"
}
}
},
"info": {
"description": "Для работы Merchant API необходим публичный адрес вашего бэкенда. [Укажите адрес](../../../console/settings-pay-split.md#callback-settings) в поле **Callback URL** в консоли Yandex Split на [странице разработчика](https://console.pay.yandex.uz/settings).\n\nВаш бэкенд должен проверять подлинность запроса, прежде чем приступать к его обработке.\n\n{% note warning %}\n\nНе привязывайте логику к конкретным IP-адресам — они могут измениться. Для проверки подлинности используйте [валидацию JWT-токена](#validate) с публичными ключами.\n\n{% endnote %}\n\n## Аутентификация запроса Яндекса {#auth}\n\nВ теле запроса Яндекс присылает JWT-токен, подписанный по алгоритму ES256.\nТокен состоит из трех частей: заголовка (header), полезной нагрузки (payload) и подписи,\nконкатенированных через точку: `<base64-encoded headers JSON>.<base64-encoded payload JSON>.<signature>`.\nРаскодированный из base64 заголовок представляет собой JSON со следующей структурой:\n\n```json\n{\n \"alg\": \"ES256\", // алгоритм подписи\n \"kid\": \"key-id\", // ID ключа, используется при определении публичного ключа\n \"iat\": \"1639408318\", // UNIX время, когда токен был выпущен\n \"exp\": \"1639429918\", // UNIX время, когда токен истекает, токен признается не валидным после наступления этого времени\n \"typ\": \"JWT\" // тип токена\n}\n```\n\nПолезная нагрузка также представляет собой JSON, конкретная структура полей которого определяется\nвызываемым методом. Подпись необходима для проверки валидности JWT-токена.\n\n{% note warning %}\n\nПеред десериализацией токена проверьте его валидность.\n\n{% endnote %}\n\n### Алгоритм проверки валидности токена {#validate}\n\nДля проверки валидности JWT-токена удобнее воспользоваться одной из стандартных библиотек\nиз списка: <https://jwt.io/libraries>. По возможности воздержитесь от ручной реализации данных проверок.\n\nВ общем случае алгоритм проверки JWT-токена с помощью библиотеки включает в себя:\n\n1. Проверку валидности подписи JWT-токена с помощью публичных JWK-ключей, размещенных по адресам:\n<https://sandbox.pay.yandex.uz/api/jwks> для тестового окружения и <https://pay.yandex.uz/api/jwks>\nдля продакшн-окружения. Публичный ключ, используемый для валидации подписи конкретного JWT-токена,\nвыбирается на основе требований `alg` и `kid` в заголовке самого токена.\n1. Проверку стандартных требований в заголовках JWT-токена: `alg`, `typ`, `iat`, `exp` и т.д.\n\nТолько в случае, если обе проверки прошли успешно, получатель может десериализовать JSON payload JWT-токена. После этого получатель должен дополнительно проверить, что `merchantId` в payload токена совпадает с ID продавца в Yandex Split. В противном случае токен не может быть использован.\n\nЕсли любая из проверок завершилась неудачей, токен считается невалидным, а запрос — неаутентифицированным. Такой запрос должен быть отклонен. Код ошибки `reasonCode` зависит от причины:\n\n- `TOKEN_EXPIRED` — срок действия токена истек;\n- `UNAUTHORIZED` — не удалось проверить подпись токена;\n- `OTHER` — другие ошибки валидации.\n\nПримеры ответов:\n\n```json\n{\n \"status\": \"fail\",\n \"reasonCode\": \"TOKEN_EXPIRED\",\n \"reason\": \"JWT token has expired\"\n}\n```\n\n```json\n{\n \"status\": \"fail\",\n \"reasonCode\": \"UNAUTHORIZED\",\n \"reason\": \"Invalid token signature\"\n}\n```\n\n```json\n{\n \"status\": \"fail\",\n \"reasonCode\": \"OTHER\",\n \"reason\": \"Invalid merchantId\"\n}\n```\n\n#### Кеширование публичных JWK-ключей {#jwk-cache}\n\nДопускается кеширование JWK-ключей, используемых для валидации JWT-токенов, на бэкенде магазина.\nВремя жизни такого кеша необходимо устанавливать таким образом, чтобы это не приводило к ошибкам валидации\nтокенов в случае ротации JWK-ключей на стороне Yandex Split. На практике\n[некоторые библиотеки](https://github.com/auth0/node-jwks-rsa#caching) по умолчанию устанавливают\nвремя жизни такого кеша в 10 минут. В случае кеширования JWK-ключей, необходимо реализовать следующие\nсценарии проверок.\n\n**Сценарий А:** Успешная валидация токена кешированными JWK-ключами.\n\n1. Приходит запрос с токеном, чей `kid` найден среди кешированных JWK-ключей, и время жизни кеша не истекло.\n1. Валидация подписи токена завершается успешно.\n1. Токен считается валидным.\n\n**Сценарий B:** Неуспешная валидация токена кешированными JWK-ключами.\n\n1. Приходит запрос с токеном, чей `kid` найден среди кешированных JWK-ключей, и время жизни кеша не истекло.\n1. Валидация подписи токена завершается неудачей.\n1. Токен считается невалидным, запрос отклоняется.\n\n**Сценарий C:** Валидация токена со сбросом кеша JWK-ключей.\n\n1. Приходит запрос с токеном, чей `kid` не найден среди кешированных JWK-ключей или время жизни кеша истекло.\n1. Продавец должен сбросить кеш JWK-ключей и запросить весь список активных JWK-ключей заново с соответствующего окружению адреса.\n1. Валидация продолжается по сценарию А или B.\n\nКак и в случае с валидацией токена, рекомендуется использовать стандартные библиотеки и воздержаться от\nручной реализации данных сценариев.\n\n### Пример кода валидации JWT-токена {#jwk-verify-example}\n\nНиже приведен пример успешной валидации JWT-токена, выпущенного в тестовом окружении.\nДля валидации токенов в продакшн-окружении должны использоваться публичные ключи, доступные по адресу `https://pay.yandex.uz/api/jwks`.\n\n{% list tabs %}\n\n- Python\n\n ```python\n import json\n from urllib.request import urlopen\n\n from jose import jwt\n\n YANDEX_JWK_ENDPOINT = \"https://sandbox.pay.yandex.uz/api/jwks\"\n\n JWT_TOKEN = (\n \"eyJhbGciOiJFUzI1NiIsImlhdCI6MTY1MDE5Njc1OCwia2lkIjoidGVzdC1rZXkiLCJ0eXAiOiJKV1QifQ.eyJjYXJ0Ijp7Iml0ZW1zIjpbeyJwcm9kdWN\"\n \"0SWQiOiJwMSIsInF1YW50aXR5Ijp7ImNvdW50IjoiMSJ9fV19LCJjdXJyZW5jeUNvZGUiOiJSVUIiLCJtZXJjaGFudElkIjoiMjc2Y2YxZjEtZjhlZC00N\"\n \"GZlLTg5ZTMtNWU0MTEzNDZkYThkIn0.YmQjHlh3ddLWgBexQ3QrwtbgAA3u1TVnBl1qnfMIvToBwinH3uH92KGB15m4NAQXdz5nhkjPZZu7RUStJt40PQ\"\n )\n\n with urlopen(YANDEX_JWK_ENDPOINT) as response:\n public_jwks = json.load(response)\n\n payload = jwt.decode(JWT_TOKEN, public_jwks, algorithms=[\"ES256\"])\n print(json.dumps(payload, indent=2))\n ```\n\n- PHP\n\n ```php\n use Firebase\\JWT\\JWT;\n use Firebase\\JWT\\JWK;\n\n $sandboxJwk = 'https://sandbox.pay.yandex.uz/api/jwks';\n\n $JWT_TOKEN =\n \"eyJhbGciOiJFUzI1NiIsImlhdCI6MTY1MDE5Njc1OCwia2lkIjoidGVzdC1rZXkiLCJ0eXAiOiJKV1QifQ.eyJjYXJ0Ijp7Iml0ZW1zIjpbeyJwcm9kdWN\"\n . \"0SWQiOiJwMSIsInF1YW50aXR5Ijp7ImNvdW50IjoiMSJ9fV19LCJjdXJyZW5jeUNvZGUiOiJSVUIiLCJtZXJjaGFudElkIjoiMjc2Y2YxZjEtZjhlZC00N\"\n . \"GZlLTg5ZTMtNWU0MTEzNDZkYThkIn0.YmQjHlh3ddLWgBexQ3QrwtbgAA3u1TVnBl1qnfMIvToBwinH3uH92KGB15m4NAQXdz5nhkjPZZu7RUStJt40PQ\";\n\n $client = new GuzzleHttp\\Client();\n $keysJson = $client->get($sandboxJwk)->getBody();\n $keysData = json_decode($keysJson, JSON_OBJECT_AS_ARRAY);\n\n $keys = JWK::parseKeySet($keysData);\n $payload = JWT::decode($JWT_TOKEN, $keys);\n\n print_r($payload);\n ```\n\n Для работы примера необходимо установить зависимости:\n\n ```bash\n composer require firebase/php-jwt\n composer require guzzlehttp/guzzle\n ```\n\n{% endlist %}\n\n## Решение проблем c вебхуками {#troubleshooting}\n\nЕсли нотификации не приходят, проверьте следующие моменты:\n\n- Неправильный адрес бэкенда\n\n Нотификации могут приходить не туда, куда ожидаете. [Указывайте Callback URL](../../../console/settings-pay-split.md#callback-settings) без `/v1/webhook` — этот путь добавится автоматически, например:\n\n #|\n || **Callback URL** | **Куда придет запрос** ||\n || `https://example.merchant.uz` | `https://example.merchant.uz/v1/webhook` ||\n || `https://example.merchant.uz/v1/webhook` | `https://example.merchant.uz/v1/webhook/v1/webhook` ||\n |#\n\n- Обработка `Content-Type`\n\n Убедитесь, что бэкенд вашего магазина готов принимать сообщения с заголовком `Content-Type: application/octet-stream`.\n\n- SSL-сертификат\n\n Система не распознает самоподписанные SSL-сертификаты. Используйте сертификат от доверенного центра сертификации.\n\n- Настройки брандмауэра\n\n Проверьте, что брандмауэр не блокирует входящие запросы и не обрезает тело запроса.",
"title": "Merchant API",
"version": "1.0.0"
},
"openapi": "3.0.3",
"paths": {
"/v1/webhook": {
"post": {
"description": "Нотификации об изменении статуса.\n\nЗапрос отправляется при изменении статуса заказа или операции по заказу.\n\nПоддерживаемые события:\n\n- `ORDER_STATUS_UPDATED` — обновление статуса заказа;\n- `OPERATION_STATUS_UPDATED` — обновление статуса операций списания, возврата или отмены платежа.\n<!-- - `SUBSCRIPTION_STATUS_UPDATED` — сейчас этот статус не отдаем? -->\n\n## Формат запроса {#webhook-format}\n\nЗапрос приходит в формате `application/octet-stream` в виде JWT-токена, подписанного по алгоритму ES256. Перед обработкой запроса проверьте его подлинность. Как это сделать, читайте в разделе [Аутентификация](../merchant-api/index.md).\n\nPayload проверенного и декодированного JWT-токена содержит JSON с данными события. Посмотрите [примеры событий](#webhook-examples).\n\n{% note warning \"Если в токене нет тела запроса\" %}\n\n- Убедитесь, что бэкенд вашего магазина готов принимать сообщение с заголовком `Content-Type: application/octet-stream`.\n- Проверьте, что брандмауэр не блокирует входящие запросы и не обрезает тело запроса.\n\nДругие ошибки смотрите в разделе [Решение проблем c вебхуками](../merchant-api/index.md#troubleshooting).\n\n{% endnote %}\n\n## Идемпотентность операций {#idempotency}\n\nПри действиях с заказом, например, при возврате средств методом [/v2/orders/{order_id}/refund](../yandex-pay-api/order/merchant_v2_refund-post.md), передавайте уникальный идентификатор операции `externalOperationId`.\n\nС помощью него вы сможете:\n- понять, по какой операции пришла нотификация;\n- проверить состояние операции через метод [v1/operations/{external_operation_id}](../yandex-pay-api/operation/merchant_v1_operations-get.md);\n- защититься от дублирования.\n\nЕсли отправите запрос повторно с тем же `externalOperationId`, то получите:\n- информацию о текущей операции, если она в процессе;\n- ошибку c `reasonCode: \"DUPLICATE_EXTERNAL_OPERATION_ID\"`, если операция завершена.\n\n## Примеры событий {#webhook-examples}\n\n### Оплата заказа\n\n{% list tabs %}\n\n- Успех\n\n ```json\n {\n \"merchantId\": \"xxxxxxxxx-xxx-5xxx-xxxxx-xxxxxxxx\",\n \"event\": \"ORDER_STATUS_UPDATED\",\n \"eventTime\": \"2023-11-26T08:11:09.359370+00:00\",\n \"order\": {\n \"orderId\": \"700aa3f04df64b3b8712d6b51f752e8b\",\n \"paymentStatus\": \"CAPTURED\"\n }\n }\n ```\n\n Посмотрите пример JWT-токена на [jwt.io](https://jwt.io/#debugger-io?token=eyJhbGciOiJFUzI1NiIsImV4cCI6MTcwMDk4NzYwMCwiaWF0IjoxNzAwOTg3MzAwLCJraWQiOiIxLW1lcmNoYW50LWFwaSIsInR5cCI6IkpXVCJ9.eyJtZXJjaGFudElkIjoieHh4eHh4eHh4LXh4eC01eHh4LXh4eHh4LXh4eHh4eHh4IiwiZXZlbnQiOiJPUkRFUl9TVEFUVVNfVVBEQVRFRCIsImV2ZW50VGltZSI6IjIwMjMtMTEtMjZUMDg6MTE6MDkuMzU5MzcwKzAwOjAwIiwib3JkZXIiOnsib3JkZXJJZCI6IjcwMGFhM2YwNGRmNjRiM2I4NzEyZDZiNTFmNzUyZThiIiwicGF5bWVudFN0YXR1cyI6IkNBUFRVUkVEIn19.xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx).\n\n **Пример запроса от Яндекса в бэкенд магазина:**\n\n ```(bash)\n curl -X POST https://test.uz/some/prefix/v1/webhook \\\n --header 'User-Agent: YandexPay/1.0' \\\n --header 'Accept: \\*/\\*' \\\n --header 'Content-Type: application/octet-stream' \\\n --header 'X-Request-Id: ff2a54885c4e45309853d2e33af1d63b\\\\_3a70f3062db640fcb2f3c34de1a27bd5' \\\n --header 'X-Request-Timeout: 13970' \\\n --compressed \\\n -d eyJhbGciOiJFUzI1NiIsImV4cCI6MTcwMDk4NzYwMCwiaWF0IjoxNzAwOTg3MzAwLCJraWQiOiIxLW1lcmNoYW50LWFwaSIsInR5cCI6IkpXVCJ9.eyJtZXJjaGFudElkIjoieHh4eHh4eHh4LXh4eC01eHh4LXh4eHh4LXh4eHh4eHh4IiwiZXZlbnQiOiJPUkRFUl9TVEFUVVNfVVBEQVRFRCIsImV2ZW50VGltZSI6IjIwMjMtMTEtMjZUMDg6MTE6MDkuMzU5MzcwKzAwOjAwIiwib3JkZXIiOnsib3JkZXJJZCI6IjcwMGFhM2YwNGRmNjRiM2I4NzEyZDZiNTFmNzUyZThiIiwicGF5bWVudFN0YXR1cyI6IkNBUFRVUkVEIn19.xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\n ```\n\n- Неудача\n\n ```json\n {\n \"merchantId\": \"xxxxxxxxx-xxx-5xxx-xxxxx-xxxxxxxx\",\n \"event\": \"ORDER_STATUS_UPDATED\",\n \"eventTime\": \"2024-04-25T07:56:29.974810+00:00\",\n \"order\": {\n \"orderId\": \"253222_1714029088\",\n \"paymentStatus\": \"FAILED\"\n }\n }\n ```\n\n Посмотрите пример JWT-токена на [jwt.io](https://jwt.io/#debugger-io?token=eyJhbGciOiJFUzI1NiIsImV4cCI6MTcxNDAzMjIyMSwiaWF0IjoxNzE0MDMxOTIxLCJraWQiOiIxLW1lcmNoYW50LWFwaSIsInR5cCI6IkpXVCJ9.eyJtZXJjaGFudElkIjoieHh4eHh4eHh4LXh4eC01eHh4LXh4eHh4LXh4eHh4eHh4IiwiZXZlbnQiOiJPUkRFUl9TVEFUVVNfVVBEQVRFRCIsImV2ZW50VGltZSI6IjIwMjQtMDQtMjVUMDc6NTY6MjkuOTc0ODEwKzAwOjAwIiwib3JkZXIiOnsib3JkZXJJZCI6IjI1MzIyMl8xNzE0MDI5MDg4IiwicGF5bWVudFN0YXR1cyI6IkZBSUxFRCJ9fQ.v9dw_cR3_b4R5v0D8WRisrSPABxhegSSpEq4kz9s10fr5cUK150yWnwJREYCGQCm5BZK1Yydsquh-WE6OyRR2APOST).\n\n{% endlist %}\n\n### Возврат\n\n{% note tip %}\n\nСначала изучите, как работают возвраты, в разделе [/v2/orders/{order_id}/refund](../yandex-pay-api/order/merchant_v2_refund-post.md).\n\n{% endnote %}\n\n#### Полный возврат\n\nНезависимо от того, меняется ли статус заказа, отправляется 2 нотификации: по операции и по заказу.\n\n{% list tabs %}\n\n- Успех\n\n 1. `OPERATION_STATUS_UPDATED` — операция возврата завершилась успешно:\n\n ```json\n {\n \"merchantId\": \"xxxxxxxxx-xxx-5xxx-xxxxx-xxxxxxxx\",\n \"event\": \"OPERATION_STATUS_UPDATED\",\n \"eventTime\": \"2024-04-19T10:27:53.323878+00:00\",\n \"operation\": {\n \"operationId\": \"73dec2cd-db5c-4386-be6d-10c5b5a2ee09\",\n \"orderId\": \"86283\",\n \"status\": \"SUCCESS\",\n \"operationType\": \"REFUND\"\n }\n }\n ```\n\n 2. `ORDER_STATUS_UPDATED` — заказ в перешел в терминальный статус `REFUNDED`. Больше нельзя вызывать возвраты.\n\n ```json\n {\n \"merchantId\": \"xxxxxxxxx-xxx-5xxx-xxxxx-xxxxxxxx\",\n \"event\": \"ORDER_STATUS_UPDATED\",\n \"eventTime\": \"2024-04-19T12:16:28.766392+00:00\",\n \"order\": {\n \"orderId\": \"86283\",\n \"paymentStatus\": \"REFUNDED\"\n }\n }\n ```\n\n Посмотрите пример JWT-токена на [jwt.io](https://jwt.io/#debugger-io?token=eyJhbGciOiJFUzI1NiIsImV4cCI6MTcxMzUyOTI4OSwiaWF0IjoxNzEzNTI4OTg5LCJraWQiOiIxLW1lcmNoYW50LWFwaSIsInR5cCI6IkpXVCJ9.eyJtZXJjaGFudElkIjoieHh4eHh4eHh4LXh4eC01eHh4LXh4eHh4LXh4eHh4eHh4IiwiZXZlbnQiOiJPUkRFUl9TVEFUVVNfVVBEQVRFRCIsImV2ZW50VGltZSI6IjIwMjQtMDQtMTlUMTI6MTY6MjguNzY2MzkyKzAwOjAwIiwib3JkZXIiOnsib3JkZXJJZCI6Ijg2MjgzIiwicGF5bWVudFN0YXR1cyI6IlJFRlVOREVEIn19.xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx).\n\n- Неудача\n\n 1. `OPERATION_STATUS_UPDATED` — операция возврата завершилась неуспешно:\n\n ```json\n {\n \"merchantId\": \"xxxxxxxxx-xxx-5xxx-xxxxx-xxxxxxxx\",\n \"event\": \"OPERATION_STATUS_UPDATED\",\n \"eventTime\": \"2024-06-13T22:27:53.323878+00:00\",\n \"operation\": {\n \"operationId\": \"73dec2cd-db5c-4386-be6d-10c5b5a2ee08\",\n \"orderId\": \"9c8aed6d-a8e5-4c6a-acd8-645538173f66\",\n \"status\": \"FAIL\",\n \"operationType\": \"REFUND\"\n }\n }\n ```\n\n Посмотрите пример JWT-токена на [jwt.io](https://jwt.io/#debugger-io?token=eyJhbGciOiJFUzI1NiIsImV4cCI6MTcxODMxNzk3NCwiaWF0IjoxNzE4MzE3Njc0LCJraWQiOiIxLW1lcmNoYW50LWFwaSIsInR5cCI6IkpXVCJ9.eyJtZXJjaGFudElkIjoieHh4eHh4eHh4LXh4eC01eHh4LXh4eHh4LXh4eHh4eHh4IiwiZXZlbnQiOiJPUEVSQVRJT05fU1RBVFVTX1VQREFURUQiLCJldmVudFRpbWUiOiIyMDI0LTA2LTEzVDIyOjI3OjUzLjMyMzg3OCswMDowMCIsIm9wZXJhdGlvbiI6eyJvcGVyYXRpb25JZCI6IjczZGVjMmNkLWRiNWMtNDM4Ni1iZTZkLTEwYzViNWEyZWUwOCIsIm9yZGVySWQiOiI5YzhhZWQ2ZC1hOGU1LTRjNmEtYWNkOC02NDU1MzgxNzNmNjYiLCJzdGF0dXMiOiJGQUlMIiwib3BlcmF0aW9uVHlwZSI6IlJFRlVORCJ9fQ.xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx).\n\n 2. `ORDER_STATUS_UPDATED` — заказ остался в предыдущем статусе `CAPTURED`:\n\n ```json\n {\n \"merchantId\": \"xxxxxxxxx-xxx-5xxx-xxxxx-xxxxxxxx\",\n \"event\": \"ORDER_STATUS_UPDATED\",\n \"eventTime\": \"2024-06-13T22:27:54.323878+00:00\",\n \"order\": {\n \"orderId\": \"9c8aed6d-a8e5-4c6a-acd8-645538173f66\",\n \"paymentStatus\": \"CAPTURED\"\n }\n }\n ```\n\n{% endlist %}\n\n#### Частичный возврат\n\nВы можете вернуть всю сумму заказа несколькими частичными возвратами. Когда сумма всех возвратов достигнет полной стоимости, заказ перейдет в терминальный статус `REFUNDED`. После этого нельзя вызывать возвраты.\n\nНезависимо от того, меняется ли статус заказа, отправляется 2 нотификации: по операции и по заказу.\n\nРассмотрим на примере заказа с тремя пачками сока.\n\n1. Совершили частичный возврат одного сока. Вам придет 2 нотификации:\n\n 1. `OPERATION_STATUS_UPDATED` — операция возврата завершилась успешно:\n\n ```json\n {\n \"merchantId\": \"xxxxxxxxx-xxx-5xxx-xxxxx-xxxxxxxx\",\n \"event\": \"OPERATION_STATUS_UPDATED\",\n \"eventTime\": \"2024-04-19T10:27:53.323878+00:00\",\n \"operation\": {\n \"operationId\": \"73dec3cs-sd5t-4356-ne6d-10c79b5d2ee09\",\n \"externalOperationId\": \"123-partial-refund-1\",\n \"orderId\": \"123\",\n \"status\": \"SUCCESS\",\n \"operationType\": \"REFUND\"\n }\n }\n ```\n\n 2. `ORDER_STATUS_UPDATED` — заказ в перешел в статус `PARTIALLY_REFUNDED`:\n\n ```json\n {\n \"merchantId\": \"xxxxxxxxx-xxx-5xxx-xxxxx-xxxxxxxx\",\n \"event\": \"ORDER_STATUS_UPDATED\",\n \"eventTime\": \"2024-04-19T12:16:28.766392+00:00\",\n \"order\": {\n \"orderId\": \"123\",\n \"paymentStatus\": \"PARTIALLY_REFUNDED\"\n }\n }\n ```\n\n2. Совершили второй частичный возврат одного сока. Вам придет 2 нотификации:\n\n 1. `OPERATION_STATUS_UPDATED` — операция возврата завершилась успешно:\n\n ```json\n {\n \"merchantId\": \"xxxxxxxxx-xxx-5xxx-xxxxx-xxxxxxxx\",\n \"event\": \"OPERATION_STATUS_UPDATED\",\n \"eventTime\": \"2024-04-19T13:27:53.323878+00:00\",\n \"operation\": {\n \"operationId\": \"28fba9ds-kl2m-7891-qw3r-45e82c7f1bb12\",\n \"externalOperationId\": \"123-partial-refund-2\",\n \"orderId\": \"123\",\n \"status\": \"SUCCESS\",\n \"operationType\": \"REFUND\"\n }\n }\n ```\n\n 2. `ORDER_STATUS_UPDATED` — заказ остался в статусе `PARTIALLY_REFUNDED`:\n\n ```json\n {\n \"merchantId\": \"xxxxxxxxx-xxx-5xxx-xxxxx-xxxxxxxx\",\n \"event\": \"ORDER_STATUS_UPDATED\",\n \"eventTime\": \"2024-04-19T13:27:54.321878+00:00\",\n \"order\": {\n \"orderId\": \"123\",\n \"paymentStatus\": \"PARTIALLY_REFUNDED\"\n }\n }\n ```\n\n3. Совершили третий частичный возврат. Сумма всех возвратов достигла полной стоимости. Вам придет 2 нотификации:\n\n 1. `OPERATION_STATUS_UPDATED` — операция возврата завершилась успешно:\n\n ```json\n {\n \"merchantId\": \"xxxxxxxxx-xxx-5xxx-xxxxx-xxxxxxxx\",\n \"event\": \"OPERATION_STATUS_UPDATED\",\n \"eventTime\": \"2024-04-19T13:40:51.323878+00:00\",\n \"operation\": {\n \"operationId\": \"64abc2ts-rj4y-3187-mf5g-56b71e9a4dd67\",\n \"externalOperationId\": \"123-partial-refund-3\",\n \"orderId\": \"123\",\n \"status\": \"SUCCESS\",\n \"operationType\": \"REFUND\"\n }\n }\n ```\n\n 2. `ORDER_STATUS_UPDATED` — заказ в перешел в терминальный статус `REFUNDED`. Больше нельзя вызывать возвраты.\n\n ```json\n {\n \"merchantId\": \"xxxxxxxxx-xxx-5xxx-xxxxx-xxxxxxxx\",\n \"event\": \"ORDER_STATUS_UPDATED\",\n \"eventTime\": \"2024-04-19T14:16:28.766392+00:00\",\n \"order\": {\n \"orderId\": \"123\",\n \"paymentStatus\": \"REFUNDED\"\n }\n }\n ```",
"operationId": "webhook",
"requestBody": {
"content": {
"application/json": {
"schema": {
"properties": {
"event": {
"description": "Тип события:\n- `ORDER_STATUS_UPDATED` — обновление статуса заказа;\n- `OPERATION_STATUS_UPDATED` — обновление статуса операций списания, возврата или отмены платежа.",
"enum": [
"TRANSACTION_STATUS_UPDATE",
"ORDER_STATUS_UPDATED",
"OPERATION_STATUS_UPDATED",
"SUBSCRIPTION_STATUS_UPDATED"
],
"type": "string"
},
"eventTime": {
"description": "Время события в формате `RFC 3339`: `YYYY-MM-DDThh:mm:ssTZD`.",
"example": "2025-05-26T21:00:36.08847+00:00",
"format": "date-time",
"type": "string"
},
"merchantId": {
"description": "ID (идентификатор) продавца.",
"format": "uuid",
"type": "string"
},
"operation": {
"allOf": [
{
"properties": {
"externalOperationId": {
"description": "Идентификатор операции в системе продавца. Должен быть уникальным.\n\nПередайте этот параметр, чтобы отслеживать конкретную операцию через метод [v1/operations/{external_operation_id}](../yandex-pay-api/operation/merchant_v1_operations-get).",
"type": "string"
},
"operationId": {
"description": "Идентификатор операции.",
"example": "5d32f295-8723-457d-81f9-ab13f17b7bd6",
"format": "uuid",
"type": "string"
},
"operationType": {
"description": "Тип операции. Подробнее о типах операций читайте в разделе [Статусы операций](../../../payments/statuses).",
"enum": [
"AUTHORIZE",
"BIND_CARD",
"REFUND",
"CAPTURE",
"VOID",
"RECURRING",
"PREPAYMENT",
"SUBMIT"
],
"type": "string"
},
"orderId": {
"description": "ID заказа, переданный в [/v1/orders](../yandex-pay-api/order/merchant_v1_orders-post.md) при создании заказа.",
"type": "string"
},
"status": {
"description": "Статус операции. Подробнее о статусах операций читайте в разделе [Статусы операций](../../../payments/statuses).",
"enum": [
"PENDING",
"SUCCESS",
"FAIL"
],
"type": "string"
}
},
"required": [
"operationId",
"operationType",
"orderId",
"status"
],
"type": "object"
}
],
"description": "Информация по операции. Приходит с событием `OPERATION_STATUS_UPDATED`"
},
"order": {
"allOf": [
{
"properties": {
"cartUpdated": {
"description": "Была ли обновлена корзина. Возвращается при оплате баллами.Если флаг имеет значение `true`, получите актуальную корзину.",
"type": "boolean"
},
"orderId": {
"description": "ID заказа, переданный в [/v1/orders](../yandex-pay-api/order/merchant_v1_orders-post.md) при создании заказа.",
"type": "string"
},
"paymentStatus": {
"description": "Статус заказа. Подробнее читайте в разделе [Статус заказа](../../../payments/statuses.md).",
"enum": [
"PENDING",
"AUTHORIZED",
"CAPTURED",
"VOIDED",
"REFUNDED",
"CONFIRMED",
"PARTIALLY_REFUNDED",
"FAILED"
],
"type": "string",
"x-enumDescriptions": {
"AUTHORIZED": "Платеж за заказ авторизован. Средства заблокированы на счету плательщика",
"CAPTURED": "Заказ успешно оплачен. Средства списаны со счета плательщика",
"CONFIRMED": "Заказ успешно оформлен",
"FAILED": "Заказ не был успешно оплачен",
"PARTIALLY_REFUNDED": "Совершён частичный возврат средств за заказ",
"PENDING": "Ожидается оплата",
"REFUNDED": "Совершён возврат средств за заказ",
"VOIDED": "Оплата отменена (voided). Списание средств не производилось"
}
}
},
"required": [
"orderId",
"paymentStatus"
],
"type": "object"
}
],
"description": "Информация по заказу. Приходит с событием `ORDER_STATUS_UPDATED`"
},
"subscription": {
"allOf": [
{
"properties": {
"customerSubscriptionId": {
"description": "ID подписки. Возвращается из SDK при успешном создании подписки. Также можно сохранить подписку при получении первой нотификации по ней. Дальнейшие обновления по этой подписке будут приходить с таким же значением этого поля.",
"format": "uuid",
"type": "string"
},
"nextWriteOff": {
"description": "Дата следующей попытки списания денег по подписке",
"format": "date-time",
"type": "string"
},
"status": {
"description": "Статус подписки",
"enum": [
"NEW",
"ACTIVE",
"CANCELLED",
"EXPIRED"
],
"type": "string"
},
"subscriptionPlanId": {
"description": "ID плана подписки, созданного в личном кабинете или через API.",
"format": "uuid",
"type": "string"
}
},
"required": [
"customerSubscriptionId",
"status",
"subscriptionPlanId"
],
"type": "object"
}
],
"description": "Состояние подписки."
}
},
"required": [
"event",
"eventTime",
"merchantId"
],
"type": "object"
}
}
}
},
"responses": {
"200": {
"content": {
"application/json": {
"schema": {
"properties": {
"status": {
"default": "success",
"type": "string"
}
},
"type": "object"
}
}
},
"description": "Вебхук успешно получен и обработан.\nТело ответа может быть любым, рекомендуем отправить `{\"status\": \"success\"}`.\nПри получении `200` Яндекс прекращает отправку повторных вебхуков."
},
"400": {
"content": {
"application/json": {
"schema": {
"properties": {
"reason": {
"description": "Описание причины ошибки.",
"type": "string"
},
"reasonCode": {
"description": "Код ошибки:\n\n- `FORBIDDEN` — заказ существует, но был оплачен не через Yandex Split;\n- `ORDER_NOT_FOUND` — заказ не найден в системе продавца;\n- `ORDER_AMOUNT_MISMATCH` — сумма заказа не совпадает с суммой в системе продавца;\n- `ORDER_DETAILS_MISMATCH` — детали заказа отличаются от данных в системе продавца;\n- `OTHER` — общая ошибка;\n- `UNAUTHORIZED` — не удалось проверить подпись JWT-токена;\n- `TOKEN_EXPIRED` — срок действия JWT-токена истек;\n- `CONFLICT` — данные в нотификации расходятся с состоянием заказа в системе продавца. Например, пришла нотификация об оплате для отмененного заказа.",
"enum": [
"FORBIDDEN",
"ITEM_NOT_FOUND",
"ORDER_NOT_FOUND",
"ORDER_AMOUNT_MISMATCH",
"ORDER_DETAILS_MISMATCH",
"OUT_OF_INVENTORY",
"PICKUP_POINT_NOT_FOUND",
"SHIPPING_DETAILS_MISMATCH",
"OTHER",
"UNAUTHORIZED",
"TOKEN_EXPIRED",
"CONFLICT"
],
"type": "string"
},
"status": {
"default": "fail",
"type": "string"
}
},
"required": [
"reasonCode"
],
"type": "object"
}
}
},
"description": "Ошибка обработки вебхука.\nПри отсутствии ответа или любом статусе кроме `200` Яндекс генерирует новый JWT-токен и повторяет отправку вебхука:\n- первые 10 раз через 5 мс;\n- далее с экспоненциально возрастающим интервалом до 15 минут;\n- затем каждые 15 минут в течение 24 часов.\nОбщее время повторных отправок — 24 часа. После этого вебхук считается недоставленным."
}
},
"summary": "/v1/webhook"
}
}
},
"servers": [
{
"description": "Production",
"url": "https://example.merchant.uz"
},
{
"description": "Sandbox",
"url": "https://sandbox.example.merchant.uz"
}
]
}