Integrate the Google Play Billing Library into your app | Google Play’s billing system | Android Developers

Note:Before reading this topic, make sure you’ve read through the Play Console Help Center documentation, which describes critical purchase-related concepts, as well as how to create and configure your products for marketing. In addition, be sure you’ve set up your Google Play configuration beforehand by following the steps in Getting ready
This topic describes how to integrate the Google Play Billing Library into your app to start selling products .
This topic includes code examples that are based on the official sample apps on GitHub. See additional resources for a complete list of sample apps and other resources that you can use while integrating .

Life of a purchase

Here’s a typical purchase flow for a one-time purchase or a subscription.

  1. Show the user what they can buy.
  2. Launch the purchase flow for the user to accept the purchase.
  3. Verify the purchase on your server.
  4. Give content to the user.
  5. Acknowledge delivery of the content. For consumable products, consume
    the purchase so that the user can buy the item again.

Subscriptions automatically renew until they are canceled. A subscription can go through the following states :

  • Active: User is in good standing and has access to the subscription.
  • Cancelled: User has cancelled but still has access until expiration.
  • In grace period: User experienced a payment issue but still has
    access while Google is retrying the payment method.
  • On hold: User experienced a payment issue and no longer has access
    while Google is retrying the payment method.
  • Paused: User paused their access and does not have access until
    they resume.
  • Expired: User has cancelled and lost access to the subscription.
    The user is considered churned at expiration.

Initialize a connection to 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 .

Add the Google Play Billing Library dependency

Note:If you’ve followed the Getting ready guide, then you’ve already added the necessary dependencies and can move on to the next section .

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

Groovy

