О подписках

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

Если вы еще не настроили продукты по подписке для своего приложения, см. раздел «Создание и настройка продуктов» .

Обзор подписок

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

С помощью базовых планов и предложений вы можете создавать несколько конфигураций для одного и того же продукта подписки. Например, вы можете создать ознакомительное предложение для пользователей, которые никогда не подписывались на ваше приложение. Аналогично, вы можете создать предложение по обновлению подписки для пользователей, которые уже подписаны.

Подробный обзор продуктов по подписке, базовых тарифных планов и предложений см. в документации в Справочном центре Play Console .

Библиотека Play Billing поддерживает следующие типы подписки:

  • Подписка — в этом типе один элемент соответствует одному праву. Например, подписка на сервис потоковой передачи музыки.

  • Подписка с дополнениями — в этом типе подписки одна покупка может включать в себя несколько различных услуг, объединенных в один пакет. Например, подписка на музыкальный стриминговый сервис и подписка на видеосервис. Для получения информации, касающейся подписки с дополнениями, см. раздел «Подписки с дополнениями» .

Интеграция предоплаченных тарифных планов

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

Для пополнения счета запустите процесс оплаты так же, как и при первоначальной покупке. Указывать, что покупка является пополнением счета, не требуется.

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

После пополнения счета следующие поля в объекте «Результат Purchase обновляются, отражая последнюю покупку пополнения:

  • Идентификатор заказа
  • Время покупки
  • Подпись
  • Купить токен
  • Признано

В следующих полях раздела Purchase всегда содержатся те же данные, что и в исходной покупке:

  • Название пакета
  • штат покупки
  • Продукты
  • Автоматическое продление

Подтверждение предоплаченной покупки

Аналогично подпискам с автоматическим продлением, после покупки необходимо подтвердить предоплаченные тарифные планы. Необходимо подтвердить как первоначальную покупку, так и любые пополнения счета. Для получения дополнительной информации см. раздел «Обработка покупок» .

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

Предоплаченные тарифные планы сроком на одну неделю и более должны быть подтверждены в течение трех дней.

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

Интеграция подписок в рассрочку

Подписка в рассрочку — это тип подписки, при котором пользователи оплачивают подписку несколькими частями в течение определенного периода времени, а не вносят всю сумму сразу.

Дополнительные соображения при оформлении подписки в рассрочку:

  • Доступность по странам : Функция подписки в рассрочку доступна только в Бразилии, Франции, Италии и Испании (актуальную информацию о доступности смотрите в консоли).
  • Установка цены : При установке цены на подписку в рассрочку в консоли, цена представляет собой сумму ежемесячного платежа. Вместе с установленным периодом действия договора это формирует общую сумму подписки на экране покупки.
  • Период действия договора : Общая продолжительность первоначального договора на подписку, в течение которой требуются ежемесячные платежи. Например, если базовый тарифный план предусматривает период действия договора в 15 месяцев, пользователь совершит 15 ежемесячных платежей в течение этого периода.
  • Продление : В контексте подписок с оплатой в рассрочку «продление» означает завершение периода действия обязательства, будь то первоначального или последующего. После первоначальной регистрации первое продление происходит по истечении всего первоначального периода действия обязательства. Последующие продления происходят после завершения каждого последующего периода действия обязательства. Типы продления для подписок с оплатой в рассрочку могут быть «автоматическое ежемесячное продление» или «автоматическое продление на тот же срок». При «автоматическом ежемесячном продлении» последующих обязательств нет, и план работает как ежемесячная подписка, где каждая ежемесячная плата за подписку представляет собой продление.
  • Расчетный период : В контексте подписок с оплатой в рассрочку это относится к периодичному графику осуществления отдельных платежей, как указано в базовом тарифном плане.
  • Изменение тарифного плана и изменение цены : В случае изменения цены и отмены заказа обязательство является окончательным. Это означает, что если пользователь хочет отменить заказ или разработчик хочет изменить цену, изменение вступает в силу по истечении срока действия обязательства. В случае изменения тарифного плана обязательство не является окончательным. Это означает, что изменение тарифного плана не обязательно должно ждать окончания срока действия обязательства, оно вступает в силу либо немедленно, либо в следующую дату платежа в зависимости от установленного способа замены.
  • Смена тарифного плана в рамках одной подписки : Смена тарифного плана с рассрочкой платежа на тарифный план без рассрочки платежа в рамках одного и того же продукта подписки не допускается.
  • Уведомления разработчика в режиме реального времени (RTDN) : Уведомление SUBSCRIPTION_CANCELLATION_SCHEDULED отправляется немедленно после инициированной пользователем отмены подписки, если платежи остаются в течение периода действия обязательства. Отмена находится в статусе ожидания и вступит в силу только по окончании периода действия обязательства. Затем, если пользователь не восстановит подписку, в конце периода действия обязательства отправляются уведомления SUBSCRIPTION_CANCELED и SUBSCRIPTION_EXPIRED .

  • Выплаты / Получение дохода : Выплаты разработчикам будут производиться по мере внесения пользователями ежемесячных платежей на тех же условиях, что и для всех других подписок. Разработчики не получают оплату авансом при оформлении пользователем подписки с рассрочкой платежа.

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

  • Доступность библиотеки Play Billing : Поле installmentDetails доступно только для PBL 7 и более поздних версий. Для PBL 5 и более поздних версий подписка с оплатой в рассрочку возвращается с помощью queryProductDetails() , но подписка не будет включать подробную информацию о платежах в рассрочку, такую ​​как количество подтвержденных платежей по плану.

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

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

Вы можете добавить в своё приложение прямую ссылку на центр подписок Google Play для неистекших подписок, которую можно определить с помощью поля subscriptionState ресурса подписки . Исходя из этого, существует несколько способов создания прямых ссылок на центр подписок Play Store.

Используйте следующий URL-адрес, чтобы перенаправить пользователей на страницу, где отображаются все их подписки, как показано на рисунках 1 и 2:

