Жизненный цикл подписки

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

Управление жизненным циклом автоматически продлеваемых подписок.

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

subs-auto-renew-state
Рисунок 1. Состояния жизненного цикла и события перехода для автоматически продлеваемых подписок.

Для обновления состояния в вашем бэкэнде вызовите API purchases.subscriptionsv2.get , указав в уведомлении токен покупки. Эта конечная точка предоставляет актуальное состояние подписки с учетом токена покупки и считается источником достоверной информации для управления подписками.

Токен покупки действителен с момента оформления подписки и в течение 60 дней после истечения срока действия. По истечении этого срока токен покупки больше недействителен для использования при вызове API разработчиков Google Play.

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

Когда пользователь приобретает подписку, вашему RTDN-клиенту отправляется сообщение SubscriptionNotification типа SUBSCRIPTION_PURCHASED . Независимо от того, получаете ли вы это уведомление, регистрируете новую покупку в приложении через PurchasesUpdatedListener или вручную получаете данные о покупках в методе onResume() вашего приложения, вам следует обработать новую покупку в вашем защищенном бэкэнде. Для этого выполните следующие шаги:

  1. Для получения ресурса подписки, содержащего информацию о последнем состоянии подписки, выполните запрос к конечной точке purchases.subscriptionsv2.get .
  2. Убедитесь, что значение поля subscriptionState равно SUBSCRIPTION_STATE_ACTIVE .
  3. Подтвердите покупку .
  4. Предоставьте пользователю доступ к контенту. Учетную запись пользователя, связанную с покупкой, можно идентифицировать с помощью объекта ExternalAccountIdentifiers из ресурса подписки, если идентификаторы были установлены во время покупки с помощью setObfuscatedAccountId и setObfuscatedProfileId .

Библиотека Play Billing также включает метод для подтверждения подписки, acknowledgePurchase() , и метод для проверки статуса подтверждения, isAcknowledged() . Однако мы рекомендуем обрабатывать покупки в бэкэнде для повышения безопасности.

Ресурс подписки для новых покупок выглядит примерно так, как в следующем примере:

{
  "kind": "androidpublisher#subscriptionPurchaseV2",
  "startTime": "2022-04-22T18:39:58.270Z",
  "regionCode": "US",
  "subscriptionState": "SUBSCRIPTION_STATE_ACTIVE",
  "latestOrderId": "GPA.3333-4137-0319-36762",
  "acknowledgementState": "ACKNOWLEDGEMENT_STATE_PENDING", // need to acknowledge new purchases
  "lineItems": [
    {
      "productId": "sub_variant_plan01",
      "expiryTime": next_renewal_date,
      "autoRenewingPlan": {
        "autoRenewEnabled": true
      }
    }
  ],
}

Продление подписки

Для подписок без рассрочки, с автоматическим продлением, уведомление SUBSCRIPTION_RENEWED отправляется при продлении подписки. Для подписок с рассрочкой уведомление SUBSCRIPTION_RENEWED отправляется каждый раз при списании средств в день выставления счета. Убедитесь, что пользователь по-прежнему имеет право на подписку, а затем обновите состояние подписки, указав новое expiryTime предоставленное в ресурсе подписки, возвращаемом API разработчиков Google Play. Ресурс подписки выглядит примерно так, как в следующем примере:

{
  "kind": "androidpublisher#subscriptionPurchaseV2",
  "startTime": "2022-04-22T18:39:58.270Z",
  "regionCode": "US",
  "subscriptionState": "SUBSCRIPTION_STATE_ACTIVE",
  "latestOrderId": "GPA.3333-4137-0319-36762",
  "acknowledgementState": "ACKNOWLEDGEMENT_STATE_ACKNOWLEDGED",
  "lineItems": [
    {
      "productId": "sub_variant_plan01",
      "expiryTime": next_renewal_date,
      "autoRenewingPlan": {
        "autoRenewEnabled": true
      }
    }
  ]
}

Подтверждение продления подписки не требуется.

Льготный период

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

Метод queryPurchasesAsync() продолжает возвращать покупки, находящиеся в льготном периоде. Если ваше приложение полагается исключительно на queryPurchasesAsync для проверки наличия у пользователя права на подписку, то оно должно автоматически обрабатывать льготные периоды, поскольку эти подписки отображаются как активные в библиотеке Play Billing.

