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

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

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

Вот типичный поток покупки для единовременной покупки или подписки.

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

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

  • Active: пользователь хорош и имеет доступ к подписке.
  • Отменен: пользователь отменил, но все еще имеет доступ до истечения срока действия.
  • В изящном периоде: пользователь столкнулся с проблемой оплаты, но все еще имеет доступ, в то время как Google повторяет способ оплаты.
  • В условиях: пользователь пережил проблему оплаты и больше не имеет доступа, в то время как Google повторяет способ оплаты.
  • Paused: User paused their access and does not have access until they resume.
  • Expired: User has cancelled and lost access to the subscription. Пользователь считается выбывшим по истечении срока действия.

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

The first step to integrate with Google Play's billing system is to add the Google Play Billing Library to your app and initialize a connection.

Добавьте зависимость от библиотеки платежей Google Play.

Add the Google Play Billing Library dependency to your app's build.gradle file as shown:

классный

dependencies {
    def billing_version = "7.0.0"

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

Котлин

dependencies {
    val billing_version = "7.0.0"

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

Если вы используете Kotlin, модуль KTX библиотеки Google Play Billing Library содержит расширения Kotlin и поддержку сопрограмм, которые позволяют вам писать идиоматический Kotlin при использовании библиотеки Google Play Billing Library. To include these extensions in your project, add the following dependency to your app's build.gradle file as shown:

классный

dependencies {
    def billing_version = "7.0.0"

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

Котлин

dependencies {
    val billing_version = "7.0.0"

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

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

Once you've added a dependency on the Google Play Billing Library, you need to initialize a BillingClient instance. BillingClient is the main interface for communication between the Google Play Billing Library and the rest of your app. BillingClient provides convenience methods, both synchronous and asynchronous, for many common billing operations. Настоятельно рекомендуется одновременно открывать одно активное соединение BillingClient , чтобы избежать нескольких обратных вызовов PurchasesUpdatedListener для одного события.

Чтобы создать BillingClient , используйте newBuilder() . You can pass any context to newBuilder() , and BillingClient uses it to get an application context. That means you don't need to worry about memory leaks. To receive updates on purchases, you must also call setListener() , passing a reference to a PurchasesUpdatedListener . This listener receives updates for all purchases in your app.

Котлин

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

After you have created a BillingClient , you need to establish a connection to Google Play.

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

You must also implement retry logic to handle lost connections to Google Play. Чтобы реализовать логику повтора, переопределите метод обратного вызова onBillingServiceDisconnected() и убедитесь, что BillingClient вызывает метод startConnection() для повторного подключения к Google Play, прежде чем делать дальнейшие запросы.

The following example demonstrates how to start a connection and test that it's ready to use:

Котлин

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

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

After you have established a connection to Google Play, you are ready to query for your available products and display them to your users.

Querying for product details is an important step before displaying your products to your users, as it returns localized product information. For subscriptions, ensure your product display follows all Play policies .

To query for in-app product details, call queryProductDetailsAsync() .

Чтобы обработать результат асинхронной операции, необходимо также указать прослушиватель, реализующий интерфейс ProductDetailsResponseListener . You can then override onProductDetailsResponse() , which notifies the listener when the query finishes, as shown in the following example:

Котлин

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

billingClient.queryProductDetailsAsync(queryProductDetailsParams) {
    billingResult,
    productDetailsList ->
      // check billingResult
      // process returned productDetailsList
}

Ява

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,
                List<ProductDetails> productDetailsList) {
            // check billingResult
            // process returned productDetailsList
        }
    }
)

При запросе сведений о продукте передайте экземпляр QueryProductDetailsParams , который определяет список строк идентификатора продукта, созданных в консоли Google Play, вместе с ProductType . The ProductType can be either ProductType.INAPP for one-time products or ProductType.SUBS for subscriptions.

Запросы с помощью расширений Kotlin

If you're using Kotlin extensions , you can query for in-app product details by calling the queryProductDetails() extension function.

queryProductDetails() leverages Kotlin coroutines so that you don't need to define a separate listener. Instead, the function suspends until the querying completes, after which you can process the result:

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 . To ensure proper support for this scenario, learn how to use backwards compatibility features in the Play Billing Library 5 migration guide .

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

The Google Play Billing Library stores the query results in a List of ProductDetails objects. Затем вы можете вызвать различные методы для каждого объекта ProductDetails в списке, чтобы просмотреть соответствующую информацию о продукте, продаваемом в приложении, например его цену или описание. To view the available product detail information, see the list of methods in the ProductDetails class.

Before offering an item for sale, check that the user does not already own the item. If the user has a consumable that is still in their item library, they must consume the item before they can buy it again.

Before offering a subscription, verify that the user is not already subscribed. Также обратите внимание на следующее:

  • queryProductDetailsAsync() returns subscription product details and a maximum of 50 offers per subscription.
  • queryProductDetailsAsync() returns only offers for which the user is eligible. Если пользователь пытается приобрести предложение, на которое он не имеет права (например, если приложение отображает устаревший список подходящих предложений), Play информирует пользователя о том, что оно не имеет права, и пользователь может выбрать покупку базового плана. вместо.

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

To start a purchase request from your app, call the launchBillingFlow() method from your app's main thread. Этот метод принимает ссылку на объект BillingFlowParams , который содержит соответствующий объект ProductDetails , полученный в результате вызова queryProductDetailsAsync() . To create a BillingFlowParams object, use the BillingFlowParams.Builder class.

Котлин

// 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)
        // For One-time product, "setOfferToken" method shouldn't be called.
        // For subscriptions, to get an offer token, 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)
            // For one-time products, "setOfferToken" method shouldn't be called.
            // For subscriptions, to get an offer token, 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);

The launchBillingFlow() method returns one of several response codes listed in BillingClient.BillingResponseCode . Be sure to check this result to ensure there were no errors launching the purchase flow. A BillingResponseCode of OK indicates a successful launch.

On a successful call to launchBillingFlow() , the system displays the Google Play purchase screen. Figure 1 shows a purchase screen for a subscription:

the google play purchase screen shows a subscription that is
            available for purchase
Figure 1. The Google Play purchase screen shows a subscription that is available for purchase.

Google Play вызывает onPurchasesUpdated() для доставки результата операции покупки прослушивателю, реализующему интерфейс PurchasesUpdatedListener . The listener is specified using the setListener() method when you initialized your client .

You must implement onPurchasesUpdated() to handle possible response codes. The following example shows how to override onPurchasesUpdated() :

Котлин

override fun onPurchasesUpdated(billingResult: BillingResult, purchases: List<Purchase>?) {
   if (billingResult.responseCode == BillingResponseCode.OK && purchases != null) {
       for (purchase in purchases) {
           handlePurchase(purchase)
       }
   } else if (billingResult.responseCode == BillingResponseCode.USER_CANCELED) {
       // Handle an error caused by a user cancelling 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) {
            handlePurchase(purchase);
        }
    } else if (billingResult.getResponseCode() == BillingResponseCode.USER_CANCELED) {
        // Handle an error caused by a user cancelling the purchase flow.
    } else {
        // Handle any other error codes.
    }
}

