Привет, я Влад, core разработчик Adapty SDK для Android. Продолжаю серию статей про то, как внедрять внутренние покупки в приложение Google Play. Остальные посты можно найти по ссылкам:
Android in-app purchases, часть 1: конфигурация и добавление в проект.
Android in-app purchases, часть 2: инициализация и обработка покупок.
Android in-app purchases, часть 3: получение активных покупок и смена подписки.
Android in-app purchases, часть 4: коды ошибок от Billing Library и как не облажаться с тестированием. — Вы тут.
Сегодня мы поговорим о кодах ошибок, которые мы можем получить от Billing Library в методе getResponseCode().
Пример того, как мы передавали ошибки в свои колбэки, можно посмотреть в этой статье. С одной ошибкой мы уже знакомы по предыдущим статьям — это USER_CANCELED, когда пользователь закрывает диалог покупки, ничего не купив. Давайте познакомимся с остальными.
Коды ошибок
Начнем с самых простых ошибок – с говорящим названием ERROR (responseCode 6) и чуть более говорящим названием DEVELOPER_ERROR (responseCode 5). Для первого случая гугл пишет в документации «Fatal error during the API action», для второго – «Invalid arguments provided to the API». Например, я смог получить DEVELOPER_ERROR, когда для запроса querySkuDetailsAsync() в билдер в setType() передал пустую строку.
Но не всё так просто. Я пошел дальше и в методе launchBillingFlow() использовал измененный SkuDetails (вытащил json из SkuDetails реального продукта, поменял в нем productId и передал в конструктор новому SkuDetails). По сути это invalid argument, и я ожидал получить DEVELOPER_ERROR, но… получил ERROR.
Отдельного упоминания заслуживает текст, который показывается в диалоге в этом кейсе — сравните английский и русский вариант:
Это, конечно, был искусственный пример. Гораздо ближе к реальности кейс, когда гугл отклонил оплату. Если при тестировании покупок с тестовой карты, о чем мы расскажем в конце статьи, в диалоге покупки выбрать «test card, always declined», вернется также ошибка ERROR, но уже с адекватным текстом.
В третьей статье, где описывалась смена подписки, мы для одного из proration mode увеличили цену годовой подписки почти в 3 раза, но не сказали, какая там должна была быть ошибка, если бы мы этого не сделали. Исправляемся.
Так как там, получается, указан неправильный proration mode, по логике мы должны получить всё ту же DEVELOPER_ERROR. Но нет, мы получаем SERVICE_UNAVAILABLE (responseCode 2). Ее же мы получаем и если указать любое левое число в качестве proration mode (это int, а не enum, нас никто не остановит), и если указать неправильный purchaseToken. Смотрим в документацию про SERVICE_UNAVAILABLE – «Network connection is down». Так, стоп…
При этом еще мы видим интересный диалог.
Что еще любопытно – в кейсе с ERROR при закрытии диалога НЕ через кнопку «ОК» (то есть, теми способами, которые интерпретируются как возврат назад) в onPurchasesUpdated() пришло, собственно, ERROR, а в случае с SERVICE_UNAVAILABLE в аналогичном кейсе приходит USER_CANCELED (но если нажать «ОК» в диалоге, то мы, как и ожидали, получим SERVICE_UNAVAILABLE).
Ну и в случае с отсутствием интернета SERVICE_UNAVAILABLE тоже приходит, тут не соврали.
Вот остальные коды ошибок с небольшими комментариями, так сказать, honorable mentions.
BILLING_UNAVAILABLE (responseCode 3). Гугл поясняет, что «Billing API version is not supported for the type requested». Я смог воспроизвести эту ошибку, разлогинившись из Google-аккаунта, а также на Хуавее без Google Play Services. Возможно, она также воспроизведется на старых телефонах, где не обновляли Google Play.
SERVICE_DISCONNECTED (responseCode -1). Приложение иногда дисконнектится от сервиса Google Play. Это может произойти, если Play Store вдруг решит обновиться. Поэтому лучше перестраховаться и коннектиться перед каждым вызовом методов Billing Library, как в предыдущих статьях. А еще мы с гуглом советуем добавить какую-нибудь retry policy, если эта ошибка всё же придет в ответе.
SERVICE_TIMEOUT (responseCode -3). Название говорит само за себя — мы слишком долго ждали ответ от Google Play.
FEATURE NOT SUPPORTED (responseCode -2). В классе BillingClient есть пять констант FeatureType. Их доступность на данном устройстве можно проверять с помощью метода billingClient.isFeatureSupported(BillingClient.FeatureType.НужнаяФича). У меня на телефоне (Xiaomi Mi A2 Lite) FEATURE_NOT_SUPPORTED вернулось только для SUBSCRIPTIONS_ON_VR. При этом для IN_APP_ITEMS_ON_VR, как и для всех остальных фич, вернулось OK.
ITEM_NOT_OWNED (responseCode 8). Возникает при попытке законсьюмить покупку, которой у нас нет. Например, повторно после успешного консьюма.
ITEM_ALREADY_OWNED (responseCode 7). А тут наоборот – при попытке купить продукт, который у нас уже есть. В таком кейсе просто нужно обновить UI и сделать кнопку покупки некликабельной.
Самая популярная ошибка
Последняя и, наверное, самая популярная ошибка в начале пути внедрения in-app purchases — это ITEM_UNAVAILABLE (responseCode 4). Она говорит о том, что продукт недоступен для покупки, но не говорит, почему. А причины могут быть самые разные: от тестирования на неправильном аккаунте или неправильной сборке до покупки неактивированного продукта.
Вот чек-лист, что нужно сделать, чтобы избежать ее при тестировании:
Отправить в ваш test track сборку с Billing Library. Это обязательное условие — при этом вы можете тестировать и на дебажных сборках с таким же applicationId, но важно, чтобы хотя бы один раз сборка с Billing Library была загружена в Play Console.
Добавить в этот test track гугл-аккаунты тестировщиков, что особенно актуально для internal testing или закрытой альфы/беты. Там же будет ссылка в разделе How testers join your test, по которой тестировщики должны будут принять приглашение.
Покупать можно только активированный продукт. После создания продукта в Play Console есть кнопка activate, более детально процесс создания продукта мы описывали в первой статье.
Удостовериться, что тестирование на устройстве проходит с гугл-аккаунта, который является тестировщиком. Очевидный пункт, но бывает всякое, и это тоже нужно проверить, если вы получили такую ошибку.
applicationId
сборки, с которой тестируется покупка, должно полностью совпадать с applicationId из Play Console. Это особенно важно для тех, у кого добавляется суффикс в дебажных сборках.Добавить email-адреса тестировщиков в раздел Setup → License Testing в левом меню аккаунта (не приложения), чтобы они покупали продукты бесплатно с тестовой карты, а не с реальной. Еще один плюс, что подписки в данном случае будут иметь тестовую длительность. Не связано с этой ошибкой, но тоже полезное знание.
Заключение
Ошибки способны сильно усложнить работу, поэтому всегда важно понимать, как они могут возникнуть. Учитывая, сколько шагов нужно пройти, чтобы получить доступ к продуктам, проще всего словить ITEM_UNAVAILABLE. Поэтому я надеюсь, что мой чек-лист вам поможет.
Про Adapty
Для более простой работы с ошибками советую попробовать Adapty SDK для внедрения внутренних покупок в приложения. Кроме технической части, Adapty даёт много преимуществ:
Встроенная аналитика позволяет быстро понять основные метрики приложения.
Когортный анализ отвечает на вопрос, как быстро сходится экономика.
А/Б тесты увеличивают выручку приложения.
Интеграции с внешними системами позволяют отправлять транзакции в сервисы атрибуции и продуктовой аналитики.
Промо-кампании уменьшают отток аудитории.
Open source SDK позволяет интегрировать подписки в приложение за несколько часов.
Серверная валидация и API для работы с другими платформами.
Познакомьтесь подробнее с этими возможностями, чтобы быстрее внедрить подписки в своё приложение и улучшить конверсии.