Синхронизация состояния подписки с вашим бэкэндом позволяет лучше отслеживать отказы в платежах и предоставляет больше контекста для снижения непроизвольного оттока клиентов. Отслеживайте сообщения SubscriptionNotification типа SUBSCRIPTION_IN_GRACE_PERIOD , чтобы получать уведомления, когда пользователь переходит в льготный период. Пока пользователь находится в льготном периоде, ресурс подписки содержит значение autoRenewEnabled = true . Google Play динамически продлевает значение expiryTime до истечения льготного периода, поскольку право на подписку должно действовать до тех пор, пока пользователь не отменит подписку или пока льготный период не достигнет своей максимальной продолжительности. Значение поля subscriptionState в течение этого периода равно SUBSCRIPTION_STATE_IN_GRACE_PERIOD . Ресурс подписки выглядит примерно так:

{
  "kind": "androidpublisher#subscriptionPurchaseV2",
  ...
  "subscriptionState": "SUBSCRIPTION_STATE_IN_GRACE_PERIOD",
  ...
  "lineItems": [
    {
      "productId": "sub_variant_plan01",
      "expiryTime": timestamp_in_future,
      "autoRenewingPlan": {
        "autoRenewEnabled": true
      }
    }
  ],
}

Play уведомляет пользователей, находящихся в льготном периоде, о том, что их платеж был отклонен, и предлагает им исправить проблемы с методом оплаты в Play Store. Когда пользователь переходит в льготный период, следует также предложить ему исправить метод оплаты, если сбой произошел непреднамеренно. Простой способ сделать это — использовать API внутриигровых сообщений . Если вы вызовете этот API, когда пользователь откроет ваше приложение, ему будет показано сообщение Play во временной панели уведомлений, информирующее пользователя об отклонении платежа. Это сообщение также содержит прямую ссылку для исправления метода оплаты в Google Play.

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

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

Доступ и восстановление в течение льготного периода

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

Рисунок 2. Хронология подписки, которая переходит в льготный период и восстанавливается до его окончания.

Важно помнить следующие моменты:

  • В течение льготного периода пользователь должен сохранять доступ к преимуществам подписки.
  • Если подписка восстанавливается в течение льготного периода, дата продления не сбрасывается.
  • Если увеличить льготный период — например, с 7 до 14 дней — пользователи, находящиеся в льготном периоде, получат расширенный доступ к преимуществам подписки.
  • Если вы сократите льготный период, у пользователей, которые уже достаточно долго находились в рамках старого льготного периода, чтобы превысить новый, права на подписку будут немедленно аннулированы. Например, если вы сократите льготный период с 14 дней до 7 дней, у пользователей, которые находились в периоде с 8 по 14 день старого льготного периода, права на подписку будут немедленно аннулированы.
  • Подписка остаётся активной, и вы не получите RTDN в течение льготного периода, пока этот льготный период не закончится.

Период молчания

Вы можете установить льготный период в 0 дней, но Play будет ждать как минимум 1 день, чтобы обеспечить достаточное время для повторных попыток оплаты. Этот «тихий» льготный период служит подстраховкой для обработки платежей. В течение этих 24 часов подписка остается в ACTIVE состоянии .

Лучший способ отслеживать изменения состояния подписки — это следить за уведомлениями разработчиков в реальном времени (RTDN) и реагировать на них. Вызывайте метод purchases.subscriptionsv2.get() в момент получения RTDN, а не в момент истечения срока действия, чтобы получить более точную информацию о статусе подписки.

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

  • SUBSCRIPTION_ON_HOLD (если включено)
  • SUBSCRIPTION_CANCELED (если отменена)
  • SUBSCRIPTION_EXPIRED (если истек)
  • SUBSCRIPTION_RENEWED (если продлена успешно)

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

блокировка счета

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

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

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

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

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

Благодаря уведомлениям разработчиков в режиме реального времени вы получаете сообщение SubscriptionNotification типа SUBSCRIPTION_ON_HOLD , когда подписка переходит в состояние блокировки учетной записи. Вызовите метод purchases.subscriptionsv2.get на вашем защищенном бэкэнд-сервере, чтобы получить информацию о новой подписке. Во время блокировки учетной записи поле expiryTime ресурса подписки устанавливается в прошедшее время, а поле subscriptionState — в значение SUBSCRIPTION_STATE_ON_HOLD .

{
  "kind": "androidpublisher#subscriptionPurchaseV2",
  ...
  "subscriptionState": "SUBSCRIPTION_STATE_ON_HOLD",
  ...
  "lineItems": [
    {
      "productId": "sub_variant_plan01",
      "expiryTime": timestamp_in_past,
      ...
    }
  ],
}

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

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

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