A successful purchase generates a Google Play purchase success screen similar to figure 2.

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

В случае успешной покупки также создается токен покупки, который представляет собой уникальный идентификатор, представляющий пользователя и идентификатор продукта для приобретенного продукта внутри приложения. Ваши приложения могут хранить токен покупки локально, однако мы рекомендуем передать токен на ваш защищенный внутренний сервер, где вы сможете затем подтвердить покупку и защититься от мошенничества. This process is further described in the following section.

The user is also emailed a receipt of the transaction containing an Order ID or a unique ID of the transaction. Пользователи получают электронное письмо с уникальным идентификатором заказа при каждой единоразовой покупке продукта, а также при первоначальной покупке подписки и последующих периодических автоматических продлениях. You can use the Order ID to manage refunds in the Google Play Console.

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

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

The Google Play purchase screen indicating that the price was customized for the user.
Figure 3. The Google Play purchase screen indicating that the price was customized for the user.

Вам необходимо обратиться к ст. 6 (1) (ea) CRD of the Consumer Rights Directive 2011/83/EU to determine if the price you are offering to users is personalized.

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

See the Consumer Help Center for more information.

Обработка покупок

Once a user completes a purchase, your app then needs to process that purchase. In most cases, your app is notified of purchases through your PurchasesUpdatedListener . Однако в некоторых случаях ваше приложение будет уведомлено о покупках путем вызова BillingClient.queryPurchasesAsync() , как описано в разделе «Извлечение покупок» .

