Интегрируйте библиотеку платежей Google Play в свое приложение.

В этом документе описывается, как интегрировать библиотеку Google Play Billing в ваше приложение, чтобы начать продавать товары.

Срок службы покупки

Вот типичный сценарий покупки: разовая покупка или подписка.

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

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

  • Активный : Пользователь имеет действующую подписку и к ней имеет доступ.
  • Отменено : Пользователь отменил подписку, но сохраняет доступ до истечения срока действия.
  • В льготный период : у пользователя возникла проблема с оплатой, но он по-прежнему имеет доступ, пока Google пытается повторно использовать способ оплаты.
  • Приостановлено : у пользователя возникла проблема с оплатой, и он больше не имеет доступа, пока Google пытается повторно использовать способ оплаты.
  • Приостановлено : Пользователь приостановил свой доступ и не будет иметь доступа до его возобновления.
  • Срок действия истек : Пользователь отменил подписку и потерял к ней доступ. По истечении срока действия подписки пользователь считается отчисленным .

Инициализировать соединение с Google Play

Первым шагом для интеграции с платежной системой Google Play является добавление библиотеки Google Play Billing в ваше приложение и инициализация соединения.

Добавьте зависимость от библиотеки Google Play Billing Library.

Добавьте зависимость Google Play Billing Library в файл build.gradle вашего приложения, как показано ниже:

Круто

dependencies {
    def billing_version = "8.3.0"

    implementation "com.android.billingclient:billing:$billing_version"
}

Котлин

dependencies {
    val billing_version = "8.3.0"

    implementation("com.android.billingclient:billing:$billing_version")
}

Если вы используете Kotlin, модуль KTX библиотеки Google Play Billing содержит расширения Kotlin и поддержку сопрограмм, позволяющие писать идиоматические коды на Kotlin при использовании библиотеки Google Play Billing. Чтобы включить эти расширения в свой проект, добавьте следующую зависимость в файл build.gradle вашего приложения, как показано ниже:

Круто

dependencies {
    def billing_version = "8.3.0"

    implementation "com.android.billingclient:billing-ktx:$billing_version"
}

Котлин

dependencies {
    val billing_version = "8.3.0"

    implementation("com.android.billingclient:billing-ktx:$billing_version")
}

Инициализация BillingClient

После добавления зависимости от библиотеки Google Play Billing Library необходимо инициализировать экземпляр BillingClient . BillingClient — это основной интерфейс для взаимодействия между библиотекой Google Play Billing Library и остальной частью вашего приложения. BillingClient предоставляет удобные методы, как синхронные, так и асинхронные, для многих распространенных операций с оплатой. Обратите внимание на следующее:

  • Рекомендуется одновременно иметь открытое только одно активное соединение BillingClient , чтобы избежать множественных вызовов функции PurchasesUpdatedListener для одного события.
  • Рекомендуется устанавливать соединение с BillingClient при запуске или переходе приложения на передний план, чтобы обеспечить своевременную обработку покупок. Этого можно добиться, используя ActivityLifecycleCallbacks зарегистрированные с помощью registerActivityLifecycleCallbacks , и отслеживая событие onActivityResumed для инициализации соединения при первом обнаружении возобновления активности. Более подробную информацию о том, почему следует придерживаться этой рекомендации, см. в разделе, посвященном обработке покупок . Также не забудьте завершить соединение при закрытии приложения.

Для создания объекта BillingClient используйте newBuilder . Вы можете передать любой контекст в newBuilder() , и BillingClient будет использовать его для получения контекста приложения. Это означает, что вам не нужно беспокоиться об утечках памяти. Для получения обновлений о покупках необходимо также вызвать setListener , передав ссылку на объект PurchasesUpdatedListener . Этот слушатель будет получать обновления для всех покупок в вашем приложении.

Котлин

private val purchasesUpdatedListener =
   PurchasesUpdatedListener { billingResult, purchases ->
       // To be implemented in a later section.
   }

private var billingClient = BillingClient.newBuilder(context)
   .setListener(purchasesUpdatedListener)
   // Configure other settings.
   .build()

Java

private PurchasesUpdatedListener purchasesUpdatedListener = new PurchasesUpdatedListener() {
    @Override
    public void onPurchasesUpdated(BillingResult billingResult, List<Purchase> purchases) {
        // To be implemented in a later section.
    }
};