После восстановления библиотека Play Billing возвращает подписку через метод queryPurchasesAsync() . Если вы используете этот метод для определения того, имеет ли пользователь право на подписку, ваше приложение должно автоматически обрабатывать восстановление подписки после блокировки аккаунта.

Прослушивайте сообщения SubscriptionNotification типа SUBSCRIPTION_RECOVERED чтобы получать уведомления о восстановлении подписки и возобновлении доступа пользователя. Если вы запрашиваете информацию о подписке после получения этого уведомления, поле expiryTime устанавливается в значение временной метки в будущем, а поле subscriptionState снова устанавливается в SUBSCRIPTION_STATE_ACTIVE :

{
  "kind": "androidpublisher#subscriptionPurchaseV2",
  ...
  "subscriptionState": "SUBSCRIPTION_STATE_ACTIVE",
  ...
  "lineItems": [
    {
      "productId": "sub_variant_plan01",
      "expiryTime": next_renewal_date,
      ...
    }
  ],
}

Если пользователь не исправит свой способ оплаты до окончания периода блокировки аккаунта, вы получите RTDN типа SUBSCRIPTION_CANCELED . Инструкции по обработке отмены см. в разделе «Отмены» . При запросе информации об отмене подписки таким образом возвращаемое поле expiryTime будет установлено на прошедшую временную метку:

{
  "kind": "androidpublisher#subscriptionPurchaseV2",
  ...
  "subscriptionState": "SUBSCRIPTION_STATE_CANCELED",
  ...
  "lineItems": [
    {
      "productId": "sub_variant_plan01",
      "expiryTime": timestamp_in_past,
      ...
    }
  ],
}

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

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

блокировка доступа к учетной записи и восстановление

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

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

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

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

Важно помнить следующие моменты:

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

Сроки действия

После истечения срока действия подписки пользователь должен потерять к ней доступ. В этом случае отправляется сообщение SubscriptionNotification типа SUBSCRIPTION_EXPIRED . Получив это уведомление, запросите API разработчиков Google Play, чтобы получить актуальный ресурс подписки . После подтверждения того, что subscriptionState имеет значение SUBSCRIPTION_STATE_EXPIRED , удалите право доступа и зарегистрируйте состояние покупки как недействительное в вашем бэкэнде. Ресурс подписки будет выглядеть примерно так, как в следующем примере:

{
  "kind": "androidpublisher#subscriptionPurchaseV2",
  ...
  "subscriptionState": "SUBSCRIPTION_STATE_EXPIRED",
  ...
  "lineItems": [
    {
      "productId": "sub_variant_plan01",
      "expiryTime": expiration_time_in_past,
      ...
    }
  ],
}

Отмены

Пользователь может добровольно отменить подписку в центре подписок Play или же отменить подписку автоматически, если не восстановит доступ после блокировки аккаунта . Разработчики также могут инициировать отмену с помощью purchases.subscriptionsv2.cancel При отмене подписки пользователь сохраняет доступ к контенту до конца текущего расчетного периода. По окончании расчетного периода доступ должен быть аннулирован.

Отмена подписки без рассрочки и с автоматическим продлением вызывает уведомление SUBSCRIPTION_CANCELED . При получении этого уведомления ресурс подписки , возвращаемый API разработчиков Google Play, имеет поле subscriptionState со значением SUBSCRIPTION_STATE_CANCELED , а поле expiryTime содержит дату, когда пользователь должен потерять доступ к подписке. Если эта дата находится в прошлом, пользователь должен немедленно потерять право на использование подписки. Это может произойти, например, если пользователь отменяет подписку, находясь под блокировкой аккаунта из-за отказа в оплате.

Информация о подписке при отмене покупки выглядит примерно так, как в следующем примере:

{
  "kind": "androidpublisher#subscriptionPurchaseV2",
  ...
  "subscriptionState": "SUBSCRIPTION_STATE_CANCELED",
  ...
  "lineItems": [
    {
      "productId": "sub_variant_plan01",
      "expiryTime": expiration_time,
      ...
    }
  ],
}

