Ваше Android-приложение теряет до 30% конверсии из-за неработающих подписок после обновления Billing Library? Многие сталкиваются с тем, что старый код, передающий skuDetails.sku в BillingFlowParams, перестал компилироваться начиная с Billing 5. Новая модель Base Plans и Offers требует полного переосмысления логики покупок. Мы — команда мобильных инженеров с 10-летним опытом — поможем внедрить Google Play Billing с нуля или провести миграцию на новую модель под ключ. Оценим ваш за 1 день.
Проблемы, которые решает миграция
Новый API упрощает управление пробными периодами и скидками. Вместо нескольких sku вы используете один ProductDetails с несколькими SubscriptionOfferDetails. Это снижает вероятность ошибок при настройке тарифов. Кроме того, Google Play Billing 6 улучшает обработку ошибок — BillingResult теперь содержит понятные коды, а не cryptic messages. Согласно Google Play Billing образцам кода, миграция на новую модель сокращает время на поддержку кода на 40%.
Как мигрировать на Billing Library 5/6?
С Billing Library 5 модель подписок полностью переработана. Появились base plans и offers — это не просто переименование, а новая иерархия объектов. Один ProductDetails содержит несколько SubscriptionOfferDetails, каждый со своим offerToken. Старый код, который передавал skuDetails.sku, теперь не компилируется.
Сравнение старой и новой модели
| Аспект | Старая модель (до Billing 5) | Новая модель (Billing 5+) |
|---|---|---|
| Идентификатор | sku (один на продукт) |
offerToken (один на каждое предложение) |
| Планы | Только один тариф | Base Plans + Offers |
| Гибкость | Нет возможности комбинировать | Пробный период, скидки, промо |
| Миграция | Не требуется | Необходим рефакторинг |
Структура подписки в Billing 6
Subscription Product ├── Base Plan (monthly) │ ├── Offer: "free-trial-7days" (offerToken_1) │ └── Offer: "default" (offerToken_2) └── Base Plan (annual) └── Offer: "default" (offerToken_3) При запуске покупки выбираем конкретный offerToken:
val productDetails = // из queryProductDetailsAsync val offerToken = productDetails.subscriptionOfferDetails ?.firstOrNull { it.offerTags.contains("default") } ?.offerToken ?: return val productDetailsParams = BillingFlowParams.ProductDetailsParams.newBuilder() .setProductDetails(productDetails) .setOfferToken(offerToken) .build() val billingFlowParams = BillingFlowParams.newBuilder() .setProductDetailsParamsList(listOf(productDetailsParams)) .build() billingClient.launchBillingFlow(activity, billingFlowParams) Если передать offerToken от одного base plan, а пользователь уже имеет подписку на другой — это upgrade/downgrade, Google обрабатывает автоматически при указании setSubscriptionUpdateParams.
Что такое Grace Period и Account Hold?
В отличие от iOS, Google Play имеет два состояния после истечения оплаты:
- Grace period (1–3 дня) — подписка технически активна, Google пытается списать
- Account hold (до 30 дней) — после grace, подписка на паузе, Google продолжает попытки
Оба состояния корректно обрабатываются через purchases.subscriptions.get в Google Play Developer API. Поле paymentState: 0 = payment pending, 1 = payment received, 2 = free trial, 3 = pending deferred upgrade.
На клиенте — через purchase.purchaseState и дополнительно через Real-Time Developer Notifications (Pub/Sub). RTDN обеспечивает обновление статусов на 90% быстрее по сравнению с polling через API.
Типы уведомлений RTDN для обработки
| Тип уведомления | Код | Описание |
|---|---|---|
| SUBSCRIPTION_RENEWED | 1 | Автоматическое продление |
| SUBSCRIPTION_CANCELED | 3 | Отмена пользователем |
| SUBSCRIPTION_ON_HOLD | 5 | Удержание из-за ошибки оплаты |
| SUBSCRIPTION_IN_GRACE_PERIOD | 6 | Период отсрочки |
| SUBSCRIPTION_RESTARTED | 7 | Подписка возобновлена |
| SUBSCRIPTION_REVOKED | 12 | Отзыв из-за возврата средств |
| SUBSCRIPTION_EXPIRED | 13 | Истечение после Account Hold |
Мы интегрируем Google Play Developer API и Pub/Sub, чтобы синхронизировать статусы на сервере.
Как настроить proration при смене тарифа?
При переходе между base plans — указываем ProrationMode:
val updateParams = BillingFlowParams.SubscriptionUpdateParams.newBuilder() .setOldPurchaseToken(currentPurchaseToken) .setSubscriptionReplacementMode( BillingFlowParams.SubscriptionUpdateParams.ReplacementMode.WITH_TIME_PRORATION ) .build() WITH_TIME_PRORATION — самый честный для пользователя: остаток текущего периода пересчитывается в дни нового тарифа. IMMEDIATE_WITHOUT_PRORATION — мгновенный переход без возврата. Наши инженеры помогут выбрать оптимальную стратегию proration под вашу модель монетизации.
Процесс работы: от аудита до деплоя
- Аналитика — аудит текущей реализации (если есть), определение base plans и offers
- Проектирование — архитектура клиент-серверного взаимодействия, RTDN схема
- Реализация — интеграция Billing Library 6+, обработка всех
purchaseState, proration, upgrade/downgrade - Тестирование — через лицензионных тестеров с имитацией expiry, сценарии отмены, grace period, account hold
- Деплой — выкатка в Play Console, мониторинг RTDN, фиксация багов по логам
Что входит в услугу
- Документация по архитектуре и коду
- Интеграция RTDN через Google Cloud Pub/Sub
- Серверная синхронизация статусов подписок
- Обработка всех callback-уведомлений
- Настройка пробных периодов и скидок через Offers
- Помощь с публикацией в Play Console
- Поддержка 30 дней после деплоя
Преимущества работы с нами
- 10+ лет опыта в мобильной разработке (iOS/Android/Flutter)
- 50+ реализованных проектов с подписками
- Сертифицированные Google Play инженеры
- Гарантия на код и SLA по инцидентам
Свяжитесь с нами — мы бесплатно оценим ваш проект и предложим оптимальное решение. Закажите интеграцию сегодня и получите стабильные подписки для вашего приложения.