https://play.google.com/store/account/subscriptions
На экране подписок Play Store отображается статус всех подписок пользователя, оплачиваемых через Google Play.
Рисунок 1. На экране подписок Play Store отображается статус всех подписок пользователя, оплачиваемых через Google Play.


Нажмите на название подписки, чтобы увидеть дополнительные сведения.
Рисунок 2. Нажмите на подписку, чтобы увидеть дополнительные сведения.

Эта прямая ссылка может быть полезна для восстановления пользователем отмененной подписки из центра подписок Play Store.

Чтобы перейти непосредственно на страницу управления непросроченной подпиской, укажите имя пакета и productId связанные с приобретенной подпиской. Для программного определения productId для существующей подписки запросите информацию в бэкэнде вашего приложения или вызовите BillingClient.queryPurchasesAsync() для получения списка подписок, связанных с конкретным пользователем. Каждая подписка содержит соответствующий productId как часть информации о статусе подписки. Каждый объект SubscriptionPurchaseLineItem , связанный с покупкой подписки, содержит значение productId связанное с подпиской, которую пользователь приобрел в этой позиции.

Используйте следующий URL-адрес, чтобы перенаправить пользователей на определенный экран управления подпиской, заменив "your-sub-product-id" и "your-app-package" на productId и имя пакета приложения соответственно:

https://play.google.com/store/account/subscriptions?sku=your-sub-product-id&package=your-app-package

Затем пользователь может управлять способами оплаты и получать доступ к таким функциям, как отмена, повторная подписка и приостановка.

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

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

  • Если вы продаете несколько уровней подписки, например, «базовую» и «премиум», вы можете позволить пользователям переключаться между уровнями, приобретая базовый план или предложение другой подписки.
  • Вы можете разрешить пользователям изменять текущий расчетный период, например, переключаться с ежемесячного на годовой тарифный план.
  • Вы также можете разрешить пользователям переключаться между автоматическим продлением и предоплаченными тарифными планами.

Вы можете стимулировать любые из этих изменений, предлагая подписки со скидкой для соответствующих пользователей. Например, вы можете создать предложение, предоставляющее 50% скидку на первый год при переходе с ежемесячного плана на годовой, и ограничить это предложение пользователями, подписанными на ежемесячный план, которые еще не приобрели это предложение. Более подробная информация о критериях соответствия предложениям доступна в Центре поддержки.

На рисунке 3 показан пример приложения с тремя различными тарифными планами:

Это приложение предлагает три уровня подписки.
Рисунок 3. Это приложение имеет три уровня подписки.

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

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

режимы замены

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

режим замены

Описание

Пример использования

Платежи, подтвержденные платежами, зарегистрированы как оплаченные (для замены подписки в рассрочку).

WITH_TIME_PRORATION

Подписка немедленно обновляется или понижается. Оставшееся время корректируется в зависимости от разницы в цене и зачисляется на счет новой подписки путем переноса следующей даты выставления счета. Это поведение по умолчанию.

Перейдите на более дорогой тариф без дополнительной оплаты.

0

CHARGE_PRORATED_PRICE

Подписка обновляется немедленно, а цикл выставления счетов остается прежним. Разница в цене за оставшийся период затем списывается с пользователя.

Примечание: Эта опция доступна только при обновлении подписки, в этом случае цена за единицу времени увеличивается.

Перейдите на более дорогой тарифный план, не меняя дату выставления счета.

1

CHARGE_FULL_PRICE

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

Примечание: Если новая подписка включает бесплатный пробный период или ознакомительное предложение, с пользователя взимается плата в размере 0 долларов США или стоимость ознакомительного предложения, в зависимости от того, какая из них действует, в момент повышения или понижения уровня подписки.

Перейдите с более короткого расчетного периода на более длительный.

1 (Примечание: 0, если новая подписка включает бесплатный пробный период.)

WITHOUT_PRORATION

Уровень подписки мгновенно повышается или понижается, и новая цена взимается при продлении подписки. Цикл выставления счетов остается прежним.

Перейдите на более высокий уровень подписки, сохранив при этом оставшийся бесплатный период.

0

DEFERRED

Уровень подписки повышается или понижается только при продлении подписки, но новая покупка включает в себя сразу же следующие два элемента:

  • Существующий товар с отключенной функцией автоматического продления и сроком действия, установленным на конец текущего расчетного цикла.
  • Новое право на использование начинает действовать после истечения срока действия существующего права. При желании вы можете разрешить пользователям вносить дополнительные изменения. Например, пользователи могут вернуться к исходному плану или инициировать новое отложенное изменение плана.

Примечание: При оформлении подписки в рассрочку изменение тарифного плана происходит в начале следующей даты платежа.

Перейдите на более дешевый тарифный план.

1

KEEP_EXISTING

В новой версии график платежей за подписку остается без изменений.

Добавляйте или удаляйте элементы подписки с дополнительными модулями, если конкретный элемент должен оставаться без изменений.

Н/Д

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

Установите режим замены для покупки.

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

Продлить подписку или сменить тарифный план в рамках одной подписки.

В консоли Google Play можно указать режим замены по умолчанию. Эта настройка позволяет выбрать, когда следует взимать плату с текущих подписчиков, если они приобретают другой базовый тарифный план или предложение для той же подписки, или повторно подписываются после отмены подписки. Доступные варианты: « Списать немедленно» (эквивалентно CHARGE_FULL_PRICE ) и «Списать в следующую дату выставления счета» (эквивалентно WITHOUT_PRORATION ). Это единственные подходящие режимы замены при смене базовых тарифных планов в рамках одной подписки.

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

Меняйте тарифные планы между подписками или отменяйте режим замены по умолчанию.

Если пользователь меняет продукт подписки — приобретает другую подписку — или если вы хотите по какой-либо причине изменить режим замены по умолчанию, вы указываете коэффициент пропорционального распределения во время выполнения в качестве части параметров процесса покупки.