Кроме того, если у вас есть клиент уведомлений разработчика в реальном времени в вашем безопасном серверном интерфейсе, вы можете зарегистрировать новые покупки, получив subscriptionNotification или oneTimeProductNotification , предупреждающее вас о новой покупке. After receiving these notifications, call the Google Play Developer API to get the complete status and update your own backend state.

Your app should process a purchase in the following way:

  1. Подтвердите покупку.
  2. Give content to the user, and acknowledge delivery of the content. Optionally, mark the item as consumed so that the user can buy the item again.

To verify a purchase, first check that the purchase state is PURCHASED . If the purchase is PENDING , then you should process the purchase as described in Handling pending transactions . Для покупок, полученных с помощью onPurchasesUpdated() или queryPurchasesAsync() , вам следует дополнительно проверить покупку, чтобы обеспечить ее законность, прежде чем ваше приложение предоставит право. To learn how to properly verify a purchase, see Verify purchases before granting entitlements .

Once you've verified the purchase, your app is ready to grant entitlement to the user. Учетную запись пользователя, связанную с покупкой, можно идентифицировать с помощью ProductPurchase.obfuscatedExternalAccountId , возвращаемого Purchases.products:get для покупок продуктов в приложении, и SubscriptionPurchase.obfuscatedExternalAccountId , возвращаемого Purchases.subscriptions:get для подписок на стороне сервера, или obfuscatedAccountId из Purchase.getAccountIdentifiers() on the client side, if one was set with setObfuscatedAccountId when the purchase was made.

After granting entitlement, your app must then acknowledge the purchase. This acknowledgement communicates to Google Play that you have granted entitlement for the purchase.

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

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

For consumables, if your app has a secure backend, we recommend that you use Purchases.products:consume to reliably consume purchases. Make sure the purchase wasn't already consumed by checking the consumptionState from the result of calling Purchases.products:get . If your app is client-only without a backend, use consumeAsync() from the Google Play Billing Library. Both methods fulfill the acknowledgement requirement and indicate that your app has granted entitlement to the user. These methods also enable your app to make the one-time product corresponding to the input purchase token available for repurchase. With consumeAsync() you must also pass an object that implements the ConsumeResponseListener interface. This object handles the result of the consumption operation. You can override the onConsumeResponse() method, which the Google Play Billing Library calls when the operation is complete.

The following example illustrates consuming a product with the Google Play Billing Library using the associated purchase token:

Котлин

