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

Функция Apple Send Consumption Information теперь запрашивает пять полей, а не двенадцать, и вот каждое из них

Когда клиент запрашивает у Apple возврат средств, полезная нагрузка Send Consumption Information это ваш ответ. Apple сократила её с двенадцати полей до пяти: три обязательных и два необязательных. Здесь каждое поле, значения, которые оно принимает, и 12-часовое окно, в течение которого его нужно отправить.

Смартфон рядом с тонкой стопкой бумажных форм, у которой оторвано большинство страниц, иллюстрирует полезную нагрузку Apple Send Consumption Information, сокращённую с двенадцати полей до пяти

Главное

  • Полезная нагрузка Apple Send Consumption Information теперь содержит пять полей вместо двенадцати. Три обязательны, customerConsented, deliveryStatus и sampleContentProvided, и два необязательны, consumptionPercentage и refundPreference.
  • customerConsented это жёсткий шлюз. Указание самой Apple таково: если клиент не согласился делиться данными о потреблении, вы вообще не отправляете полезную нагрузку.
  • deliveryStatus несёт ваш самый сильный сигнал. DELIVERED говорит, что покупка сработала, а четыре значения UNDELIVERED сообщают Apple, что клиент так и не получил работающий продукт.
  • consumptionPercentage заменило старый четырёхступенчатый enum consumptionStatus точным числом в milliunits, где 100,000 milliunits означает, что товар был полностью потреблён.
  • refundPreference теперь строка, а не число. Три значения это DECLINE, GRANT_FULL и GRANT_PRORATED, и оно выражает предпочтение, а не решение. Решение по-прежнему за Apple.
  • Вы отправляете полезную нагрузку методом PUT на конечную точку Send Consumption Information с ключом по идентификатору транзакции, и Apple возвращает 202 Accepted с пустым телом. Окно составляет 12 часов после CONSUMPTION_REQUEST.
  • Для автопродлеваемых подписок Apple сама рассчитывает потребление по прошедшему времени, поэтому consumptionPercentage предназначено для расходуемых и непродлеваемых покупок.

Список фактов, которые Apple запрашивает у вас, когда клиент хочет вернуть деньги, стал заметно короче. Полезная нагрузка Send Consumption Information, единственный ответ, который разработчик может отправить в процесс принятия решения о возврате в App Store, раньше содержала двенадцать полей. Теперь их пять. Apple сократила её примерно во время WWDC24, свернув большинство прежних вопросов о профиле аккаунта в два простых и одно число. Меньшее число полей это не мелкая правка. Она меняет то, какие доказательства взвешивает Apple, и меняет то, в каких полях вы не можете позволить себе ошибиться.

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

Что такое Send Consumption Information

Send Consumption Information это конечная точка App Store Server API, которую вы вызываете после того, как Apple отправляет на ваш сервер уведомление CONSUMPTION_REQUEST. Это уведомление означает, что клиент запросил у Apple возврат средств за встроенную покупку и Apple хочет получить ваши данные, прежде чем принять решение. Вы отвечаете, отправляя небольшое тело JSON, ConsumptionRequest, на эту конечную точку методом PUT. Apple читает его, взвешивает относительно истории клиента и выносит решение. Вы никогда не решаете вопрос о возврате. Вы предоставляете факты.

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

Пять полей, которые Apple запрашивает сейчас

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

ПолеОбязательноеТипЧто содержит
customerConsentedДаBooleanСогласился ли клиент делиться данными о потреблении с Apple
deliveryStatusДаStringДоставило ли ваше приложение работающий продукт
sampleContentProvidedДаBooleanПредлагали ли вы бесплатный образец или пробную версию до покупки
consumptionPercentageНетIntegerКакая часть покупки была потреблена, в milliunits
refundPreferenceНетStringПредпочтительный для вас исход по запросу на возврат

customerConsented это шлюз

customerConsented это Boolean, и именно это поле определяет, отправляете ли вы что-либо вообще. Оно фиксирует, согласился ли клиент разрешить вам делиться данными о потреблении с Apple. Указание Apple прямое: если клиент не дал согласия, не отправляйте информацию о потреблении. Так что это не поле, которое вы переключаете в true, чтобы усилить свою позицию. Оно отражает реальное да или нет, которым вы уже должны располагать, а значение false здесь означает, что остальную часть полезной нагрузки отправлять не следует.

deliveryStatus это поле, которое влияет на возвраты

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

ЗначениеЧто оно сообщает Apple
DELIVEREDПриложение доставило работающую встроенную покупку
UNDELIVERED_QUALITY_ISSUEПокупка не была доставлена из-за проблемы с качеством
UNDELIVERED_WRONG_ITEMКлиент получил не тот товар
UNDELIVERED_SERVER_OUTAGEСбой сервера остановил доставку
UNDELIVERED_OTHERПокупка не была доставлена по иной причине

