Типы платежей за подписки

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

Если вы ещё не настроили товары с подпиской для своего приложения, прочитайте статью Как создать и настроить товары.

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

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

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

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

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

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

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

Интеграция с планами с предоплатой

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Дополнительные сведения о подписках с рассрочкой платежа

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

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

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

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

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

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

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

Чтобы перенаправлять пользователей на страницу со всеми их подписками, используйте следующий URL (см. рис. 1 и 2):

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


Нажмите на подписку, чтобы посмотреть дополнительную информацию.
Рисунок 2. Нажмите на подписку, чтобы посмотреть дополнительную информацию.

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

Чтобы создать прямую ссылку на страницу управления для подписки, срок действия которой не истек, укажите название пакета и 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 Console можно задать режим замены по умолчанию. Эта настройка позволяет выбрать дату списания средств для пользователей, сменивших основной тарифный план или активировавших специальное предложение для текущей подписки, а также для тех, кто оформил подписку повторно после отмены. Доступны следующие варианты: Списать средства немедленно (CHARGE_FULL_PRICE) и Списать средства в следующий платежный день (WITHOUT_PRORATION). Это единственные подходящие режимы замены при смене основного плана в рамках одной подписки.

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

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

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

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

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

Примеры замены и ее особенности

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

Сэмвайз оформил подписку на онлайн-контент в приложении "Садовод". У него ежемесячная подписка на первый уровень контента, который включает только текстовые материалы. Подписка стоит 2 долл. США в месяц и продлевается первого числа каждого месяца.

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

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

WITH_TIME_PRORATION

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

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

CHARGE_PRORATED_PRICE

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

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

WITHOUT_PRORATION

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

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

DEFERRED

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

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

CHARGE_FULL_PRICE

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

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

KEEP_EXISTING

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

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

Как инициировать изменение подписки в приложении

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

Используйте SubscriptionProductReplacementParams для замены (рекомендуется)

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

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

  • У свойства SubscriptionProductReplacementParams есть два метода установки:

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

  • Если для любого из элементов ProductDetailsParams вызывается функция setSubscriptionProductReplacementParams(), функция 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 добавляется в подписку немедленно.

Kotlin

val billingClient: BillingClient = this.billingClient
val replacementModeForBasePlan: Int = SubscriptionProductReplacementParams.ReplacementMode.KEEP_EXISTING
val replacementModeForAddon: Int = SubscriptionProductReplacementParams.ReplacementMode.CHARGE_PRORATED_PRICE

val purchaseTokenOfExistingSubscription: String = "your_old_purchase_token"

// ProductDetails instances obtained from queryProductDetailsAsync();

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

val productDetailsParams2 =
    ProductDetailsParams.newBuilder()
        .setProductDetails(productDetails2) // 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) // 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.

Kotlin

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.

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

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

Отложенная замена псевдонима

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

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

Раньше для этого использовался устаревший метод ProrationMode.DEFERRED, но в библиотеке Play Платежей версии 6 ProrationMode.DEFERREDон больше не поддерживается. В таблице ниже описаны различия в поведении:

Время

ProrationMode.DEFERRED (поддержка прекращена)

ReplacementMode.DEFERRED

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

PurchasesUpdatedListener вызывается после покупки с указанием статуса: удалось ли повысить или понизить уровень.

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

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

PurchasesUpdatedListener вызывается после покупки и сообщает, удалось ли повысить или понизить уровень.

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

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

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

RTDN SUBSCRIPTION_PURCHASED не отправляется после процесса покупки. Информация о новой покупке ещё не передана на сервер.

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

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

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

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

При замене – первое продление после покупки (в приложении)

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

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

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

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

При замене – первое продление после процесса покупки (на сервере)

Новая покупка может быть обработана и подтверждена при отправке первого уведомления RTDN SUBSCRIPTION_RENEWED.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Если используются разные SKU: зависит от режима пропорционального распределения.

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

Как определить изменение статуса подписки

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

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

Уведомление о скором истечении срока действия подписки в приложении

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

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

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

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

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

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

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

Пока подписка отменена, но ещё активна, пользователи могут восстановить ее в центре подписок Google Play, нажав Оформить подписку снова (ранее – Восстановить). Это позволяет сохранить подписку и токен покупки.

