Отмена заказа
Эндпоинт
POST /v1/orders/{id}/cancel
{"reason": "guest_request", "comment": "необязательный комментарий, до 500 символов"}
reason — обязателен, одно из: guest_request, payment_failed, duplicate, other. comment — необязателен.
Что реально происходит (три ветки)
Отмена — это не отдельный переход state-машины из Жизненного цикла заказа, а отдельное поле cancelState. Сервер обрабатывает вызов в одной транзакции с блокировкой строки и выбирает одну из веток по текущему state заказа:
Текущий state заказа | Что происходит | HTTP-ответ |
|---|---|---|
queued (плагин ещё не забрал заказ) | заказ немедленно переводится в state = cancelled (терминальное состояние создания), cancelState = cancelled. Плагин никогда его не увидит — claim-запрос его больше не выбирает | 200 с телом заказа |
claimed, prepared, create_sending, reconciliation_required, created_verified (заказ уже в работе) | state не меняется, выставляется cancelState = requested. Дальше отмену должен выполнить плагин на кассе (см. ниже) | 202 с телом заказа |
rejected_final, manual_review, cancelled, либо cancelState уже финальный | ничего не меняется — это no-op, кроме случая, когда предыдущая попытка отмены уже была отклонена плагином | 200 (no-op) с телом заказа, либо 409 cancel_rejected |
Вызов идемпотентен по самому заказу: повторный POST .../cancel на уже отменённый/финальный заказ безопасен и возвращает 200, а не ошибку.
Важный нюанс: если cancelState уже стал cancelled, повторный вызов cancel никогда не переставит его обратно в requested, даже если формально state заказа всё ещё «в работе» — это осознанная защита от повторного попадания уже отменённого заказа в очередь на отмену у плагина.
Что делает плагин с заказом «в работе» (cancelState = requested)
Партнёру эти эндпоинты не нужны напрямую — они между AploDelivery и плагином на кассе, — но их поведение объясняет, почему отмена заказа, который уже начал готовиться, может закончиться отказом:
- Плагин видит
cancelState = requestedтолько у заказов вstate = created_verified(то есть уже реально созданных в iiko) — опрашивает через отдельный claim-эндпоинт, независимый от claim-а на создание. - Плагин вызывает отмену в iiko только если
DeliveryStatusзаказа сейчасUnconfirmedилиNew. Если доставка уже перешла дальше (Waiting,OnWayи т. д.) — плагин отменить её не может и сообщает отказ с кодомnot_new. - Плагин сообщает исход:
cancelledилиrejected(сdetailCode):not_new(доставка уже прошла стадиюNew),not_found(заказ не найден в iiko на момент попытки),sdk_error(техническая ошибка iiko SDK),no_cancel_cause(в справочнике кассы не настроена причина отмены для этой точки). - Если плагин подтвердил
cancelled— заказу также проставляетсяdeliveryStatus = Cancelled.
Практическое следствие: окно, в котором отмена заказа «в работе» гарантированно сработает — это пока deliveryStatus ещё Unconfirmed/New. Как только доставка перешла в Waiting, автоматическая отмена скорее всего закончится rejected/not_new, и дальше это уже не программный, а человеческий процесс — ресторан должен обработать отмену вручную.
409 cancel_rejected
Если предыдущая попытка отмены уже была отклонена плагином, повторный POST .../cancel возвращает 409 с {"error":"cancel_rejected"} вместо тела заказа. Правильная реакция на клиенте:
- показать гостю «свяжитесь с рестораном» (или аналогичный текст на вашей стороне);
- не повторять отмену автоматически — сервер не выполнит её иначе, ситуация не изменится сама по себе без вмешательства человека.
Ещё один граничный случай: заказ так и не был создан
Если отмена запрошена, пока заказ ещё «в работе» (cancelState = requested), а создание тем временем окончательно провалилось (state → rejected_final) — сервер сам закрывает это состояние: раз в iiko ничего создано не было, cancelState автоматически переводится в cancelled и уходит вебхук order.cancel_result, без участия плагина. Исключение — manual_review: этот исход неоднозначен (человеку, возможно, всё же придётся проверить iiko вручную), поэтому cancelState там остаётся requested до ручного разбора — это известное ограничение, не баг вашей интеграции.
Поля в теле заказа
| Поле | Значения | Смысл |
|---|---|---|
cancelState | none \ | requested \ |
cancelReason | значение reason, присланное партнёром, либо null | причина, которую вы указали при вызове |
cancelDetail | код причины отказа плагина (например not_new), либо null | заполняется только когда cancelState = rejected |
Вебхуки, связанные с отменой
order.cancel_requested — при переходе cancelState → requested на заказе «в работе»; order.cancel_result — при получении исхода от плагина (cancelled/rejected) или при автоматическом закрытии из-за rejected_final. Переход queued → cancelled идёт по общему order.state_changed (это переход state, а не отдельное «событие отмены» — см. Вебхуки).