sampleContentProvided отвечает на вопрос о справедливости

sampleContentProvided это Boolean. Оно фиксирует, предоставили ли вы клиенту бесплатный образец, пробную версию или понятную информацию о том, что делает покупка, до того как он её совершил. Значение true здесь это небольшой сигнал справедливости: у клиента была возможность узнать, что он покупает. Само по себе оно ничего не решает, но это одно из всего трёх обязательных полей, так что Apple явно хочет видеть его в каждом ответе.

consumptionPercentage теперь число, а не статус

Это поле изменилось больше всего. В старой полезной нагрузке был consumptionStatus, enum из четырёх ступеней: UNDECLARED, NOT_CONSUMED, PARTIALLY_CONSUMED, FULLY_CONSUMED. Новая полезная нагрузка заменяет его на consumptionPercentage, целое число, измеряемое в milliunits. 100,000 milliunits означает, что товар был полностью потреблён, значит 50,000 это половина, а 0 это нетронуто. Точное число лучше деления на четыре корзины, потому что позволяет сказать, что клиент израсходовал 90 процентов пакета кредитов, а не округлять вниз до частично потреблённого.

Одна оговорка, на которой многие спотыкаются. Для автопродлеваемых подписок Apple сама рассчитывает потребление по прошедшему времени, поэтому consumptionPercentage предназначено для расходуемых и непродлеваемых покупок. Отправляйте его там, где оно применимо, и позвольте Apple выводить его там, где оно неприменимо.

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

refundPreference выражает предпочтение, а не приговор

refundPreference это необязательная строка, и именно здесь вы сообщаете Apple, какой исход предпочли бы. Оно тоже изменило форму. Старое поле было числом со значениями вроде prefer-grant, prefer-decline и no-preference. Новое это именованная строка с тремя значениями.

ЗначениеО чём вы просите
GRANT_FULLВы предпочли бы, чтобы Apple предоставила полный возврат
GRANT_PRORATEDВы предпочли бы частичный возврат, отражающий использованное
DECLINEВы предпочли бы, чтобы Apple отклонила возврат

Что Apple убрала и почему это важно

Старый ConsumptionRequest содержал двенадцать полей. Семь из них исчезли из того, что вы отправляете. accountTenure, lifetimeDollarsPurchased, lifetimeDollarsRefunded, playTime, userStatus, platform и appAccountToken были той половиной полезной нагрузки, что профилировала аккаунт, полями, которые требовали разложить клиента по корзинам по тому, как долго у него есть аккаунт, сколько он потратил, сколько ему вернули и как долго он пользовался приложением.

Apple убрала их по причине, которую стоит отметить. Эти поля требовали от разработчиков передать профиль клиента, и большинство разработчиков либо оставляли их незаполненными, либо угадывали. Пять оставшихся касаются покупки и доставки, фактов, которые вы действительно можете проверить в собственных системах. Сдвиг происходит от того, кто клиент, к тому, что произошло с этой конкретной покупкой.

Старая полезная нагрузкаТекущая полезная нагрузка
Всего полей125
Обязательные поляФактически не обеспечивались3
Сигнал о потребленииconsumptionStatus, четыре корзиныconsumptionPercentage, точные milliunits
Предпочтение по возвратуЧисловой enumИменованная строка, 3 значения
Профилирование аккаунтастаж, совокупные траты, возвраты, игровое время, статусУдалено

Конечная точка и часы

Вы отправляете полезную нагрузку методом HTTP PUT на конечную точку Send Consumption Information, ключом служит идентификатор транзакции спорной покупки: PUT /inApps/v1/transactions/consumption/{transactionId}. Успех возвращает 202 Accepted, а тело ответа пустое. Этот пустой ответ ожидаем, а не ошибка. Он подтверждает, что Apple поставила ваши данные в очередь, и ничего не говорит о конечном исходе, который приходит позже уведомлением REFUND или REFUND_DECLINED.

Часы это та часть, которую нельзя растянуть. Apple даёт вам 12 часов с момента CONSUMPTION_REQUEST на ответ. Используется только ваш первый ответ, поэтому первая полезная нагрузка должна быть полной и правильной. Apple может отправить CONSUMPTION_REQUEST несколько раз по одной и той же покупке, но срок по каждому фиксирован, и ручная проверка редко укладывается в 12 часов с учётом часовых поясов и выходных.

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

Деньги ушли ещё до того, как случился возврат