private BillingClient billingClient = BillingClient.newBuilder(context)
    .setListener(purchasesUpdatedListener)
    // Configure other settings.
    .build();

Подключиться к Google Play

После создания объекта BillingClient необходимо установить соединение с Google Play.

Для подключения к Google Play вызовите startConnection . Процесс подключения является асинхронным, и вам необходимо реализовать интерфейс BillingClientStateListener для получения обратного вызова после завершения настройки клиента и его готовности к дальнейшим запросам.

Если вы не включили автоматическое переподключение к сервису , вам также необходимо реализовать логику повторных попыток для обработки потерянных соединений с Google Play. Для реализации логики повторных попыток переопределите метод обратного вызова onBillingServiceDisconnected() и убедитесь, что BillingClient вызывает метод startConnection() для повторного подключения к Google Play перед выполнением дальнейших запросов. Если вы включили автоматическое переподключение к сервису , вы можете реализовать этот метод как пустую операцию.

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

Котлин

billingClient.startConnection(object : BillingClientStateListener {
    override fun onBillingSetupFinished(billingResult: BillingResult) {
        if (billingResult.responseCode ==  BillingResponseCode.OK) {
            // The BillingClient is ready. You can query purchases here.
        }
    }
    override fun onBillingServiceDisconnected() {
        // If automatic service reconnection is enabled, this can be left empty (no-op)
        // because the library handles retries. You can still use this for non-retry
        // tasks like logging or updating the UI to reflect a disconnected state.
        // Otherwise, try to restart the connection on the next request to
        // Google Play by calling the startConnection() method.
    }
})

Java

billingClient.startConnection(new BillingClientStateListener() {
    @Override
    public void onBillingSetupFinished(BillingResult billingResult) {
        if (billingResult.getResponseCode() ==  BillingResponseCode.OK) {
            // The BillingClient is ready. You can query purchases here.
        }
    }
    @Override
    public void onBillingServiceDisconnected() {
        // If automatic service reconnection is enabled, this can be left empty (no-op)
        // because the library handles retries. You can still use this for non-retry
        // tasks like logging or updating the UI to reflect a disconnected state.
        // Otherwise, try to restart the connection on the next request to
        // Google Play by calling the startConnection() method.
    }
});

Автоматическое восстановление соединения

Благодаря добавлению метода enableAutoServiceReconnection() в BillingClient.Builder в версии 8.0.0, библиотека Play Billing теперь может автоматически восстанавливать соединение со службой, если вызов API выполняется во время отключения службы. Это может привести к уменьшению количества ответов SERVICE_DISCONNECTED поскольку переподключение обрабатывается внутри системы до выполнения вызова API.

Как включить автоматическое переподключение

При создании экземпляра BillingClient используйте метод enableAutoServiceReconnection() в классе BillingClient.Builder , чтобы включить автоматическое переподключение.

Котлин

val billingClient = BillingClient.newBuilder(context)
    .setListener(listener)
    .enablePendingPurchases()
    .enableAutoServiceReconnection() // Add this line to enable reconnection
    .build()

Java

BillingClient billingClient = BillingClient.newBuilder(context)
    .setListener(listener)
    .enablePendingPurchases()
    .enableAutoServiceReconnection() // Add this line to enable reconnection
    .build();

Показать товары, доступные для покупки

После установления соединения с Google Play вы готовы запрашивать информацию о доступных товарах и отображать их пользователям.

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

Для запроса сведений о разовом продукте вызовите метод queryProductDetailsAsync . Этот метод может возвращать несколько предложений в зависимости от конфигурации вашего разового продукта. Для получения дополнительной информации см. раздел «Несколько вариантов покупки и предложений для разовых продуктов» .

Для обработки результата асинхронной операции необходимо также указать слушатель, реализующий интерфейс ProductDetailsResponseListener . Затем можно переопределить onProductDetailsResponse , который уведомляет слушатель о завершении запроса, как показано в следующем примере:

Котлин

val queryProductDetailsParams =
    QueryProductDetailsParams.newBuilder()
        .setProductList(
            ImmutableList.of(
                Product.newBuilder()
                    .setProductId("product_id_example")
                    .setProductType(ProductType.SUBS)
                    .build()))
        .build()