Для корректного указания ReplacementMode в SubscriptionProductReplacementParams или SubscriptionUpdateParams в рамках конфигурации процесса покупки во время выполнения необходимо учитывать следующие ограничения:

  • При переходе на более высокий, более низкий тарифный план или при инициировании перехода на предоплаченный тариф с предоплаченного тарифа, тарифа с автоматическим продлением или тарифа в рассрочку, единственным допустимым режимом замены является CHARGE_FULL_PRICE . Если вы укажете любой другой режим замены, покупка не удастся, и пользователю будет показано сообщение об ошибке.
  • При переключении тарифных планов в рамках одной подписки с предоплаченного плана на план с автоматическим продлением на план с автоматическим продлением допустимыми режимами пропорционального распределения являются CHARGE_FULL_PRICE и WITHOUT_PRORATION . Если вы укажете любой другой режим пропорционального распределения, покупка не удастся, и пользователю будет показано сообщение об ошибке.
  • Переход на тарифный план без рассрочки в рамках одного и того же продукта подписки не допускается.
  • При использовании режима замены KEEP_EXISTING в SubscriptionProductReplacementParams для сохранения платежа по товару без изменений во время замены, старый идентификатор продукта должен совпадать с новым идентификатором продукта. Режим KEEP_EXISTING не поддерживается в SubscriptionUpdateParams .

Примеры и модели поведения, заменяющие друг друга.

Чтобы понять, как работает каждый режим пропорционального распределения, рассмотрим следующий сценарий:

У Сэмвайза есть подписка на онлайн-контент из приложения Country Gardener. У него ежемесячная подписка на версию Tier 1 , которая содержит только текст. Эта подписка стоит ему 2 доллара в месяц и продлевается первого числа каждого месяца.

15 апреля Сэмвайз решил перейти на годовую версию подписки Tier 2 , которая включает видеообновления и стоит 36 долларов в год .

При обновлении подписки разработчик выбирает режим пропорционального распределения. Ниже приведено описание того, как каждый режим пропорционального распределения влияет на подписку Samwise:

WITH_TIME_PRORATION

Подписка Сэмвайза на первый уровень заканчивается немедленно. Поскольку он оплатил полный месяц (с 1 по 30 апреля), но перешёл на более дорогой тариф в середине срока действия подписки, половина стоимости месячной подписки (1 доллар) засчитывается в счёт новой подписки. Однако, поскольку новая подписка стоит 36 долларов в год, баланс в 1 доллар покрывает только 10 дней (с 16 по 25 апреля); поэтому 26 апреля с него списывается 36 долларов за новую подписку, и ещё 36 долларов 26 апреля каждого последующего года.

Вам следует вызвать обработчик PurchasesUpdatedListener вашего приложения в момент успешного завершения покупки, и вы сможете получить информацию о новой покупке в рамках вызова queryPurchasesAsync() . Ваш бэкэнд немедленно получит уведомление для разработчиков в режиме реального времени SUBSCRIPTION_PURCHASED .

CHARGE_PRORATED_PRICE

Этот режим можно использовать, потому что стоимость подписки уровня 2 за единицу времени (36 долларов в год = 3 доллара в месяц) выше, чем стоимость подписки уровня 1 за единицу времени (2 доллара в месяц). Подписка Сэмвайза уровня 1 заканчивается немедленно. Поскольку он оплатил полный месяц, но использовал только половину, половина стоимости месячной подписки (1 доллар) применяется к его новой подписке. Однако, поскольку эта новая подписка стоит 36 долларов в год, оставшиеся 15 дней стоят 1,50 доллара; поэтому с него взимается разница в 0,50 доллара за новую подписку. 1 мая с Сэмвайза списывается 36 долларов за новый уровень подписки, и еще 36 долларов 1 мая каждого последующего года.

Вам следует вызвать обработчик PurchasesUpdatedListener вашего приложения в момент успешного завершения покупки, и вы сможете получить информацию о новой покупке в рамках вызова queryPurchasesAsync() . Ваш бэкэнд немедленно получит уведомление для разработчиков в режиме реального времени SUBSCRIPTION_PURCHASED .

WITHOUT_PRORATION

Подписка Сэмвайза уровня 1 немедленно повышается до уровня 2 без дополнительной платы, и 1 мая с него списывается 36 долларов за новый уровень подписки, а затем еще 36 долларов 1 мая каждого последующего года.

Вам следует вызвать обработчик PurchasesUpdatedListener вашего приложения в момент успешного завершения покупки, и вы сможете получить информацию о новой покупке в рамках вызова queryPurchasesAsync() . Ваш бэкэнд немедленно получит уведомление для разработчиков в режиме реального времени SUBSCRIPTION_PURCHASED .

DEFERRED

Подписка Сэмвайза первого уровня действует до 30 апреля. 1 мая вступает в силу подписка второго уровня , и с Сэмвайза будет списана плата в размере 36 долларов за новый уровень подписки.

Вам следует вызвать обработчик PurchasesUpdatedListener вашего приложения в момент успешного завершения покупки, когда вы сможете получить информацию о новой покупке в рамках вызова queryPurchasesAsync() . Ваш бэкэнд немедленно получит уведомление разработчика в реальном времени SUBSCRIPTION_PURCHASED . Вам следует обработать покупку так же, как и любую другую новую покупку в этот момент. В частности, убедитесь, что вы подтвердили новую покупку. Обратите внимание, что startTime новой подписки заполняется в момент вступления замены в силу, что происходит, когда истекает срок действия старой подписки. В этот момент вы получаете RTDN SUBSCRIPTION_RENEWED для нового плана подписки. Подробнее о поведении ReplacementMode.DEFERRED в разделе «Обработка отложенной замены» .

CHARGE_FULL_PRICE

Подписка Сэмвайза на первый уровень заканчивается немедленно. Его подписка на второй уровень начинается сегодня, и с него списывается 36 долларов. Поскольку он оплатил полный месяц, но использовал только половину, половина стоимости месячной подписки (1 доллар) будет учтена в его новой подписке. Поскольку новая подписка стоит 36 долларов в год, к периоду подписки добавится 1/36 года (примерно 10 дней). Таким образом, следующая оплата для Сэмвайза будет произведена через 1 год и 10 дней с сегодняшнего дня и составит 36 долларов. После этого с него будет взиматься 36 долларов каждый последующий год.

