This guide describes how to integrate the APIs to offer alternative billing with user choice in 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:
enableUserChoiceBilling
. - You need to register an
UserChoiceBillingListener
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 userChoiceBillingListener =
UserChoiceBillingListener { userChoiceDetails ->
// Handle alternative billing choice.
}
var billingClient = BillingClient.newBuilder(context)
.setListener(purchasesUpdatedListener)
.enablePendingPurchases()
.enableUserChoiceBilling(userChoiceBillingListener)
.build()
Java
private PurchasesUpdatedListener purchasesUpdatedListener = new PurchasesUpdatedListener() {
@Override
public void onPurchasesUpdated(BillingResult billingResult, List<Purchase> purchases) {
// Handle new Google Play purchase.
}
};
private UserChoiceBillingListener userChoiceBillingListener = new UserChoiceBillingListener() {
@Override
public void userSelectedAlternativeBilling(
UserChoiceDetails userChoiceDetails) {
// Handle new Google Play purchase.
}
};
private BillingClient billingClient = BillingClient.newBuilder(context)
.setListener(purchasesUpdatedListener)
.enablePendingPurchases()
.enableUserChoiceBilling(userChoiceBillingListener)
.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 check:
- The system checks if the user's Google Play country is a
country that supports alternative billing with user choice (i.e. a supported
country). If the user's Google Play country is supported, Google Play checks
whether alternative billing has been enabled based on the configuration of
the
BillingClient
.- If alternative billing with user choice has been enabled, the purchase flow shows the user choice UX.
- If alternative billing with user choice 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 a supported country, the purchase flow shows the standard Google Play billing system UX, without user choice.
User's Play country is a supported country |
User's Play country is not a supported country |
|
---|---|---|
enableUserChoiceBilling called during BillingClient setup |
User sees user choice UX |
User sees standard Google Play billing system UX |
enableUserChoiceBilling 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
UserChoiceBillingListener
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 UserChoiceDetails
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 UserChoiceBillingListener
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 alternative billing system, you
must report the transaction to Google Play by calling the Google Play Developer
API from your backend within 24 hours, providing the
externalTransactionToken
and additional transaction details. See the
backend integration guide for more details.
The following example demonstrates how to implement the
UserChoiceBillingListener
:
Kotlin
private val userChoiceBillingListener =
UserChoiceBillingListener { userChoiceDetails ->
// Get the products being purchased by the user.
val products = userChoiceDetails.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(
userChoiceDetails.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 userChoiceBillingListener userChoiceBillingListener = new UserChoiceBillingListener() {
@Override
public void userSelectedAlternativeBilling(
UserChoiceDetails userChoiceDetails) {
// Get the products being purchased by the user.
List<Product> products =
userChoiceDetails.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(
userChoiceDetails.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 alternative billing with user choice, 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
Subscription plan changes including 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.
Add-ons that depend on an existing subscription, share the same payment method,
and align recurring charges are handled as upgrades. For other add-ons, users
should be able to choose which billing system they want to use. Initiate a new
purchase experience by using launchBillingFlow()
, as described in Launch the
user choice billing flow.
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 UserChoiceBillingListener 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 UserChoiceBillingListener 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:
val offerTokenNewPlan = productDetailsNewPlan
.getSubscriptionOfferDetails(selectedOfferIndex)
.getOfferToken()
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:
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)
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.
Test alternative billing
License testers should be used to test your alternative billing integration. You won't be invoiced for transactions that have been initiated by license tester accounts. See Test in-app billing with application licensing for more information on configuring license testers.
Next steps
Once you've finished in-app integration, you're ready to integrate your backend.