AploDelivery Partner API — портал разработчика · v2 (2026-10-16)

Отмена заказа

Работает

Эндпоинт

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 и плагином на кассе, — но их поведение объясняет, почему отмена заказа, который уже начал готовиться, может закончиться отказом:

  1. Плагин видит cancelState = requested только у заказов в state = created_verified (то есть уже реально созданных в iiko) — опрашивает через отдельный claim-эндпоинт, независимый от claim-а на создание.
  2. Плагин вызывает отмену в iiko только если DeliveryStatus заказа сейчас Unconfirmed или New. Если доставка уже перешла дальше (Waiting, OnWay и т. д.) — плагин отменить её не может и сообщает отказ с кодом not_new.
  3. Плагин сообщает исход: cancelled или rejected (с detailCode): not_new (доставка уже прошла стадию New), not_found (заказ не найден в iiko на момент попытки), sdk_error (техническая ошибка iiko SDK), no_cancel_cause (в справочнике кассы не настроена причина отмены для этой точки).
  4. Если плагин подтвердил 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 до ручного разбора — это известное ограничение, не баг вашей интеграции.

Поля в теле заказа

ПолеЗначенияСмысл
cancelStatenone \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, а не отдельное «событие отмены» — см. Вебхуки).

Связанное