suspend fun handlePurchase(purchase: Purchase) {
    // Purchase retrieved from BillingClient#queryPurchasesAsync or your PurchasesUpdatedListener.
    val purchase : Purchase = ...;

    // Verify the purchase.
    // Ensure entitlement was not already granted for this purchaseToken.
    // Grant entitlement to the user.

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

Ява

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

    // Verify the purchase.
    // Ensure entitlement was not already granted for this purchaseToken.
    // Grant entitlement to the user.

    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 .

If your app is client-only, use BillingClient.acknowledgePurchase() from the Google Play Billing Library in your app. Прежде чем подтвердить покупку, ваше приложение должно проверить, было ли оно уже подтверждено, с помощью метода isAcknowledged() в библиотеке платежей Google Play.

The following example shows how to acknowledge a purchase using the Google Play Billing Library:

Котлин

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

suspend fun handlePurchase() {
    if (purchase.purchaseState === PurchaseState.PURCHASED) {
        if (!purchase.isAcknowledged) {
            val acknowledgePurchaseParams = AcknowledgePurchaseParams.newBuilder()
                    .setPurchaseToken(purchase.purchaseToken)
            val ackPurchaseResult = withContext(Dispatchers.IO) {
               client.acknowledgePurchase(acknowledgePurchaseParams.build())
            }
        }
     }
}

Ява

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

void handlePurchase(Purchase purchase) {
    if (purchase.getPurchaseState() == PurchaseState.PURCHASED) {
        if (!purchase.isAcknowledged()) {
            AcknowledgePurchaseParams acknowledgePurchaseParams =
                AcknowledgePurchaseParams.newBuilder()
                    .setPurchaseToken(purchase.getPurchaseToken())
                    .build();
            client.acknowledgePurchase(acknowledgePurchaseParams, acknowledgePurchaseResponseListener);
        }
    }
}

Подписки

Subscriptions are handled similarly to non-consumables. Если возможно, используйте Purchases.subscriptions.acknowledge от API разработчика Google Play, чтобы надежно подтвердить покупку у вашего безопасного бэкэнда. Убедитесь, что покупка ранее не была признана, проверяя acknowledgementState в ресурсе покупки по Purchases.subscriptions:get . В противном случае вы можете подтвердить подписку с использованием BillingClient.acknowledgePurchase() из библиотеки Google Play Billing после проверки isAcknowledged() . All initial subscription purchases need to be acknowledged. Subscription renewals don't need to be acknowledged. For more information about when subscriptions need to be acknowledged, see the Sell subscriptions topic.

Получение покупок

Listening to purchase updates using a PurchasesUpdatedListener is not sufficient to ensure your app processes all purchases. It's possible that your app might not be aware of all the purchases a user has made. Here are some scenarios where your app could lose track or be unaware of purchases:

  • Сетевые проблемы во время покупки : пользователь делает успешную покупку и получает подтверждение от Google, но их устройство теряет сетевое подключение до того, как их устройство получит уведомление о покупке через PurchasesUpdatedListener .
  • Multiple devices : A user buys an item on one device and then expects to see the item when they switch devices.
  • Handling purchases made outside your app : Some purchases, such as promotion redemptions, can be made outside of your app.

Чтобы справиться с этими ситуациями, убедитесь, что ваше приложение вызывает BillingClient.queryPurchasesAsync() в вашем методе onResume() , чтобы гарантировать, что все покупки успешно обрабатываются, как описано при обработке покупок .

В следующем примере показано, как получить покупки подписки пользователя. Обратите внимание, что queryPurchasesAsync() возвращает только активные подписки и неосказанные единовременные покупки.

Котлин

val params = QueryPurchasesParams.newBuilder()
               .setProductType(ProductType.SUBS)

// uses queryPurchasesAsync Kotlin extension function
val purchasesResult = billingClient.queryPurchasesAsync(params.build())

// check purchasesResult.billingResult
// process returned purchasesResult.purchasesList, e.g. display the plans user owns

Ява

billingClient.queryPurchasesAsync(
    QueryPurchasesParams.newBuilder()
      .setProductType(ProductType.SUBS)
      .build(),
    new PurchasesResponseListener() {
      public void onQueryPurchasesResponse(BillingResult billingResult, List<Purchase> purchases) {
        // check billingResult
        // process returned purchase list, e.g. display the plans user owns

      }
    }
);

Обработка покупок, совершенных вне вашего приложения

Some purchases, such as promotion redemptions, can happen outside of your app. Когда пользователь совершает покупку вне вашего приложения, он ожидает, что ваше приложение покажет сообщение в приложении или использует какой-то механизм уведомлений, чтобы сообщить пользователю, что приложение правильно получило и обрабатывает покупку. Некоторые приемлемые механизмы:

  • Показывать всплывающее окно в приложении.
  • Deliver the message to an in-app message box, and clearly stating that there is a new message in the in-app message box.
  • Используйте уведомление ОС.

Keep in mind that it is possible for your app to be in any state when your app recognizes the purchase. It is even possible for your app to not even be installed when the purchase was made. Users expect to receive their purchase when they resume the app, regardless of the state in which the app is.

You must detect purchases regardless of the state in which the app is when the purchase was made. However, there are some exceptions where it may be acceptable to not immediately notify the user that the item was received. Например:

  • During the action part of a game, where showing a message may distract the user. In this case, you must notify the user after the action part is over.
  • During cutscenes, where showing a message may distract the user. In this case, you must notify the user after the cutscene is over.
  • During the initial tutorial and user setup parts of the game. We recommend you notify new users of the reward immediately after they open the game or during initial user set up. However, it is acceptable to wait until the main game sequence is available to notify the user.

Always keep the user in mind when deciding when and how to notify your users of purchases made outside of your app. Каждый раз, когда пользователь не сразу получает уведомление, он может запутаться и может прекратить использование вашего приложения, связываться с поддержкой пользователей или жаловаться на это в социальных сетях. ПРИМЕЧАНИЕ. PurchasesUpdatedListener зарегистрированы в контексте вашего приложения для обработки обновлений покупок, включая покупки, инициированные вне вашего приложения. This means that if your application process does not exist, your PurchasesUpdatedListener would not be notified. This is why your app should call BillingClient.queryPurchasesAsync() in the onResume() method as mentioned in Fetch Purchases .

Обработка ожидающих транзакций

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

For example, a user can initiate a transaction by choosing a physical store where they'll pay later with cash. The user receives a code through both notification and email. When the user arrives at the physical store, they can redeem the code with the cashier and pay with cash. Google then notifies both you and the user that payment has been received. Затем ваше приложение может предоставить право пользователю.

Call enablePendingPurchases() as part of initializing the BillingClient to enable pending transactions for your app. Your app must enable and support pending transactions for one-time products. Before adding support, be sure you understand the purchase lifecycle for pending transactions.

Когда ваше приложение получает новую покупку через PurchasesUpdatedListener или в результате вызова queryPurchasesAsync() , используйте метод getPurchaseState() , чтобы определить, является ли состояние покупки PURCHASED или PENDING . You should grant entitlement only when the state is PURCHASED .

Если ваше приложение запускается, когда пользователь завершает покупку, ваш PurchasesUpdatedListener вызывается снова, и PurchaseState теперь PURCHASED . At this point, your app can process the purchase using the standard method for processing purchases . Ваше приложение также должно вызывать queryPurchasesAsync() в методе onResume() вашего приложения для обработки покупок, которые перешли в состояние PURCHASED , пока ваше приложение не работало.

Когда покупка переходит с PENDING на PURCHASED , ваш клиент уведомлений для разработчиков в реальном времени получает уведомление ONE_TIME_PRODUCT_PURCHASED или SUBSCRIPTION_PURCHASED . If the purchase is cancelled, you receives a ONE_TIME_PRODUCT_CANCELED or SUBSCRIPTION_PENDING_PURCHASE_CANCELED notification. This can happen if your customer does not complete payment in the required timeframe. Note that you can always use the Google Play Developer API to check the current state of a purchase.

Работа с оптовыми закупками

Поддерживается в версиях 4.0 и выше библиотеки Google Play Billing, Google Play позволяет клиентам приобрести более одного из тех же продуктов в приложении в одной транзакции, указав количество из корзины покупки. Your app is expected to handle multi-quantity purchases and grant entitlement based on the specified purchase quantity.

To honor multi-quantity purchases, your app's provisioning logic needs to check for an item quantity. You can access a quantity field from one of the following APIs:

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

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

getBillingConfigAsync() provides the country the user uses for Google Play.

You can query the user's billing configuration after creating a BillingClient . The following code snippet describes how to make a call to getBillingConfigAsync() . Handle the response by implementing the BillingConfigResponseListener . This listener receives updates for all billing config queries initiated from your app.

Если возвращенный 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
        }
      }
    });