При выборе режима пропорционального распределения обязательно ознакомьтесь с нашими рекомендациями по замене .

KEEP_EXISTING

У Сэмвайза есть подписка на онлайн-контент в приложении Country Gardener. У него ежемесячная подписка на План 1, включающий базовый контент. Стоимость этой подписки начинается с 2 долларов в месяц в течение 3 месяцев, а затем составляет 4 доллара в месяц. Сэмвайз оформил подписку 1 апреля. Приложение Country Gardener предлагает План 2 в качестве дополнительного специализированного контента за 3 доллара в месяц. 15 апреля Сэмвайз добавил План 2 к своей подписке на приложение Country Gardener, сохранив при этом План 1. График платежей Сэмвайза выглядит следующим образом:

  • Цена за план 2 рассчитывается пропорционально и составляет 1,50 доллара США, оплата производится 15 апреля.
  • Цена составляет 5,00 долларов США в месяц в течение последующих 2 месяцев и включает в себя как вводную цену для Плана 1, так и обычную цену для Плана 2.
  • Впоследствии — регулярный ежемесячный платеж в размере 7,00 долларов.

Внесение изменений в подписку непосредственно в приложении.

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

Для замены используйте SubscriptionProductReplacementParams (предпочтительный вариант).

В следующем примере показано, как обновить подписку с помощью SubscriptionProductReplacementParams .

  • Объект BillingFlowParams.ProductDetailsParams теперь имеет метод setSubscriptionProductReplacementParams() для указания информации о замене на уровне продукта.

  • У SubscriptionProductReplacementParams есть два метода-сеттера:

    • setOldProductId: Это старый продукт, который будет заменен продуктом из текущих ProductDetails.
    • setReplacementMode: Это режим замены на уровне элемента. Режимы по сути те же, что и в SubscriptionUpdateParams , но сопоставление значений было обновлено.
  • Существующие параметры обновления уровня покупки, заданные в BillingFlowParams.setSubscriptionUpdateParams() следует преобразовать в setOldPurchaseToken() .

  • После вызова setSubscriptionProductReplacementParams() для любого из ProductDetailsParams , SubscriptionUpdateParams.setSubscriptionReplacementMode() не будет иметь никакого эффекта.

Приведенный ниже пример кода демонстрирует, как изменить тарифный план подписки с ( old_product_1 , old_product_2 ) на ( product_1 , product_2 , product_3 ). В этом сценарии product_1 заменяет old_product_1 , product_2 заменяет old_product_2 , а product_3 немедленно добавляется к подписке.

Котлин

val billingClient: BillingClient = ...
val replacementModeForBasePlan: Int = ...
val replacementModeForAddon: Int = ...

val purchaseTokenOfExistingSubscription: String = "your_old_purchase_token"

// ProductDetails instances obtained from queryProductDetailsAsync();

val productDetailsParams1 =
    ProductDetailsParams.newBuilder()
        .setProductDetails(productDetails1_obj) // Required: Set the ProductDetails object
        .setSubscriptionProductReplacementParams(
            SubscriptionProductReplacementParams.newBuilder()
                .setOldProductId("old_product_id_1")
                .setReplacementMode(replacementModeForBasePlan)
                .build()
        )
        .build()

val productDetailsParams2 =
    ProductDetailsParams.newBuilder()
        .setProductDetails(productDetails2_obj) // Required: Set the ProductDetails object
        .setSubscriptionProductReplacementParams(
            SubscriptionProductReplacementParams.newBuilder()
                .setOldProductId("old_product_id_2")
                .setReplacementMode(replacementModeForAddon)
                .build()
        )
        .build()

// Example for a third item without replacement params
val productDetailsParams3 =
    ProductDetailsParams.newBuilder()
        .setProductDetails(productDetails3_obj) // Required: Set the ProductDetails object
        .build()

val newProductDetailsList = listOf(
    productDetailsParams1,
    productDetailsParams2,
    productDetailsParams3
)

val billingFlowParams =
    BillingFlowParams.newBuilder()
        .setSubscriptionUpdateParams(
            SubscriptionUpdateParams.newBuilder()
                .setOldPurchaseToken(purchaseTokenOfExistingSubscription)
                .build()
        )
        .setProductDetailsParamsList(newProductDetailsList)
        .build()

// To launch the billing flow:
// billingClient.launchBillingFlow(activity, billingFlowParams)

Java

BillingClient billingClient = ;

int replacementModeForBasePlan =;
int replacementModeForAddon =;
// ProductDetails obtained from queryProductDetailsAsync().
ProductDetailsParams productDetails1 =
  ProductDetailsParams.newBuilder()
      .setSubscriptionProductReplacementParams(
           SubscriptionProductReplacementParams.newBuilder()
               .setOldProductId("old_product_id_1")
               .setReplacementMode(replacementModeForBasePlan))
               .build();
ProductDetailsParams productDetails2 =
  ProductDetailsParams.newBuilder()
      .setSubscriptionProductReplacementParams(
           SubscriptionProductReplacementParams.newBuilder()
               .setOldProductId("old_product_id_2")
               .setReplacementMode(replacementModeForAddon))
               .build();
ProductDetailsParams productDetails3 = ...;

ArrayList newProductDetailsList = new ArrayList<>();
newProductDetailsList.add(productDetails1);
newProductDetailsList.add(productDetails2);
newProductDetailsList.add(productDetails3);

BillingFlowParams billingFlowParams =
    BillingFlowParams.newBuilder()
        .setSubscriptionUpdateParams(
          SubscriptionUpdateParams.newBuilder()
              .setOldPurchaseToken(purchaseTokenOfExistingSubscription)
             .build())
        .setProductDetailsParamsList(productDetailsList)
        .build();

billingClient.launchBillingFlow(billingFlowParams);

Установите параметр SubscriptionUpdateParams для замены (устарело)

В следующем примере показано, как обновить подписку с помощью SubscriptionUpdateParams .

Котлин

