Вернуть деньги за заказ
Производит полный или частичный возврат платежа. Создает операцию возврата, которая будет исполнена через некоторое время.
Метод является асинхронным.
Если в ответе статус 5XX, повторите запрос.
Совет
Пошаговые инструкции см. в разделе Самостоятельная интеграция.
Скачайте OpenAPI-спецификацию в разделе Specification.
Аутентификация запросов
В каждом запросе передавайте в HTTP-заголовки:
|
|
Заголовок: Выпустите его личном кабинете по инструкции. |
|
|
Заголовок: Токен кассового ПО. Получите его в личном кабинете или у менеджера интеграции. |
Если в запросе отсутствуют или переданы недействительные токены, сервер вернет статус 401 Unauthorized.
Пример запроса:
curl https://pay.yandex.ru/api/merchant/cash-register/v1/accounts \
--header 'Authorization: Api-Key <YandexPayApiKey>' \
--header 'Software-Authorization: <SoftwareAuthorization>' \
Статусы и ограничения
Возвраты доступны для платежей в одном из статусов paymentStatus:
CAPTURED— заказ успешно оплачен, средства списаны со счета плательщика;PARTIALLY_REFUNDED— совершен частичный возврат средств за заказ.
После успешного выполнения возврата статус платежа paymentStatus изменится:
- на
REFUNDED, если был произведен полный возврат; - на
PARTIALLY_REFUNDED, если после совершения возврата в заказе остались позиции.
Чтобы узнать результат возврата, настройте получение нотификаций /webhook. Если получение нотификаций невозможно, настройте поллинг метода /order/{order_id}/.
Находите нужную вам операцию по externalOperationId и проверяйте ее статус status:
|
|
Операция возврата не завершена. Запросите статус позже. |
|
|
Операция возврата запущена успешно. Фискализируйте чек возврата. |
|
|
Операция возврата не запущена. Повторно инициируйте возврат методом /orders/{orderId}/refund. |
Нельзя оформить возврат, если:
- сумма возврата меньше 1 рубля;
- после возврата в заказе останется меньше 1 рубля.
Виды возвратов
Доступен полный или частичный возврат платежа.
Полный возврат
Для выполнения полного возврата передайте:
externalOperationId— уникальный идентификатор операции на стороне продавца;refundAmount— сумма к возврату.
Корзину возвращаемых позиций refundCart можно не указывать, так как возврат осуществляется на всю корзину.
Частичный возврат
Для выполнения частичного возврата передайте:
externalOperationId— уникальный идентификатор операции на стороне продавца;refundAmount— сумма к возврату. Должна быть равна сумме возвращаемых позиций изrefundCart;refundCart— корзина возвращаемых позиций.
Формат refundCart зависит от логики возвратов, которая была выбрана при создании заказа.
Две логики возвратов
Cash Register API поддерживает две логики работы с возвратами. Логика определяется при создании заказа по наличию в позициях корзины cart цены за единицу товара unitPrice.
Ограничения
Если заказ уже создан, логика не может быть изменена.
Нельзя смешивать логики в одном заказе.
Различия проявляются при частичных возвратах — когда в refundCart указываются конкретные позиции для возврата.
|
Виды логики |
Базовая |
Расширенная |
|
Когда использовать |
В корзине только позиции с целыми ценами |
В корзине есть позиции с нецелыми ценами, весовые товары |
|
Как включить |
В |
В |
|
Какие поля
указывать в |
Одно из двух:
|
|
|
Сумма возврата |
Считается автоматически, могут быть проблемы с округлением |
Вы указываете точную сумму в |
Примеры
Базовая логика (без unitPrice)
Рассмотрим на примере заказа orderId = Order-123 со следующей корзиной без unitPrice:
{
"items": [
{
"productId": "id-1",
"title": "Шариковая ручка",
"total": "50", // 10 ручек по 5 рублей
"quantity": {
"count": "10"
}
},
{
"productId": "id-2",
"title": "Блокнот",
"total": "400", // 2 блокнота по 200 рублей
"quantity": {
"count": "2"
}
}
]
}
Выполним несколько возвратов по очереди:
Частичный возврат по количеству
Вернуть 2 ручки:
curl -X PUT 'https://pay.yandex.ru/api/merchant/cash-register/v1/orders/Order-123/refund' \
--header 'Authorization: Api-Key <YandexPayApiKey>' \
--header 'Software-Authorization: <SoftwareAuthorization>' \
--header 'Content-Type: application/json' \
--data '{
"externalOperationId": "Order-123_refund_1",
"refundAmount": "10",
"refundCart": {
"items": [
{
"productId": "id-1",
"quantityCount": "2"
}
]
}
}'
Результат:
- Возвращено: 2 ручки (
quantityCount) на сумму 10 рублей (refundAmount). - Осталось в заказе: 8 ручек (40 рублей) и 2 блокнота (400 рублей).
- Параметр
priceпропущен, но можно указать его прежнее значение.
|
До возврата |
После возврата |
|
|
Частичный возврат по цене
Уменьшить цену каждого блокнота на 30 рублей:
curl -X PUT 'https://pay.yandex.ru/api/merchant/cash-register/v1/orders/Order-123/refund' \
--header 'Authorization: Api-Key <YandexPayApiKey>' \
--header 'Software-Authorization: <SoftwareAuthorization>' \
--header 'Content-Type: application/json' \
--data '{
"externalOperationId": "Order-123_refund_2",
"refundAmount": "60",
"refundCart": {
"items": [
{
"productId": "id-2",
"price": "30"
}
]
}
}'
Результат:
- Цена блокнота уменьшилась с 200 до 170 рублей — на 30 рублей (
price). - Возвращено: 30 рублей * 2 блокнота = 60 рублей (
refundAmount). - Осталось в заказе: 8 ручек (40 рублей) и 2 блокнота (340 рублей).
- Параметр
quantityCountпропущен, но можно указать его прежнее значение.
|
До возврата |
После возврата |
|
|
Полный возврат
После двух частичных возвратов в заказе осталось: 8 ручек на сумму 40 рублей и 2 блокнота на сумму 340 рублей — всего 380 рублей.
Вернуть оставшуюся сумму (380 рублей):
curl -X PUT 'https://pay.yandex.ru/api/merchant/cash-register/v1/orders/Order-123/refund' \
--header 'Authorization: Api-Key <YandexPayApiKey>' \
--header 'Software-Authorization: <SoftwareAuthorization>' \
--header 'Content-Type: application/json' \
--data '{
"externalOperationId": "Order-123_refund_3",
"refundAmount": "380"
}'
Результат: корзина пустая, возвращена вся сумма заказа.
|
До возврата |
После возврата |
|
|
Расширенная логика (с unitPrice)
Рассмотрим на примере заказа orderId = Order-456 со следующей корзиной с unitPrice:
{
"items": [
{
"productId": "id-1",
"title": "Шариковая ручка",
"total": "10", // 3 ручки по 3.33 рубля
"quantity": {
"count": "3"
},
"unitPrice": "3.33" // цена одной ручки
},
{
"productId": "id-3",
"title": "Говядина",
"total": "400", // 500 грамм по 800 руб./кг.
"quantity": {
"count": "0.5" // 500 грамм = 0.5 кг.
},
"unitPrice": "800" // цена за килограмм
}
]
}
Выполним несколько возвратов по очереди:
Частичный возврат по количеству
Вернуть 1 ручку:
curl -X PUT 'https://pay.yandex.ru/api/merchant/cash-register/v1/orders/Order-456/refund' \
--header 'Authorization: Api-Key <YandexPayApiKey>' \
--header 'Software-Authorization: <SoftwareAuthorization>' \
--header 'Content-Type: application/json' \
--data '{
"externalOperationId": "Order-456_refund_1",
"refundAmount": "3.33",
"refundCart": {
"items": [
{
"productId": "id-1",
"quantityCount": "1",
"total": "3.33"
}
]
}
}'
Результат:
- Возвращено: 1 ручка на сумму 3.33 рубля (
refundAmount,total). - Осталось в заказе: 2 ручки (6.67 рубля) и 0.5 кг говядины (400 рублей).
|
До возврата |
После возврата |
|
|
Частичный возврат весового товара
Вернуть 100 г = 0.1 кг говядины:
curl -X PUT 'https://pay.yandex.ru/api/merchant/cash-register/v1/orders/Order-456/refund' \
--header 'Authorization: Api-Key <YandexPayApiKey>' \
--header 'Software-Authorization: <SoftwareAuthorization>' \
--header 'Content-Type: application/json' \
--data '{
"externalOperationId": "Order-456_refund_2",
"refundAmount": "80",
"refundCart": {
"items": [
{
"productId": "id-3",
"quantityCount": "0.1",
"total": "80"
}
]
}
}'
Результат:
- Возвращено: 0.1 кг * 800 руб/кг = 80 рублей (
refundAmount,total). - Осталось в заказе: 2 ручки (6.67 рубля) и 0.4 кг говядины (320 рублей).
|
До возврата |
После возврата |
|
|
Частичный возврат по цене
Уменьшить цену говядины на 100 руб/кг:
curl -X PUT 'https://pay.yandex.ru/api/merchant/cash-register/v1/orders/Order-456/refund' \
--header 'Authorization: Api-Key <YandexPayApiKey>' \
--header 'Software-Authorization: <SoftwareAuthorization>' \
--header 'Content-Type: application/json' \
--data '{
"externalOperationId": "Order-456_refund_3",
"refundAmount": "40",
"refundCart": {
"items": [
{
"productId": "id-3",
"price": "100",
"total": "40"
}
]
}
}'
Результат:
- Цена говядины уменьшилась с 800 до 700 руб/кг — на 100 руб/кг (
price). - Возвращено: 0.4 кг * 100 руб/кг = 40 рублей (
refundAmount,total). - Осталось в заказе: 2 ручки (6.67 рубля) и 0.4 г говядины (280 рублей).
|
До возврата |
После возврата |
|
|
Request
PUT
https://sandbox.pay.yandex.ru/api/merchant/cash-register/v1/orders/{orderId}/refund
Sandbox
PUT
https://pay.yandex.ru/api/merchant/cash-register/v1/orders/{orderId}/refund
Production
Path parameters
|
Name |
Description |
|
orderId |
Type: string Уникальный идентификатор заказа на стороне продавца. Должен совпадать с Max length: Example: `` |
Body
application/json
{
"externalOperationId": "0196578f-c97d-78fa-a58b-ba483e05cc44",
"refundAmount": "55.00",
"refundCart": {
"items": [
{
"productId": "id-1",
"price": "5.50",
"quantityCount": "10",
"total": "55.00"
}
]
},
"branchId": "01967cb8-f2c4-7293-9027-2976830c9485",
"managerId": "01967cb9-67b9-7942-99c2-bf95331d137d",
"motive": "Товар не устроил"
}
|
Name |
Description |
|
externalOperationId |
Type: string Уникальный идентификатор операции на стороне продавца. Max length: Example: |
|
refundAmount |
Type: string<double> Сумма к возврату. Минимальная сумма возврата — 1 рубль. Для частичного возврата, Для полного возврата Example: |
|
branchId |
Type: string Идентификатор точки продаж в системе продавца. Max length: Example: |
|
managerId |
Type: string Идентификатор менеджера в системе продавца. Max length: Example: |
|
motive |
Type: string Причина возврата. Max length: Example: |
|
refundCart |
Type: RefundCart Корзина возвращаемых позиций. Можно не указывать для полного возврата. Example
|
RefundCartItem
|
Name |
Description |
|
productId |
Type: string Идентификатор возвращаемого товара в системе продавца. Max length: Example: |
|
price |
Type: string<double> На сколько уменьшить цену за единицу товара после выполнения операции. Может быть полезным, если необходимо вернуть часть денег за товар без изменения количества. Если не указывать это поле в запросе, то считается, что цена осталась прежней. Пример:
Example: |
|
quantityCount |
Type: string<double> Количество единиц товара, которое необходимо вернуть. Если не указывать это поле в запросе, то считается, что количество не изменилось. Example: |
|
total |
Type: string<double> Сумма возврата за позицию. Используется в расширенной логике возвратов. Example: |
Example
{
"productId": "id-1",
"price": "5.50",
"quantityCount": "10",
"total": "55.00"
}
RefundCart
Корзина возвращаемых позиций. Можно не указывать для полного возврата.
|
Name |
Description |
|
items |
Type: RefundCartItem[] Min items: Example
|
Example
{
"items": [
{
"productId": "id-1",
"price": "5.50",
"quantityCount": "10",
"total": "55.00"
}
]
}
Responses
200 OK
Операция возврата успешно запущена.
Body
application/json
{
"data": {
"operation": {
"operationId": "019647c1-f086-750a-9b2f-7ed0fcce69d2",
"operationType": "REFUND",
"externalOperationId": "019647c2-8283-7ebe-85c8-b57c66ddc5e3",
"status": "PENDING",
"amount": "55.00",
"reason": "example"
}
}
}
|
Name |
Description |
|
data |
Type: RefundOrderResultData Example
|
OrderOperation
Операция заказа.
|
Name |
Description |
|
amount |
Type: string<double> Сумма операции в фиатной валюте. Example: |
|
operationId |
Type: string<uuid> Уникальный идентификатор операции. Example: |
|
operationType |
Type: string Тип операции. Enum: |
|
status |
Type: string Статус операции. Enum: |
|
externalOperationId |
Type: string Уникальный идентификатор операции на стороне продавца. Max length: Example: |
|
reason |
Type: string Причина ошибки. Max length: Example: |
Example
{
"operationId": "019647c1-f086-750a-9b2f-7ed0fcce69d2",
"operationType": "REFUND",
"externalOperationId": "019647c2-8283-7ebe-85c8-b57c66ddc5e3",
"status": "PENDING",
"amount": "55.00",
"reason": "example"
}
RefundOrderResultData
|
Name |
Description |
|
operation |
Type: OrderOperation Операция заказа. Example
|
Example
{
"operation": {
"operationId": "019647c1-f086-750a-9b2f-7ed0fcce69d2",
"operationType": "REFUND",
"externalOperationId": "019647c2-8283-7ebe-85c8-b57c66ddc5e3",
"status": "PENDING",
"amount": "55.00",
"reason": "example"
}
}
400 Bad Request
Ошибка валидации или бизнес-логики.
Body
application/json
{
"status": "fail",
"reason": "Invalid cart item parameter: total = 200.5004",
"reasonCode": "VALIDATION_ERROR"
}
All of 2 types
-
Type: Error
Example
{ "status": "fail", "reason": "example" } -
Type: object
reasonCode
Type: BadRequestErrorReasonCode
Код ошибки:
VALIDATION_ERROR— Ошибка валидации. Тело или параметры запроса не соответствуют спецификации.QRC_ID_NOT_FOUND— QR-код не найден, QR-табличка не привязана к продавцу.ORDER_COLLISION— Заказ с таким идентификатором уже существует, при этом его параметры отличаются от указанных в запросе.ORDER_NOT_FOUND— Заказ не найден.ORDER_ALREADY_CAPTURED— Заказ уже оплачен.ORDER_NOT_CAPTURED— Заказ не оплачен.ORDER_REFUND_COLLISION— Операция возврата заказа уже существует, при этом ее параметры отличаются от указанных в запросе.ORDER_REFUND_WITHOUT_CART_FOR_PARTIAL_REFUND— Запрос на частичный возврат без указания корзины. Для проведения частичного возврата заполните полеrefundCart.ORDERUPDATE_UNKNOWN_ITEM_PRODUCT_ID—productIdотсутствует в корзине заказа.ORDERUPDATE_DUPLICATE_ITEM_PRODUCT_ID—productIdдолжен быть уникальным.ORDERUPDATE_INVALID_ITEM_PRICE_WITH_QUANTITY— Некорректная комбинация цены и количества единиц товара для возврата.ORDERUPDATE_INVALID_ITEM_PRICE_RANGE— Неверно задана цена за единицу товара для возврата.ORDERUPDATE_INVALID_ITEM_QUANTITY— Неверно задано количество единиц товара для возврата.ANOTHER_REFUND_IN_PROGRESS— Другой возврат уже в процессе, дождитесь его завершения.
Список кодов ошибок открытый и будет дополняться, необходимо уметь обрабатывать неизвестные коды ошибок.
Enum:
VALIDATION_ERROR,QRC_ID_NOT_FOUND,ORDER_COLLISION,ORDER_NOT_FOUND,ORDER_ALREADY_CAPTURED,ORDER_NOT_CAPTURED,ORDER_REFUND_COLLISION,ORDER_REFUND_WITHOUT_CART_FOR_PARTIAL_REFUND,ORDERUPDATE_UNKNOWN_ITEM_PRODUCT_ID,ORDERUPDATE_DUPLICATE_ITEM_PRODUCT_ID,ORDERUPDATE_INVALID_ITEM_PRICE_RANGE,ORDERUPDATE_INVALID_ITEM_PRICE_WITH_QUANTITY,ORDERUPDATE_INVALID_ITEM_QUANTITY,ANOTHER_REFUND_IN_PROGRESSreason
Type: string
Example:
Invalid cart item parameter: total = 200.5004Example
{ "reasonCode": "VALIDATION_ERROR", "reason": "Invalid cart item parameter: total = 200.5004" }
Error
|
Name |
Description |
|
reason |
Type: string Описание ошибки. Example: |
|
status |
Type: string Default: Const: |
Example
{
"status": "fail",
"reason": "example"
}
BadRequestErrorReasonCode
Код ошибки:
VALIDATION_ERROR— Ошибка валидации. Тело или параметры запроса не соответствуют спецификации.QRC_ID_NOT_FOUND— QR-код не найден, QR-табличка не привязана к продавцу.ORDER_COLLISION— Заказ с таким идентификатором уже существует, при этом его параметры отличаются от указанных в запросе.ORDER_NOT_FOUND— Заказ не найден.ORDER_ALREADY_CAPTURED— Заказ уже оплачен.ORDER_NOT_CAPTURED— Заказ не оплачен.ORDER_REFUND_COLLISION— Операция возврата заказа уже существует, при этом ее параметры отличаются от указанных в запросе.ORDER_REFUND_WITHOUT_CART_FOR_PARTIAL_REFUND— Запрос на частичный возврат без указания корзины. Для проведения частичного возврата заполните полеrefundCart.ORDERUPDATE_UNKNOWN_ITEM_PRODUCT_ID—productIdотсутствует в корзине заказа.ORDERUPDATE_DUPLICATE_ITEM_PRODUCT_ID—productIdдолжен быть уникальным.ORDERUPDATE_INVALID_ITEM_PRICE_WITH_QUANTITY— Некорректная комбинация цены и количества единиц товара для возврата.ORDERUPDATE_INVALID_ITEM_PRICE_RANGE— Неверно задана цена за единицу товара для возврата.ORDERUPDATE_INVALID_ITEM_QUANTITY— Неверно задано количество единиц товара для возврата.ANOTHER_REFUND_IN_PROGRESS— Другой возврат уже в процессе, дождитесь его завершения.
Список кодов ошибок открытый и будет дополняться, необходимо уметь обрабатывать неизвестные коды ошибок.
Type: string
Enum: VALIDATION_ERROR, QRC_ID_NOT_FOUND, ORDER_COLLISION, ORDER_NOT_FOUND, ORDER_ALREADY_CAPTURED, ORDER_NOT_CAPTURED, ORDER_REFUND_COLLISION, ORDER_REFUND_WITHOUT_CART_FOR_PARTIAL_REFUND, ORDERUPDATE_UNKNOWN_ITEM_PRODUCT_ID, ORDERUPDATE_DUPLICATE_ITEM_PRODUCT_ID, ORDERUPDATE_INVALID_ITEM_PRICE_RANGE, ORDERUPDATE_INVALID_ITEM_PRICE_WITH_QUANTITY, ORDERUPDATE_INVALID_ITEM_QUANTITY, ANOTHER_REFUND_IN_PROGRESS
401 Unauthorized
Ошибка авторизации.
Body
application/json
{
"status": "fail",
"reason": "Authorization header is missing",
"reasonCode": "AUTHENTICATION_ERROR"
}
All of 2 types
-
Type: Error
Example
{ "status": "fail", "reason": "example" } -
Type: object
reasonCode
Type: UnauthorizedErrorReasonCode
Код ошибки:
AUTHENTICATION_ERROR— Ошибка авторизации. Повторно получите API-ключ.
Список кодов ошибок открытый и будет дополняться, необходимо уметь обрабатывать неизвестные коды ошибок.
Enum:
AUTHENTICATION_ERRORreason
Type: string
Example:
Authorization header is missingExample
{ "reasonCode": "AUTHENTICATION_ERROR", "reason": "Authorization header is missing" }
UnauthorizedErrorReasonCode
Код ошибки:
AUTHENTICATION_ERROR— Ошибка авторизации. Повторно получите API-ключ.
Список кодов ошибок открытый и будет дополняться, необходимо уметь обрабатывать неизвестные коды ошибок.
Type: string
Const: AUTHENTICATION_ERROR
Example: AUTHENTICATION_ERROR
429 Too Many Requests
Слишком много запросов, повторите запрос позже.
Если ошибка сохраняется при разумном использовании API, обратитесь в поддержку.
500 Internal Server Error
Внутренняя ошибка сервера. Тело ответа может отсутствовать или иметь произвольный формат.
При получении статуса 5XX, повторите запрос.