billingClient.queryProductDetailsAsync(queryProductDetailsParams) {
    billingResult,
    queryProductDetailsResult ->
      if (billingResult.getResponseCode() == BillingResponseCode.OK) {
               for (ProductDetails productDetails : queryProductDetailsResult.getProductDetailsList()) {
                 // Process successfully retrieved product details here.
               }

               for (UnfetchedProduct unfetchedProduct : queryproductDetailsResult.getUnfetchedProductList()) {
                 // Handle any unfetched products as appropriate.
               }
            }
}

Java

QueryProductDetailsParams queryProductDetailsParams =
    QueryProductDetailsParams.newBuilder()
        .setProductList(
            ImmutableList.of(
                Product.newBuilder()
                    .setProductId("product_id_example")
                    .setProductType(ProductType.SUBS)
                    .build()))
        .build();

billingClient.queryProductDetailsAsync(
    queryProductDetailsParams,
    new ProductDetailsResponseListener() {
        public void onProductDetailsResponse(BillingResult billingResult,
                QueryProductDetailsResult queryProductDetailsResult) {
            if (billingResult.getResponseCode() == BillingResponseCode.OK) {
               for (ProductDetails productDetails : queryProductDetailsResult().getProductDetailsList()) {
                 // Process success retrieved product details here.
               }

               for (UnfetchedProduct unfetchedProduct : queryproductDetailsResult.getUnfetchedProductList()) {
                 // Handle any unfetched products as appropriate.
               }
            }
        }
    }
)

При запросе сведений о продукте передайте экземпляр QueryProductDetailsParams , содержащий список строковых идентификаторов продуктов, созданных в Google Play Console, а также ProductType . ProductType может быть либо ProductType.INAPP для разовых продуктов, либо ProductType.SUBS для подписок.

Запрос с использованием расширений Kotlin

Если вы используете расширения Kotlin , вы можете получить одноразовые сведения о продукте, вызвав функцию расширения queryProductDetails() .

Функция queryProductDetails() использует корутины Kotlin, поэтому вам не нужно определять отдельный слушатель. Вместо этого функция приостанавливается до завершения запроса, после чего вы можете обработать результат:

suspend fun processPurchases() {
    val productList = listOf(
        QueryProductDetailsParams.Product.newBuilder()
            .setProductId("product_id_example")
            .setProductType(BillingClient.ProductType.SUBS)
            .build()
    )
    val params = QueryProductDetailsParams.newBuilder()
    params.setProductList(productList)

    // leverage queryProductDetails Kotlin extension function
    val productDetailsResult = withContext(Dispatchers.IO) {
        billingClient.queryProductDetails(params.build())
    }

    // Process the result.
}

В редких случаях некоторые устройства не могут поддерживать ProductDetails и queryProductDetailsAsync() , обычно из-за устаревших версий Google Play Services . Для обеспечения надлежащей поддержки в этом сценарии ознакомьтесь с руководством по миграции на Play Billing Library 7 , чтобы узнать, как использовать функции обратной совместимости.

Обработайте результат

Библиотека Google Play Billing сохраняет результаты запроса в объекте QueryProductDetailsResult . Объект QueryProductDetailsResult содержит List объектов ProductDetails . Затем вы можете вызывать различные методы для каждого объекта ProductDetails в списке, чтобы просмотреть соответствующую информацию об успешно полученном одноразовом продукте, например, его цену или описание. Чтобы просмотреть доступную подробную информацию о продукте, см. список методов в классе ProductDetails .

QueryProductDetailsResult также содержит List объектов UnfetchedProduct . Затем вы можете запросить каждый объект UnfetchedProduct, чтобы получить код состояния, соответствующий причине сбоя получения данных. Чтобы просмотреть доступную информацию о неполученных товарах, см. список методов в классе UnfetchedProduct .

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

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

  • Для подписок метод queryProductDetailsAsync() возвращает подробную информацию о продукте подписки и максимум 50 подходящих для пользователя предложений на каждую подписку. Если пользователь пытается приобрести неподходящее предложение (например, если приложение отображает устаревший список подходящих предложений), Play сообщает пользователю о его несоответствии условиям, и пользователь может выбрать вместо этого приобретение базового плана.

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

Запустите процесс покупки

Чтобы инициировать запрос на покупку из вашего приложения, вызовите метод launchBillingFlow() из основного потока приложения. Этот метод принимает ссылку на объект BillingFlowParams , содержащий соответствующий объект ProductDetails , полученный при вызове queryProductDetailsAsync . Для создания объекта BillingFlowParams используйте класс BillingFlowParams.Builder .

Котлин

// An activity reference from which the billing flow will be launched.
val activity : Activity = ...;