Для подписок с оплатой в рассрочку при отмене, инициированной пользователем, отправляется уведомление SUBSCRIPTION_CANCELLATION_SCHEDULED , если платежи остаются в течение периода действия обязательства. Отмена находится в состоянии ожидания и вступает в силу по окончании текущего периода действия обязательства. Когда вы получаете это уведомление, в ресурсе подписки, возвращаемом из Google Play Developer API, поле subscriptionState устанавливается в значение SUBSCRIPTION_STATE_ACTIVE поскольку подписка с оплатой в рассрочку остается активной до конца периода действия обязательства. Однако присутствует пустой объект pendingCancellation. После отправки уведомления SUBSCRIPTION_CANCELED в конце периода действия обязательства отправляется уведомление SUBSCRIPTION_EXPIRED .

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

{
  "kind": "androidpublisher#subscriptionPurchaseV2",
  ...
  "subscriptionState": "SUBSCRIPTION_STATE_ACTIVE",
  ...
  "lineItems": [
    {
      "productId": "sub_plan01",
      "expiryTime": expiration_time,
      "autoRenewingPlan": {
        "autoRenewEnabled": true,
        "recurringPrice": {
          "currencyCode": "USD",
          "units": "1",
          "nanos": 990000000
        },
        "installmentDetails": {
          "initialCommittedPaymentsCount": 6,
          "remainingCommittedPaymentsCount": 5,
          "pendingCancellation": {}
      ...
        }
      }
    }
  ],
}

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

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

Отмены

Подписку можно отозвать по разным причинам, включая отзыв подписки вашей административной панелью с помощью purchases.subscriptionsv2.revoke или возврат средств за покупку. В этой ситуации немедленно отзовите право пользователя на использование подписки. При этом отправляется сообщение SubscriptionNotification типа SUBSCRIPTION_REVOKED . После получения этого уведомления поле subscriptionState ресурса подписки , возвращаемого API разработчиков Google Play, будет установлено в значение SUBSCRIPTION_STATE_EXPIRED .

Ресурс подписки для отозванной покупки выглядит примерно так, как в следующем примере:

{
  "kind": "androidpublisher#subscriptionPurchaseV2",
  ...
  "subscriptionState": "SUBSCRIPTION_STATE_EXPIRED",
  ...
  "lineItems": [
    {
      "productId": "sub_variant_plan01",
      "expiryTime": expiration_time,
      ...
    }
  ]
}

Отложенные подписки

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

Вы можете использовать API purchases.subscriptionsv2.defer для переноса даты выставления счетов по подпискам (включая подписки с дополнениями). При переносе подписки с дополнениями все элементы подписки переносятся на один и тот же срок.

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

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

Ресурс подписки для отложенной подписки выглядит примерно так, как в следующем примере:

{
  "kind": "androidpublisher#subscriptionPurchaseV2",
  ...
  "subscriptionState": "SUBSCRIPTION_STATE_ACTIVE",
  ...
  "lineItems": [
    {
      "productId": "sub_variant_plan01",
      "expiryTime": timestamp_in_future,
      ...
    }
  ],
}

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

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

Повторное оформление подписки Еженедельно Ежемесячно Трехмесячный Шесть месяцев Ежегодный
Доступные варианты длительности паузы * 1 неделя
2 недели
3 недели
4 недели
1 месяц
2 месяца
3 месяца
1 месяц
2 месяца
3 месяца
1 месяц
2 месяца
3 месяца
Н/Д
* Возможны изменения в любое время.

Приостановка подписки вступает в силу только после окончания текущего расчетного периода. В период приостановки подписки пользователь не имеет к ней доступа и не оплачивает стоимость продления. По окончании периода приостановки подписка возобновляется, и Google пытается её продлить. Если возобновление проходит успешно, подписка снова становится активной. Если возобновление не удаётся из-за проблем с оплатой, пользователь переходит в состояние блокировки аккаунта, как показано на рисунках 5 и 6:

Рисунок 5. Пользователь приостанавливает, а затем возобновляет свою подписку.
Рисунок 6. Пользователь приостанавливает свою подписку, а затем вводит опцию блокировки учетной записи.

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

Когда подписка пользователя приостановлена, библиотека Play Billing не возвращает информацию о подписке через метод queryPurchasesAsync() , если параметр includeSuspendedSubscriptions в параметре QueryPurchasesParams не установлен в значение true. Если подписка возобновлена, метод queryPurchasesAsync() возвращает информацию о ней снова.

Отслеживайте RTDN-сообщения, чтобы быть в курсе, когда пользователь приостанавливает свою подписку. Эти уведомления также позволяют сообщать пользователям вашего приложения о том, что они приостановили подписку и больше не имеют к ней доступа. Кроме того, следует предоставить пользователю возможность вручную возобновить подписку в любое время, используя прямую ссылку на Google Play .

Когда пользователь инициирует приостановку своей подписки, отправляется сообщение SubscriptionNotification типа SUBSCRIPTION_PAUSE_SCHEDULE_CHANGED . В этот момент пользователь должен сохранить доступ к своей подписке до следующей даты продления, а ресурс подписки содержит autoRenewEnabled = true . В этот момент значение поля subscriptionState равно SUBSCRIPTION_STATE_ACTIVE .

При наступлении паузы отправляется сообщение SubscriptionNotification типа SUBSCRIPTION_PAUSED . В этом случае пользователь должен потерять доступ к своей подписке, а ресурс подписки содержит autoRenewEnabled = true , и поле subscriptionState устанавливается в значение SUBSCRIPTION_STATE_PAUSED . О времени следующего продления подписки можно узнать, проверив объект PausedStateContext .

Сообщение SubscriptionNotification типа SUBSCRIPTION_RECOVERED отправляется, если подписка возобновляется автоматически по окончании периода приостановки или если пользователь решил возобновить подписку вручную.

В случае неудачной оплаты при попытке возобновить подписку после паузы отправляется сообщение SubscriptionNotification типа SUBSCRIPTION_ON_HOLD .

Обе ситуации следует разрешать в соответствии с описанием в разделе «Блокировка учетной записи» .

Повторная подписка

Для автоматически продлеваемых базовых тарифных планов в Google Play Store может отображаться кнопка «Повторно подписаться» . Эта кнопка позволяет пользователям возобновить доступ к подписке. Она может не отображаться по разным причинам, например, если срок действия подписки истек давно.

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

Хотя кнопка всегда имеет надпись «Повторная подписка» , ее функциональность зависит от состояния подписки.

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

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

Восстановите до истечения срока действия.

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

Если ваше приложение синхронизирует состояние подписки с бэкэндом, вам следует прослушивать сообщение SubscriptionNotification типа SUBSCRIPTION_RESTARTED . После получения этого RTDN ваше приложение сможет ответить на уведомление, зафиксировать, что подписка теперь готова к продлению, и прекратить отображение сообщений о восстановлении в приложении. Ресурс подписки выглядит примерно так, как в следующем примере:

{
  "kind": "androidpublisher#subscriptionPurchaseV2",
  ...
  "subscriptionState": "SUBSCRIPTION_STATE_ACTIVE",
  ...
  "lineItems": [
    {
      "productId": "sub_variant_plan01",
      "expiryTime": next_renewal_date
      ...
    }
  ],
}

Повторно подпишитесь после истечения срока действия.

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

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

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

  1. Вызовите метод purchases.subscriptionsv2.get с новым токеном покупки из RTDN. Ответ для этого типа покупки вне приложения включает поле outOfAppPurchaseContext , присутствующее исключительно для неподтвержденных покупок повторной подписки. Это поле содержит:

    • expiredExternalAccountIdentifiers : Объект ExternalAccountIdentifiers содержащий поля obfuscatedAccountId и obfuscatedProfileId , которые были настроены для предыдущей истекшей подписки, если они были заданы.
    • expiredPurchaseToken : Токен покупки последней истекшей подписки.

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

  2. Для подтверждения покупки вызовите метод purchases.subscriptions.acknowledge .

    • При желании вы можете отправить obfuscatedAccountId и obfuscatedProfileId пользователя, если вы настроили их с помощью setObfuscatedAccountId и setObfuscatedProfileId в процессе оплаты внутри приложения.

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

Повышение, понижение уровня подписки и повторная подписка

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

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

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

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

{
  "kind": "androidpublisher#subscriptionPurchaseV2",
  ...
  "subscriptionState": "SUBSCRIPTION_STATE_ACTIVE",
  "linkedPurchaseToken": old_purchase_token,
  ...
  "lineItems": [
    {
      "productId": "sub_variant_plan01",
      "expiryTime": next_renewal_date,
      "autoRenewingPlan": {
        "autoRenewEnabled": true
      }
    }
  ],
}

Изменение цен

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

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

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

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

Когда пользователь соглашается на повышение цены подписки, вы получаете сообщение SubscriptionNotification типа SUBSCRIPTION_PRICE_CHANGE_UPDATED .

Обработка продления подписки после изменения цены.

При снижении цены или при продлении подписки вы получите уведомление SubscriptionNotification типа SUBSCRIPTION_RENEWED . Рассматривайте это уведомление как любое другое уведомление о продлении .