dependencies {
    def billing_version = "6.0.0"

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

Kotlin

dependencies {
    val billing_version = "6.0.0"

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

If you’re using Kotlin, the Google Play Billing Library KTX module contains
Kotlin extensions and coroutines support that enable you to write
idiomatic Kotlin when using the Google Play Billing Library. To include
these extensions in your project, add the following dependency to your
app’s build.gradle file as shown:

Groovy

dependencies {
    def billing_version = "6.0.0"

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

Kotlin

dependencies {
    val billing_version = "6.0.0"

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

Initialize a 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. It’s strongly recommended that you have one
active BillingClient
connection open at one time to avoid multiple PurchasesUpdatedListener callbacks
for a single event.

To create a BillingClient, use
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.

Kotlin

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

private var billingClient = BillingClient.newBuilder(context)
   .setListener(purchasesUpdatedListener)
   .enablePendingPurchases()
   .build()

Java

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

private BillingClient billingClient = BillingClient.newBuilder(context)
    .setListener(purchasesUpdatedListener)
    .enablePendingPurchases()
    .build();

Note:BillingResult contains a
BillingResult contains a

The Google Play Billing Library returns errors in the form of BillingResult. Acontains a BillingResponseCode, which categorizes possible billing-related errors that your app can encounter. For example, if you receive a SERVICE_DISCONNECTED error code, your app should reinitialize the connection with Google Play. Additionally, acontains a debug message, which is useful during development to diagnose errors.

Connect to Google Play

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

To connect to Google Play, call
startConnection().
The connection process is asynchronous, and you must implement a
BillingClientStateListener
to receive a callback once the setup of the client is complete and it’s
ready to make further requests.

You must also implement retry logic to handle lost connections to
Google Play. To implement retry logic, override the
onBillingServiceDisconnected()
callback method, and make sure that the BillingClient calls the
startConnection()
method to reconnect to Google Play before making further requests.

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

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

Java

billingClient.startConnection(new BillingClientStateListener() {
    @Override
    public void onBillingSetupFinished(BillingResult billingResult) {
        if (billingResult.getResponseCode() ==  BillingResponseCode.OK) {
            // The BillingClient is ready. You can query purchases here.
        }
    }
    @Override
    public void onBillingServiceDisconnected() {
        // Try to restart the connection on the next request to
        // Google Play by calling the startConnection() method.
    }
});

Note:BillingClient connection when
executing any methods.

It’s strongly recommended that you implement your own connection retry logic and override the onBillingServiceDisconnected() method. Make sure you maintain theconnection when executing any methods.

Show products available to buy

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().

To handle the result of the asynchronous operation, you must also specify a
listener which implements the
ProductDetailsResponseListener
interface. You can then override
onProductDetailsResponse(),
which notifies the listener when the query finishes, as shown in the following
example:

Kotlin

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

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

When querying for product details, pass an instance of
QueryProductDetailsParams
that specifies a list of product ID strings created in
Google Play Console along with a ProductType. The ProductType
can be either ProductType.INAPP for one-time products or
ProductType.SUBS for subscriptions.

Querying with Kotlin extensions

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 = ArrayList()
    productList.add(
              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.
}

Rarely, some devices are unable to support ProductDetails and queryProductDetailsAsync(),
usually due to outdated versions of
Google Play Services. To ensure
proper support for this scenario, learn how to use backwards compatibility features in the
Play Billing Library 5 migration guide.

Process the result

The Google Play Billing Library stores the query results in a List of
ProductDetails
objects. You can then call a variety of methods on each ProductDetails
object in the list to view relevant information about an in-app product,
such as its price or description. To view the available product detail
information, see the list of methods in the
ProductDetails
class.

Before offering an item for marketing, 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. Also note the following :

  • queryProductDetailsAsync() returns subscription product details and
    a maximum of 50 offers per subscription.
  • queryProductDetailsAsync() returns only offers for which the user
    is eligible. If the user attempts to purchase an offer for which
    they’re ineligible (for example, if the app is displaying an outdated
    list of eligible offers), Play informs the user that they are ineligible,
    and the user can choose to purchase the base plan instead.

Note:

Some Android devices might have an older version of the Google Play Store app that doesn’t support certain products types, such as subscriptions. Before your app enters the billing flow, you can call isFeatureSupported() to determine whether the device supports the products you want to sell. For a list of product types that can be supported, see BillingClient.FeatureType

Launch the purchase flow

To start a purchase request from your app, call the
launchBillingFlow()
method from your app’s main thread. This method takes a reference to a
BillingFlowParams
object that contains the relevant
ProductDetails object
obtained from calling
queryProductDetailsAsync().
To create a BillingFlowParams object, use the
BillingFlowParams.Builder class.

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

Java

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

ImmutableList productDetailsParamsList =
    ImmutableList.of(
        ProductDetailsParams.newBuilder()
             // retrieve a value for "productDetails" by calling queryProductDetailsAsync()
            .setProductDetails(productDetails)
            // to get an offer token, 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);

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 calls onPurchasesUpdated() to deliver the result of
the purchase operation to a listener that implements the
PurchasesUpdatedListener interface. 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():

Kotlin

override fun onPurchasesUpdated(billingResult: BillingResult, purchases: List?) {
   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.
   }
}

Java

@Override
void onPurchasesUpdated(BillingResult billingResult, List 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 màn hình hiển thị similar to figure 2 .
google play's purchase success screen
Figure 2. Google Play’s purchase success
screen.

A successful purchase also generates a purchase token, which is a unique identifier that represents the user and the product ID for the in-app product they purchased. Your apps can store the purchase token locally, though we recommend passing the token to your secure backend server where you can then verify the purchase and protect against fraud. 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. Users receive an email with a unique Order ID for each one-time product purchase, and also for the initial subscription purchase and subsequent recurring automatic renewals. You can use the Order ID to manage refunds in the Google Play Console .

Indicate a personalized price

If your app can be distributed to users in the European Union, use the
setIsOfferPersonalized() method to disclose to users that an item’s price
was personalized using automated decision-making.

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.

You must consult Art. 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() takes a boolean input. When true, the Play UI
includes the disclosure. When false, the UI omits the disclosure. The
default value is false.

See the Consumer Help Center for more information .

Processing purchases

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.
However, there are cases where your app will be made aware of purchases by
calling
BillingClient.queryPurchasesAsync()
as described in Fetching purchases.

Additionally, if you have a
Real Time Developer Notifications client
in your secure backend, you can register new purchases by receiving a
subscriptionNotification or a oneTimeProductNotification (only for pending
purchases) alerting you of a new purchase. 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. Verify the purchase.
  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. For purchases received from
onPurchasesUpdated()
or queryPurchasesAsync(),
you should further verify the purchase to ensure legitimacy before your
app grants entitlement. 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. The user account associated with the purchase can be
identified with the
ProductPurchase.obfuscatedExternalAccountId
returned by
Purchases.products:get
for in app product purchases and the
SubscriptionPurchase.obfuscatedExternalAccountId
returned by
Purchases.subscriptions:get
for subscriptions on the server side, or the
obfuscatedAccountId
from
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 .
Note:If you don’t acknowledge a purchase within three days, the user automatically receives a refund, and Google Play revokes the purchase .
The process to grant entitlement and acknowledge the purchase depends on whether the purchase is a consumable, a non-consumable, or a subscription .

Consumable Products

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 :

Kotlin

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

Java

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

Note:Because consumption requests can occasionally fail, you must check your secure backend server to ensure that each purchase token hasn’t been used so your app doesn’t grant entitlement multiple times for the same purchase. Alternatively, your app can wait until you receive a successful consumption response from Google Play before granting entitlement. If you choose to withhold purchases from the user until Google Play sends a successful consumption response, you must be very careful not to lose track of the purchases for which you have sent a consumption request .

Non-consumable Products

To acknowledge non-consumable purchases, if your app has a secure backend, we
recommend using
Purchases.products:acknowledge
to reliably acknowledge purchases.
Make sure the purchase hasn’t been previously acknowledged by checking the
acknowledgementState
from the result of calling
Purchases.products:get.

If your app is client-only, use
BillingClient.acknowledgePurchase()
from the Google Play Billing Library in your app. Before acknowledging a
purchase, your app should check whether it was already acknowledged by using the
isAcknowledged()
method in the Google Play Billing Library.

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

Kotlin

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

Java

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

Subscriptions are handled similarly to non-consumables. If possible, use
Purchases.subscriptions.acknowledge
from the Google Play Developer API to reliably acknowledge the purchase
from your secure backend. Verify that the purchase hasn’t been previously
acknowledged by checking the
acknowledgementState
in the purchase resource from
Purchases.subscriptions:get.
Otherwise, you can acknowledge a subscription using
BillingClient.acknowledgePurchase()
from the Google Play Billing Library after checking
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.

Fetching purchases

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:

  • Network Issues during the purchase: A user makes a successful
    purchase and receives confirmation from Google, but their device loses
    network connectivity before their device receives notification of
    the purchase through the 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.

To handle these situations, be sure that your app calls
BillingClient.queryPurchasesAsync()
in your onResume() method to ensure that all purchases
are successfully processed as described in processing purchases.

The following example shows how to fetch for a user’s subscription purchases.
Note that queryPurchasesAsync() returns only active subscriptions and
non-consumed one-time purchases.

Kotlin

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


Java

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

      }
    }
);

Fetching purchase history

queryPurchaseHistoryAsync() returns the most recent purchase made by
the user for each product, even if that purchase is expired, canceled,
or consumed.

If you’re using Kotlin extensions, you can use the queryPurchaseHistory() extension function.

Kotlin

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

// uses queryPurchaseHistory Kotlin extension function
val purchaseHistoryResult = billingClient.queryPurchaseHistory(params.build())

// check purchaseHistoryResult.billingResult
// process returned purchaseHistoryResult.purchaseHistoryRecordList, e.g. display purchase

Java

billingClient.queryPurchaseHistoryAsync(
    QueryPurchaseHistoryParams.newBuilder()
        .setProductType(ProductType.SUBS)
        .build(),
    new PurchaseHistoryResponseListener() {
      public void onPurchaseHistoryResponse(
        BillingResult billingResult, List purchasesHistoryList) {
          // check billingResult
          // process returned purchase history list, e.g. display purchase history
        }
    }
);

Handling purchases made outside your app

Some purchases, such as promotion redemptions, can happen outside of your app. When a user makes a purchase outside of your app, they expect your app to show an in-app message, or use some kind of notification mechanism to let the user know that the app correctly received and processed the purchase. Some acceptable mechanisms are :

  • Show an in-app popup.
  • Deliver the message to an in-app message box, and clearly stating that there
    is a new message in the in-app message box.
  • Use an OS notification message.

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. For example :

  • 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. Any time a user doesn’t immediately receive
a notification, they may get confused, and may stop using your app, contact user
support, or complain about it on social media.
Note: PurchasesUpdatedListener
is registered with your application context
to handle purchase updates, including purchases initiated outside of your app.
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.

Handling pending transactions

Note:

Pending transactions are required in Google Play Billing Library
versions 2.0 and higher. You should handle pending transactions explicitly.

Note:Additional forms of payment are not available for subscriptions purchases .
Google Play supports pending transactions, or transactions that require one or more additional steps between when a user initiates a purchase and when the payment method for the purchase is processed. Your app should not grant entitlement to these types of purchases until Google notifies you that the user’s payment method was successfully charged .

For example, a user can create a PENDING purchase of an in-app item by
choosing cash as their form of payment. The user can then choose a physical
store where they will complete the transaction and receive 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 cash has been received. Your app can then grant
entitlement to the user.

Your app must support pending transactions by calling
enablePendingPurchases()
as part of initializing your app.

When your app receives a new purchase, either through your
PurchasesUpdatedListener
or as a result of calling
queryPurchasesAsync(),
use the getPurchaseState()
method to determine whether the purchase state is PURCHASED or PENDING.

Note:

You should grant entitlement only when the state is PURCHASED. Use
getPurchaseState() instead of getOriginaljson() and make sure to
correctly handle PENDING transactions.

If your app is running when the user completes the purchase, your
PurchasesUpdatedListener
is called again, and the PurchaseState is now PURCHASED. At this point,
your app can process the purchase using the standard method for
processing one-time purchases. Your app should also call
queryPurchasesAsync() in your app’s onResume() method to
handle purchases that have transitioned to the PURCHASED state while your
app was not running.

Note:

You should acknowledge a purchase only when the state is PURCHASED, i.e.
Do not acknowledge it while a purchase is in PENDING state. The three day
acknowledgement window begins only when the purchase state transitions from
‘PENDING’ to ‘PURCHASED’.

Your app can also use
Real-time developer notifications
with pending purchases by listening for
OneTimeProductNotifications. When the purchase transitions from PENDING
to PURCHASED, your app receives a
ONE_TIME_PRODUCT_PURCHASED notification. If the purchase is cancelled,
your app receives a ONE_TIME_PRODUCT_CANCELED notification. This can
happen if your customer does not complete payment in the required timeframe.
When receiving these notifications, you can use the Google Play Developer
API, which includes a PENDING state for
Purchases.products.

Note:Pending transactions can be tested using license testers. In addition to two test credit cards, license testers have access to two test instruments for delayed forms of payment where the payment automatically completes or cancels after a couple of minutes. While testing your application, you should verify that your application does not grant entitlement or acknowledge the purchase immediately after purchasing with either of these two instruments. When purchasing using the test instrument that automatically completes, you should verify that your application grants entitlement and acknowledges the purchase after completion .
You can find detailed steps on how to test this scenario at Test pending purchases .

Handling multi-quantity purchases

Supported in versions 4.0 and higher of the Google Play Billing Library, Google Play allows customers to purchase more than one of the same in-app product in one transaction by specifying a quantity from the purchase cart. Your app is expected to handle multi-quantity purchases and grant entitlement based on the specified purchase quantity .
Note:

Multi-quantity is meant for consumable in-app products, products that
can be purchased, consumed, and purchased again. Do not enable this feature for
products that are not meant to be purchased repeatedly.

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:

  • getQuantity()
    from the Google Play Billing Library.
  • Purchases.products.quantity
    from the Google Play Developer API.

Once you’ve added logic to handle multi-quantity purchases, you then need to enable the multi-quantity feature for the corresponding product on the in-app product management page in the Google Play Developer Console .

Note:Be sure your app honors multi-quantity purchases before enabling the feature in the console. You might need to force an update to a version of your app that provides tư vấn before you can enable the feature on a product .

Integrate the Google Play Billing Library into your app | Google Play’s billing system | Android Developers

Bài viết liên quan
Hotline 24/7: O984.666.352
Alternate Text Gọi ngay