This guide describes how to integrate the alternative billing APIs into your app.
Play Billing Library setup
Add the Play Billing Library dependency to your Android app. To use the alternative billing APIs you need to use version 5.2 or higher. If you need to migrate from an earlier version, follow the instructions in the migration guide before you attempt to implement alternative billing.
Connect to Google Play
The first steps in the integration process are the same as the ones described in the Google Play Billing integration guide, with a few modifications when initializing your BillingClient:
- You need to call a new method to indicate that you want to offer the user
a choice of billing options:
enableAlternativeBilling
. - You need to register an
AlternativeBillingListener
for handling cases where the user chooses alternative billing.
The following example demonstrates initializing a BillingClient
with these
modifications:
Kotlin
val purchasesUpdatedListener = PurchasesUpdatedListener { billingResult, purchases -> // Handle new Google Play purchase. } val alternativeBillingListener = AlternativeBillingListener { alternateChoiceDetails -> // Handle alternative billing choice. } var billingClient = BillingClient.newBuilder(context) .setListener(purchasesUpdatedListener) .enablePendingPurchases() .enableAlternativeBilling(alternativeBillingListener) .build()
Java
private PurchasesUpdatedListener purchasesUpdatedListener = new PurchasesUpdatedListener() { @Override public void onPurchasesUpdated(BillingResult billingResult, List<Purchase> purchases) { // Handle new Google Play purchase. } }; private AlternativeBillingListener alternativeBillingListener = new AlternativeBillingListener() { @Override public void userSelectedAlternativeBilling( AlternativeChoiceDetails alternateChoiceDetails) { // Handle new Google Play purchase. } }; private BillingClient billingClient = BillingClient.newBuilder(context) .setListener(purchasesUpdatedListener) .enablePendingPurchases() .enableAlternativeBilling(alternativeBillingListener) .build();
After you initialize the BillingClient
, you need to
establish a connection to Google Play
as described in the integration guide.
Display available products
You can display available products to the user in the same way as with a Google Play billing system integration. When your user has seen the products available for purchase and selects one to buy, launch the user choice billing flow as described in the following section.
Launch the user choice billing flow
Launch the user choice billing flow by calling launchBillingFlow()
. This works
the same as launching a purchase flow
with a Google Play billing system integration: you provide a ProductDetails
instance and an offerToken
corresponding to the product and the offer that the
user wants to acquire. If the user chooses Google Play's billing system, this
information is used to continue the purchase flow.
When developers call launchBillingFlow
, the Google Play billing system
performs the following logic:
The system checks if the user's Google Play country is South Korea. If the user's Google Play country is South Korea, Google Play checks whether alternative billing has been enabled based on the configuration of the
BillingClient
.- If alternative billing has been enabled, the purchase flow shows the new UX.
- If alternative billing is not enabled, the purchase flow shows the standard Google Play billing system UX, without user choice.
If the user's Google Play country is not South Korea, the purchase flow shows the standard Google Play billing system UX, without user choice.
User's Play country is South Korea | User's Play country is not South Korea | |
---|---|---|
enableAlternativeBilling called during BillingClient setup |
User sees new UX | User sees standard Google Play billing system UX |
enableAlternativeBilling not called during BillingClient setup |
User sees standard Google Play billing system UX | User sees standard Google Play billing system UX |
Handle the user selection
How you handle the rest of the purchase flow differs depending on whether the user selected Google Play's billing system or an alternative billing system.
When the user selects an alternative billing system
If the user chooses the alternative billing system, Google Play calls the
AlternativeBillingListener
to notify the app that it needs to launch the
purchase flow in the alternative billing system. In particular, the
userSelectedAlternativeBilling()
method is called.
The external transaction token provided in the AlternativeChoiceDetails
object represents a signature for the user's choice to enter the alternative
billing flow. Use this token to report any transaction resulting from
this choice as explained in the backend integration
guide.
The AlternativeBillingListener
should perform the following actions:
- Get the product or products being purchased by the user so that they can be presented in the purchase flow in the alternative billing system.
- Collect the string received as the external transaction token and send it to your backend to persist it. This is used later to report the external transaction to Google Play if the user completes this specific purchase.
- Launch the developer's alternative purchase flow.
If the user completes the purchase using the developer's alternative billing
system, you must report the transaction to Google Play by calling the Google
Play Developer API from your backend, providing the externalTransactionToken
and additional transaction details. See the backend integration
guide for more details.
The following example demonstrates how to implement the
AlternativeBillingListener
:
Kotlin
private val alternativeBillingListener = AlternativeBillingListener { alternativeChoiceDetails -> // Get the products being purchased by the user. val products = alternativeChoiceDetails.products // Send external transaction token to developer backend server // this devBackend object is for demonstration purposes, // developers can implement this step however best fits their // app to backend communication. devBackend.sendExternalTransactionStarted( alternativeChoiceDetails.externalTransactionToken, user ) // Launch alternative billing // ... // The developer backend handles reporting the transaction // to Google Play's backend once the alternative billing // purchase is completed. }
Java
private AlternativeBillingListener alternativeBillingListener = new AlternativeBillingListener() { @Override public void userSelectedAlternativeBilling( AlternativeChoiceDetails alternateChoiceDetails) { // Get the products being purchased by the user. List<Product> products = alternativeChoiceDetails.getProducts(); // Send external transaction token to developer backend server // this devBackend object is for demonstration purposes, // developers can implement this step however best fits their // app to backend communication. devBackend.sendExternalTransactionStarted( alternateChoiceDetails.getExternalTransactionToken(), user ); // Launch alternative billing // ... // The developer backend handles reporting the transaction // to Google Play's backend once the alternative billing // purchase is completed. } };
When the user selects Google Play's billing system
If the user chooses Google Play's billing system, they continue with the purchase through Google Play.
- See Processing purchases in the library integration guide for more information about how to handle new in-app purchases through Google Play's billing system.
- See New subscriptions in the subscription management guide for additional guidance for subscription purchases.
Handle changes in subscription
For developers using an alternative billing system, purchases need to be either
processed through Google Play's billing system or reported with an
externalTransactionId
, depending on the user's choice. Changes to existing
subscriptions that were processed through the user choice flow can be made
through the same billing system until expiration.
This section describes how to handle some common subscription change scenarios.
Upgrade and downgrade flows
Upgrade and downgrade flows should be handled differently depending on whether the subscription was originally bought through Google Play's billing system or through an alternative billing system.
Subscriptions bought through an alternative billing system
For subscriptions that were originally bought through the developer's alternative billing system after user choice, users requesting an upgrade or a downgrade should proceed through the developer's alternative billing system without going through the user choice experience again.
To do this, call launchBillingFlow
when the user requests an upgrade or a
downgrade. Instead of specifying a SubscriptionUpdateParams
object in the
parameters, use setOriginalExternalTransactionId
, providing the
external transaction ID for the original purchase. This does not display
the user choice screen, given that the user choice for the original purchase is
preserved for upgrades and downgrades. The call to launchBillingFlow
in this
case generates a new external transaction token for the transaction that you
can retrieve from the callback.
Kotlin
// The external transaction ID from the current // alternative billing subscription. val externalTransactionId = //... ; val billingFlowParams = BillingFlowParams.newBuilder() .setProductDetailsParamsList( listOf( BillingFlowParams.ProductDetailsParams.newBuilder() // Fetched via queryProductDetailsAsync. .setProductDetails(productDetailsNewPlan) // offerIdToken can be found in // ProductDetails=>SubscriptionOfferDetails. .setOfferToken(offerTokenNewPlan) .build() ) ) .setSubscriptionUpdateParams( BillingFlowParams.SubscriptionUpdateParams.newBuilder() .setOriginalExternalTransactionId(externalTransactionId) .build() val billingResult = billingClient.launchBillingFlow(activity, billingFlowParams) // When the user selects the alternative billing flow, // the AlternativeBillingListener is triggered.
Java
// The external transaction ID from the current // alternative billing subscription. String externalTransactionId = //... ; BillingFlowParams billingFlowParams = BillingFlowParams.newBuilder() .setProductDetailsParamsList( ImmutableList.of( ProductDetailsParams.newBuilder() // Fetched via queryProductDetailsAsync. .setProductDetails(productDetailsNewPlan) // offerIdToken can be found in // ProductDetails=>SubscriptionOfferDetails .setOfferToken(offerTokenNewPlan) .build() ) ) .setSubscriptionUpdateParams( SubscriptionUpdateParams.newBuilder() .setOriginalExternalTransactionId(externalTransactionId) .build() ) .build(); BillingResult billingResult = billingClient.launchBillingFlow(activity, billingFlowParams); // When the user selects the alternative billing flow, // the AlternativeBillingListener is triggered.
When the upgrade or downgrade is completed in the alternative billing system, you need to report a new transaction using the external transaction token obtained through the previous call for the new subscription purchase.
Subscriptions bought through Google Play's billing system
Similarly, users that bought their current subscription through Google Play's billing system after user choice should be shown the upgrade or downgrade flow in Google Play's billing system. The following instructions describe how you would launch the purchase flow for an upgrade or downgrade through Google Play's billing system:
Identify the
offerToken
of the selected offer for the new plan:Kotlin
val offerTokenNewPlan = productDetailsNewPlan .getSubscriptionOfferDetails(selectedOfferIndex) .getOfferToken()
Java
String offerTokenNewPlan = productDetailsNewPlan .getSubscriptionOfferDetails(selectedOfferIndex) .getOfferToken();
Send the correct information to Google Play's billing system to process the new purchase, including the purchase token for the existing subscription:
Kotlin
val billingFlowParams = BillingFlowParams.newBuilder().setProductDetailsParamsList( listOf( BillingFlowParams.ProductDetailsParams.newBuilder() .setProductDetails(productDetailsNewPlan) .setOfferToken(offerTokenNewPlan) .build() ) ) .setSubscriptionUpdateParams( BillingFlowParams.SubscriptionUpdateParams.newBuilder() .setOldPurchaseToken(oldToken) .setReplaceProrationMode(BillingFlowParams.ProrationMode.IMMEDIATE_AND_CHARGE_FULL_PRICE) .build() ) .build() BillingClient.launchBillingFlow(activity, billingFlowParams)
Java
BillingFlowParams billingFlowParams = BillingFlowParams.newBuilder() .setProductDetailsParamsList( ImmutableList.of( ProductDetailsParams.newBuilder() // Fetched via queryProductDetailsAsync .setProductDetails(productDetailsNewPlan) // offerIdToken can be found in // ProductDetails=>SubscriptionOfferDetails. .setOfferToken(offerTokenNewPlan) .build() ) ) .setSubscriptionUpdateParams( SubscriptionUpdateParams.newBuilder() // purchaseToken can be found in // Purchase#getPurchaseToken .setOldPurchaseToken("old_purchase_token") .setReplaceProrationMode(ProrationMode.IMMEDIATE_AND_CHARGE_FULL_PRICE) .build() ) .build(); BillingResult billingResult = billingClient.launchBillingFlow(activity, billingFlowParams);
This purchase proceeds in Google Play's billing system, and your app
receives the PurchasesUpdatedListener.onPurchaseUpdated
call with the result
of the purchase. If the purchase was successful, the onPurchaseUpdated
method
also receives the new purchase information, and your backend receives a
SUBSCRIPTION_PURCHASED
Real Time Developer Notification. When pulling the
status for the new purchase, a linkedPurchaseToken
attribute links to the
old subscription purchase so that you can retire it
as recommended.
Subscription cancellations and restorations
Users should be able to cancel their subscription at any time. When a user cancels a subscription, the termination of the entitlement may be deferred until the paid period ends. For example, if a user cancels a monthly subscription halfway through the month, they may continue to access the service for the remaining ~2 weeks until their access is removed. During this period, the subscription is still technically active, so the user can use the service.
It is not uncommon that users decide to reverse the cancellation during this active period. In this guide, this is called a restoration. The following sections describe how to handle restoration scenarios in your alternative billing API integration.
Subscriptions bought through an alternative billing system
If you have an external transaction ID for a canceled subscription, it's not
necessary to call launchBillingFlow
to restore the subscription, so it
shouldn't be used for this type of activation. If a user restores their
subscription while still in the active period of a canceled subscription, no
transaction occurs at that time; you can just continue reporting renewals when
the current cycle expires and the next renewal occurs. This includes cases where
the user receives a credit or special renewal price as part of the restoration
(for example, a promotion to encourage the user to continue their subscription).
Subscriptions bought through Google Play's billing system
Generally, users can restore subscriptions on Google Play's billing system. For
canceled subscriptions that were originally purchased on Google Play's billing
system, the user may choose to undo the cancellation while the subscription is
active through Google Play's
Resubscribe feature. In that case,
you receive a SUBSCRIPTION_RESTARTED
Real Time Developer Notification in
your backend, and a new purchase token is not issued—the original token
is used to continue the subscription. To learn how to manage restoration in
Google Play's billing system, see
Restorations in the subscription
management guide.
You can also trigger a restoration in Google Play's billing system from the app
by calling launchBillingFlow
. See Before subscription expiration -
in-app for an explanation of
how to do this. In the case of users that went through the user choice flow for
the original purchase (which was canceled but is still active), the system
automatically detects their choice and displays the user interface for
restoring these purchases. They are asked to confirm their re-purchase of the
subscription through Google Play, but they don't need to go through the user
choice flow again. A new purchase token is issued for the user in this
case. Your backend receives a SUBSCRIPTION_PURCHASED
Real Time Developer
Notification, and the linkedPurchaseToken
value for the new purchase status
is set as in the case of an upgrade or downgrade, with the old purchase
token for the subscription that was canceled.
Resubscriptions
If a subscription completely expires, whether it is due to cancellation or payment decline without recovery (an expired account hold), then the user must resubscribe if they want to restart the entitlement.
Resubscribing can also be enabled through the app by processing it similarly to
a standard signup. Users should be able to choose which billing system they want
to use. launchBillingFlow
may be called in this case, as described in Launch
the user choice billing flow.
Next steps
Once you've finished in-app integration, you're ready to integrate your backend.