Intégrer la bibliothèque Google Play Billing à votre application

Cet article explique comment intégrer la bibliothèque Google Play Billing à votre application pour commencer à vendre des produits.

Cycle de vie d'un achat

Voici un parcours d'achat standard pour un achat ou un abonnement ponctuels.

  1. Montrez aux utilisateurs ce qu'ils peuvent acheter.
  2. Lancez le parcours d'achat pour que l'utilisateur accepte l'achat.
  3. Vérifiez l'achat sur votre serveur.
  4. Fournissez le contenu à l'utilisateur.
  5. Confirmez la livraison du contenu. Pour les produits consommables, utilisez l'achat afin que l'utilisateur puisse l'acheter à nouveau.

Les abonnements sont renouvelés automatiquement jusqu'à ce qu'ils soient résiliés. Un abonnement peut présenter les états suivants :

  • Actif : l'utilisateur est en règle et a accès à l'abonnement.
  • Annulé : l'utilisateur a annulé l'abonnement, mais a toujours accès jusqu'à sa date d'expiration.
  • En délai de grâce : l'utilisateur a rencontré un problème de paiement, mais a toujours accès à l'élément pendant que Google tente à nouveau le mode de paiement.
  • En attente : l'utilisateur a rencontré un problème de paiement et n'y a plus accès pendant que Google tente à nouveau le mode de paiement.
  • Suspendu : l'utilisateur a suspendu son accès et n'y a pas accès tant qu'il ne l'a pas réactivé.
  • Expiré : l'utilisateur a résilié son abonnement et n'y a plus accès. L'utilisateur est considéré comme perdu après l'expiration.

Initialiser une connexion à Google Play

La première étape pour intégrer le système de facturation de Google Play consiste à ajouter la bibliothèque Google Play Billing à votre application et à initialiser une connexion.

Ajouter la dépendance de la bibliothèque Google Play Billing

Ajoutez la dépendance Google Play Billing au fichier build.gradle de votre application, comme indiqué ci-dessous :

Groovy