val offerToken = productDetails
        .getSubscriptionOfferDetails(selectedOfferIndex)
        .getOfferToken()

val billingParams = BillingFlowParams.newBuilder().setProductDetailsParamsList(
       listOf(
           BillingFlowParams.ProductDetailsParams.newBuilder()
               .setProductDetails(productDetails)
               .setOfferToken(offerToken)
               .build()
       )
       ).setSubscriptionUpdateParams(
           BillingFlowParams.SubscriptionUpdateParams.newBuilder()
               .setOldPurchaseToken("old_purchase_token")
               .setSubscriptionReplacementMode(
                 BillingFlowParams.ReplacementMode.CHARGE_FULL_PRICE
               )
               .build()
       ).build()

billingClient.launchBillingFlow(
    activity,
    billingParams
   )
// ...

Java

String offerToken = productDetails
    .getSubscriptionOfferDetails(selectedOfferIndex)
    .getOfferToken();

BillingFlowParams billingFlowParams = BillingFlowParams.newBuilder()
    .setProductDetailsParamsList(
        ImmuableList.of(
            ProductDetailsParams.newBuilder()
                // fetched via queryProductDetailsAsync
                .setProductDetails(productDetails)
                // offerToken can be found in
                // ProductDetails=>SubscriptionOfferDetails
                .setOfferToken(offerToken)
                .build()))
    .setSubscriptionUpdateParams(
        SubscriptionUpdateParams.newBuilder()
            // purchaseToken can be found in Purchase#getPurchaseToken
            .setOldPurchaseToken("old_purchase_token")
            .setSubscriptionReplacementMode(ReplacementMode.CHARGE_FULL_PRICE)
            .build())
    .build();

BillingResult billingResult = billingClient.launchBillingFlow(activity, billingFlowParams);
// ...

Рекомендации по замене

В таблице ниже представлены различные сценарии пропорционального распределения нагрузки, а также наши рекомендации для каждого из них:

Сценарий Рекомендуемый режим замены Результат
Переход на более дорогой тарифный план CHARGE_PRORATED_PRICE Пользователь получает доступ немедленно, при этом период оплаты остается тем же.
Переход на более дешевый тарифный план DEFERRED Пользователь уже оплатил более дорогой тариф, поэтому доступ к сервису сохраняется до следующей даты выставления счета.
Обновление во время бесплатного пробного периода с сохранением пробного периода. WITHOUT_PRORATION Пользователь переходит на более высокий тарифный план на оставшийся период пробного периода без дополнительной платы.
Обновление во время бесплатного пробного периода — прекращение доступа к бесплатному пробному периоду. CHARGE_PRORATED_PRICE Пользователь немедленно получает доступ к новому тарифному плану, оставшаяся стоимость бесплатного пробного периода переносится на следующий период. Перенесенная стоимость рассчитывается на основе стоимости базового тарифного плана.
Сохранение неизменного графика платежей по некоторым пунктам подписки при добавлении или удалении других пунктов подписки из раздела «Подписка с дополнениями». KEEP_EXISTING Пользователь продолжает платить старую цену за неизмененный товар. Новые товары добавляются немедленно. Другие старые товары можно заменить, указав режим замены, или удалить.

Обработка покупок, изменяющих подписку.

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

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

API для разработчиков Google Play возвращает linkedPurchaseToken в ресурсе подписки , когда покупка заменяет существующую. Вы можете проверить itemReplacement в SubscriptionPurchaseLineItem в новой покупке, чтобы понять детали замены на уровне элемента. Кроме того, вы можете использовать поле offerPhase для определения текущей фазы предложения (например, период пропорционального распределения или бесплатный пробный период) для новой подписки, чтобы обеспечить персонализированный пользовательский опыт. Обязательно аннулируйте токен, предоставленный в linkedPurchaseToken , чтобы гарантировать, что старый токен не будет использован для доступа к вашим сервисам. См. раздел «Обновления, понижения уровня и повторная регистрация» для получения информации об обработке покупок с обновлением и понижением уровня.

После получения нового токена покупки выполните ту же процедуру проверки, что и при проверке нового токена покупки . Обязательно подтвердите эти покупки с помощью BillingClient.acknowledgePurchase() из библиотеки Google Play Billing или Purchases.subscriptions:acknowledge из API разработчиков Google Play.

Обработка отложенной замены

Режим отложенной замены позволяет пользователю использовать оставшийся лимит средств по старому тарифному плану, прежде чем перейти на новый тарифный план.

При использовании ReplacementMode.DEFERRED для новой покупки, queryPurchasesAsync() возвращает новый токен покупки после завершения процесса покупки, который остается связанным со старым продуктом до тех пор, пока отложенная замена не произойдет в следующую дату продления, после чего возвращается новый продукт.

Ранее подобный пользовательский интерфейс можно было реализовать с помощью устаревшего метода ProrationMode.DEFERRED , но ProrationMode.DEFERRED устарел начиная с Play Billing Library 6. См. следующую таблицу, чтобы понять, в чем заключаются различия в поведении:

Время

ProrationMode.DEFERRED (устарело)

ReplacementMode.DEFERRED

Сразу после успешного завершения процесса покупки (в приложении)

PurchasesUpdatedListener вызывается после покупки и сообщает о том, было ли обновление или понижение версии программы успешным.

Право на использование старого тарифного плана сохраняется до следующей даты продления. Чтобы гарантировать предоставление приложению правильных прав, queryPurchasesAsync() возвращает объект Purchase с исходным токеном покупки и исходными правами до тех пор, пока не произойдет замена.

Новый токен для покупки пока не отображен, поэтому его обработка невозможна.

PurchasesUpdatedListener вызывается после покупки и сообщает о том, было ли обновление или понижение версии программы успешным.

queryPurchasesAsync() возвращает объект purchase с новым токеном покупки сразу же, а также исходное право пользования, связанное с ним.

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

Сразу после успешного завершения процесса покупки (в бэкэнде)

SUBSCRIPTION_PURCHASED RTDN не отправляется после завершения процесса покупки. Бэкенд еще не уведомлен о новой покупке.