val productDetailsParamsList = listOf(
    BillingFlowParams.ProductDetailsParams.newBuilder()
        // retrieve a value for productDetails by calling queryProductDetailsAsync()
        .setProductDetails(productDetails)
        // Get the offer token:
        // a. For one-time products, call ProductDetails.getOneTimePurchaseOfferDetailsList()
        // for a list of offers that are available to the user.
        // b. For subscriptions, call ProductDetails.subscriptionOfferDetails()
        // for a list of offers that are available to the user.
        .setOfferToken(selectedOfferToken)
        .build()
)

val billingFlowParams = BillingFlowParams.newBuilder()
    .setProductDetailsParamsList(productDetailsParamsList)
    .build()

// Launch the billing flow
val billingResult = billingClient.launchBillingFlow(activity, billingFlowParams)

Java

// An activity reference from which the billing flow will be launched.
Activity activity = ...;

ImmutableList<ProductDetailsParams> productDetailsParamsList =
    ImmutableList.of(
        ProductDetailsParams.newBuilder()
             // retrieve a value for "productDetails" by calling queryProductDetailsAsync()
            .setProductDetails(productDetails)
            // Get the offer token:
            // a. For one-time products, call ProductDetails.getOneTimePurchaseOfferDetailsList()
            // for a list of offers that are available to the user.
            // b. For subscriptions, call ProductDetails.subscriptionOfferDetails()
            // for a list of offers that are available to the user.
            .setOfferToken(selectedOfferToken)
            .build()
    );

BillingFlowParams billingFlowParams = BillingFlowParams.newBuilder()
    .setProductDetailsParamsList(productDetailsParamsList)
    .build();

// Launch the billing flow
BillingResult billingResult = billingClient.launchBillingFlow(activity, billingFlowParams);

Метод launchBillingFlow() возвращает один из нескольких кодов ответа, перечисленных в BillingClient.BillingResponseCode . Обязательно проверьте этот результат, чтобы убедиться в отсутствии ошибок при запуске процесса покупки. Код BillingResponseCode , равный OK указывает на успешный запуск.

При успешном вызове функции launchBillingFlow() система отображает экран покупки в Google Play. На рисунке 1 показан экран покупки подписки:

На экране покупки Google Play отображается подписка, которая доступна для покупки.
Рисунок 1. На экране покупки в Google Play отображается доступная для приобретения подписка.

Google Play вызывает метод onPurchasesUpdated() для передачи результата операции покупки слушателю, реализующему интерфейс PurchasesUpdatedListener . Слушатель указывается с помощью метода setListener() при инициализации клиента .

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

Котлин

override fun onPurchasesUpdated(billingResult: BillingResult, purchases: List<Purchase>?) {
   if (billingResult.responseCode == BillingResponseCode.OK && purchases != null) {
       for (purchase in purchases) {
           // Process the purchase as described in the next section.
       }
   } else if (billingResult.responseCode == BillingResponseCode.USER_CANCELED) {
       // Handle an error caused by a user canceling the purchase flow.
   } else {
       // Handle any other error codes.
   }
}

Java

@Override
void onPurchasesUpdated(BillingResult billingResult, List<Purchase> purchases) {
    if (billingResult.getResponseCode() == BillingResponseCode.OK
        && purchases != null) {
        for (Purchase purchase : purchases) {
            // Process the purchase as described in the next section.
        }
    } else if (billingResult.getResponseCode() == BillingResponseCode.USER_CANCELED) {
        // Handle an error caused by a user canceling the purchase flow.
    } else {
        // Handle any other error codes.
    }
}

В случае успешной покупки на экране Google Play отобразится сообщение об успешной покупке, аналогичное рисунку 2.

Экран подтверждения покупки в Google Play
Рисунок 2. Экран подтверждения покупки в Google Play.

Успешная покупка также генерирует токен покупки, который представляет собой уникальный идентификатор пользователя и идентификатор продукта для одноразовой покупки. Ваши приложения могут хранить токен покупки локально, хотя мы настоятельно рекомендуем передавать токен на ваш защищенный сервер, где вы сможете проверить покупку и защититься от мошенничества. Этот процесс более подробно описан в разделе «Обнаружение и обработка покупок» .

Пользователю также отправляется электронное письмо с подтверждением транзакции, содержащее идентификатор заказа (Order ID) или уникальный идентификатор транзакции. Пользователи получают электронное письмо с уникальным идентификатором заказа для каждой разовой покупки продукта, а также для первоначальной покупки подписки и последующих автоматических продлений. Идентификатор заказа можно использовать для управления возвратами средств в консоли Google Play.

Укажите индивидуальную цену

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

Экран покупки в Google Play, указывающий на то, что цена была настроена индивидуально для пользователя.
Рисунок 3. Экран покупки в Google Play, указывающий на то, что цена была настроена индивидуально для пользователя.

Чтобы определить, является ли предлагаемая вами цена персонализированной, необходимо обратиться к статье 6 (1) (ea) Директивы о правах потребителей 2011/83/ЕС.

Функция setIsOfferPersonalized() принимает логическое значение. Если true , пользовательский интерфейс Play отображает уведомление. Если false , уведомление о предложении опускается. Значение по умолчанию — false .

Для получения дополнительной информации обратитесь в Центр помощи потребителям .

Прикрепить идентификаторы пользователей

При запуске процесса покупки ваше приложение может прикрепить любые имеющиеся у вас идентификаторы пользователя, совершающего покупку, используя obfuscatedAccountId или obfuscatedProfileId . Примером идентификатора может быть зашифрованная версия логина пользователя в вашей системе. Установка этих параметров может помочь Google выявлять мошенничество . Кроме того, это может помочь вам убедиться, что покупки приписываются правильному пользователю, как обсуждалось в разделе «Предоставление прав пользователям» .

Выявление и обработка покупок

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

Ваше приложение отслеживает новые покупки и завершенные незавершенные покупки одним из следующих способов:

  1. Вызов onPurchasesUpdated происходит, когда ваше приложение вызывает launchBillingFlow (как обсуждалось в предыдущем разделе) или когда ваше приложение работает с активным подключением к библиотеке платежей, и когда совершается покупка вне вашего приложения или завершается ожидающая покупка. Например, когда член семьи подтверждает ожидающую покупку на другом устройстве.
  2. Когда ваше приложение вызывает метод queryPurchasesAsync для получения информации о покупках пользователя.

Что касается пункта #1, onPurchasesUpdated будет автоматически вызываться для новых или завершенных покупок, пока ваше приложение запущено и имеет активное подключение к библиотеке Google Play Billing. Если ваше приложение не запущено или у него нет активного подключения к библиотеке Google Play Billing, onPurchasesUpdated не будет вызываться. Помните, что вашему приложению рекомендуется поддерживать активное подключение, пока оно находится на переднем плане, чтобы получать своевременные обновления о покупках.

Для выполнения пункта #2 необходимо вызвать BillingClient.queryPurchasesAsync() , чтобы гарантировать обработку всех покупок вашим приложением. Рекомендуется делать это после успешного установления соединения с библиотекой Google Play Billing (что рекомендуется при запуске приложения или его переходе на передний план, как обсуждалось в разделе инициализации BillingClient ). Этого можно добиться, вызвав queryPurchasesAsync при получении успешного результата в методе onServiceConnected . Следование этой рекомендации крайне важно для обработки таких событий и ситуаций, как:

  • Проблемы с сетью во время покупки : Пользователь может успешно совершить покупку и получить подтверждение от Google, но его устройство теряет сетевое соединение до того, как устройство и ваше приложение получат уведомление о покупке через PurchasesUpdatedListener .
  • Использование нескольких устройств : Пользователь может купить товар на одном устройстве, а затем ожидать увидеть этот же товар при переключении на другое устройство.
  • Обработка покупок, совершенных вне вашего приложения : Некоторые покупки, например, использование промокодов, можно совершать вне вашего приложения.
  • Обработка переходов состояния покупки : Пользователь может завершить оплату покупки, находящейся в состоянии «ОЖИДАЕТСЯ», пока ваше приложение не запущено, и ожидать подтверждения завершения покупки при открытии приложения.
  • Приостановленные подписки : Подписка может быть приостановлена ​​в течение жизненного цикла подписки . Метод BillingClient.queryPurchasesAsync() вернет приостановленные подписки только в том случае, если параметр includeSuspendedSubscriptions установлен в QueryPurchasesParams.Builder . Приостановленные подписки не возвращаются в PurchasesUpdatedListener .

Как только ваше приложение обнаружит новую или завершенную покупку, оно должно:

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

Эти шаги подробно описаны в следующих разделах, после чего последует раздел, в котором будут подведены итоги всех шагов.

Подтвердите покупку

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

Предоставить пользователю право на использование сервиса.

После подтверждения покупки ваше приложение может предоставить пользователю право на покупку и уведомить его об этом. Перед предоставлением права на покупку убедитесь, что ваше приложение проверяет, находится ли состояние покупки PURCHASED . Если покупка находится в статусе «ОЖИДАНИЕ», ваше приложение должно уведомить пользователя о том, что ему еще необходимо выполнить действия для завершения покупки, прежде чем право на покупку будет предоставлено. Предоставляйте право на покупку только тогда, когда покупка переходит из статуса «ОЖИДАНИЕ» в статус «УСПЕШНО». Дополнительную информацию можно найти в разделе «Обработка ожидающих транзакций» .

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

Для предварительных заказов покупка находится в состоянии «ОЖИДАНИЕ» до наступления даты релиза. Предварительный заказ будет завершен в момент релиза, и его состояние изменится на «ПОКУПАНО» без дополнительных действий.

Уведомить пользователя

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

  • Во время динамичных сцен или кат-сцен отображение сообщения может отвлекать пользователя. В этом случае необходимо уведомить пользователя после завершения динамичной части игры.
  • В процессе первоначального обучения и настройки пользователя в игре. Например, пользователь мог совершить покупку вне вашего приложения до его установки. Мы рекомендуем уведомлять новых пользователей о вознаграждении сразу после запуска игры или во время первоначальной настройки пользователя. Если ваше приложение требует от пользователя создания учетной записи или входа в систему перед предоставлением ему права на получение вознаграждения, рекомендуется сообщить пользователю, какие шаги необходимо выполнить для получения покупки. Это крайне важно, поскольку возврат средств за покупки производится через 3 дня, если ваше приложение не обработало покупку.

При уведомлении пользователя о покупке Google Play рекомендует следующие способы:

  • Отобразить диалоговое окно внутри приложения.
  • Отправьте сообщение во встроенный в приложение чат, четко указав, что в этом чате появилось новое сообщение.
  • Используйте уведомление от операционной системы.

Уведомление должно информировать пользователя о полученной им выгоде. Например: «Вы приобрели 100 золотых монет!». Кроме того, если покупка была результатом участия в программе, такой как Play Pass, ваше приложение должно сообщать об этом пользователю. Например: «Предметы получены! Вы только что получили 100 самоцветов с Play Pass. Продолжить.». Для каждой программы могут быть рекомендации по рекомендуемому тексту для отображения пользователям информации о выгодах.

Уведомить Google о завершении покупки

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

Расходные материалы

Для расходуемых товаров, если ваше приложение имеет защищенный бэкэнд, мы рекомендуем использовать Purchases.products:consume для надежного использования покупок. Убедитесь, что покупка еще не была использована, проверив consumptionState в результате вызова Purchases.products:get . Если ваше приложение работает только на стороне клиента и не имеет бэкэнда, используйте consumeAsync() из библиотеки Google Play Billing. Оба метода выполняют требование подтверждения и указывают, что ваше приложение предоставило пользователю право на использование товара. Эти методы также позволяют вашему приложению сделать одноразовый продукт, соответствующий введенному токену покупки, доступным для повторной покупки. При consumeAsync() необходимо также передать объект, реализующий интерфейс ConsumeResponseListener . Этот объект обрабатывает результат операции потребления. Вы можете переопределить метод onConsumeResponse() , который вызывается библиотекой Google Play Billing после завершения операции.

Следующий пример иллюстрирует использование продукта в библиотеке Google Play Billing с помощью соответствующего токена покупки:

Котлин

    val consumeParams =
        ConsumeParams.newBuilder()
            .setPurchaseToken(purchase.getPurchaseToken())
            .build()
    val consumeResult = withContext(Dispatchers.IO) {
        client.consumePurchase(consumeParams)
    }

Java

    ConsumeParams consumeParams =
            ConsumeParams.newBuilder()
                .setPurchaseToken(purchase.getPurchaseToken())
                .build();

    ConsumeResponseListener listener = new ConsumeResponseListener() {
        @Override
        public void onConsumeResponse(BillingResult billingResult, String purchaseToken) {
            if (billingResult.getResponseCode() == BillingResponseCode.OK) {
                // Handle the success of the consume operation.
            }
        }
    };

    billingClient.consumeAsync(consumeParams, listener);

Нерасходные товары

Для подтверждения покупок товаров, не являющихся расходными материалами, если ваше приложение использует защищенную серверную часть, мы рекомендуем использовать Purchases.products:acknowledge для надежного подтверждения покупок. Убедитесь, что покупка ранее не была подтверждена, проверив acknowledgementState в результате вызова Purchases.products:get .

Если ваше приложение предназначено только для клиентского использования, используйте BillingClient.acknowledgePurchase() из библиотеки Google Play Billing Library. Перед подтверждением покупки ваше приложение должно проверить, была ли она уже подтверждена, используя метод isAcknowledged() из библиотеки Google Play Billing Library.

В следующем примере показано, как подтвердить покупку с помощью библиотеки Google Play Billing:

Котлин

val client: BillingClient = ...
val acknowledgePurchaseResponseListener: AcknowledgePurchaseResponseListener = ...

val acknowledgePurchaseParams = AcknowledgePurchaseParams.newBuilder()
    .setPurchaseToken(purchase.purchaseToken)
val ackPurchaseResult = withContext(Dispatchers.IO) {
     client.acknowledgePurchase(acknowledgePurchaseParams.build())
}

Java

BillingClient client = ...
AcknowledgePurchaseResponseListener acknowledgePurchaseResponseListener = ...

AcknowledgePurchaseParams acknowledgePurchaseParams =
                AcknowledgePurchaseParams.newBuilder()
                    .setPurchaseToken(purchase.getPurchaseToken())
                    .build();
 client.acknowledgePurchase(acknowledgePurchaseParams, acknowledgePurchaseResponseListener);

Подписки

Подписки обрабатываются аналогично товарам, не являющимся расходными материалами. По возможности используйте Purchases.subscriptions.acknowledge из API разработчиков Google Play, чтобы надежно подтвердить покупку из вашей защищенной серверной части. Убедитесь, что покупка ранее не была подтверждена, проверив acknowledgementState в ресурсе покупки из Purchases.subscriptions:get . В противном случае вы можете подтвердить подписку, используя BillingClient.acknowledgePurchase() из библиотеки Google Play Billing после проверки isAcknowledged() . Все первоначальные покупки подписок должны быть подтверждены. Подтверждение продления подписки не требуется. Для получения дополнительной информации о том, когда необходимо подтверждать подписки, см. раздел «Продажа подписок» .

Краткий обзор

Следующий фрагмент кода демонстрирует краткое изложение этих шагов.

Котлин

fun handlePurchase(Purchase purchase) {
    // Purchase retrieved from BillingClient#queryPurchasesAsync or your
    // onPurchasesUpdated.
    Purchase purchase = ...;

    // Step 1: Send the purchase to your secure backend to verify the purchase
    // following
    // https://developer.android.com/google/play/billing/security#verify
.
    // Step 2: Update your entitlement storage with the purchase. If purchase is
    // in PENDING state then ensure the entitlement is marked as pending and the
    // user does not receive benefits yet. It is recommended that this step is
    // done on your secure backend and can combine in the API call to your
    // backend in step 1.

    // Step 3: Notify the user using appropriate messaging (delaying
    // notification if needed as discussed above).

    // Step 4: Notify Google the purchase was processed using the steps
    // discussed in the processing purchases section.
}

Java

void handlePurchase(Purchase purchase) {
    // Purchase retrieved from BillingClient#queryPurchasesAsync or your
    // onPurchasesUpdated.
    Purchase purchase = ...;

    // Step 1: Send the purchase to your secure backend to verify the purchase
    // following
    // https://developer.android.com/google/play/billing/security#verify

    // Step 2: Update your entitlement storage with the purchase. If purchase is
    // in PENDING state then ensure the entitlement is marked as pending and the
    // user does not receive benefits yet. It is recommended that this step is
    // done on your secure backend and can combine in the API call to your
    // backend in step 1.

    // Step 3: Notify the user using appropriate messaging (delaying
    // notification if needed as discussed above).

    // Step 4: Notify Google the purchase was processed using the steps
    // discussed in the processing purchases section.
}

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

Обработка незавершенных транзакций

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

Например, пользователь может инициировать транзакцию, выбрав физический магазин, где он позже расплатится наличными. Пользователь получает код как в виде уведомления, так и по электронной почте. Прибыв в магазин, пользователь может использовать код у кассира и расплатиться наличными. Затем Google уведомляет вас и пользователя о получении платежа. После этого ваше приложение может предоставить пользователю право на получение бонуса.

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

