Все статьи
Deep dive8 мин чтения

Apple может отменить уже выданный возврат средств, и отменённый возврат, который игнорирует ваш сервер, блокирует доступ клиенту, который заплатил

Когда App Store отменяет уже выданный возврат средств, он ожидает, что ваш сервер восстановит доступ, который вы отозвали. Вот как работают уведомления о возврате, отклонённом возврате и отменённом возврате в App Store и Google Play, и во что обходится каждое из них, когда вы его игнорируете.

Светящийся замок на телефоне рядом с банковской выпиской и недосягаемым ключом, символизирующий отменённый возврат, который блокирует доступ клиенту, который заплатил

Главное

  • App Store отправляет уведомление REFUND_REVERSED, когда отменяет ранее выданный возврат средств из-за того, что клиент его оспорил, и инструкция Apple однозначна: если ваше приложение отозвало контент или услуги, оно должно их восстановить.
  • Уведомление REFUND означает, что App Store уже вернул средства по транзакции, поэтому ваш сервер должен отозвать право доступа. Уведомление REFUND_DECLINED означает, что Apple отклонила запрос, и клиент сохраняет и доступ, и списанные средства.
  • Если вы отзываете доступ при возврате, но никогда не обрабатываете отмену, клиент, которому восстановили списание, остаётся заблокированным. Это обращение в поддержку, отзыв на одну звезду, а в Google Play разочарование, которое может обернуться возвратным платежом, теперь стоящим вам денег.
  • revocationReason от Apple сообщает вам, почему произошёл возврат: value 1 означает, что Apple вернула средства из-за реальной или предполагаемой проблемы в вашем приложении, value 0 означает другую причину, например случайную покупку.
  • В Google Play нет уведомления об отмене. Он отправляет VoidedPurchaseNotification, когда покупка аннулируется, и отдельное PendingRefundReviewNotification для возвратных платежей, а остальное вы сверяете с помощью pull-модели Voided Purchases API.
  • Google Play даёт вам 24 часа на ответ на PendingRefundReviewNotification путём вызова orders.reviewrefund, и он учитывает только ваш первый вызов. С 3 августа 2026 года проигранный возвратный платёж обходится разработчику в цену за вычетом сервисного сбора Google плюс банковский сбор.
  • Обрабатывайте уведомления о возврате из App Store идемпотентно. Повторные доставки нормальны, поэтому привязывайте каждый отзыв и восстановление к id транзакции и делайте повторное уведомление операцией без эффекта.

Возврат средств не всегда последнее слово. App Store может отменить уже выданный возврат после того, как клиент его оспорит, и когда этот отменённый возврат попадает на ваш сервер, он несёт одну инструкцию: верните доступ. Большинство команд подключают простое уведомление REFUND, отрезают клиента и на этом останавливаются. Они никогда не строят вторую половину. Поэтому, когда приходит отмена, ничего не срабатывает, и клиент, который снова платит, остаётся заблокированным и лишённым того, что купил. Вот как работает полный набор уведомлений о возврате в App Store и Google Play, и во что каждое из них вам обходится, когда вы его игнорируете.

App Store отправляет три уведомления о возврате, а не одно

Большинство механизмов обработки возврата построено для одного события: деньги вернулись, отрезаем клиента. Поток App Store Server Notifications V2 на самом деле несёт три отдельных исхода возврата, и они требуют трёх разных действий. Два из них меняют то, к чему клиент может получить доступ. Один из них отменяет первое. Вот полный набор, словами самой Apple.

УведомлениеЧто оно означаетЧто делает ваш сервер
CONSUMPTION_REQUESTКлиент запросил возврат, и Apple нужны данные о потребленииОтправьте полезную нагрузку о потреблении в течение 12 часов
REFUNDApp Store вернул средства по транзакцииОтзовите право доступа для этой транзакции
REFUND_DECLINEDApp Store отклонил запрос на возвратНичего; клиент сохраняет доступ и списание
REFUND_REVERSEDApp Store отменил выданный им возвратВосстановите контент или услугу, которые вы отозвали

REFUND, то уведомление, которое обрабатывает каждая команда

