Как добавить In-app Billing в приложение

Автор: | 27.12.2017

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

В этой статье мы рассмотрим, как можно реализовать встроенные покупки на примере своего приложения Менеджер паролей от Wi-Fi сетей.

Возможность покупок в приложениях реализована благодаря In-app Billing. In-app Billing — это сервис Google Play, который позволяет продавать цифровой контент внутри приложений. Этот сервис можно использовать для продажи широкого спектра контента, включая загружаемый контент, такой как мультимедийные файлы и фотографии, виртуальный контент, такой как уровни игры или различные вспомогательные предметы, премиальные услуги и многое другое.

Встроенные покупки можно подключить для любого приложения, опубликованного в Google Play. Ничего особенного для этого не требуется, только аккаунт разработчика Google Play Console и аккаунт продавца Google Wallet. Android SDK также содержит пример приложения с реализованными встроенными покупками.

Как работают встроенные покупки?

Ваше приложение обращается к сервису In-app Billing с помощью API, который предоставляется приложением Google Play, установленным на устройстве. Затем Google Play передает платежные запросы и ответы на запросы между вашим приложением и сервером Google Play. Таким образом, ваше приложение никогда напрямую не связывается с сервером Google Play. Вместо этого ваше приложение отправляет запросы в приложение Google Play через межпроцессную связь (IPC) и получает от него ответы, нет необходимости поддерживать какие-либо соединения между вашим приложением и сервером Google Play.

In-app Billing поддерживает широкую совместимость, он работает на устройствах под управлением Android 2.2 (API 8) или выше, на которых установлена последняя версия приложения Google Play.

API In-app Billing предоставляет следующие возможности:

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

Интеграция In-app Billing в приложение

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

Checkout — это реализация In-app Billing API. Большим плюсом здесь является, что с помощью этой библиотеки можно сделать интеграцию встроенных покупок в приложение намного проще, чем если бы это делалось вручную с нуля.

Checkout решает общие проблемы, с которыми могут столкнуться разработчики при работе с покупками, например:

  • Как отменить все запросы, когда активность уничтожена?
  • Как запросить информацию о покупках в фоновом потоке?
  • Как проверить покупку?
  • Как загрузить все покупки с использованием данных continuationToken или SKU (уникальный идентификатор продукта)?
  • Как добавить покупки с минимумом шаблонного кода?

Checkout может быть использован с любым фреймворком или без него. Он имеет четкое разграничение функциональности, доступной в разных контекстах: покупки могут быть сделаны только в активности, тогда как SKU может быть загружен в сервис или класс Application.

Перед началом работы библиотеку нужно добавить в проект. Для этого в файле build.gradle модуля приложения добавить зависимость в блок dependencies.

dependencies {
  ...
  compile 'org.solovyev.android:checkout:1.2.1'
}

Для работы с покупками требуется специальное разрешение com.android.vending.BILLING, которое будет добавлено в AndroidManifest.xml автоматически с помощью Gradle. Вы также можете добавить его вручную, добавив в файл манифеста следующую строчку перед элементом <application>:

<uses-permission android:name="com.android.vending.BILLING"/>

Создадим экземпляр класса Billing в Application, откуда затем будем брать его при необходимости. Если у вас нет класса Application в проекте, вы можете легко создать его. Для этого нужно добавить в файле AndroidManifest.xml в элемент <application> атрибут android:name=».Имя класса», например:

<application
    android:name=".App"
    android:allowBackup="true"
    android:fullBackupContent="@xml/mybackupscheme"
    android:icon="@mipmap/ic_launcher"
    android:label="@string/app_name"
    android:resizeableActivity="true"
    android:roundIcon="@mipmap/ic_launcher_round"
    android:theme="@style/Theme.DesignDemo">

После этого нужно поставить курсор на имя класса, нажать Alt + Enter и выбрать опцию «Create class», после чего Android Studio создаст его.

В этом классе нам нужно  добавить следующий код:

public class App extends Application {
  public void onCreate() {
    super.onCreate();
  }

  private final Billing mBilling = new Billing(this, new Billing.DefaultConfiguration() {
    @Override public String getPublicKey() {
      return BASE64_PUBLIC_KEY;
    }
  });

  public Billing getBilling() {
    return mBilling;
  }
}

BASE64_PUBLIC_KEY это ключ, который используется для установления безопасного подключения между вашим приложением и сервером Google Play. Получить этот ключ вы можете в Google Play Console, перейдя в раздел «Инструменты разработки»«Службы и API». Там в «Лицензирование и продажа контента» вы увидите сгенерированный для вашего приложения ключ, который нужно будет добавить в приложение, например, объявить как строковую константу в классе Application.

Класс Billing это основной класс для работы с библиотекой, он отвечает за:

  • подключение и отключение услуг биллинга;
  • выполнение платежных запросов;
  • кеширование результатов запросов;
  • создание объектов Checkout;
  • логирование;

Для того, чтобы избежать множественных подключений к службе In-app Billing, следует использовать только один экземпляр класса Billing, именно по этой причине мы и создаём его в классе Application.

Теперь в классе активности при её создании инициализируем экземпляр класса ActivityCheckout, который наследует от базового класса Checkout.

private ActivityCheckout mCheckout;
...
mCheckout = Checkout.forActivity(mainView.getActivity(), App.get().getBilling());
mCheckout.start();
mCheckout.createPurchaseFlow(new PurchaseListener());