SUBSCRIPTION_PURCHASED RTDN со старым product_id отправляется сразу после завершения процесса покупки для получения нового токена покупки.

Вызов метода purchases.subscriptionsv2.get с новым токеном покупки возвращает покупку, содержащую поле 'startTime', указывающее время покупки, и две позиции в списке покупок:

  • Один из вариантов представляет собой старое право на получение пособия и имеет «время истечения срока действия» в будущем. Старое право на получение пособия не будет продлено и содержит объект DeferredItemReplacement , представляющий собой произведение нового права на получение пособия. Это указывает на предстоящую замену старого права на получение пособия по истечении срока его действия.
  • Один из вариантов представляет собой недавно приобретенное право пользования. Для параметра 'expiryTime' значение не задано.

Сообщение SUBSCRIPTION_EXPIRED отправлено для старого токена покупки. При вызове метода purchases.subscriptionsv2.get со старым токеном покупки он отображается как просроченный (право на использование старого плана переносится на новую покупку на оставшееся время).

При замене — первое продление после завершения процесса покупки (приложения).

queryPurchasesAsync() возвращает новый объект Purchase, содержащий новый токен покупки и право на приобретение.

Новый токен для покупки теперь доступен, поэтому его следует обработать .

queryPurchasesAsync() возвращает объект purchase с новым токеном покупки и связанным с ним новым правом на приобретение .

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

При замене — первое продление после завершения процесса покупки (бэкэнд).

Теперь новую покупку можно обработать и подтвердить после отправки первого RTDN-сообщения SUBSCRIPTION_RENEWED.

Объект linkedPurchaseToken в ресурсе подписки можно использовать для определения того, какому пользователю в вашей системе управления подписками (если таковой имеется) следует обновить данные о новом праве доступа.

Новая покупка была обработана и подтверждена, когда для нового токена покупки был отправлен RTDN-сообщение SUBSCRIPTION_PURCHASED, которое было записано как 'startTime'.

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

При вызове метода purchases.subscriptionsv2.get с новым токеном покупки возвращается покупка с двумя позициями :

  • Один из вариантов представляет собой старое право, у которого `expiryTime` находится в прошлом, а значение для DeferredItemReplacement не задано.
  • Один из вариантов представляет собой новое право на использование, с указанием времени истечения срока действия в будущем и включенным флагом автоматического продления.

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

Управление клиентами

Используя уведомления разработчиков в режиме реального времени, вы можете в режиме реального времени отслеживать, когда пользователь решает отменить подписку. Если пользователь отменяет подписку, но до истечения срока ее действия, вы можете отправить ему push-уведомления или сообщения внутри приложения с просьбой продлить подписку.

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

До истечения срока подписки После истечения срока подписки
В приложении В Play Store В приложении В Play Store
Функция возврата Подписка внутри приложения Восстановить Подписка внутри приложения Повторная подписка
Пользователь проходит процедуру оформления заказа. Да Нет Да Да
Подписка пользователя остается привязанной к тому же артикулу (SKU). Пользователь может зарегистрироваться для получения одного и того же или разных артикулов. Да Пользователь может зарегистрироваться для получения одного и того же или разных артикулов. Да
Создает новый токен для покупки Да Нет Да Да
Включено по умолчанию Нет Да, поддержка необходима всем разработчикам. Нет

Приложения без библиотеки платежей версии 2.0 и выше: Нет

Приложения с Billing Library 2.0+: Да. Разработчики могут отказаться от этой функции в консоли.

Когда с пользователя взимается плата

Если используется тот же артикул: конец текущего расчетного периода.

При использовании разных артикулов: зависит от режима пропорционального распределения.

Окончание текущего расчетного периода Немедленно Немедленно
Требуется реализация Добавьте в приложение интерфейс для повторной регистрации.

Обнаружение изменений в состоянии подписки

Прямая ссылка на Play Store

Добавьте в приложение интерфейс для повторной регистрации. Обработка покупок вне приложения

До истечения срока подписки — в приложении

Для отмененных, но еще не истекших подписок вы можете разрешить подписчикам восстановить подписку в вашем приложении, используя тот же процесс покупки внутри приложения, что и для новых подписчиков. Убедитесь, что ваш пользовательский интерфейс отражает наличие у пользователя действующей подписки. Например, вы можете отобразить текущую дату истечения срока действия подписки и цену повторного оформления с кнопкой «Активировать» .

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

  • Инициируйте новую покупку подписки с тем же артикулом.
  • Новая подписка заменяет старую и продлевается в тот же день истечения срока действия. Старая подписка немедленно помечается как истекшая.
  • Например, у Ахиллеса есть подписка на приложение Example Music, срок действия которой истекает 1 августа. 10 июля он продлевает подписку на один месяц по той же цене за месяц. Новая подписка рассчитывается пропорционально оставшемуся балансу, активируется немедленно и по-прежнему продлевается 1 августа.

Если вы хотите предложить другую цену — например, новую бесплатную пробную версию или скидку за возвращение клиента — вы можете вместо этого предложить пользователю другой артикул (SKU):

  • Инициируйте обновление или понижение версии с другим артикулом, используя режим замены WITHOUT_PRORATION .
  • Новая подписка заменяет старую и продлевается в ту же дату истечения срока действия. С пользователя взимается цена нового SKU, включая любые вводные цены, на первоначальную дату истечения срока действия. Если старая подписка была создана с использованием зашифрованного идентификатора учетной записи, этот же идентификатор следует передавать в BillingFlowParams для повышения и понижения уровня подписки.
  • Например, у Ахиллеса есть подписка на приложение Example Music, срок действия которой истекает 1 августа. 10 июля он продлевает подписку на годовую версию по льготной цене. Новая подписка активируется немедленно, и с пользователя списывается льготная цена 1 августа.
  • Если вы решите включить бесплатную пробную версию или вводную цену в свой продукт для возвращения клиентов, убедитесь, что пользователь имеет на это право, сняв флажок « Разрешить одну бесплатную пробную версию для каждого приложения» в консоли Google Play, что ограничивает пользователя одной бесплатной пробной версией для каждого приложения.

