В этом документе описано, как интегрировать библиотеку Google Play Платежей в приложение, чтобы начать продавать товары.
Жизненный цикл покупки
Ниже описан типичный процесс покупки контента, оплачиваемого однократно, или подписки.
- Покажите пользователю, что он может купить.
- Запустите процесс покупки, чтобы пользователь мог подтвердить ее.
- Проверьте покупку на своем сервере.
- Предоставьте пользователю контент.
- Подтвердите получение контента. Если товар расходуемый, используйте его, чтобы пользователь мог купить его снова.
Подписки продлеваются автоматически, пока вы их не отмените. Подписка может иметь следующие статусы:
- Активна. У пользователя хорошая репутация, и у него есть доступ к подписке.
- Отменена. Пользователь отменил подписку, но у него есть доступ к контенту до окончания срока ее действия.
- Льготный период. У пользователя возникла проблема с оплатой, но у него по-прежнему есть доступ, пока Google пытается списать средства с помощью способа оплаты.
- Приостановлена. У пользователя возникла проблема с оплатой, и у него больше нет доступа к контенту. Google пытается списать средства, используя указанный способ оплаты.
- Приостановлено. Пользователь приостановил доступ и не сможет им пользоваться, пока не возобновит.
- Истек срок действия. Пользователь отменил подписку и потерял к ней доступ. По истечении срока действия подписки пользователь считается отказавшимся.
Как инициализировать подключение к Google Play
Чтобы интегрировать платежную систему Google Play, сначала добавьте в приложение библиотеку Google Play Billing Library и установите подключение.
Как добавить зависимость библиотеки 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. } });
Как автоматически восстановить подключение
В версии 8.0.0 в BillingClient.Builder появился метод enableAutoServiceReconnection(). Теперь библиотека Play Платежей может автоматически восстанавливать подключение к сервису, если вызов 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 вызывает метод 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 Console.
Как указать персональную цену
Если ваше приложение распространяется среди пользователей в Европейском союзе, при вызове launchBillingFlow используйте метод setIsOfferPersonalized(), чтобы сообщить пользователям, что цена товара была персонализирована с помощью автоматизированного принятия решений.
Вам необходимо ознакомиться со статьей 6 (1) (ea) CRD Директивы ЕС о защите прав потребителей 2011/83/EU, в которой дано определение персональной цены.
setIsOfferPersonalized() принимает логическое значение. Если true, в интерфейсе Google Play будет показано сообщение о раскрытии информации. Если false, то в интерфейсе не будет раскрытия информации. Значение по умолчанию – false.
Дополнительную информацию можно найти в Справочном центре для потребителей.
Как прикреплять идентификаторы пользователей
При запуске процесса покупки приложение может прикрепить любые идентификаторы пользователя, совершающего покупку, с помощью obfuscatedAccountId или obfuscatedProfileId. Например, это может быть обфусцированная версия имени пользователя, вошедшего в аккаунт в вашей системе. Эти параметры помогают Google выявлять мошенничество. Кроме того, это поможет вам убедиться, что покупки приписываются правильному пользователю, как описано в разделе Предоставление прав пользователям.
Как обнаруживать и обрабатывать покупки
Обнаружение и обработка покупки, описанные в этом разделе, применимы ко всем типам покупок, включая покупки вне приложения, например активацию промокодов.
Приложение может обнаруживать новые покупки и завершенные покупки в ожидании одним из следующих способов:
- Когда функция
onPurchasesUpdatedвызывается в результате вызова приложением функцииlaunchBillingFlow(как описано в предыдущем разделе) или когда приложение работает с активным подключением к библиотеке Платежей, если покупка совершается вне приложения или завершается незавершенная покупка. Например, участник семейной группы одобряет незавершенную покупку на другом устройстве. - Когда приложение вызывает queryPurchasesAsync, чтобы запросить покупки пользователя.
В первом случае метод onPurchasesUpdated будет автоматически вызываться для новых или завершенных покупок, пока приложение запущено и активно подключение к библиотеке Google Play Платежей. Если приложение не запущено или у него нет активного подключения к библиотеке Google Play Платежей, метод onPurchasesUpdated не будет вызван. Помните, что пока приложение активно, рекомендуется поддерживать подключение, чтобы своевременно получать обновления о покупках.
Для пункта 2 необходимо вызвать метод BillingClient.queryPurchasesAsync(), чтобы ваше приложение обработало все покупки. Рекомендуем делать это, когда приложение успешно устанавливает связь с библиотекой Google Play Платежей (например, при запуске или переходе на передний план, как описано в разделе Инициализация BillingClient). Для этого вызовите queryPurchasesAsync, когда получите успешный результат для onServiceConnected. Соблюдение этой рекомендации необходимо для обработки событий и ситуаций, таких как:
- Проблемы с сетью во время покупки. Пользователь может успешно совершить покупку и получить подтверждение от Google, но его устройство потеряет подключение к сети до того, как устройство и ваше приложение получат уведомление о покупке через
PurchasesUpdatedListener. - Несколько устройств. Пользователь может купить товар на одном устройстве, а затем ожидать, что он будет доступен и на другом.
- Обработка покупок, совершенных за пределами приложения. Некоторые покупки, например активация промокодов, могут совершаться за пределами приложения.
- Обработка изменений статуса покупки. Пользователь может завершить оплату незавершенной покупки, когда ваше приложение не запущено. В этом случае он ожидает получить подтверждение покупки, когда откроет приложение.
- Приостановленные подписки. Подписка может быть приостановлена в течение жизненного цикла подписки. 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(). Все первоначальные покупки подписок должны быть подтверждены. Подтверждать продление подписки не нужно. Подробную информацию о том, когда нужно подтверждать подписки, вы найдете в статье Продажа подписок.
Как обрабатывать отклоненные или невыполненные покупки
Если покупка не прошла проверку на сервере, проверку соответствия требованиям, проверку на мошенничество и злоупотребление или если ваша система не может предоставить право, не предоставляйте его и не подтверждайте покупку.
Если покупка не подтверждена, то по истечении срока подтверждения средства автоматически возвращаются покупателю, а покупка отменяется. Однако не стоит полагаться на это и использовать срок подтверждения для отклонения покупок. Если вы не подтвердите заказ в течение этого времени, он останется неподтвержденным. Google Play не сможет определить, было ли это намеренным отказом или задержкой обработки.
Вместо этого немедленно отмените отклоненные покупки и верните за них средства через серверную часть, используя Google Play Developer API.
- Подписки: вызов
purchases.subscriptionsv2.revokeс параметром ""fullRefund": {}" вrevocationContext. В этом случае подписка немедленно отменяется, доступ к ней закрывается и инициируется полный возврат средств. - Контент для однократных покупок. Вызовите метод
orders.refundс параметромrevoke, заданным какtrue, чтобы немедленно вернуть средства за заказ и отозвать право на доступ к контенту. При вызове этого метода необходимо передать идентификатор заказа, который можно получить с помощью методаPurchase.getOrderId()илиpurchases.productsv2.getproductpurchasev2. Не вызывайте функцииconsumeAsync(),acknowledgePurchase(),Purchases.products:consumeиPurchases.products:acknowledgeдля отклоненных покупок. - Поведение уведомлений для разработчиков в режиме реального времени и аннулированных покупок. Отзывы и возвраты, инициированные разработчиком (
purchases.subscriptionsv2.revokeиorders.refundс параметромrevoke, установленным на значениеtrue), активируют уведомления для разработчиков в режиме реального времени и регистрируются в API аннулированных покупок (purchases.voidedpurchases.listс полем ответаvoidedSource, установленным на значение1для разработчика):- Подписки – отправляет уведомления
SUBSCRIPTION_REVOKEDиVoidedPurchaseNotification. - Контент для однократных покупок: отправляет уведомление
VoidedPurchaseNotification(иONE_TIME_PRODUCT_CANCELED, если в Play Console настроено получение всех уведомлений о контенте для однократных покупок). - Если вызвать функцию
orders.refundбез параметраrevoke, установленного на значениеtrue, будет выполнен возврат средств за заказ без отзыва права на покупку. Уведомление об отзыве или аннулировании покупки не будет отправлено. Подробнее о том, как управлять подписками и разовыми покупками…
- Подписки – отправляет уведомления
Recap
Ниже приведено краткое описание этих шагов.
Отправьте данные о покупке на защищенный сервер, чтобы проверить ее перед предоставлением доступа к контенту.
Обновите хранилище прав, добавив в него покупку. Если покупка находится в состоянии PENDING, убедитесь, что право на контент помечено как ожидающее и пользователь пока не получает преимуществ. Рекомендуется выполнять этот шаг на защищенном сервере. Его можно объединить с вызовом API к серверу на шаге 1.
Уведомите пользователя, используя подходящее сообщение (при необходимости отложите уведомление, как описано выше).
Уведомите Google об обработке покупки, выполнив инструкции из раздела "Обработка покупок".
Чтобы проверить, правильно ли вы выполнили эти шаги, следуйте инструкциям по тестированию.
Как обрабатывать транзакции в обработке
Google Play поддерживает транзакции с ожиданием, то есть транзакции, для которых требуется выполнить одно или несколько дополнительных действий после того, как пользователь инициирует покупку и до того, как будет обработан способ оплаты. Приложение не должно предоставлять доступ к таким покупкам, пока Google не уведомит вас о том, что средства были успешно списаны со счета пользователя.
Например, пользователь может начать транзакцию, выбрав обычный магазин, в котором он позже оплатит покупку наличными. Пользователь получает код в уведомлении и по электронной почте. Придя в магазин, пользователь может показать код кассиру и оплатить покупку наличными. После этого Google уведомляет вас и пользователя о получении платежа. После этого ваше приложение может предоставить пользователю право на контент.
Вызовите enablePendingPurchases() при инициализации BillingClient, чтобы включить транзакции в обработке для вашего приложения. Приложение должно поддерживать транзакции в обработке для контента для однократных покупок. Прежде чем добавлять поддержку, ознакомьтесь с жизненным циклом покупки для ожидающих транзакций.
Когда приложение получает новую покупку через PurchasesUpdatedListener или в результате вызова queryPurchasesAsync, используйте метод getPurchaseState(), чтобы определить, имеет ли покупка статус PURCHASED или PENDING. Предоставлять право доступа следует, только если статус – PURCHASED.
Если ваше приложение запущено и у вас есть активное подключение к библиотеке Play Платежей, когда пользователь совершает покупку, снова вызывается метод PurchasesUpdatedListener, а PurchaseState теперь имеет значение PURCHASED. После этого приложение может обработать покупку стандартным способом, описанным в разделе Обнаружение и обработка покупок. Чтобы обрабатывать покупки, которые перешли в статус PURCHASED, когда приложение не было запущено, вызовите метод queryPurchasesAsync() в методе onResume().
Когда статус покупки меняется с PENDING на PURCHASED, клиент real_time_developer_notifications получает уведомление 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:
getQuantity()из библиотеки Google Play Платежей.Purchases.products.quantityиз Google Play Developer API;
После того как вы добавите логику для обработки покупок нескольких товаров, вам нужно будет включить функцию покупки нескольких товаров для соответствующего товара на странице управления товарами, оплачиваемыми однократно, в Google Play Developer 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 } });