Как интегрировать библиотеку Google Play Платежей в приложение

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

Жизненный цикл покупки

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

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

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

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

Как инициализировать подключение к Google Play

Чтобы интегрировать платежную систему Google Play, сначала добавьте в приложение библиотеку Google Play Платежей и установите подключение.

Как добавить зависимость от библиотеки Google Play Платежей

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

Классный

dependencies {
    def billing_version = "9.1.0"

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

Котлин

dependencies {
    val billing_version = "9.1.0"

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

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

Классный

dependencies {
    def billing_version = "9.1.0"

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

Котлин

dependencies {
    val billing_version = "9.1.0"

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

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

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

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

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

Kotlin

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 перед отправкой дальнейших запросов. Если вы включили автоматическое повторное подключение сервиса, этот метод можно реализовать как пустую операцию.

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

Kotlin

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.
                    // It's a good practice to query products after the connection is established.
                    queryProductDetails();
                }
            }

            @Override
            public void onBillingServiceDisconnected() {
                // Try to restart the connection on the next request to
                // Google Play by calling the startConnection() method.
                // This is automatically handled by the library when you call a method that requires a connection.
            }
        });

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

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

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

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

Kotlin

val billingClient = BillingClient.newBuilder(context)
    .setListener(listener)
    .enablePendingPurchases(PendingPurchasesParams.newBuilder().enableOneTimeProducts().build())
    .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, вы сможете запрашивать доступные товары и показывать их пользователям.

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

Чтобы запросить информацию о контенте для однократных покупок, вызовите метод queryProductDetailsAsync. Этот метод может возвращать несколько предложений в зависимости от настроек контента для однократных покупок. Подробнее о нескольких способах покупки и предложениях для контента для однократных покупок…

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

Kotlin

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

billingClient.queryProductDetailsAsync(queryProductDetailsParams) {
        billingResult,
        queryProductDetailsResult ->
    if (billingResult.responseCode == BillingResponseCode.OK) {
        for (productDetails in queryProductDetailsResult.productDetailsList) {
            // Process successfully retrieved product details here.
        }

        for (unfetchedProduct in queryProductDetailsResult.unfetchedProductList) {
            // 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 successfully 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. Чтобы обеспечить поддержку этого сценария, ознакомьтесь с информацией о функциях обратной совместимости в руководстве по переходу на библиотеку Play Платежей версии 7.

Как обработать результат

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

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

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

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

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

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

Как запустить процесс покупки

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

Kotlin

// 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.getSubscriptionOfferDetails()
        // 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<BillingFlowParams.ProductDetailsParams> productDetailsParamsList =
    ImmutableList.of(
        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.getSubscriptionOfferDetails()
            // 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():

Kotlin

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

После успешной покупки появится экран подтверждения, похожий на тот, что показан на рисунке 2.

Экран успешной покупки в Google Play
Рисунок 2. Экран успешной покупки в Google Play.

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

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

Как указать персональную цену

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

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

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

setIsOfferPersonalized() принимает логическое значение. Когда true, в интерфейсе Google Play показывается раскрытие информации. Когда false, в интерфейсе не показывается раскрытие информации. Значение по умолчанию – false.

Дополнительную информацию можно найти в Справочном центре для потребителей.

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

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

Как обнаруживать и обрабатывать покупки

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

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

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

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

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

  • Проблемы с сетью во время покупки. Пользователь может успешно совершить покупку и получить подтверждение от Google, но его устройство потеряет связь с сетью до того, как оно и ваше приложение получат уведомление о покупке через PurchasesUpdatedListener.
  • Несколько устройств. Пользователь может купить товар на одном устройстве, а затем ожидать, что он будет доступен и на другом.
  • Обработка покупок, совершенных вне приложения. Некоторые покупки, например активация промокодов, могут быть совершены вне приложения.
  • Обработка переходов между состояниями покупки. Пользователь может оплатить покупку со статусом PENDING, когда приложение не запущено, и ожидать подтверждения оплаты при следующем запуске.
  • Приостановленные подписки. Подписка может быть приостановлена в течение жизненного цикла подписки. BillingClient.queryPurchasesAsync() возвращает информацию о заблокированных подписках, только если параметр includeSuspendedSubscriptions задан для QueryPurchasesParams.Builder. Приостановленные подписки не возвращаются в PurchasesUpdatedListener.

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

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

Ниже мы подробно расскажем о каждом из этих шагов, а затем приведем их краткий перечень.

Как подтвердить покупку

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

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

После того как приложение проверит покупку, оно может предоставить пользователю право на контент и уведомить его об этом. Прежде чем предоставлять право на использование, убедитесь, что ваше приложение проверяет, что статус покупки – PURCHASED. Если покупка имеет статус PENDING, приложение должно уведомить пользователя о том, что для ее завершения необходимо выполнить определенные действия. Предоставляйте право только после того, как статус покупки изменится с "В ожидании" на "Куплено". Дополнительную информацию можно найти в статье Как обрабатывать транзакции, ожидающие выполнения.

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

При предзаказе покупка имеет статус "В ожидании" до момента запуска. Покупка будет завершена в момент выхода игры, и ее статус изменится на "Куплено" без дополнительных действий.

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

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

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

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

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

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

Уведомление Google об обработке покупки

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

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

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

В следующем примере показано, как использовать библиотеку Google Play Платежей для получения информации о товаре с помощью связанного с ним токена покупки:

Kotlin

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 =
        (billingResult, 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 Платежей. Перед подтверждением покупки приложение должно проверить, было ли оно уже подтверждено, с помощью метода isAcknowledged() в библиотеке Google Play Платежей.

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

Kotlin

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

Java

if (purchase.getPurchaseState() == Purchase.PurchaseState.PURCHASED) {
    if (!purchase.isAcknowledged()) {
        AcknowledgePurchaseParams acknowledgePurchaseParams =
                AcknowledgePurchaseParams.newBuilder()
                        .setPurchaseToken(purchase.getPurchaseToken())
                        .build();
        billingClient.acknowledgePurchase(acknowledgePurchaseParams, (billingResult) -> {
            // Acknowledgment handled.
        });
    }
}

Подписки

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

Recap

Ниже приведены краткие инструкции.

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

  2. Обновите хранилище прав доступа, добавив в него информацию о покупке. Если покупка имеет статус PENDING, убедитесь, что право на контент также имеет статус PENDING и пользователь ещё не получил доступ к контенту. Рекомендуем выполнить этот шаг на защищенном сервере и объединить его с вызовом API на сервере, описанным в шаге 1.

  3. Уведомите пользователя, используя подходящее сообщение (при необходимости отложите уведомление, как обсуждалось ранее).

  4. Уведомите Google об обработке покупки, выполнив инструкции из раздела "Обработка покупок".

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

Как обрабатывать транзакции, ожидающие подтверждения

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

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

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

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

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

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

Как обрабатывать заказы с несколькими товарами

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

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

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

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

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

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

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

Kotlin

// 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 {
                // Handle errors
            }
        }
    }
)

Java

GetBillingConfigParams getBillingConfigParams = GetBillingConfigParams.newBuilder().build();
billingClient.getBillingConfigAsync(
        getBillingConfigParams,
        (billingResult, billingConfig) -> {
            if (billingResult.getResponseCode() == BillingResponseCode.OK && billingConfig != null) {
                String countryCode = billingConfig.getCountryCode();
            } else {
                // TODO: Handle errors
            }
        });