dependencies {
    def billing_version = "6.2.1"

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

Kotlin

dependencies {
    val billing_version = "6.2.1"

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

Si vous utilisez Kotlin, le module KTX de la bibliothèque Google Play Billing contient des extensions et des coroutines Kotlin qui vous permettent d'écrire du code Kotlin idiomatique lors de l'utilisation de la bibliothèque Google Play Billing. Pour inclure ces extensions dans votre projet, ajoutez la dépendance suivante au fichier build.gradle de votre application, comme indiqué ci-dessous :

Groovy

dependencies {
    def billing_version = "6.2.1"

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

Kotlin

dependencies {
    val billing_version = "6.2.1"

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

Initialiser un BillingClient

Une fois que vous avez ajouté une dépendance à la bibliothèque Google Play Billing, vous devez initialiser une instance BillingClient. BillingClient est l'interface principale de communication entre la bibliothèque Google Play Billing et le reste de votre application. BillingClient fournit des méthodes pratiques, synchrones et asynchrones, utiles pour de nombreuses opérations de facturation courantes. Nous vous recommandons vivement d'avoir une connexion BillingClient active simultanément pour éviter plusieurs rappels PurchasesUpdatedListener liés à un seul événement.

Pour créer un BillingClient, utilisez newBuilder(). Vous pouvez transmettre n'importe quel contexte à newBuilder(), et BillingClient l'utilise pour obtenir un contexte d'application. Vous n'avez donc plus à vous soucier des fuites de mémoire. Pour recevoir des mises à jour pour les achats, vous devez également appeler setListener(), en transmettant une référence à PurchasesUpdatedListener. Cet écouteur reçoit des mises à jour pour tous les achats dans votre application.

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<Purchase> purchases) {
        // To be implemented in a later section.
    }
};

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

Se connecter à Google Play

Après avoir créé un BillingClient, vous devez établir une connexion à Google Play.

Pour vous connecter à Google Play, appelez startConnection(). Le processus de connexion est asynchrone. Vous devez implémenter BillingClientStateListener pour recevoir un rappel une fois que la configuration du client est terminée et est prête à envoyer d'autres requêtes.

Vous devez également mettre en œuvre une logique de nouvelle tentative pour gérer les connexions perdues à Google Play. Pour implémenter une logique de nouvelle tentative, contournez la méthode de rappel onBillingServiceDisconnected() et assurez-vous que BillingClient appelle la méthode startConnection() pour vous reconnecter à Google Play avant d'envoyer d'autres requêtes.

L'exemple suivant montre comment démarrer une connexion et tester si elle est fonctionnelle :

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

Afficher les produits disponibles à l'achat

Une fois la connexion à Google Play établie, vous pouvez interroger les produits disponibles et les présenter à vos utilisateurs.

L'interrogation des informations détaillées sur un produit est une étape importante avant de présenter vos produits aux utilisateurs, car elle renvoie des informations localisées sur le produit. Pour les abonnements, assurez-vous que l'affichage du produit respecte toutes les règles de Google Play.

Pour interroger les informations sur un produit intégré, appelez queryProductDetailsAsync().

Pour gérer le résultat de l'opération asynchrone, vous devez également spécifier un écouteur qui implémente l'interface ProductDetailsResponseListener. Vous pouvez ensuite contourner onProductDetailsResponse(), qui avertit l'écouteur lorsque la requête est terminée, comme illustré dans l'exemple suivant :

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

Lorsque vous interrogez les informations détaillées sur un produit, transmettez une instance de QueryProductDetailsParams qui spécifie une liste de chaînes d'ID produit créées dans la Google Play Console avec un élément ProductType. ProductType peut être de type ProductType.INAPP pour des produits ponctuels ou de type ProductType.SUBS pour les abonnements.

Interroger avec des extensions Kotlin

Si vous utilisez des extensions Kotlin, vous pouvez interroger les informations détaillées sur un produit intégré en appelant la fonction d'extension queryProductDetails().

queryProductDetails() exploite les coroutines Kotlin. Vous n'avez donc pas besoin de définir un écouteur distinct. Au lieu de cela, la fonction est suspendue jusqu'à la fin de l'interrogation, après quoi vous pouvez traiter le résultat :

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

Dans de rares cas, certains appareils ne peuvent pas prendre en charge ProductDetails ni queryProductDetailsAsync(), généralement en raison de versions obsolètes des services Google Play. Pour assurer la compatibilité dans ce scénario, découvrez comment utiliser les fonctionnalités de rétrocompatibilité dans le guide de migration de la bibliothèque Play Billing 5.

Traiter le résultat

La bibliothèque Google Play Billing stocke les résultats de la requête dans un List d'objets ProductDetails. Vous pouvez ensuite appeler diverses méthodes sur chaque objet ProductDetails de la liste pour afficher des informations pertinentes sur un produit intégré, telles que son prix ou sa description. Pour afficher des informations détaillées disponibles sur le produit, consultez la liste des méthodes de la classe ProductDetails.

Avant de proposer un article à la vente, vérifiez que l'utilisateur n'en est pas déjà le propriétaire. Si l'utilisateur dispose d'un consommable qui se trouve toujours dans sa bibliothèque d'articles, il doit le consommer avant de pouvoir l'acheter à nouveau.

Avant de proposer un abonnement, vérifiez que l'utilisateur n'est pas déjà abonné. Notez également ce qui suit :

  • queryProductDetailsAsync() renvoie les détails des produits sur abonnement et un maximum de 50 offres par abonnement.
  • queryProductDetailsAsync() renvoie uniquement les offres pour lesquelles l'utilisateur est éligible. Si l'utilisateur tente d'acheter une offre pour laquelle il n'est pas éligible (par exemple, si l'application affiche une liste obsolète d'offres éligibles), Google Play informe l'utilisateur qu'il n'est pas éligible. L'utilisateur peut alors choisir d'acheter le forfait de base à la place.

Lancer le parcours d'achat

Pour démarrer une demande d'achat depuis votre application, appelez la méthode launchBillingFlow() à partir du thread principal de votre application. Cette méthode transmet une référence à un objet BillingFlowParams contenant l'objet ProductDetails approprié obtenu lors de l'appel de queryProductDetailsAsync(). Pour créer un objet BillingFlowParams, utilisez la classe 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)
        // 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)

Java

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

La méthode launchBillingFlow() renvoie l'un des codes de réponse recensés dans BillingClient.BillingResponseCode. Assurez-vous que ce résultat ne comporte aucune erreur lors du lancement du parcours d'achat. Un BillingResponseCode de type OK indique un lancement réussi.

Lors d'un appel réussi à launchBillingFlow(), le système affiche l'écran d'achat Google Play. La figure 1 illustre l'écran d'achat d'un abonnement :

L&#39;écran d&#39;achat Google Play affiche un abonnement disponible à l&#39;achat
Figure 1 : L'écran d'achat de Google Play affiche un abonnement disponible à l'achat.

Google Play appelle onPurchasesUpdated() pour transmettre le résultat de l'opération d'achat à un écouteur qui implémente l'interface PurchasesUpdatedListener. L'écouteur est spécifié à l'aide de la méthode setListener() lors de l'initialisation du BillingClient.

Vous devez implémenter onPurchasesUpdated() pour gérer les éventuels codes de réponse. L'exemple suivant montre comment contourner onPurchasesUpdated() :

Kotlin

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

Java

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

Un achat réussi génère un écran de confirmation d'achat Google Play semblable à la figure 2.

Écran de confirmation d&#39;achat de Google Play
Figure 2 : Écran de confirmation d'achat de Google Play.

Un achat réussi génère également un jeton d'achat, c'est-à-dire un identifiant unique qui représente l'utilisateur et l'ID du produit intégré qu'il a acheté. Vos applications peuvent stocker le jeton d'achat localement, mais nous vous recommandons de le transmettre à votre serveur backend sécurisé, où vous pourrez ensuite vérifier l'achat et vous protéger contre la fraude. Ce processus est décrit plus en détail dans la section suivante.

L'utilisateur reçoit également un reçu par e-mail contenant un ID de commande ou un ID unique de la transaction. Les utilisateurs reçoivent un e-mail contenant un ID de commande unique pour chaque achat de produit ponctuel, ainsi que pour l'achat initial de l'abonnement et les renouvellements automatiques récurrents ultérieurs. Vous pouvez utiliser cet ID de commande pour gérer les remboursements dans la Google Play Console.

Indiquer un prix personnalisé

Si votre application peut être distribuée aux utilisateurs de l'Union européenne, utilisez la méthode setIsOfferPersonalized() pour indiquer aux utilisateurs que le prix d'un article a été personnalisé à l'aide de la prise de décision automatisée.

Écran d&#39;achat Google Play indiquant que le prix a été personnalisé pour l&#39;utilisateur
Figure 3 : Écran d'achat Google Play indiquant que le prix a été personnalisé pour l'utilisateur.

Vous devez consulter l'article 6 (1) (ea) CRD de la directive sur les droits des consommateurs 2011/83/UE pour déterminer si le prix que vous proposez aux utilisateurs est personnalisé.

setIsOfferPersonalized() accepte une entrée booléenne. Si true, l'interface utilisateur Google Play indique que le prix a été personnalisé. Si false, l'interface utilisateur omet ce message. La valeur par défaut est false.

Pour obtenir plus d'informations, consultez le Centre d'aide consommateur.

Traiter les achats

Une fois qu'un utilisateur a effectué un achat, votre application doit le traiter. Dans la plupart des cas, votre application est informée des achats via votre PurchasesUpdatedListener. Toutefois, dans certains cas, votre application est informée des achats en appelant BillingClient.queryPurchasesAsync(), comme décrit dans la section Récupération des achats.

De plus, si vous avez un client Notifications en temps réel pour les développeurs dans votre backend sécurisé, vous pouvez enregistrer de nouveaux achats en recevant une subscriptionNotification ou une oneTimeProductNotification (uniquement pour les achats en attente) vous avertissant d'un nouvel achat. Après avoir reçu ces notifications, appelez l'API Google Play Developer pour obtenir l'état complet et mettre à jour l'état de votre backend.

Pour traiter un achat, votre application doit effectuer les opérations suivantes :

  1. Vérifier l'achat.
  2. Fournir le contenu à l'utilisateur et confirmer la livraison. Dans certains cas, marquer l'article comme consommé pour que l'utilisateur puisse l'acheter à nouveau.

Pour vérifier un achat, vérifiez d'abord que l'état de l'achat est PURCHASED. Si l'achat est PENDING, vous devez le traiter comme décrit dans Gérer les transactions en attente. Pour les achats reçus de onPurchasesUpdated() ou queryPurchasesAsync(), vous devez vérifier l'achat plus précisément afin de vous assurer de sa légitimité avant que votre application accorde les droits d'accès. Pour savoir comment vérifier correctement un achat, consultez Vérifier les achats avant d'accorder des droits.

Une fois l'achat vérifié, votre application est prête à accorder un droit d'accès à l'utilisateur. Le compte utilisateur associé à l'achat peut être identifié à l'aide du ProductPurchase.obfuscatedExternalAccountId renvoyé par Purchases.products:get pour les achats de produits intégrés et du SubscriptionPurchase.obfuscatedExternalAccountId renvoyé par Purchases.subscriptions:get pour les abonnements côté serveur ; ou le obfuscatedAccountId à partir de Purchase.getAccountIdentifiers() côté client, s'il a été défini avec setObfuscatedAccountId lors de l'achat.

Une fois les droits d'accès accordés, votre application doit confirmer l'achat. Cette confirmation indique à Google Play que vous avez accordé les droits d'accès à l'achat.

Le processus permettant d'accorder des droits d'accès et de confirmer l'achat dépend de la nature de l'achat : consommable, non consommable ou abonnement.

Produits consommables

Pour les consommables, si votre application dispose d'un backend sécurisé, nous vous recommandons d'utiliser Purchases.products:consume pour consommer de manière fiable les achats. Assurez-vous que l'achat n'a pas déjà été consommé en vérifiant le consumptionState à partir du résultat de l'appel de Purchases.products:get. Si votre application est côté client uniquement sans backend, utilisez consumeAsync() à partir de la bibliothèque Google Play Billing. Les deux méthodes répondent aux exigences de confirmation et indiquent que votre application a accordé des droits à l'utilisateur. Ces méthodes permettent également à votre application de proposer à nouveau à la vente le produit ponctuel correspondant au jeton d'achat indiqué. Avec consumeAsync(), vous devez également transmettre un objet qui implémente l'interface ConsumeResponseListener. Cet objet gère le résultat de l'opération de consommation. Vous pouvez contourner la méthode onConsumeResponse(), que la bibliothèque Google Play Billing appelle une fois l'opération terminée.

L'exemple suivant illustre la consommation d'un produit avec la bibliothèque Google Play Billing à l'aide du jeton d'achat associé :

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

Produits non consommables

Pour confirmer les achats de produits non consommables, si votre application dispose d'un backend sécurisé, nous vous recommandons d'utiliser Purchases.products:acknowledge pour confirmer de manière fiable les achats. Assurez-vous que l'achat n'a pas déjà été confirmé en vérifiant le acknowledgementState à partir du résultat de l'appel de Purchases.products:get.

Si votre application est côté client uniquement, utilisez BillingClient.acknowledgePurchase() depuis la bibliothèque Google Play Billing. Avant de confirmer un achat, votre application doit vérifier s'il a déjà été confirmé à l'aide de la méthode isAcknowledged() dans la bibliothèque Google Play Billing.

L'exemple suivant montre comment confirmer un achat à l'aide de la bibliothèque Google Play Billing :

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

Abonnements

Les abonnements sont traités de la même manière que les produits non consommables. Si possible, utilisez Purchases.subscriptions.acknowledge depuis l'API Google Play Developer pour confirmer l'achat de manière fiable à partir de votre backend sécurisé. Déterminez si l'achat n'a pas déjà été confirmé en vérifiant le acknowledgementState dans la ressource d'achat de Purchases.subscriptions:get. Sinon, vous pouvez confirmer un abonnement à l'aide de BillingClient.acknowledgePurchase() dans la bibliothèque Google Play Billing après avoir vérifié isAcknowledged(). Tous les achats initiaux d'abonnements doivent être confirmés. Il n'est pas nécessaire de confirmer le renouvellement de l'abonnement. Pour en savoir plus sur la confirmation des abonnements, consultez la rubrique Vendre des abonnements.

Récupérer les achats

Pour vous assurer que votre application traite tous les achats, il n'est pas suffisant d'utiliser un PurchasesUpdatedListener en ce qui concerne les mises à jour d'achats. Il est possible que votre application ne soit pas à jour quant à la totalité des achats effectués par un utilisateur. Voici quelques situations lors desquelles votre application peut perdre de vue un achat ou en ignorer l'existence :

  • Problèmes de réseau lors de l'achat : un utilisateur effectue un achat et reçoit la confirmation de Google, mais son appareil perd la connectivité réseau avant que son appareil ne reçoive une notification de l'achat via PurchasesUpdatedListener.
  • Plusieurs appareils : un utilisateur achète un article sur un appareil, puis s'attend à pouvoir le voir lorsqu'il change d'appareil.
  • Gérer les achats effectués en dehors de votre application : certains achats, par exemple l'utilisation d'offres promotionnelles, peuvent être effectués en dehors de votre application.

Pour gérer ces situations, assurez-vous que votre application appelle BillingClient.queryPurchasesAsync() dans votre méthode onResume() pour vous assurer que tous les achats sont bien traités comme décrit dans Traiter les achats.

L'exemple suivant montre comment récupérer les achats d'abonnement d'un utilisateur. Notez que queryPurchasesAsync() ne renvoie que les abonnements actifs et les achats ponctuels non consommés.

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

      }
    }
);