Класс Checkout это средний уровень библиотеки, он использует класс Billing в определённом контексте (в Application, активности или сервисе), проверяет, поддерживаются ли покупки на устройстве и выполняет запросы. ActivityCheckout это подкласс, который способен покупать различные предметы, для создания его экземпляра нужно вызвать метод Checkout.forActivity() и передать в параметры активность и экземпляр Billing.

Метод start() запускает созданный экземпляр и отправляет запрос, который проверяет, поддерживается ли биллинг на этом устройстве.

Метод createPurchaseFlow() создаёт постоянный поток для покупок со слушателем, который будет получать обновления данных о покупках. Код слушателя выглядит следующим образом:

private class PurchaseListener extends EmptyRequestListener<Purchase> {
  @Override public void onSuccess(@Nonnull Purchase purchase) {
    if (purchase.sku.equals(AD_FREE)) {
      SP.setBoolean(mainView.getContext(), AD_FREE, true);
    }
    if (purchase.sku.equals(DONATE)) {
      Toast.makeText(mainView.getContext(), R.string.message_donate_tnx, Toast.LENGTH_LONG)
          .show();
    }
  }
}

Класс PurchaseListener наследует от EmptyRequestLisneter<Purchase>, который имеет методы onSuccess() и onError().  В данном случае, если пользователь купит отключение рекламы или сделает пожертвование, то слушатель получит данные о покупке и выполнит нужные операции.

Теперь нужно создать экземпляр класса Invertory.

mCheckout = Checkout.forActivity(mainView.getActivity(), App.get().getBilling());
mCheckout.start();
mCheckout.createPurchaseFlow(new PurchaseListener());
Inventory mInventory = mCheckout.makeInventory();
mInventory.load(
    Inventory.Request.create().loadAllPurchases().loadSkus(ProductTypes.IN_APP, AD_FREE),
    new InventoryCallback());

Класс Invertory загружает данные о продуктах, SKU и покупках. Его жизненный цикл связан с жизненным циклом Checkout, в котором он был создан.

Метод makeInvertory() создаёт экземпляр Invertory и привязывает его к нужному объекту Checkout.

Метод load() отправляет запрос на получение данных о продуктах и асинхронно загружает результат в callback. В параметрах формируется запрос, какие именно продукты нужно получить (в данном случае, все имеющиеся, а именно донаты и отключение рекламы). Код коллбека, который принимает результат запроса, представлен ниже:

private class InventoryCallback implements Inventory.Callback {
  @Override public void onLoaded(@Nonnull Inventory.Products products) {
    final Inventory.Product product = products.get(ProductTypes.IN_APP);
    if (!product.supported) {
      Crashlytics.log(Log.ERROR, "MainPresenterImpl.InventoryCallback",
          "Billing is not supported, user can't purchase anything");
      isBillingSupported = false;
      return;
    }
    List<Purchase> list = product.getPurchases();
    if (mainView != null) {
      if (list.size() == 0) SP.setBoolean(mainView.getContext(), AD_FREE, false);
      if (product.getSku(AD_FREE) != null) {
        adFreePrice = product.getSku(AD_FREE).price;
      }
      if (product.isPurchased(AD_FREE)) {
        SP.setBoolean(mainView.getContext(), AD_FREE, true);
        adRemoved = true;
        Ads.getInstance().hideBanner();
      }
    }
  }
}

Метод onLoaded() вызывается, когда все данные загружены. В нём проверяются различные данные о продуктах. Например, можно проверить с помощью поля supported можно проверить, поддерживается ли продукт, а метод getSku() возвращает идентификатор продукта. Если нужно узнать стоимость продукта на основе локали устройства, то следует вызывать getSku(TYPE).price.

Метод isPurchased() проверяет, был ли продукт куплен пользователем. В случае с рекламой это будет означать, что её следует отключать.

Теперь нужно отправлять платёжные запросы сервису. Для этого в настройках приложения есть две кнопки «Удалить рекламу» и «Поддержать проект материально».

Обработка кнопки отключения рекламы выглядит следующим образом:

mCheckout.whenReady(new Checkout.EmptyListener() {
  @Override public void onReady(@NonNull BillingRequests requests) {
    requests.purchase(ProductTypes.IN_APP, AD_FREE, null, mCheckout.getPurchaseFlow());
    Crashlytics.log(Log.INFO, "MainPresenterImpl.removeAds", "Ads was removed");
  }
});

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

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

mCheckout.whenReady(new Checkout.EmptyListener() {
  @Override public void onReady(@NonNull BillingRequests requests) {
    requests.purchase(ProductTypes.IN_APP, DONATE, null, mCheckout.getPurchaseFlow());
    Crashlytics.log(Log.INFO, "MainPresenterImpl.buyDonate", "Got donate");
  }
});

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

Читайте также

Как добавить In-app Billing в приложение: 5 комментариев

  1. grobikon

    А физ. лицо может зарегистрировать Google Wallet? Или нужно ИП открывать?

    1. Владимир Автор записи

      Может, в этом плене Google очень демократичная компания. Выплаты начнутся по достижении порога в 100$

  2. grobikon

    А как тестировать покупки? Я прописал в play console тестового пользователя и через него покупаю например рекламу. Деньги не списываются но покупка числиться у него в google play.
    Но в классе InventoryCallback mPurchases = 0.
    Как правильно тестировать?

    1. Владимир Автор записи

      Здравствуйте! К сожалению, насчёт тестирования не получится что-либо подсказать, попробуйте написать автору библиотеки на гитхабе.

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *