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

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

Жизнь покупки

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

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

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

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

Инициализируйте подключение к Google Play

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

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

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

Круто

dependencies {
    def billing_version = "8.0.0"

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

Котлин

dependencies {
    val billing_version = "8.0.0"

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

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

Круто

dependencies {
    def billing_version = "8.0.0"

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

Котлин

dependencies {
    val billing_version = "8.0.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()

Ява

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() {
        // Try to restart the connection on the next request to
        // Google Play by calling the startConnection() method.
    }
})

Ява

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() {
        // 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 Library теперь может автоматически восстанавливать соединение со службой при вызове API во время её отключения. Это может привести к сокращению количества ответов SERVICE_DISCONNECTED , поскольку восстановление соединения выполняется внутри системы до выполнения вызова API.

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

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

Котлин

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

Ява

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

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

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

Запрос информации о продукте — важный шаг перед показом ваших товаров пользователям, поскольку он возвращает локализованную информацию о продукте. Для подписок убедитесь, что отображение вашего продукта соответствует всем правилам Google 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.
               }
            }
}

Ява

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 Library сохраняет результаты запроса в объекте 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)

Ява

// 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.
   }
}

Ява

@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.

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

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

Укажите персональную цену

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

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

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

setIsOfferPersonalized() принимает логическое значение. При true пользовательский интерфейс Play включает раскрытие информации. При значении false пользовательский интерфейс не раскрывает информацию. Значение по умолчанию — false .

Более подробную информацию можно получить в Центре поддержки потребителей .

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

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

Обнаружение и обработка покупок

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

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

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

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

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

  • Проблемы с сетью во время покупки : пользователь может совершить успешную покупку и получить подтверждение от Google, но его устройство теряет сетевое подключение до того, как его устройство и ваше приложение получат уведомление о покупке через 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 Library. Оба метода соответствуют требованию подтверждения и указывают, что ваше приложение предоставило пользователю право. Эти методы также позволяют вашему приложению сделать одноразовый продукт, соответствующий введенному токену покупки, доступным для повторной покупки. С помощью consumeAsync() необходимо также передать объект, реализующий интерфейс ConsumeResponseListener . Этот объект обрабатывает результат операции использования. Вы можете переопределить метод onConsumeResponse() , который библиотека Google Play Billing Library вызывает после завершения операции.

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

Котлин

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

Ява

    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 Library:

Котлин

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

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

Ява

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 после проверки 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.
}

Ява

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 Library, когда пользователь завершает покупку, ваш PurchasesUpdatedListener вызывается снова, а PurchaseState теперь имеет PURCHASED . На этом этапе ваше приложение может обработать покупку, используя стандартный метод Detecting and Processing Purchases . Ваше приложение также должно вызывать 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 Library, Google Play позволяет клиентам приобретать несколько товаров одного и того же одноразового использования за одну транзакцию, указав количество в корзине покупок. Ожидается, что ваше приложение будет обрабатывать покупки с несколькими товарами и предоставлять право на покупку в зависимости от указанного количества.

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

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

Запросить конфигурацию выставления счетов пользователю

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

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

Если возвращенный BillingResult не содержит ошибок, вы можете проверить поле countryCode в объекте BillingConfig , чтобы получить страну воспроизведения пользователя.

Котлин

// 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
            }
        }
    })

Ява

// 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
        }
      }
    });

Напоминания о брошенной корзине на главной странице Google Play Игр (включены по умолчанию)

Для разработчиков игр, монетизирующих свой продукт через разовые продукты, одним из способов продажи единиц хранения (SKU), активных в Google Play Console, вне вашего приложения является функция напоминания о прекращении покупки в корзине, которая побуждает пользователей завершить ранее прекращенные покупки в Google Play Store. Эти покупки совершаются вне вашего приложения, из главного экрана Google Play Games в Google Play Store.

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

На следующих изображениях показано напоминание об брошенной корзине, появляющееся в магазине Google Play:

на экране Google Play Store отображается     подсказка о покупке для ранее оставленной покупки
Рис. 2. На экране Google Play Store отображается приглашение к покупке, от которого вы ранее отказались.

на экране Google Play Store отображается     подсказка о покупке для ранее оставленной покупки
Рис. 3. На экране Google Play Store отображается запрос на покупку, от которой вы ранее отказались.