Получив токен покупки, обработайте покупку так же, как и при оформлении новой подписки. Кроме того, API разработчиков Google Play возвращает объект linkedPurchaseToken в ресурсе подписки. Обязательно аннулируйте предоставленный в linkedPurchaseToken токен , чтобы старый токен не использовался для доступа к вашим сервисам.

До истечения срока подписки — в Play Store

Если подписка отменена, но всё ещё активна, пользователи могут восстановить её в центре подписок Google Play, нажав кнопку «Восстановить подписку » (ранее — « Подписаться »). При этом сохраняется та же подписка и токен покупки.

В разделе подписок приложения Google Play Store отображается отмененная подписка с кнопкой повторной подписки
Рисунок 8. Раздел «Аккаунт > Подписки» в приложении Google Play Store, показывающий отмененную подписку с кнопкой «Повторно подписаться» .

Для получения дополнительной информации о восстановлении подписок см. раздел «Восстановление» .

После истечения срока подписки — в приложении

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

  • Чтобы предложить пользователям скидку, вы можете предложить идентификатор продукта со специальной ценой для вашей подписки, также называемый SKU для возврата средств . Вы можете предоставить это предложение в своем приложении или уведомить пользователя о предложении вне приложения, например, по электронной почте.
  • Чтобы оформить подписку на повторное использование, запустите процесс покупки в своем Android-приложении, используя библиотеку Google Play Billing. Это тот же процесс, что и при оформлении новой подписки, но вы можете указать артикул (SKU), доступный пользователю.
  • Если вы решите включить бесплатную пробную версию или вводную цену в свой продукт для возвращения клиентов, убедитесь, что пользователь имеет на это право, сняв флажок « Разрешить одну бесплатную пробную версию для каждого приложения» в консоли Google Play, что ограничивает пользователя одной бесплатной пробной версией для каждого приложения.
  • Если пользователь повторно подписывается на тот же товар, он больше не имеет права на бесплатные пробные периоды или льготную цену. Убедитесь, что ваш пользовательский интерфейс это отражает.

Получив токен покупки, обработайте покупку так же, как и при оформлении новой подписки. В ресурсе подписки вы не получите linkedPurchaseToken .

После истечения срока подписки — в Play Store

Если эта функция включена, пользователи могут повторно подписаться на тот же товар в течение года после истечения срока действия подписки, нажав кнопку «Повторно подписаться» в центре подписок Google Play. При этом будет сгенерирована новая подписка и токен покупки.

В разделе подписок приложения Google Play Store отображается отмененная и истекшая подписка с кнопками "Повторная подписка" и "Удалить"
Рисунок 9. Раздел «Аккаунт > Подписки» в приложении Google Play Store, отображающий отмененную и истекшую подписку с кнопками «Повторно подписаться» и «Удалить» .

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

Продвигайте свою подписку

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

Для бесплатных пробных периодов Google Play проверяет наличие у пользователя действительного способа оплаты перед началом пробного периода. Некоторые пользователи могут увидеть эту проверку в виде блокировки или списания средств с их платежного метода. Эта блокировка или списание являются временными и впоследствии отменяются или возвращаются.

После окончания пробного периода с платежного метода пользователя списывается полная сумма подписки.

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

Отменить или аннулировать

Вы можете использовать API разработчиков Google Play для отмены или отзыва подписки. Эта функция также доступна в консоли Google Play .

  • Отмена : Пользователи могут отменить подписку в Google Play. Вы также можете предоставить пользователям возможность отменить подписку в своем приложении или на своем веб-сайте. Ваше приложение должно обрабатывать эти отмены, как описано в разделе «Отмена» .

  • Отзыв подписки : При отзыве подписки пользователь немедленно теряет к ней доступ. Это может быть полезно, например, в случае технической ошибки, которая помешала пользователю получить доступ к вашему продукту, и пользователь не хочет продолжать его использовать. Ваше приложение должно обрабатывать такие отмены, как описано в разделе «Отзыв подписки» .

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

Прекращает продление Отменить доступ
Отмена Да Нет
Отменить Да Да

Отложить выставление счетов для абонента

Вы можете продлить срок действия подписки, используя метод subscriptionsv2.defer . При отсрочке подписки с дополнениями все элементы подписки откладываются на тот же срок. В течение периода отсрочки пользователь остается подписанным на ваш контент с полным доступом, но плата за него не взимается. Дата продления подписки обновляется в соответствии с новой датой.

Для предоплаченных тарифов вы можете использовать API отсрочки платежей, чтобы отложить время истечения срока действия.

Отложенная оплата позволяет вам делать следующее:

  • Предоставьте пользователям бесплатный доступ в качестве специального предложения, например, одну неделю бесплатного доступа при покупке фильма.
  • Предоставьте клиентам бесплатный доступ в знак доброй воли.

Отсрочка платежа по каждому вызову API может составлять от одного дня до одного года. Для дальнейшей отсрочки платежа можно повторно вызвать API до наступления новой даты выставления счета.

Например, у Дарси есть ежемесячная подписка на онлайн-контент приложения Fishing Quarterly. Обычно с нее списывают 1,25 фунта стерлингов первого числа каждого месяца. В марте она приняла участие в онлайн-опросе, проведенном издателем приложения. Издатель вознаградил ее шестью бесплатными неделями, отложив следующий платеж до 15 мая, то есть на шесть недель позже ранее запланированной даты выставления счета в апреле.

  1. С Дарси не взимается плата за апрель и начало мая, и она по-прежнему имеет доступ к контенту. 15 мая с нее будет списана обычная абонентская плата в размере 1,25 фунта стерлингов за месяц. Следующая дата продления подписки теперь — 15 июня.

При отсрочке платежа вы можете уведомить пользователя по электронной почте или в приложении об изменении даты выставления счета.

Обработка отказов в платежах