Когда App Store обрабатывает возврат, он отправляет уведомление REFUND на URL, который вы настроили, и определение Apple простое: оно «указывает, что App Store успешно вернул средства по транзакции для расходуемого In-App Purchase, нерасходуемого In-App Purchase, автопродлеваемой подписки или подписки без автопродления». Вы сохраняете возвращённую транзакцию, отзываете всё, что она купила, и Apple просит вас сообщить клиенту о том, что изменилось, с помощью контекстного сообщения в приложении. Это уведомление, которое все подключают первым, и часто единственное.

REFUND_DECLINED, то уведомление, которое ничего от вас не требует

REFUND_DECLINED означает именно то, что говорит: «App Store отклонил запрос на возврат». Клиент попросил, Apple сказала нет, и транзакция остаётся в силе. В доступе клиента ничего не меняется, поэтому ваша логика прав доступа здесь ничего не делает. Ценность этого уведомления в учёте. Оно завершает цикл запроса на возврат, на который вы, возможно, ответили CONSUMPTION_REQUEST, и подтверждает, что у клиента по-прежнему есть то, за что он заплатил. Относитесь к нему как к записи, а не как к действию.

REFUND_REVERSED, то уведомление, которое застаёт команды врасплох

Это уведомление, которое большинство конвейеров обработки возврата никогда не обрабатывают. Определение Apple недвусмысленно: REFUND_REVERSED «указывает, что App Store отменил ранее выданный возврат из-за спора, поднятого клиентом. Если ваше приложение отозвало контент или услуги в результате соответствующего возврата, оно должно их восстановить». Прочитайте это дважды. Apple выдала клиенту возврат, вы отозвали доступ, затем Apple решила, что возврат не должен оставаться в силе, и отозвала его. Списание снова активно. Клиент заплатил, и если ваш сервер умеет только отзывать, он по-прежнему заблокирован. Отменённый возврат это единственное событие возврата, которое возвращает доступ, и это то, к чему почти никто не готовится.

Во что на самом деле обходится вам отменённый возврат

Пропущенная отмена это не ошибка округления. Проследите за деньгами в обоих направлениях, потому что ошибка в любой из половин имеет цену.

Пропустите отмену, и вы оставите платящего клиента заблокированным. Apple восстановила списание, поэтому клиент снова потратился, а ваше приложение отказывает ему в том, что он купил. Немедленная цена это время поддержки и возврат из доброй воли, который вам теперь, возможно, придётся выдать самому, на этот раз без возвращающейся комиссии магазина, чтобы смягчить его. Более медленная цена это отзыв и отток клиентов, а в Google Play то же самое разочарование от блокировки и есть именно то, что превращается в возвратный платёж.

Пропустите первоначальный возврат, и вы продолжите обслуживать клиента, который ничего не заплатил. Зеркальная ошибка это вообще никогда не отзывать. Клиент, которому вернули средства, но который по-прежнему генерирует изображения, вызывает ваши API и заполняет ваше хранилище, накапливает реальные расходы на продажу, которая была отменена. Вычисления, сторонние вызовы и хранилище это деньги, которые вы уже потратили, и ничего из этого не возвращается вместе с возвратом средств.

  • Расходы на поддержку: человек, отвечающий на обращение по поводу доступа, который ваш собственный код удалил и так и не восстановил.
  • Возвраты из доброй воли: повторная выдача денег клиенту, которого вы ошибочно заблокировали, без возвращаемой комиссии магазина за ручной жест.
  • Напрасные траты: вычисления, вызовы API и хранилище, потреблённые аккаунтом с возвратом, который вы так и не отрезали.
  • Риск возвратного платежа: в Google Play клиент, который чувствует себя дважды списанным, может оспорить платёж, и проигранный спор теперь ложится на вас.

Отменённый возврат и простой возврат это один и тот же поток webhook, направленный в противоположные стороны. Обработайте один и пропустите другой, и вы заплатите с обоих концов.

Рука, тянущая бумажный чек обратно по столу, символизирующая отменённый возврат, который отменяет возврат App Store, на который ваш сервер уже отреагировал