Возврат за расходуемую покупку это не чистая отмена. К тому моменту, когда клиент просит вернуть деньги за пакет кредитов или партию AI-генераций, вы уже потратились на их доставку: инференс на GPU по каждому запросу, вызовы API сторонних моделей с оплатой за токен, хранилище для всего, что вы произвели, и любые выплаты авторам или партнёрам, привязанные к этому использованию. Цена из магазина возвращается клиенту. Ваши затраты на доставку к вам не возвращаются. Так что возврат, который вы могли бы оспорить, это не выход в ноль, это чистый убыток на всё, что вы заплатили за обслуживание аккаунта.

Пять полей это то, как вы избегаете двойной оплаты

deliveryStatus, установленный в DELIVERED, и высокий consumptionPercentage это два факта, которые сообщают Apple, что клиент получил и использовал продукт. Они ваше доказательство того, что вычисления, вызовы API и хранилище выполнили свою работу. Оставьте полезную нагрузку неотправленной, и Apple об этом не узнает. Она решает по претензии клиента, возврат вероятнее пройдёт, а вы принимаете на себя и отменённый доход, и стоящие за ним затраты на доставку.

Чарджбэки это худшая дверь, и молчание ведёт к ней

Клиент, который не смог добиться своего через процесс возврата у Apple, всё ещё может оспорить списание в своём банке. Чарджбэк по карте окончателен, он влечёт фиксированный сбор за оспаривание и выводит решение из рук Apple и ваших. Хороший ответ на CONSUMPTION_REQUEST удерживает спор внутри системы Apple, где у вас есть право голоса. Игнорирование подталкивает пограничные случаи к единственному каналу, где у вас его нет.

Как это делает RefundHalt

Пять полей выглядят просто, пока вам не приходится заполнять их правильно, в течение 12 часов, по каждому CONSUMPTION_REQUEST, привязанными к нужной транзакции. RefundHalt перехватывает уведомление, считывает ваши собственные записи о доставке и использовании по этой покупке и отправляет полезную нагрузку автоматически до закрытия окна. deliveryStatus отражает то, что действительно показывают ваши логи, consumptionPercentage берётся из реального использования, а не из догадки, а refundPreference следует политике, которую вы задали один раз. Вы получаете спорный возврат, рассмотренный с доказательствами, а не упущенный срок и решение, принятое без вас.

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

Сколько полей сейчас у Apple Send Consumption Information?
Пять. Три обязательны, customerConsented, deliveryStatus и sampleContentProvided, и два необязательны, consumptionPercentage и refundPreference. В предыдущей версии полезной нагрузки было двенадцать полей, и Apple убрала профилирующие аккаунт, такие как accountTenure, lifetimeDollarsPurchased и userStatus.
Что означает deliveryStatus в запросе о потреблении?
deliveryStatus сообщает Apple, доставило ли ваше приложение работающую встроенную покупку. DELIVERED означает, что доставило. Четыре значения UNDELIVERED, UNDELIVERED_QUALITY_ISSUE, UNDELIVERED_WRONG_ITEM, UNDELIVERED_SERVER_OUTAGE и UNDELIVERED_OTHER, каждое говорит, что не доставило, по указанной причине. Это самый сильный сигнал в полезной нагрузке, поэтому он должен совпадать с вашими собственными логами.
consumptionPercentage это процент или необработанное число?
Это целое число, измеряемое в milliunits, а не обычный процент. 100,000 milliunits означает, что клиент полностью потребил покупку, значит 50,000 это половина, а 0 это нетронуто. Оно заменило старый enum consumptionStatus, у которого было всего четыре корзины от непотреблённого до полностью потреблённого.
Останавливает ли возврат установка refundPreference в DECLINE?
Нет. refundPreference выражает предпочтительный для вас исход, оно ничего не решает. DECLINE сообщает Apple, что вы предпочли бы не возвращать средства, а GRANT_FULL или GRANT_PRORATED сообщают обратное, но Apple взвешивает ваше предпочтение относительно истории клиента и собственной политики и принимает окончательное решение.
Что если клиент не дал согласия делиться данными о потреблении?
Тогда вам не следует отправлять полезную нагрузку. customerConsented это обязательный Boolean, и указание Apple таково: если клиент не согласился делиться данными о потреблении, вы вообще не отвечаете на CONSUMPTION_REQUEST. Согласие это реальное да или нет, которым вы уже должны располагать, а не значение, которое вы ставите в true, чтобы помочь своему делу.
Сколько времени у меня есть на отправку информации о потреблении?
12 часов с момента, когда Apple отправляет уведомление CONSUMPTION_REQUEST. Вы отвечаете методом PUT на конечную точку Send Consumption Information, и успех возвращает 202 Accepted с пустым телом. Используется только ваш первый ответ, поэтому первая полезная нагрузка должна быть полной, и ручной процесс редко укладывается в это окно.

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

RefundHalt

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

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

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

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