Обрабатывать случаи, когда добровольное повышение цены не принимается.

Если пользователь не согласился на повышение цены подписки до того, как ему потребуется продлить подписку по более высокой цене, он автоматически отписывается, и вы получаете сообщение SubscriptionNotification типа SUBSCRIPTION_CANCELED . Обработайте это событие, как описано в разделе «Отмены» .

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

В соответствии с новыми правилами Южной Кореи (КР), пользователи подписки в регионе КР должны дать согласие на любое повышение цен, которое произойдет после окончания бесплатного пробного или ознакомительного периода.

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

Когда начнётся период действия согласия или пользователь предоставит согласие, вы получите сообщение SubscriptionNotification типа SUBSCRIPTION_PRICE_STEP_UP_CONSENT_UPDATED .

Разница между повышением цены и изменением цены.

A price step-up refers to an increase in the subscription price, due to a transition from one offer phase to another. For example, a subscription moving from a free trial to a regular price.

However, a price change refers to price updates initiated by you (developer) for the base plan price for a subscription. For example, opt-in price increase or opt-out price increase.

Handle lifecycle for prepaid plans

As with auto-renewing subscriptions, you must acknowledge prepaid plans after each new purchase . In the case of prepaid plans, you must fully process both the initial purchase and any top-ups, because the user has to go through the purchase flow every time.

Due to the potential for short prepaid plan durations, it's important to acknowledge the purchase as soon as possible. Prepaid plans with a duration of one week or longer must be acknowledged within 3 days. Prepaid plans with a duration shorter than one week must be acknowledged within half of the plan duration. For example, developers have 1.5 days to acknowledge purchase of a three-day prepaid plan.

Figure 8. Lifecycle states and transition events for subscription purchases.

A SubscriptionNotification message with type SUBSCRIPTION_PURCHASED is sent to your RTDN client whenever a prepaid plan subscription is purchased, including every top-up. Call the purchases.subscriptionsv2.get method to check for the latest prepaid plan subscription state.

A new purchase token is issued for top-up purchases, and you receive the previous purchase token in the linkedPurchaseToken field as part of the new subscription purchase state. The purchase token is valid from subscription signup until 60 days after expiration. After this date, the purchase token is no longer valid to use to call the Google Play Developer API.

The subscription resource for a prepaid plan purchase looks similar to the following example:

{
  "kind": "androidpublisher#subscriptionPurchaseV2",
  "startTime": "2022-04-22T18:39:58.270Z",
  "regionCode": "US",
  "subscriptionState": "SUBSCRIPTION_STATE_ACTIVE",
  "latestOrderId": "GPA.3333-4137-0319-36762",
  "acknowledgementState": "ACKNOWLEDGEMENT_STATE_ACKNOWLEDGED",
  "lineItems": [
    {
      "productId": "prepaid_plan01",
      "expiryTime": expiry_date,
      "prepaidPlan": {
        "allowExtendAfterTime": timestamp_after_which_topups_are_allowed
      }
    }
  ]
}

You can see when the entitlement ends in the expiryTime field. Top-up purchases increase the entitlement time by accumulating it. That means that if the user tops up before their original entitlement ends, the new time is added on top of their previous expiration date.

You might want to display a message in your app informing the user that their prepaid subscriptions can be extended with a top-up. To know when a user will be able to top-up, check the allowExtendAfterTime field in the subscription resource.

Prepaid plans don't auto-renew, so they can't be canceled. If a user wants to cancel a prepaid plan, they can let it reach its expiration date.

SubscriptionPurchaseV2 fields for prepaid plans

New fields have been added to support prepaid plans, which are extended by the user instead of automatically renewing. All fields apply to prepaid plans as they do for auto-renewing subscriptions, with the following exceptions:

  • [New field] lineItems[0].prepaid_plan.allowExtendAfterTime : denotes when a user will be allowed to buy another top-up to extend their prepaid plan, as a user is allowed to have only one unconsumed top-up at a time.
  • [New field] SubscriptionState : specifies the subscription object state. For prepaid plans, this value is always either ACTIVE , PENDING , or CANCELED .
  • lineItems[0].expiryTime : This field is always present for prepaid plans.
  • paused_state_context : This field is never present, as prepaid plans cannot pause.
  • lineItems[0].auto_renewing_plan : Not present for prepaid plans.
  • canceled_state_context : Not present for prepaid plans, as this field applies only to users who actively cancel a subscription.
  • lineItems[0].productId : This field replaces subscriptionId from previous versions.