Почему Apple отменяет возврат, и как читать revocationReason

Отмена не случайна. Apple связывает её со «спором, поднятым клиентом», то есть клиент оспаривает решение о возврате постфактум. Когда возврат был впервые выдан, транзакция несла revocationDate и revocationReason, и эту причину стоит прочитать, прежде чем что-либо ниже по потоку начнёт на неё реагировать.

  • revocationReason 1: App Store вернул средства «из-за реальной или предполагаемой проблемы в вашем приложении». Это сигнал о вашем продукте, а не только об этом клиенте.
  • revocationReason 0: App Store вернул средства «по другим причинам, например из-за случайной покупки». Никакого сигнала о качестве приложения не прилагается.

Когда для этой транзакции приходит REFUND_REVERSED, отзыв отменяется. Ваша логика восстановления должна найти исходный id транзакции, подтвердить, что вы её отозвали, и вернуть право доступа точно таким, каким оно было.

Google Play не отправляет отмену, поэтому вы вместо этого проводите сверку

Модель Google Play отличается, и это различие важно, если вы пропускаете обе площадки через один обработчик webhook. Эквивалента REFUND_REVERSED у Google нет. Уведомления разработчика в реальном времени от Google разделяют события возврата на два сообщения, а отмены обрабатываются сверкой, а не push-уведомлением.

Уведомление об аннулированной покупке

Когда покупка в Google Play аннулируется, ваш сервер получает VoidedPurchaseNotification. В нём указываются purchaseToken и orderId, productType подписки или разовой покупки, и refundType, который представляет собой либо полное аннулирование, либо частичный возврат на основе количества для покупок с несколькими единицами. Google говорит, что этих данных достаточно, чтобы найти нужную покупку и скорректировать право доступа. Для чего-то большего он направляет вас к Voided Purchases API, pull-модели, которая перечисляет аннулированные заказы в пределах диапазона временных меток, который вы запрашиваете.

Проверка возвратного платежа и её 24-часовой таймер

Возвратные платежи приходят через другое сообщение, PendingRefundReviewNotification. Когда клиент оспаривает списание в своём банке, Google Play отправляет это уведомление и запускает таймер. У вас есть 24 часа, чтобы вызвать orders.reviewrefund с предпочтением по возврату и любыми доказательствами использования, чтобы Google мог оспорить незаконный возвратный платёж от вашего имени. Google учитывает ваш первый вызов и игнорирует остальные. Это аналог Google для CONSUMPTION_REQUEST от Apple, единственное окно, где ваша сторона спора имеет значение.

Поскольку push-уведомления об отмене нет, возвратный платёж, который Google оспаривает и выигрывает, не приходит в виде аккуратного события восстановления. Вы сверяете его с Voided Purchases API и собственными записями. Урок тот же, что и в App Store: аннулированный заказ не всегда окончателен, и состояние ваших прав доступа должно уметь двигаться назад, а не только вперёд.

Событие возвратаApp StoreGoogle Play
Возврат выданУведомление REFUNDVoidedPurchaseNotification
Возврат отклонёнУведомление REFUND_DECLINEDНет отдельного сообщения
Возврат отменёнУведомление REFUND_REVERSEDНет push; сверка через Voided Purchases API
Окно доказательств спораCONSUMPTION_REQUEST, 12 часовPendingRefundReviewNotification, 24 часа
Кто может выдать возвратТолько AppleGoogle, или вы во вкладке «Заказы»

Как обрабатывать каждое уведомление о возврате, никого не блокируя

