Задача: добавить встроенные покупки в Android приложение. Использовать google in-app billing v3 для реализации одноразовых платежей и подписок.
Монетизация через In-app purchases
Бесплатные приложения позволяют получить больше установок, что логично. Но как в таком случае монетизировать программу, не считая рекламу внутри приложения? Android позволяет проводить микротранзакции внутри приложения. Это могут быть как единоразовые платежи, так и подписки.
Единоразовые платежи в Android приложении
Для реализации таких платежей, необходимо в Google play консоли разработчика создать покупку. Это делается в секции «Ограниченный контент» на странице приложения.
Внутри раздела вы создаете тот «предмет», который можно будет купить в вашем приложении. Необходимо указать идентификатор, имя, описание и цену. Всю эту информацию пользователь увидит в интерфейсе Google play во всплывающем окне подтверждения оплаты.
Подписки в Android приложении
С подписками примерно тоже самое. За исключением того, что секция «цены» содержит дополнительные поля.
Тут необходимо указать также
- Расчетный период — раз в какое время будет происходить оплата
- Бесплатный пробный период — если хотите дать пользователю попробовать функционал в течении нескольких дней
- Начальная цена — тут можно задать льготную цены для новых подписчиков
- Льготный период — сколько есть времени у пользователя, чтобы устранить ошибку с оплатой если таковая имеется, перед тем, как подписка автоматически отменится.
Библиотеки для внедрения покупок в приложение
Можно реализовать покупки внутри приложения, используя напрямую инструменты, предоставленные Гуглом.
Я же предпочитаю использовать стороннюю, зарекомендовавшую себя библиотеку https://github.com/anjlab/android-inapp-billing-v3. Она предоставляет субъективно более дружественный интерфейс к API оплаты от Google.
Инициализация BillingProcessor
Первым делом необходимо инициализировать внутренний процессор и проверить, поддерживаются ли покупки устройством.
mBillingProcessor = new BillingProcessor(this, GPLAY_LICENSE, this); boolean isAvailable = BillingProcessor.isIabServiceAvailable(this); if (!isAvailable) { mProgress.setVisibility(View.GONE); showMsg(getString(R.string.billing_not_available)); } else { mBillingProcessor.initialize(); }
Как только процессор подгрузился можно загрузить купленные продукты.
@Override public void onBillingInitialized() { /* * Called when BillingProcessor was initialized and it's ready to purchase */ showMsg("onBillingInitialized"); if (mBillingProcessor.loadOwnedPurchasesFromGoogle()) { handleLoadedItems(); } }
Восстановление купленных продуктов
После успешной загрузки, библиотека может выдать список уже приобретенных товаров если он доступен в кэше. Также внутри доступна информация о каждом продукте. Например локальная цена в правильной валюте.
К тому же, если например кэш еще не готов, в программу придет асинхронный вызов, что история покупок была восстановлена.
@Override public void onPurchaseHistoryRestored() { /* * Called when purchase history was restored and the list of all owned PRODUCT ID's * was loaded from Google Play */ showMsg("onPurchaseHistoryRestored"); handleLoadedItems(); }
Так или иначе мы получаем список ранее купленных вещей в нашем Android приложении. Давайте его обработаем и покажем в UI.
private void handleLoadedItems() { mProgress.setVisibility(View.GONE); boolean isOneTimePurchaseSupported = mBillingProcessor.isOneTimePurchaseSupported(); if (isOneTimePurchaseSupported) { mSingleTimePaymentButton.setVisibility(View.VISIBLE); mConsumabelButton.setVisibility(View.VISIBLE); } else { showMsg(getString(R.string.one_time_payment_not_supported)); } boolean isSubsUpdateSupported = mBillingProcessor.isSubscriptionUpdateSupported(); if (isSubsUpdateSupported) { mSubscriptionButton.setVisibility(View.VISIBLE); } else { showMsg(getString(R.string.subscription_not_supported)); } setupConsumableButtons(mBillingProcessor.listOwnedProducts().contains(ONE_TIME_PAYMENT)); setupSubscription(mBillingProcessor.listOwnedSubscriptions().contains(SUBSCRIPTION)); } private void setupConsumableButtons(boolean isPurchased) { mConsumabelButton.setEnabled(isPurchased); mSingleTimePaymentButton.setEnabled(!isPurchased); if (isPurchased) { mSingleTimePaymentButton.setText(R.string.already_bought); mConsumabelButton.setText(R.string.consume_one_time); } else { SkuDetails details = mBillingProcessor.getPurchaseListingDetails(ONE_TIME_PAYMENT); mSingleTimePaymentButton.setText(getString(R.string.one_time_payment_value, details.priceText)); mConsumabelButton.setText(R.string.not_bought_yet); } } private void setupSubscription(boolean isPurchased) { mSubscriptionButton.setEnabled(!isPurchased); if (isPurchased) { mSubscriptionButton.setText(R.string.already_subscribed); } else { SkuDetails details = mBillingProcessor.getSubscriptionListingDetails(SUBSCRIPTION); mSubscriptionButton.setText(getString(R.string.subscription_value, details.priceText)); } }
Обработка успешной покупки внутри приложения
Результат о совершенной покупки приходит асинхронно. Нам остается только его обработать и «выдать» цифровой товар пользователю.
Важно: существуют способы обмануть серверы google что покупка успешно состоялась, но на самом деле никакой оплаты не было. Например, есть программы, которые выступают прослойкой и эмулируют успешный ответ. Правильный способ этого избежать — проверять покупки на своем доверенном сервере. Общение server-server намного сложнее подделать, потому что у злоумышленника нет доступа к вашему ресурсу. Как реализовать такую проверку рассказывали на stackoverflow. Также есть уже готовые библиотеки. Например вот одна из них.
В данном примере мы опустим проверку и будем предполагать, что покупка корректная.
@Override public void onProductPurchased(String productId, TransactionDetails details) { /* * Called when requested PRODUCT ID was successfully purchased */ showMsg("onProductPurchased"); if (checkIfPurchaseIsValid(details.purchaseInfo)) { showMsg("purchase: " + productId + " COMPLETED"); switch (productId) { case ONE_TIME_PAYMENT: setupConsumableButtons(true); break; case SUBSCRIPTION: setupSubscription(true); break; } } else { showMsg("fakePayment"); } }
Тестирование внутренних покупок в Android приложении
Есть 2 способа, с помощью которых можно тестировать встроенные покупки.
Тестирование покупок с помощью статических ответов
Еще до публикации приложения можно использовать тестовые ID покупок, чтобы получать те или иные ответы.
- android.test.purchased
- android.test.canceled
- android.test.item_unavailable
Тестирование покупок с реальными даннымиэ
Куда удобнее проверить, что именно ваши конкретные товары работают правильно. Что ID корректно прописаны, данные корректно приходят. Для этого необходимо опубликовать ваше приложение хотя бы в канале для внутреннего тестирования.
Когда вы сами у себя тестировщик и скачиваете приложение через Google play — все ваши внутренние покупки будут корректно приходить. При этом ничего платить не надо. Гугл предоставляет «тестовые карточки» прямо в своем интерфейсе. Вы сможете проверить как отрабатывает ваше приложение при успешном ответе или отказе.
Реализованный пример покупок внутри приложения с in-app billing v3
Пример можно скачать и посмотреть в Google play