1. Introduction
Cet atelier de programmation vous apprend à utiliser la bibliothèque Google Play Billing (PBL) pour gérer les modifications de forfaits. Vous découvrirez l'impact des différents modes de remplacement sur les tarifs et les droits d'accès des utilisateurs, tout en apprenant à traiter les notifications en temps réel pour les développeurs (RTDN) côté backend.
Audience
Conçu pour les développeurs d'applications Android, cet atelier de programmation fournit des conseils sur l'implémentation de fonctionnalités sophistiquées de gestion des abonnements. Ces consignes vous aident à offrir aux utilisateurs une expérience fluide pour passer à un forfait supérieur ou inférieur, ou pour changer d'abonnement.
Ce que vous allez apprendre...
- Découvrez comment créer des abonnements dans la Play Console.
- Comment choisir le bon
ReplacementMode(par exemple,WITH_TIME_PRORATIONouDEFERRED) pour qu'il corresponde aux règles de mise à niveau et de rétrogradation de votre application. - Comment configurer
BillingFlowParamsdanslaunchBillingFlowpour déclencher le processus d'achat Google Play pour le remplacement d'un forfait. - Utiliser les notifications en temps réel pour les développeurs (RTDN) et l'API
purchases.subscriptionsv2pour révoquer l'ancien accès et accorder un nouvel accès de manière sécurisée sur votre backend
Prérequis
- Accès à la Google Play Console avec un compte de développeur. Si vous n'avez pas de compte de développeur, vous devez en créer un.
- L'application exemple de cet atelier de programmation, que vous pouvez télécharger depuis GitHub.
- Android Studio
2. Créer l'application exemple
Cet atelier de programmation utilise une application Android exemple pour vous montrer comment implémenter des remplacements d'abonnements dans PBL. L'application exemple est conçue pour être une application Android entièrement fonctionnelle. Elle dispose d'un code source complet qui montre les aspects suivants :
- Intégrer l'application à PBL
- Implémenter les remplacements d'abonnement
Si vous connaissez déjà les remplacements d'abonnement et PBL, vous pouvez télécharger l'application exemple et l'essayer.
La vidéo de démonstration suivante montre à quoi ressemblera l'application exemple et comment elle se comportera une fois déployée et exécutée.
Prérequis
Avant de créer et de déployer l'application exemple, procédez comme suit :
- Créez un compte de développeur Google Play Console. Si vous disposez déjà d'un compte de développeur, ignorez cette étape.
- Créez une application dans la Play Console en activant les fonctionnalités de monétisation. Vous pouvez également utiliser une application existante dans la Play Console. Si les fonctionnalités de monétisation ne sont pas activées pour votre application, suivez ces instructions pour les configurer.
- Installez Android Studio.
Créer
Pour compiler l'application exemple comme requis pour suivre l'atelier de programmation :
- Téléchargez l'application exemple depuis GitHub.
- Mettez à jour le
applicationIddans lebuild.gradlede l'application exemple pour refléter l'ID d'application de votre application dans la Play Console. - Créez l'application exemple.
Remarque : Cette opération permet de créer l'application pour les tests locaux. Toutefois, l'exécution de l'application ne récupère pas les produits ni les prix, car les abonnements requis n'ont pas encore été créés dans la Play Console. La section suivante explique comment créer des abonnements dans la console pour les développeurs.
3. Créer des abonnements dans la Play Console
Le système d'abonnements de Google Play vous aide à créer, gérer et vendre des abonnements de manière flexible. Dans la Play Console, vous pouvez configurer des abonnements avec plusieurs forfaits de base, chacun contenant plusieurs offres. Les offres d'abonnement peuvent présenter différentes options de tarification et d'éligibilité. Pour cet atelier de programmation, vous allez créer trois abonnements : Forfait Premium, Forfait de base et Forfait Lite. Vous simulerez ainsi une offre d'abonnement typique à différents prix. Chacune d'elles disposera d'un forfait de base mensuel récurrent unique.
Créer un abonnement
Pour créer un abonnement
- Ouvrez la Play Console, puis accédez à la page Abonnements (Monétiser avec Play > Produits > Abonnements).
- Cliquez sur Créer un abonnement.
- Saisissez les détails de votre abonnement :
- ProductID : saisissez un ID produit unique. Saisissez
premium_plan. - Nom : saisissez un nom court pour l'abonnement. Exemple :
Premium Plan.
- ProductID : saisissez un ID produit unique. Saisissez
- Cliquez sur Créer.
Créer le forfait de base
- Ouvrez la Play Console, puis accédez à la page Abonnements (Monétiser avec Play > Produits > Abonnements).
- À côté de l'abonnement pour lequel vous souhaitez créer un forfait de base, cliquez sur la flèche vers la droite pour afficher les détails.
- Cliquez sur Ajouter un forfait de base.
- Saisissez un ID de forfait de base. Exemple :
monthly-auto-renewing. - Sélectionnez le type Renouvellement automatique.
- Pour le forfait de base à renouvellement automatique, définissez les éléments suivants :
- Période de facturation : mensuelle.
- Délai de grâce : sept jours.
- Changements de forfait et d'offre : Facturer à la date de facturation.
- Se réabonner : Autoriser.
- Dans la section Prix et disponibilité, cliquez sur Définir les prix pour définir le prix du forfait de base.
- Sélectionnez tous les pays et régions, puis cliquez sur Définir le prix.
- Définissez le prix de ce forfait de base sur 10 $, puis cliquez sur Mettre à jour.
- Une fois le prix du forfait de base défini, cliquez sur Enregistrer, puis sur Activer en bas à droite.
Créer des abonnements pour l'application exemple
Pour les besoins de cet atelier de programmation, créez deux abonnements supplémentaires avec la configuration suivante :
- Forfait de base
- ID produit : basic_plan
- Nom : Forfait de base
- ID du forfait de base : monthly-auto-renewing
- Prix : 5 $
- Forfait Lite
- ID produit : lite_plan
- Nom : Forfait Lite
- ID du forfait de base : monthly-auto-renewing
- Prix : 3 $
L'application exemple est configurée pour utiliser ces ID de produit et de forfait de base. Vous pouvez créer différentes souscriptions avec différentes configurations. Dans ce cas, vous devrez modifier l'application exemple pour utiliser l'ID de produit que vous avez créé.
Vidéo sur la création d'un abonnement
La vidéo suivante montre comment créer un abonnement dans la Play Console.
4. Remplacements d'abonnement
Les développeurs qui intègrent PBL peuvent proposer à leurs abonnés existants diverses options pour modifier leur abonnement afin de mieux répondre à leurs besoins :
- Si vous vendez plusieurs niveaux d'abonnement, tels que des abonnements de base et premium, vous pouvez permettre aux utilisateurs de changer de niveau en achetant un autre forfait de base ou une autre offre.
- Vous pouvez autoriser les utilisateurs à modifier leur période de facturation actuelle, par exemple en passant d'un forfait mensuel à un forfait annuel.
- Vous pouvez également autoriser les utilisateurs à basculer entre le forfait à renouvellement automatique et le forfait prépayé.
Lorsque les utilisateurs décident de changer d'abonnement, ou de passer à un forfait supérieur ou inférieur, vous spécifiez un mode de remplacement qui détermine le montant au prorata de la période de facturation en cours et le moment où le droit d'accès est modifié pour les utilisateurs.
La bibliothèque Play Billing fournit plusieurs options ReplacementMode pour contrôler ce comportement.
Modes de remplacement disponibles
WITH_TIME_PRORATION: l'abonnement passe immédiatement à un forfait supérieur ou inférieur. Le temps restant est ajusté en fonction de la différence de prix, puis crédité sur le nouvel abonnement en modifiant la date de facturation suivante. Il s'agit du comportement par défaut.CHARGE_PRORATED_PRICE: l'abonnement passe à un niveau supérieur immédiatement, et le cycle de facturation reste le même. La différence de prix pour la période restante est ensuite facturée à l'utilisateur.CHARGE_FULL_PRICE: l'abonnement passe immédiatement au forfait supérieur ou inférieur, et l'utilisateur paie immédiatement le tarif complet du nouveau forfait. La valeur restante de l'abonnement précédent est transférée sur le nouveau forfait ou calculée au prorata de la durée restante au moment du changement de forfait.WITHOUT_PRORATION: l'abonnement passe immédiatement à un forfait supérieur ou inférieur, et le nouveau tarif est facturé lors du renouvellement de l'abonnement. Le cycle de facturation reste le même.DEFERRED: l'abonnement ne passe à un forfait supérieur ou inférieur qu'au moment du renouvellement.
5. WITH_TIME_PRORATION
Dans ce mode de remplacement, l'abonnement passe immédiatement à un forfait supérieur ou inférieur. Le temps restant est ajusté en fonction de la différence de prix, puis crédité sur le nouvel abonnement en repoussant la date de facturation suivante. Il s'agit du comportement par défaut.
Exemple de scénario
Un utilisateur passe d'un forfait Basic (4,99 € par mois) à un forfait Premium (9,99 € par mois) le 15 avril, soit à mi-parcours de son cycle de facturation mensuel.
Dans ce cas, on a :
- L'utilisateur a immédiatement accès au forfait Premium.
- Google Play calcule automatiquement la période de prorata. Par exemple, si Play calcule que les 15 jours restants du forfait Basic correspondent à 7 jours du forfait Premium, la prochaine date de facturation est avancée au 21 avril.
- Aucun paiement immédiat n'est requis de la part de l'utilisateur.
Extrait de code
// ProductDetails for the plan to be switched to
ProductDetails productDetails = ...;
// The specific offer token for the toBeSwitched plan's base plan
String offerToken = "...";
// The purchase token of the user's current subscription
String oldPurchaseToken = "...";
// The productId for the user's current subscription
String oldProductId = "...";
// The replacementMode to replace the user's subscription
int replacementMode = SubscriptionProductReplacementParams.ReplacementMode.WITH_TIME_PRORATION;
SubscriptionProductReplacementParams subscriptionProductReplacementParams =
SubscriptionProductReplacementParams.newBuilder()
.setOldProductId(oldProductId)
.setReplacementMode(replacementMode)
.build();
ProductDetailsParams productDetailsParams =
ProductDetailsParams.newBuilder()
.setProductDetails(productDetails)
.setSubscriptionProductReplacementParams(subscriptionProductReplacementParams)
.setOfferToken(offerToken)
.build();
List<ProductDetailsParams> productDetailsParamsList = ImmutableList.of(productDetailsParams);
BillingFlowParams billingFlowParams =
BillingFlowParams.newBuilder()
.setProductDetailsParamsList(productDetailsParamsList)
.setSubscriptionUpdateParams(
SubscriptionUpdateParams.newBuilder().setOldPurchaseToken(oldPurchaseToken).build())
.build();
billingClient.launchBillingFlow(activity, billingFlowParams);
Mise à niveau avec WITH_TIME_PRORATION
Pour simuler ce scénario :
- Dans le
MainActivityde l'application exemple, remplacezreplacementModedans l'extrait de code parSubscriptionProductReplacementParams.ReplacementMode.WITH_TIME_PRORATION. - Recompilez et lancez l'application.
- Résiliez les abonnements existants (le cas échéant) sur le Google Play Store. Ils expireront.
- Souscrivez la formule Basic.
- Passez au forfait Premium.
Les droits d'accès de l'utilisateur sont immédiatement mis à niveau vers le forfait Premium. Le montant à payer immédiatement par l'utilisateur est de 0 $. La valeur restante du forfait Basic est proratisée en temps pour le forfait Premium, ce qui avance la prochaine date de renouvellement. Le montant du renouvellement de 9,99 $ sera facturé à l'utilisateur à la nouvelle date de facturation.
Rétrogradation avec WITH_TIME_PRORATION
Pour simuler ce scénario :
- Dans le
MainActivityde l'application exemple, remplacezreplacementModedans l'extrait de code parSubscriptionProductReplacementParams.ReplacementMode.WITH_TIME_PRORATION. - Recompilez et lancez l'application.
- Résiliez les abonnements existants (le cas échéant) sur le Google Play Store. Ils expireront.
- Souscrivez la formule Basic.
- Passez au forfait Lite.
Les droits d'accès de l'utilisateur sont immédiatement rétrogradés au forfait Lite. Le montant à payer immédiatement est de 0,00 $. La valeur restante du forfait Basic est proratisée en temps pour le forfait Lite, ce qui repousse considérablement la prochaine date de renouvellement. Le montant du renouvellement de 2,99 $ sera facturé à la nouvelle date de facturation.
Conclusion
Dans cette section, vous avez appris comment WITH_TIME_PRORATION modifie les droits d'accès des utilisateurs sans frais immédiats en ajustant le délai avant le prochain renouvellement en fonction de la différence de prix. Il s'agit d'une stratégie par défaut efficace pour mettre à niveau ou rétrograder les utilisateurs.
6. CHARGE_PRORATED_PRICE
Dans ce mode de remplacement, l'abonnement passe à un niveau supérieur immédiatement, et le cycle de facturation reste le même. La différence de prix pour la période restante est ensuite facturée à l'utilisateur.
Remarque : Cette option n'est disponible que pour le passage à un abonnement de niveau supérieur, où le prix par unité de temps augmente.
Exemple de scénario
Un utilisateur du forfait Basic (4,99 € par mois) décide de passer au forfait Premium (9,99 € par mois) le 20 avril, alors qu'il reste environ 10 jours dans son cycle de facturation mensuel.
Dans ce cas, on a :
- L'utilisateur a immédiatement accès au forfait Premium.
- La différence au prorata pour les 10 jours restants du cycle de facturation en cours est immédiatement facturée à l'utilisateur. Cela équivaut à environ 2,99 $, soit 10 jours du forfait Premium.
- La date de facturation de l'utilisateur ne change pas.
Extrait de code
// ProductDetails for the plan to be switched to
ProductDetails productDetails = ...;
// The specific offer token for the toBeSwitched plan's base plan
String offerToken = "...";
// The purchase token of the user's current subscription
String oldPurchaseToken = "...";
// The productId for the user's current subscription
String oldProductId = "...";
// The replacementMode to replace the user's subscription
int replacementMode = SubscriptionProductReplacementParams.ReplacementMode.CHARGE_PRORATED_PRICE;
SubscriptionProductReplacementParams subscriptionProductReplacementParams =
SubscriptionProductReplacementParams.newBuilder()
.setOldProductId(oldProductId)
.setReplacementMode(replacementMode)
.build();
ProductDetailsParams productDetailsParams =
ProductDetailsParams.newBuilder()
.setProductDetails(productDetails)
.setSubscriptionProductReplacementParams(subscriptionProductReplacementParams)
.setOfferToken(offerToken)
.build();
List<ProductDetailsParams> productDetailsParamsList = ImmutableList.of(productDetailsParams);
BillingFlowParams billingFlowParams =
BillingFlowParams.newBuilder()
.setProductDetailsParamsList(productDetailsParamsList)
.setSubscriptionUpdateParams(
SubscriptionUpdateParams.newBuilder().setOldPurchaseToken(oldPurchaseToken).build())
.build();
billingClient.launchBillingFlow(activity, billingFlowParams);
Passer à un forfait supérieur avec CHARGE_PRORATED_PRICE
Pour simuler ce scénario :
- Dans le
MainActivityde l'application exemple, remplacezreplacementModedans l'extrait de code parSubscriptionProductReplacementParams.ReplacementMode.CHARGE_PRORATED_PRICE. - Recompilez et lancez l'application.
- Résiliez les abonnements existants (le cas échéant) sur le Google Play Store. Ils expireront.
- Souscrivez la formule Basic.
- Passez au forfait Premium.
L'utilisateur passe immédiatement au forfait Premium, en conservant la date de renouvellement d'origine. Le montant à payer immédiatement correspond à la différence au prorata entre les prix des forfaits Premium et Basic pour les jours restants du cycle en cours. À la date de renouvellement, le montant total de 9,99 $ sera facturé à l'utilisateur pour le renouvellement de son abonnement Premium.
Passer à un forfait inférieur avec CHARGE_PRORATED_PRICE
Pour simuler ce scénario :
- Dans le
MainActivityde l'application exemple, remplacezreplacementModedans l'extrait de code parSubscriptionProductReplacementParams.ReplacementMode.CHARGE_PRORATED_PRICE. - Recompilez et lancez l'application.
- Résiliez les abonnements existants (le cas échéant) sur le Google Play Store. Ils expireront.
- Souscrivez la formule Basic.
- Passez au forfait Lite.
Ce mode de remplacement génère une erreur lors d'un changement de forfait à un niveau inférieur, car il n'est disponible que pour les changements de forfait à un niveau supérieur où le prix par unité de temps augmente. Le flux de facturation échouera et affichera une erreur indiquant à l'utilisateur que le mode de calcul au prorata n'est pas compatible avec les changements d'abonnement à un forfait inférieur.
Conclusion
Cette section expliquait comment CHARGE_PRORATED_PRICE permet aux utilisateurs de passer immédiatement à un forfait supérieur en leur facturant la différence de prix exacte pour la période de facturation restante, tout en conservant le cycle de facturation. Cela est utile lorsque l'utilisateur souhaite passer à un niveau plus cher sans modifier sa date de facturation.
7. CHARGE_FULL_PRICE
Dans ce mode de remplacement, l'abonnement passe immédiatement au forfait supérieur ou inférieur, et l'utilisateur paie immédiatement le tarif complet du nouveau forfait. La valeur restante de l'abonnement précédent est transférée sur le nouveau forfait ou calculée au prorata de la durée restante au moment du changement de forfait.
Exemple de scénario
Un utilisateur dispose du forfait Basic (4,99 $ par mois à partir du 1er avril). Le 20 avril, l'utilisateur souhaite passer au forfait Premium (9,99 $ par mois).
Dans ce cas, on a :
- Le prix complet du forfait Premium (9,99 $) est immédiatement facturé à l'utilisateur.
- La valeur restante du forfait Basic (par exemple, 10 jours) est convertie en temps équivalent pour le forfait Premium. Dans cet exemple, 10 jours de Basic équivalent à 5 jours de Premium.
- La prochaine date de renouvellement de l'utilisateur est ajustée pour inclure cette période au prorata. La nouvelle date de renouvellement devient donc le 25 mai (20 avril + 1 mois + 5 jours).
- Les renouvellements suivants auront lieu tous les mois à partir du 25 mai.
Extrait de code
// ProductDetails for the plan to be switched to
ProductDetails productDetails = ...;
// The specific offer token for the toBeSwitched plan's base plan
String offerToken = "...";
// The purchase token of the user's current subscription
String oldPurchaseToken = "...";
// The productId for the user's current subscription
String oldProductId = "...";
// The replacementMode to replace the user's subscription
int replacementMode = SubscriptionProductReplacementParams.ReplacementMode.CHARGE_FULL_PRICE;
SubscriptionProductReplacementParams subscriptionProductReplacementParams =
SubscriptionProductReplacementParams.newBuilder()
.setOldProductId(oldProductId)
.setReplacementMode(replacementMode)
.build();
ProductDetailsParams productDetailsParams =
ProductDetailsParams.newBuilder()
.setProductDetails(productDetails)
.setSubscriptionProductReplacementParams(subscriptionProductReplacementParams)
.setOfferToken(offerToken)
.build();
List<ProductDetailsParams> productDetailsParamsList = ImmutableList.of(productDetailsParams);
BillingFlowParams billingFlowParams =
BillingFlowParams.newBuilder()
.setProductDetailsParamsList(productDetailsParamsList)
.setSubscriptionUpdateParams(
SubscriptionUpdateParams.newBuilder().setOldPurchaseToken(oldPurchaseToken).build())
.build();
billingClient.launchBillingFlow(activity, billingFlowParams);
Mettre à niveau avec CHARGE_FULL_PRICE
Pour simuler ce scénario :
- Dans le
MainActivityde l'application exemple, remplacezreplacementModedans l'extrait de code parSubscriptionProductReplacementParams.ReplacementMode.CHARGE_FULL_PRICE. - Recompilez et lancez l'application.
- Résiliez les abonnements existants (le cas échéant) sur le Google Play Store. Ils expireront.
- Souscrivez la formule Basic.
- Passez au forfait Premium.
L'utilisateur passe immédiatement au forfait Premium. Le montant à payer immédiatement correspond au prix total du forfait Premium, soit 9,99 $. Toute valeur restante du forfait Basic est convertie en temps sur le nouveau forfait Premium, ce qui repousse légèrement la première date de renouvellement. Le montant du renouvellement sera ensuite de 9, 99 € par période.
Rétrogradation avec CHARGE_FULL_PRICE
Pour simuler ce scénario :
- Dans le
MainActivityde l'application exemple, remplacezreplacementModedans l'extrait de code parSubscriptionProductReplacementParams.ReplacementMode.CHARGE_FULL_PRICE. - Recompilez et lancez l'application.
- Résiliez les abonnements existants (le cas échéant) sur le Google Play Store. Ils expireront.
- Souscrivez la formule Basic.
- Passez au forfait Lite.
L'utilisateur passe immédiatement au forfait Lite et un nouveau cycle de facturation commence. Le montant à payer immédiatement correspond au prix cible total de 2,99 $. La partie inutilisée du forfait Basic est proratisée en temps pour le nouveau forfait Lite, ce qui repousse la date du premier renouvellement. Le montant du renouvellement sera ensuite de 2, 99 € par période.
Conclusion
Dans cette section, nous avons vu comment CHARGE_FULL_PRICE facture à l'utilisateur la totalité du montant à payer le jour du changement, en commençant immédiatement un nouveau cycle de facturation. Tout solde restant de l'ancien forfait s'applique de manière linéaire à la prochaine date de renouvellement.
8. WITHOUT_PRORATION
Dans ce mode de remplacement, l'abonnement passe immédiatement à un forfait supérieur ou inférieur, et le nouveau tarif est facturé lors du renouvellement de l'abonnement.
Exemple de scénario
Un utilisateur dispose du forfait Basic (4,99 $ par mois à partir du 1er avril). Le 20 avril, l'utilisateur souhaite passer au forfait Premium (9,99 $ par mois).
Dans ce cas, on a :
- L'utilisateur a immédiatement accès au forfait Premium.
- L'utilisateur n'aura pas à payer le nouveau prix de 9,99 $ avant la prochaine date de renouvellement de l'abonnement (1er mai).
Extrait de code
// ProductDetails for the plan to be switched to
ProductDetails productDetails = ...;
// The specific offer token for the toBeSwitched plan's base plan
String offerToken = "...";
// The purchase token of the user's current subscription
String oldPurchaseToken = "...";
// The productId for the user's current subscription
String oldProductId = "...";
// The replacementMode to replace the user's subscription
int replacementMode = SubscriptionProductReplacementParams.ReplacementMode.WITHOUT_PRORATION;
SubscriptionProductReplacementParams subscriptionProductReplacementParams =
SubscriptionProductReplacementParams.newBuilder()
.setOldProductId(oldProductId)
.setReplacementMode(replacementMode)
.build();
ProductDetailsParams productDetailsParams =
ProductDetailsParams.newBuilder()
.setProductDetails(productDetails)
.setSubscriptionProductReplacementParams(subscriptionProductReplacementParams)
.setOfferToken(offerToken)
.build();
List<ProductDetailsParams> productDetailsParamsList = ImmutableList.of(productDetailsParams);
BillingFlowParams billingFlowParams =
BillingFlowParams.newBuilder()
.setProductDetailsParamsList(productDetailsParamsList)
.setSubscriptionUpdateParams(
SubscriptionUpdateParams.newBuilder().setOldPurchaseToken(oldPurchaseToken).build())
.build();
billingClient.launchBillingFlow(activity, billingFlowParams);
Mise à niveau avec WITHOUT_PRORATION
Pour simuler ce scénario :
- Dans le
MainActivityde l'application exemple, remplacezreplacementModedans l'extrait de code parSubscriptionProductReplacementParams.ReplacementMode.WITHOUT_PRORATION. - Recompilez et lancez l'application.
- Résiliez les abonnements existants (le cas échéant) sur le Google Play Store. Ils expireront.
- Souscrivez la formule Basic.
- Passez au forfait Premium.
L'utilisateur passe immédiatement au forfait Premium tout en conservant sa date de renouvellement existante. Le montant à payer immédiatement est de 0,00 $. L'utilisateur a accès au forfait Premium pendant la durée restante du cycle en cours, avant de passer au nouveau montant de renouvellement de 9,99 $ à la prochaine date de facturation.
Rétrograder avec WITHOUT_PRORATION
Pour simuler ce scénario :
- Dans le
MainActivityde l'application exemple, remplacezreplacementModedans l'extrait de code parSubscriptionProductReplacementParams.ReplacementMode.WITHOUT_PRORATION. - Recompilez et lancez l'application.
- Résiliez les abonnements existants (le cas échéant) sur le Google Play Store. Ils expireront.
- Souscrivez la formule Basic.
- Passez au forfait Lite.
L'utilisateur est immédiatement rétrogradé au forfait Lite et perd les fonctionnalités Basic pour lesquelles il a payé. Le montant à payer immédiatement est de 0,00 $. Le cycle de facturation se poursuit sans modification, et l'utilisateur paiera le nouveau tarif réduit de 2, 99 $ lors de son prochain renouvellement prévu.
Conclusion
Cette section a montré comment WITHOUT_PRORATION échange immédiatement les droits d'accès de l'utilisateur sans frais de paiement, tout en laissant le cycle de facturation intact.
9. DEFERRED
Dans ce mode de remplacement, l'abonnement passe à un forfait supérieur ou inférieur uniquement au moment du renouvellement, mais le nouvel achat est immédiatement émis. L'élément existant est défini comme non renouvelable et expire à la fin du cycle de facturation en cours, tandis que le droit d'accès nouvellement demandé commence juste après.
Exemple de scénario
Un utilisateur dispose du forfait Basic (4,99 $ par mois à partir du 1er avril). Le 20 avril, l'utilisateur souhaite passer au forfait Premium (9,99 $ par mois).
Dans ce cas, on a :
- L'utilisateur n'est pas facturé immédiatement.
- L'utilisateur continue de bénéficier des fonctionnalités de base jusqu'à la fin du cycle de facturation en cours (30 avril).
- L'abonnement passera automatiquement à Premium à la prochaine date de renouvellement (1er mai).
Extrait de code
// ProductDetails for the plan to be switched to
ProductDetails productDetails = ...;
// The specific offer token for the toBeSwitched plan's base plan
String offerToken = "...";
// The purchase token of the user's current subscription
String oldPurchaseToken = "...";
// The productId for the user's current subscription
String oldProductId = "...";
// The replacementMode to replace the user's subscription
int replacementMode = SubscriptionProductReplacementParams.ReplacementMode.DEFERRED;
SubscriptionProductReplacementParams subscriptionProductReplacementParams =
SubscriptionProductReplacementParams.newBuilder()
.setOldProductId(oldProductId)
.setReplacementMode(replacementMode)
.build();
ProductDetailsParams productDetailsParams =
ProductDetailsParams.newBuilder()
.setProductDetails(productDetails)
.setSubscriptionProductReplacementParams(subscriptionProductReplacementParams)
.setOfferToken(offerToken)
.build();
List<ProductDetailsParams> productDetailsParamsList = ImmutableList.of(productDetailsParams);
BillingFlowParams billingFlowParams =
BillingFlowParams.newBuilder()
.setProductDetailsParamsList(productDetailsParamsList)
.setSubscriptionUpdateParams(
SubscriptionUpdateParams.newBuilder().setOldPurchaseToken(oldPurchaseToken).build())
.build();
billingClient.launchBillingFlow(activity, billingFlowParams);
Mettre à niveau avec DEFERRED
Pour simuler ce scénario :
- Dans le
MainActivityde l'application exemple, remplacezreplacementModedans l'extrait de code parSubscriptionProductReplacementParams.ReplacementMode.DEFERRED. - Recompilez et lancez l'application.
- Résiliez les abonnements existants (le cas échéant) sur le Google Play Store. Ils expireront.
- Souscrivez la formule Basic.
- Passez au forfait Premium.
L'utilisateur conservera le forfait Basic jusqu'à la fin de son cycle de facturation actuel. Le montant à payer immédiatement est de 0,00 $. À la date de renouvellement, leur abonnement passe au forfait Premium et le nouveau montant de renouvellement de 9,99 $ leur est facturé.
Rétrograder avec DEFERRED
Pour simuler ce scénario :
- Dans le
MainActivityde l'application exemple, remplacezreplacementModedans l'extrait de code parSubscriptionProductReplacementParams.ReplacementMode.DEFERRED. - Recompilez et lancez l'application.
- Résiliez les abonnements existants (le cas échéant) sur le Google Play Store. Ils expireront.
- Souscrivez la formule Basic.
- Passez au forfait Lite.
L'utilisateur conservera le forfait Basic jusqu'à la fin de son cycle de facturation actuel. Le montant à payer immédiatement est de 0,00 $. À la date de renouvellement, leur abonnement passera au forfait Lite et le nouveau montant de renouvellement de 2,99 $ leur sera facturé.
Conclusion
Cette section explique comment le mode de remplacement DEFERRED reporte une mise à niveau ou une rétrogradation jusqu'à la fin de la période payée d'un utilisateur actif. Cette option est particulièrement idéale pour les rétrogradations, car elle permet de ne pas perdre les fonctionnalités déjà achetées.
10. Traitement côté serveur et côté client
Lorsqu'un utilisateur remplace un abonnement, assurez-vous que votre application et votre backend gèrent correctement le changement pour éviter des problèmes tels que des interruptions de service ou une double facturation.
Exemple de scénario
- L'utilisateur dispose d'un forfait mensuel Basic (product_id
basic_planet purchase_tokenbasic_purchase_token_123). - L'utilisateur passe à un forfait Premium en utilisant un mode de remplacement immédiat (
WITHOUT_PRORATION,WITH_TIME_PRORATION,CHARGE_PRORATED_PRICEouCHARGE_FULL_PRICE). - Une fois le changement d'abonnement effectué, Google le considère comme un NOUVEL abonnement et crée un nouveau jeton d'achat différent pour le forfait Premium (product_id
premium_planet purchase_tokenpremium_purchase_token_123).
Traitement côté client
onPurchasesUpdated
Une fois l'achat de remplacement effectué, PurchasesUpdatedListener est déclenché. Même s'il s'agit d'un changement, Google Play considère le Forfait Premium comme un nouvel achat.
L'application recevra un objet Purchase contenant le jeton d'achat premium_purchase_token_123 et l'ID de produit premium_plan. Vous devez traiter cette demande exactement comme celle d'un nouvel abonné : validez le jeton et préparez-vous à accorder l'accès.
@Override
public void onPurchasesUpdated(BillingResult billingResult, List<Purchase> purchases) {
if (billingResult.getResponseCode() == BillingResponseCode.OK && purchases != null) {
for (Purchase purchase : purchases) {
// purchase.getPurchaseToken() = premium_purchase_token_123
// purchase.getProducts() will contain premium_plan
// Verify the purchase and grant entitlement
handleNewPurchase(purchase);
}
}
}
queryPurchasesAsync
queryPurchasesAsync ne renvoie que les abonnements actifs achetés depuis votre application. Vous devez vous appuyer sur cette méthode pour déterminer quel droit d'accès afficher à l'utilisateur. Pour les remplacements immédiats, queryPurchasesAsync() ne renverra plus l'ancien jeton d'achat BASIC et ne renverra plus que le nouveau jeton d'achat PREMIUM.
Appelez cette méthode chaque fois que votre application reprend ou qu'un achat est finalisé. Si le jeton Premium est présent, accordez immédiatement les fonctionnalités Premium et supprimez les fonctionnalités de base.
Traitement backend (RTDN)
Lorsqu'un remplacement a lieu, Google Play envoie une notification en temps réel pour les développeurs (NTRD) au sujet Pub/Sub que vous avez configuré.
- En cas de remplacement immédiat, Google envoie une notification en temps réel pour les développeurs
SUBSCRIPTION_PURCHASEDavec le nouveau jeton d'achat.Exemple de charge utile de notification en temps réel pour les développeurs{ "version":"1.0", "packageName":"com.google.play.billing.samples.subscriptions", "eventTimeMillis":"...", "subscriptionNotification": { "version":"1.0", "notificationType":4, // SUBSCRIPTION_PURCHASED "purchaseToken":"premium_purchase_token_123" //purchase token for the new subscription } } - Lorsque votre serveur reçoit le nouveau jeton d'achat de la RTDN, appelez l'API
purchases.subscriptionsV2avec le nouveau jeton d'achat pour récupérer les détails de l'achat. La réponse de l'API contient un champlinkedPurchaseTokenqui permet de déterminer si le jeton d'achat fait référence à un nouvel achat d'abonnement ou à un remplacement d'abonnement. - En cas de remplacement d'un abonnement,
linkedPurchaseTokenfait référence au jeton d'achat de l'ancien abonnement. Dans ce scénario, il s'agit debasic_purchase_token_123.Exemple de réponseGET purchases.subscriptionsV2curl \ 'https://androidpublisher.googleapis.com/androidpublisher/v3/applications/<application_id>/purchases/subscriptionsv2/tokens/premium_purchase_token_123' \ --header 'Authorization: Bearer [YOUR_ACCESS_TOKEN]' \ --header 'Accept: application/json' { "kind": "androidpublisher#subscriptionPurchaseV2", "startTime": "...", "subscriptionState": "SUBSCRIPTION_STATE_ACTIVE", "latestOrderId": "GPA.<order_id>", "linkedPurchaseToken": "basic_purchase_token_123", // The purchase token of the subscription that was replaced (Basic Plan in this case) "acknowledgementState": "ACKNOWLEDGEMENT_STATE_ACKNOWLEDGED", "lineItems": [ { "productId": "premium_plan", // productID of the new subscription (Premium Plan in this case) "expiryTime": "....", "autoRenewingPlan": {...}, "offerDetails": { "basePlanId": "monthly-auto-renewing" // base plan ID of the new subscription }, "itemReplacement": { // Details about the subscription replacement "productId": "subscription_basic", // productID of the old subscription (Basic Plan in this case) "replacementMode": "CHARGE_PRORATED_PRICE", // Replacement strategy used for this subscription change "basePlanId": "monthly-auto-renewing" // base plan ID of the old subscription }, "offerPhase": {...} } ], "etag": "<etag_value>" } - Vous devez confirmer le nouvel achat Premium. Vous pouvez effectuer cette opération dans votre application ou sur votre backend. Si vous ne confirmez pas l'achat dans un délai de trois jours, l'utilisateur sera remboursé et le droit d'accès sera révoqué. Pour en savoir plus sur le traitement et la confirmation des achats, consultez la documentation pour les développeurs.
Conclusion
Cette section a abordé les étapes à suivre pour gérer les remplacements d'abonnement immédiats sur le client et votre backend. Vous avez appris que Google Play traitait le nouveau forfait comme un tout nouvel achat et émettait un nouveau jeton d'achat. Sur le client, vous devez traiter ce nouvel achat à l'aide de PurchasesUpdatedListener et mettre à jour les droits d'accès en fonction de la réponse de queryPurchasesAsync. Dans le backend, vous devez écouter les RTDN SUBSCRIPTION_PURCHASED pour le nouveau jeton, utiliser l'API purchases.subscriptionsv2 pour identifier le linkedPurchaseToken de l'ancien abonnement et révoquer rapidement l'accès associé à l'ancien jeton tout en accordant le nouveau droit d'accès. N'oubliez pas de toujours confirmer le nouvel achat.
11. Traiter les remplacements DIFFÉRÉS
Contrairement aux modes de remplacement immédiat, ReplacementMode.DEFERRED diffère la modification de l'abonnement et la mise à jour des droits d'accès jusqu'à la fin du cycle de facturation en cours. La gestion des remplacements différés nécessite une logique spécifique pour s'assurer que les utilisateurs reçoivent le bon droit d'accès au bon moment.
Exemple de scénario
- L'utilisateur dispose d'un forfait mensuel Basic (product_id
basic_planet purchase_tokenbasic_purchase_token_123) qui se renouvelle le 15 avril. - Le 1er avril, l'utilisateur décide de passer à un forfait Premium à l'aide de
ReplacementMode.DEFERRED. - Google crée immédiatement un NOUVEAU jeton d'achat pour le forfait Premium (product_id
premium_planet purchase_tokenpremium_purchase_123), mais le montant à facturer à l'utilisateur et le droit d'accès sont prévus pour le 15 avril.
Traiter un remplacement différé
1. Immédiatement après la réussite du parcours d'achat (application)
PurchasesUpdatedListenerest appelé une fois le parcours d'achat terminé. L'application recevra un objetPurchasecontenant le nouveau jeton d'achatpremium_purchase_token_123. Toutefois, product_id fera toujours référence à l'ancienbasic_plan, car l'utilisateur n'a droit qu'au forfait Basic. Vous devez traiter cette demande exactement comme un nouvel achat et confirmer le jeton.@Override public void onPurchasesUpdated(BillingResult billingResult, List<Purchase> purchases) { if (billingResult.getResponseCode() == BillingResponseCode.OK && purchases != null) { for (Purchase purchase : purchases) { // purchase.getPurchaseToken() = premium_purchase_token_123 // purchase.getProducts() will contain basic_plan // Verify and acknowledge the purchase handleNewPurchase(purchase); } } }queryPurchasesAsyncrenvoie immédiatement l'achat avec le nouveau jeton d'achat (premium_purchase_token_123) et le droit d'accès d'origine (basic_plan) qui lui est associé. Vous pouvez vous appuyer sur cette méthode pour continuer à accorder le droit d'accès au forfait Basic à l'utilisateur.
2. Immédiatement après la réussite du parcours d'achat (backend)
- La notification en temps réel pour les développeurs SUBSCRIPTION_PURCHASED est envoyée immédiatement après le parcours d'achat pour le nouveau jeton d'achat (
premium_purchase_token_123).Exemple de charge utile de notification en temps réel{ "version":"1.0", "packageName":"com.google.play.billing.samples.subscriptions", "eventTimeMillis":"...", "subscriptionNotification": { "version":"1.0", "notificationType":4, // SUBSCRIPTION_PURCHASED "purchaseToken":"premium_purchase_token_123" //purchase token for the new subscription } } - Appelez
GET purchases.subscriptionsv2avec le nouveau jeton d'achat pour récupérer les détails de l'achat. La réponse contient deux lignes.- L'une représentant l'ancien abonnement (forfait de base) et comportant un
expiryTimedans le futur. L'ancien abonnement ne sera pas renouvelé et comporte undeferredItemReplacementcontenant le nouvel abonnement (forfait Premium). Cela indique qu'un remplacement de l'ancien droit d'accès est en attente à son expiration. - Un représentant l'abonnement nouvellement souscrit. Aucune valeur n'est définie pour "expiryTime".
{ "kind": "androidpublisher#subscriptionPurchaseV2", "startTime": "2026-05-07T15:50:11.383Z", "subscriptionState": "SUBSCRIPTION_STATE_ACTIVE", "latestOrderId": "GPA.<order_id>", "linkedPurchaseToken": "basic_purchase_token_123", "acknowledgementState": "ACKNOWLEDGEMENT_STATE_ACKNOWLEDGED", "lineItems": [ { "productId": "premium_plan", // Premium Plan has no expiry time "autoRenewingPlan": {...}, "offerDetails": {...}, "itemReplacement": {. // Subscription replacement details "productId": "basic_plan", "replacementMode": "DEFERRED", "basePlanId": "monthly-auto-renewing" }, "offerPhase": {} }, { "productId": "basic_plan", // Subscription to be replaced "expiryTime": "2026-05-07T15:54:34.768Z", // Expiry time in the future "autoRenewingPlan": {}, "offerDetails": {...}, "deferredItemReplacement": { // identifier indicating this subscription will be replaced upon renewal "productId": "subscription_premium" }, "latestSuccessfulOrderId": "GPA.<order_id>", "itemReplacement": {...}, } ], "etag": "<etag>" } - L'une représentant l'ancien abonnement (forfait de base) et comportant un
- Vous devez confirmer le nouveau jeton d'achat. Vous pouvez effectuer cette opération dans votre application ou sur votre backend. Pour en savoir plus sur le traitement et la confirmation des achats, consultez la documentation pour les développeurs.
- La notification en temps réel pour les développeurs SUBSCRIPTION_EXPIRED est envoyée pour l'ancien jeton d'achat (
basic_purchase_token_123).Exemple de charge utile de notification en temps réel pour les développeurs{ "version":"1.0", "packageName":"com.google.play.billing.samples.subscriptions", "eventTimeMillis":"...", "subscriptionNotification": { "version":"1.0", "notificationType":13, // SUBSCRIPTION_EXPIRED "purchaseToken":"basic_purchase_token_123" //purchase token for the old subscription } } - Lorsque vous appelez l'API
GET purchases.subscriptionsv2avec l'ancien jeton d'achat, il apparaît comme expiré (SUBSCRIPTION_STATE_EXPIRED). Les droits d'accès de l'ancien forfait sont transférés vers le nouveau pour le temps restant.
3. À la date de remplacement : premier renouvellement après le parcours d'achat (application)
queryPurchasesAsyncrenvoie l'achat avec le nouveau jeton d'achat (premium_purchase_token_123) et le nouvel abonnement qui lui est associé (premium_plan).- Le nouvel achat devrait déjà avoir été traité au moment de la réussite du parcours d'achat. Vous n'avez donc aucune action spéciale à effectuer, à part vous assurer que l'utilisateur a accès à l'abonnement approprié.
4. À la date de remplacement : premier renouvellement après le parcours d'achat (backend)
- Avec
ReplacementMode.DEFERRED, les premiers renouvellements suivent le comportement standard de tout autre renouvellement qui traite les RTDNSUBSCRIPTION_RENEWED. Vous n'avez pas besoin de gérer une logique spéciale pour les remplacements lorsque cet événement se produit. - Appelez
GET purchases.subscriptionsv2avec le nouveau jeton d'achat pour récupérer les détails de l'achat. La réponse contient deux lignes.- L'une représentant l'ancien abonnement (forfait de base) et comportant un
expiryTimedans le passé. L'ancienne souscription n'aura plus de valeur définie pour le champdeferredItemReplacement. - L'autre représentant le nouvel abonnement avec un
expiryTimeà venir et le champautoRenewEnableddéfini surtrue.
{ "kind": "androidpublisher#subscriptionPurchaseV2", "subscriptionState": "SUBSCRIPTION_STATE_ACTIVE", "latestOrderId": "GPA.<order_id>..0", "linkedPurchaseToken": "basic_purchase_token_123", // purchase token of the old subscription "acknowledgementState": "ACKNOWLEDGEMENT_STATE_ACKNOWLEDGED", "lineItems": [ { "productId": "premium_plan", // New subscription "expiryTime": "2026-05-07T16:00:09.437Z", // Expiry time set in the future "autoRenewingPlan": { "autoRenewEnabled": true, // Auto Renewing Flag set to True "recurringPrice": {...} }, "offerDetails": {...}, "latestSuccessfulOrderId": "GPA.<order_id>..0", "itemReplacement": {. // Details of the subscription replacement "productId": "basic_plan", "replacementMode": "DEFERRED", "basePlanId": "monthly-auto-renewing" }, "offerPhase": {...} }, { "productId": "basic_plan", // Old subscription, Does not contains the deferredItemReplacement field "expiryTime": "2026-05-07T15:54:34.768Z", // Expiry time set in the past "autoRenewingPlan": {}, "offerDetails": {...}, "latestSuccessfulOrderId": "GPA.<order_id>..0", "itemReplacement": {...}, } ], "etag": "<etag>" } - L'une représentant l'ancien abonnement (forfait de base) et comportant un
Conclusion
Cette section décrit le traitement spécifique requis pour ReplacementMode.DEFERRED. Vous avez appris que, contrairement aux modes immédiats, la modification des droits d'accès ne se produit qu'à la fin du cycle de facturation en cours. Cette section a abordé les étapes nécessaires pour que votre application et votre backend traitent correctement l'achat initial, accusent réception du nouveau jeton et gèrent le changement de droits d'accès lorsque l'ancien abonnement expire et que le nouveau devient actif.
12. Bac à sable de remplacement d'abonnement
La fonctionnalité Replacement Playground de l'application exemple vous permet de tester les changements d'abonnement (passage à un forfait supérieur ou inférieur) pour les produits d'abonnement configurés dans votre compte Google Play Console. Cette section explique comment utiliser la fonctionnalité Replacement Playground (Bac à sable de remplacement).
Configuration
Pour utiliser la fonctionnalité Replacement Playground, assurez-vous de disposer des éléments suivants :
- Le
packageIdde votre fichierbuild.gradlecorrespond à l'application configurée dans votre Google Play Console. - Votre compte utilisateur test est enregistré en tant que testeur de licence dans la Google Play Console. Pour en savoir plus sur les tests de licence, consultez Tester l'implémentation de la facturation de votre application.
Bac à sable de remplacement d'abonnement
L'application exemple inclut un onglet Replacement Playground (Bac à sable de remplacement) qui vous permet de simuler des modifications d'abonnement. Vous pouvez interroger les abonnements définis dans la Play Console et tester le passage de l'un à l'autre à l'aide de différents modes de remplacement. Ce terrain de jeu vous aide à comprendre comment les différents modes affectent les cycles de facturation et les droits d'accès pour vos abonnements. Vous pouvez ainsi déterminer les options qui répondent le mieux aux besoins de votre entreprise.
Pour simuler des remplacements à l'aide de l'atelier, procédez comme suit :
- Accédez à l'onglet Playground.
- Si vous disposez d'un abonnement actif, il sera mis en évidence. Il s'agit de l'abonnement qui sera remplacé.

- Si vous n'avez pas d'abonnement actif, vous devez d'abord en souscrire un.
- Le terrain de jeu liste les forfaits Basic, Premium et Lite créés par défaut pour cet atelier de programmation.
- Pour effectuer des tests avec d'autres forfaits configurés dans votre Play Developer Console, cliquez sur Ajouter un forfait personnalisé, puis recherchez-les par
productIdetbasePlanId. - Souscrivez l'abonnement sélectionné.
- L'abonnement actif que l'utilisateur vient d'acheter devrait maintenant s'afficher.

- Sélectionnez l'abonnement cible vers lequel l'utilisateur souhaite passer .
- Sélectionnez un mode de remplacement pour la transition.

- Cliquez sur le bouton Tester le remplacement pour simuler le remplacement de l'abonnement.
- La bottom sheet Google Play Billing doit s'afficher avec les détails calculés du remplacement de l'abonnement (tels que les frais immédiats et les ajustements du cycle de facturation).

- Effectuez la transaction.
- Accédez à la page Gérer les abonnements dans l'application Play Store pour afficher les modifications apportées à l'abonnement actif, ainsi que les nouvelles dates de renouvellement et les nouveaux prix.

13. Étapes suivantes
- Découvrez comment maximiser votre intégration de Play Billing.
- N'oubliez pas de suivre les bonnes pratiques pour valider et traiter les achats sur votre backend sécurisé une fois que les utilisateurs commencent à acheter ces produits.
Documents de référence
14. Félicitations
Félicitations ! Vous avez correctement implémenté les remplacements d'abonnement avec différents modes de prorata et configuré la gestion du backend pour les transitions de forfait.
Connaissances acquises
- Configurer
SubscriptionProductReplacementParamsavec des modes de remplacement spécifiques. - Différence entre les mises à niveau immédiates et les rétrogradations différées.
- Comment retirer les anciens jetons d'abonnement à l'aide de
linkedPurchaseTokenet des numéros RTDN
Enquête
Vos commentaires sur cet atelier de programmation sont très importants. Prenez quelques minutes pour répondre à notre enquête.