Вам не нужны отдельные конвейеры для каждой площадки. Вам нужен обработчик, который может перемещать право доступа в обоих направлениях и относится к каждому сообщению как к возможно продублированному.

  • Стройте восстановление, а не только отзыв. Для каждого пути, который удаляет доступ по REFUND, напишите обратный, который восстанавливает его по REFUND_REVERSED, привязанный к тому же id транзакции.
  • Сделайте это идемпотентным. Обе площадки могут доставить одно и то же уведомление более одного раза, поэтому привязывайте каждый отзыв и восстановление к id транзакции или заказа и делайте повтор операцией без эффекта.
  • Читайте причину, прежде чем действовать. Используйте revocationReason, чтобы отличить возврат из-за качества приложения от случайного, и направляйте связанные с качеством приложения тому, кто отвечает за качество продукта.
  • Отвечайте на окна доказательств вовремя. Отправляйте данные о потреблении Apple в течение 12 часов после CONSUMPTION_REQUEST и вызывайте orders.reviewrefund в течение 24 часов после PendingRefundReviewNotification.
  • Сохраняйте каждое событие. Храните REFUND_DECLINED и необработанные уведомления, чтобы отмену, пришедшую позже, можно было сопоставить с возвратом, который она отменяет.

Ничто из этого не меняет того, происходит ли возврат. Это меняет то, заметит ли когда-нибудь клиент по ту сторону отменённого возврата, что ваш сервер ошибся.

Частые вопросы

Что такое уведомление REFUND_REVERSED в App Store?
Это App Store сообщает вашему серверу, что он отменил ранее выданный возврат, потому что клиент его оспорил. Инструкция Apple однозначна: если ваше приложение отозвало контент или услуги в результате этого возврата, оно должно их восстановить. Списание снова активно, поэтому клиент должен получить свой доступ обратно.
Что мне делать, когда я получаю уведомление REFUND_DECLINED?
Ничего с доступом клиента. REFUND_DECLINED означает, что App Store отклонил запрос на возврат, поэтому транзакция остаётся в силе, и клиент сохраняет то, за что заплатил. Относитесь к нему как к записи, которая закрывает запрос на возврат, часто тот, на который вы ответили CONSUMPTION_REQUEST.
Отправляет ли Google Play уведомление, когда возврат или возвратный платёж отменяется?
Нет. У Google Play нет эквивалента REFUND_REVERSED от Apple. Он отправляет VoidedPurchaseNotification, когда покупка аннулируется, и PendingRefundReviewNotification для возвратных платежей, но оспоренный возвратный платёж, который Google выигрывает, не отправляется вам обратно. Вы сверяете его с помощью Voided Purchases API и собственных записей.
Сколько времени у меня есть, чтобы ответить на возвратный платёж Google Play?
24 часа. Когда Google Play отправляет PendingRefundReviewNotification, у вас есть 24 часа, чтобы вызвать orders.reviewrefund с предпочтением по возврату и доказательствами использования. Google учитывает только ваш первый вызов. С 3 августа 2026 года проигранный возвратный платёж обходится разработчику в цену за вычетом сервисного сбора Google плюс банковский сбор.
Что сообщает мне revocationReason по возвращённой транзакции App Store?
Он сообщает вам, почему Apple вернула средства. Value 1 означает, что Apple вернула средства из-за реальной или предполагаемой проблемы в вашем приложении, что является продуктовым сигналом. Value 0 означает другую причину, например случайную покупку. Его прочтение позволяет отделить возвраты, указывающие на баг, от рутинных.

Источники и дополнительное чтение

RefundHalt

Автопилот возвратов для App Store и Google Play

Читайте дальше

Deep dive8 мин чтения

Кто платит за возврат в вашем приложении, в основном вы, но не за ту комиссию, которую вы, кажется, теряете

Когда клиенту возвращают деньги, и Apple, и Google возвращают свою комиссию, поэтому вы теряете не долю магазина. Вот кто платит за возврат в вашем приложении, что на самом деле уходит из вашей выплаты и почему чарджбэк обходится дороже обычного возврата.

Deep dive8 мин чтения

Отмена подписки и возврат средств, это две разные вещи, и только одна из них возвращает деньги клиенту

Отмена подписки просто останавливает следующее списание, клиент сохраняет доступ до конца оплаченного периода, и никакие деньги не двигаются. Возврат отменяет уже прошедший платёж и забирает доступ вместе с ним. Вот где эти два пути расходятся, во что каждый обходится вам и почему только возврат когда-либо доходит до вашего сервера.

Следующий запрос на возврат уже в пути.

Настройте RefundHalt за то время, что уходит на чтение очередного письма в поддержку о возврате, который вы не успели оспорить.