Если возникают проблемы с оплатой при продлении подписки, Google будет периодически пытаться продлить подписку в течение некоторого времени, прежде чем отменить её. Этот период восстановления может включать льготный период, за которым следует период блокировки аккаунта. В течение этого времени Google отправляет пользователю электронные письма и уведомления с просьбой обновить способ оплаты.

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

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

В консоли Google Play можно указать продолжительность льготного периода и блокировки аккаунта для каждого автоматически продлеваемого базового тарифного плана. Указание значений меньше значений по умолчанию может снизить количество восстановленных подписок после отклонения платежа.

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

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

Встроенные сообщения в приложении

Если вы включили внутриигровые сообщения с InAppMessageCategoryId.TRANSACTIONAL , Google Play будет показывать пользователям сообщения в течение льготного периода и при блокировке аккаунта один раз в день, предоставляя им возможность исправить платеж, не выходя из приложения.

Snackbar уведомляет пользователя о необходимости исправить платеж.
Рисунок 20. Snackbar уведомляет пользователя о необходимости исправить платеж.

Мы рекомендуем вызывать этот API каждый раз, когда пользователь открывает приложение, чтобы определить, следует ли отображать сообщение.

Если пользователь успешно восстановил свою подписку, вы получите код ответа SUBSCRIPTION_STATUS_UPDATED вместе с токеном покупки. Затем используйте этот токен покупки для вызова API разработчиков Google Play и обновления статуса подписки в вашем приложении.

Интегрировать внутриприложениевый обмен сообщениями

Чтобы отображать пользователю сообщения внутри приложения, используйте BillingClient.showInAppMessages() .

Вот пример запуска процесса обмена сообщениями внутри приложения:

Котлин

val inAppMessageParams = InAppMessageParams.newBuilder()
        .addInAppMessageCategoryToShow(InAppMessageCategoryId.TRANSACTIONAL)
        .build()

billingClient.showInAppMessages(activity,
        inAppMessageParams,
        object : InAppMessageResponseListener() {
            override fun onInAppMessageResponse(inAppMessageResult: InAppMessageResult) {
                if (inAppMessageResult.responseCode == InAppMessageResponseCode.NO_ACTION_NEEDED) {
                    // The flow has finished and there is no action needed from developers.
                } else if (inAppMessageResult.responseCode
                        == InAppMessageResponseCode.SUBSCRIPTION_STATUS_UPDATED) {
                    // The subscription status changed. For example, a subscription
                    // has been recovered from a suspend state. Developers should
                    // expect the purchase token to be returned with this response
                    // code and use the purchase token with the Google Play
                    // Developer API.
                }
            }
        })

Java

InAppMessageParams inAppMessageParams = InAppMessageParams.newBuilder()
        .addInAppMessageCategoryToShow(InAppMessageCategoryId.TRANSACTIONAL)
        .build();

billingClient.showInAppMessages(activity,
        inAppMessageParams,
        new InAppMessageResponseListener() {
            @Override
            public void onInAppMessageResponse(InAppMessageResult inAppMessageResult) {
                if (inAppMessageResult.responseCode
                        == InAppMessageResponseCode.NO_ACTION_NEEDED) {
                    // The flow has finished and there is no action needed from developers.
                } else if (inAppMessageResult.responseCode
                        == InAppMessageResponseCode.SUBSCRIPTION_STATUS_UPDATED) {
                    // The subscription status changed. For example, a subscription
                    // has been recovered from a suspend state. Developers should
                    // expect the purchase token to be returned with this response
                    // code and use the purchase token with the Google Play
                    // Developer API.
                }
            }
        });

Обработка транзакций, ожидающих подтверждения подписки.

Незавершенные транзакции могут происходить при первоначальной покупке, пополнении счета, обновлении или понижении уровня подписки. Покупка подписки начинается со состояния SUBSCRIPTION_STATE_PENDING , после чего переходит в состояние SUBSCRIPTION_STATE_ACTIVE . Если транзакция истекает или отменяется пользователем, она переходит в состояние SUBSCRIPTION_STATE_PENDING_PURCHASE_EXPIRED . Вы должны обновлять права пользователя только после завершения транзакции.

Изменение состояния подписки при первоначальной покупке с незавершенными транзакциями происходит довольно просто. Ваше приложение получает сообщение Purchase со состоянием PENDING , когда пользователь инициирует незавершенную транзакцию. После завершения транзакции ваше приложение снова получает сообщение Purchase со статусом PURCHASED . Вашему RTDN-клиенту отправляется сообщение SubscriptionNotification типа SUBSCRIPTION_PURCHASED . Следуйте стандартной процедуре для проверки покупки, предоставления пользователю доступа к контенту и подтверждения покупки. Если транзакция истекает или отменяется, вашему RTDN-клиенту отправляется сообщение SubscriptionNotification типа SUBSCRIPTION_PENDING_PURCHASE_CANCELED . В таких случаях пользователь никогда не должен был получить доступ к контенту.

Пополнение, обновление или понижение уровня подписки с незавершенными транзакциями влечет за собой изменение состояния как старой, так и новой подписки. Когда пользователь инициирует незавершенную транзакцию пополнения, обновления или понижения уровня подписки, ваше приложение получает объект Purchase для старой подписки с объектом PendingPurchaseUpdate . В этот момент пользователь все еще владеет старой подпиской и еще не получил новую. Вызов методов getProducts() и getPurchaseToken() для объекта PendingPurchaseUpdate возвращает идентификаторы продуктов и токен покупки новой подписки. После завершения транзакции ваше приложение получает объект Purchase с установленным токеном покупки верхнего уровня для новой подписки и состоянием PURCHASED . Вашему RTDN-клиенту отправляется сообщение SubscriptionNotification типа SUBSCRIPTION_PURCHASED . Только в этот момент следует заменить старый токен покупки на новый и обновить доступ пользователя к контенту. Если транзакция истекает или отменяется, вашему RTDN-клиенту отправляется сообщение SubscriptionNotification типа SUBSCRIPTION_PENDING_PURCHASE_CANCELED . В таких случаях пользователь должен по-прежнему иметь доступ к содержимому старой подписки.