Раздел подписок в приложении Google Play с отмененной подпиской и кнопкой &quot;Возобновить&quot;.
Рисунок 8. Раздел "Аккаунт > Подписки" в приложении Google Play с отмененной подпиской и кнопкой Возобновить подписку.

Подробнее о восстановлении подписок…

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

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

  • Чтобы предложить пользователям скидку, вы можете создать идентификатор товара со специальной ценой на подписку. Он также называется SKU для возврата пользователей. Вы можете предложить скидку в приложении или уведомить пользователя о ней за его пределами, например по электронной почте.
  • Чтобы начать восстановление подписки, запустите процесс покупки в приложении для Android с помощью библиотеки Google Play Платежей. Процесс такой же, как при оформлении новой подписки, но вы можете выбрать SKU, который будет доступен пользователю.
  • Если вы решили добавить бесплатный пробный период или начальную цену в SKU для возврата пользователей, убедитесь, что пользователь соответствует требованиям. Для этого снимите флажок Разрешить один бесплатный пробный период на приложение в Google Play Console. Он ограничивает количество бесплатных пробных периодов для одного приложения.
  • Если пользователь оформит подписку на тот же SKU повторно, он больше не сможет воспользоваться бесплатным пробным периодом или начальной ценой. Убедитесь, что это отражено в интерфейсе.

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

После окончания подписки в Google Play

Если эта функция включена, пользователи могут повторно оформить подписку на тот же SKU в течение года после ее окончания. Для этого нужно нажать Оформить подписку снова в центре подписок Google Play. Будет создан новый токен подписки и покупки.

Раздел &quot;Подписки&quot; в приложении Google Play, где показана отмененная и истекшая подписка с кнопками &quot;Оформить снова&quot; и &quot;Удалить&quot;
Рисунок 9. Раздел "Аккаунт > Подписки" в приложении Google Play с отмененной и истекшей подпиской и кнопками Возобновить и Удалить.

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

Продвижение подписки

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

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

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

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

Как отложить оплату для подписчика

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

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

Отложенный платеж позволяет:

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

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

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

  1. С Дарси не списываются средства за апрель и начало мая, но у нее сохраняется доступ к контенту. 15 мая с нее списывается обычная плата за подписку в размере 1, 25 фунта стерлингов. Теперь дата продления подписки – 15 июня.

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

Как устранять проблемы с отклоненными платежами

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

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

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

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

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

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

Если с платежом возникла проблема, Google Play может показать пользователям уведомление. Чтобы упростить этот процесс, вы можете использовать функцию обмена сообщениями в приложении, чтобы сообщить пользователям о любых ожидающих платежах. Если вы включили сообщения в приложении, уведомление о проблемах с платежами будет показываться в течение льготного периода и периода блокировки аккаунта один раз в день. Это позволяет пользователям исправить платежные данные, не покидая приложение. Подробнее о сообщениях в приложении…

Сообщения в приложении

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

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

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

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

Как интегрировать сообщения в приложении

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

Вот пример того, как запустить цепочку сообщений в приложении:

Kotlin

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

// Note: To display the in-app message, PBL requires an activity instance that
// can provide a valid window token. This token is necessary for the Play Store
// to display the message overlay correctly on top of the application's window.
// The passed Activity must be in a state where its window is created and
// attached to the WindowManager.
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
                // is recovered from a suspended state, or a user confirms a
                // price increase. 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();

// Note: To display the in-app message, PBL requires an activity instance that
// can provide a valid window token. This token is necessary for the Play Store
// to display the message overlay correctly on top of the application's window.
// The passed Activity must be in a state where its window is created and
// attached to the WindowManager.
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
                    // is recovered from a suspended state, or a user confirms a
                    // price increase. Developers should expect the purchase
                    // token to be returned with this response code and use
                    // the purchase token with the Google Play Developer API.
                }
            }
        });

Как обрабатывать отмены и статусы ожидания

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

Отмена или отзыв

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

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

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

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

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

Как обрабатывать транзакции, ожидающие подтверждения

Транзакции в ожидании могут возникать при первоначальной покупке, пополнении баланса, переходе на более дорогой или дешевый тарифный план. Покупка подписки начинается со статуса 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. В таких случаях у пользователя должен оставаться доступ к контенту старой подписки.