Récupérer l'historique des achats

queryPurchaseHistoryAsync() renvoie l'achat le plus récent effectué par l'utilisateur pour chaque produit, même si cet achat a expiré, a été annulé ou a été consommé.

Si vous utilisez des extensions Kotlin, vous pouvez utiliser la fonction d'extension queryPurchaseHistory().

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

Gérer les achats effectués en dehors de votre application

Certains achats (utilisation d'offres promotionnelles, par exemple) peuvent se produire en dehors de votre application. Lorsqu'un utilisateur effectue un achat en dehors de votre application, il s'attend à ce qu'un message s'affiche dans l'application ou qu'une notification lui indique que l'application a bien reçu et traité l'achat. Voici quelques-uns des mécanismes acceptés :

  • Afficher un pop-up dans l'application
  • Transmettre le message dans une zone de message dans l'application et indiquer clairement qu'un nouveau message s'y trouve.
  • Utiliser un message de notification de l'OS.

N'oubliez pas qu'il est possible qu'un achat soit reconnu par votre application, peu importe l'état dans lequel elle se trouve. Il est même possible que votre application ne soit même pas installée lors de l'achat. Les utilisateurs s'attendent à recevoir leur achat lorsqu'ils réactivent l'application, quel que soit l'état de l'application.

Vous devez détecter les achats, quel que soit l'état de leur application au moment où ils ont été effectués. Toutefois, dans certains cas, il peut être acceptable de ne pas informer immédiatement l'utilisateur que l'article a été reçu. Par exemple :

  • En plein milieu d'un jeu, l'affichage d'un message peut distraire l'utilisateur. Dans ce cas, vous devez prévenir l'utilisateur une fois la partie terminée.
  • Pendant les cinématiques, l'affichage d'un message peut distraire l'utilisateur. Dans ce cas, vous devez avertir l'utilisateur une fois la cinématique terminée.
  • Au cours du tutoriel de départ et de la configuration d'un jeu. Nous vous recommandons d'informer immédiatement les nouveaux utilisateurs de la récompense reçue dès qu'ils ouvrent le jeu ou lors de la configuration initiale de l'utilisateur. Cependant, il est possible d'attendre que la séquence de jeu principale soit disponible pour avertir l'utilisateur.

Tenez toujours compte de l'utilisateur lorsque vous choisissez quand et comment l'informer des achats effectués en dehors de votre application. Chaque fois qu'un utilisateur ne reçoit pas immédiatement une notification, il peut se sentir perdu et cesser d'utiliser votre application, contacter l'assistance utilisateur ou s'en plaindre sur les réseaux sociaux. Remarque : PurchasesUpdatedListener est enregistré dans le contexte de votre application afin de pouvoir gérer les mises à jour des achats, y compris ceux effectués en dehors de votre application. Ainsi, si votre processus d'application n'existe pas, votre PurchasesUpdatedListener ne sera pas notifié. C'est pourquoi votre application doit appeler BillingClient.queryPurchasesAsync() dans la méthode onResume(), comme indiqué dans la section Récupérer les achats.

Traiter les transactions en attente

Google Play accepte les transactions en attente ou celles nécessitant une ou plusieurs étapes supplémentaires entre le moment où l'utilisateur effectue un achat et le traitement du mode de paiement associé. Votre application ne doit pas accorder de droit à ces types d'achats tant que Google ne vous a pas informé que le mode de paiement de l'utilisateur a été débité.

Par exemple, un utilisateur peut créer un achat PENDING d'un article intégré à l'application en choisissant le paiement en espèces. L'utilisateur peut ensuite choisir un magasin physique dans lequel il terminera la transaction et recevra un code par notification et par e-mail. Lorsque l'utilisateur arrive au magasin physique, il peut passer par le caissier pour utiliser le code et payer en espèces. Google vous informe ensuite, vous et l'utilisateur, qu'un paiement en espèces a été effectué. Votre application peut alors accorder des droits à l'utilisateur.

Votre application doit prendre en charge les transactions en attente en appelant enablePendingPurchases() lors de l'initialisation de votre application.

Lorsque votre application reçoit un nouvel achat, via votre PurchasesUpdatedListener ou suite à un appel queryPurchasesAsync(), utilisez la méthode getPurchaseState() pour déterminer si l'état d'achat est PURCHASED ou PENDING.

Si votre application est en cours d'exécution lorsque l'utilisateur effectue l'achat, votre PurchasesUpdatedListener est appelé à nouveau et PurchaseState est désormais PURCHASED. À ce stade, votre application peut traiter l'achat à l'aide de la méthode standard de traitement des achats ponctuels. Votre application doit également appeler queryPurchasesAsync() dans la méthode onResume() de votre application pour gérer les achats qui sont passés à l'état PURCHASED lorsque votre application n'était pas en cours d'exécution.

Votre application peut également utiliser les notifications en temps réel pour les développeurs avec les achats en attente en écoutant OneTimeProductNotifications. Lorsque l'achat passe de PENDING à PURCHASED, votre application reçoit une notification ONE_TIME_PRODUCT_PURCHASED. Si l'achat est annulé, votre application reçoit une notification ONE_TIME_PRODUCT_CANCELED. Cela peut se produire si votre client n'effectue pas le paiement dans le délai requis. Lorsque vous recevez ces notifications, vous pouvez utiliser l'API Google Play Developer, qui inclut un état PENDING pour Purchases.products.

Pour connaître la procédure détaillée permettant de tester ce scénario, consultez la section Tester les achats en attente.

Gérer les achats de quantités multiples

Compatible avec les versions 4.0 et ultérieures de la bibliothèque Google Play Billing, Google Play permet aux clients d'acheter plus d'un produit intégré dans l'application en une seule transaction en spécifiant une quantité dans le panier d'achat. Votre application doit gérer les achats de quantités multiples et accorder des droits en fonction de la quantité d'achats spécifiée.

Pour respecter les achats de quantités multiples, la logique de provisionnement de votre application doit vérifier une quantité d'articles. Vous pouvez accéder à un champ quantity à partir de l'une des API suivantes :

Une fois que vous avez ajouté une logique pour gérer les achats de quantités multiples, vous devez activer la fonctionnalité de quantités multiples pour le produit correspondant sur la page de gestion des produits intégrés à l'application de la Google Play Console.

Interroger la configuration de facturation de l'utilisateur

getBillingConfigAsync() indique le pays de l'utilisateur sur Google Play.

Vous pouvez interroger la configuration de facturation de l'utilisateur après avoir créé un BillingClient. L'extrait de code suivant explique comment appeler getBillingConfigAsync(). Gérez la réponse en implémentant le BillingConfigResponseListener. Cet écouteur reçoit des mises à jour pour toutes les requêtes de configuration de facturation lancées à partir de votre application.

Si le BillingResult renvoyé ne contient aucune erreur, vous pouvez vérifier le champ countryCode dans l'objet BillingConfig pour obtenir le pays de l'utilisateur dans 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 {
                // TODO: Handle errors
            }
        }
    })

Java

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