Когда ваше приложение получает новую покупку, будь то через PurchasesUpdatedListener или в результате вызова queryPurchasesAsync , используйте метод getPurchaseState() , чтобы определить, находится ли состояние покупки в PURCHASED или PENDING . Предоставлять право на покупку следует только тогда, когда состояние покупки — PURCHASED .

Если ваше приложение запущено и у вас есть активное подключение к библиотеке Play Billing, когда пользователь завершает покупку, ваш PurchasesUpdatedListener вызывается снова, и PurchaseState теперь имеет PURCHASED . На этом этапе ваше приложение может обработать покупку, используя стандартный метод обнаружения и обработки покупок . Ваше приложение также должно вызвать queryPurchasesAsync() в методе onResume() вашего приложения, чтобы обрабатывать покупки, которые перешли в состояние PURCHASED , пока ваше приложение не работало.

Когда статус покупки меняется с PENDING на PURCHASED , ваш клиент real_time_developer_notifications получает уведомление ONE_TIME_PRODUCT_PURCHASED или SUBSCRIPTION_PURCHASED . Если покупка отменена, вы получите уведомление ONE_TIME_PRODUCT_CANCELED или SUBSCRIPTION_PENDING_PURCHASE_CANCELED . Это может произойти, если ваш клиент не завершит оплату в требуемый срок. Обратите внимание, что вы всегда можете использовать API разработчиков Google Play, чтобы проверить текущий статус покупки.

Обработка покупок в больших количествах

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

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

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

Запросить настройки выставления счетов пользователя.

getBillingConfigAsync() предоставляет информацию о стране, которую пользователь использует для доступа к Google Play.

Вы можете запросить конфигурацию выставления счетов пользователя после создания объекта BillingClient . Следующий фрагмент кода описывает, как вызвать метод getBillingConfigAsync() . Обработайте ответ, реализовав интерфейс BillingConfigResponseListener . Этот слушатель получает обновления для всех запросов конфигурации выставления счетов, инициированных вашим приложением.

If the returned BillingResult contains no errors, you can then check the countryCode field in the BillingConfig object to obtain the user's Play Country.

Котлин

// Use the default GetBillingConfigParams.
val getBillingConfigParams = GetBillingConfigParams.newBuilder().build()
billingClient.getBillingConfigAsync(getBillingConfigParams,
    object : BillingConfigResponseListener {
        override fun onBillingConfigResponse(
            billingResult: BillingResult,
            billingConfig: BillingConfig?
        ) {
            if (billingResult.responseCode == BillingResponseCode.OK
                && billingConfig != null) {
                val countryCode = billingConfig.countryCode
                ...
            } else {
                // TODO: Handle errors
            }
        }
    })

Java

// Use the default GetBillingConfigParams.
GetBillingConfigParams getBillingConfigParams = GetBillingConfigParams.newBuilder().build();
billingClient.getBillingConfigAsync(getBillingConfigParams,
    new BillingConfigResponseListener() {
      public void onBillingConfigResponse(
          BillingResult billingResult, BillingConfig billingConfig) {
        if (billingResult.getResponseCode() == BillingResponseCode.OK
            && billingConfig != null) {
            String countryCode = billingConfig.getCountryCode();
            ...
         } else {
            // TODO: Handle errors
        }
      }
    });

Cart abandonment reminders in Google Play Games home (enabled by default)

For Games developers that monetize through one-time products, one way in which stock-keeping units (SKUs) that are active in Google Play Console can be sold outside of your app is the Cart Abandonment Reminder feature, which nudges users to complete their previously abandoned purchases while browsing the Google Play Store. These purchases happen outside of your app, from the Google Play Games home in the Google Play Store.

This feature is enabled by default to help users pick up where they left off and to help developers maximize sales. However, you can opt your app out of this feature by submitting the Cart Abandonment Reminder feature opt-out form . For best practices on managing SKUs within the Google Play Console, see Create an in-app product .

The following images show the Cart Abandonment Reminder appearing on the Google Play Store:

the Google Play Store screen shows a
    purchase prompt for a previously abandoned purchase
Figure 2. The Google Play Store screen shows a purchase prompt for a previously abandoned purchase.

the Google Play Store screen shows a
    purchase prompt for a previously abandoned purchase
Figure 3. The Google Play Store screen shows a purchase prompt for a previously abandoned purchase.