Appendix D: Migrating from PBL 7 to PBL 8
Google Play Billing Library 8.0 is a major release that removes deprecated APIs, renames confusing terminology, and introduces new features that simplify billing integration. If you have a working PBL 7 integration, you cannot simply bump the version number and expect everything to compile. Several APIs that were deprecated in PBL 6 and 7 are now gone entirely, and a few method signatures have changed in ways that require code updates.
This appendix walks you through every breaking change, shows you the before and after code for each migration step, and gives you a checklist to follow so nothing falls through the cracks. If you are starting a new project from scratch, you can skip this appendix. But if you have an existing PBL 7 codebase, read this carefully before upgrading.
Breaking Changes Summary
The following table summarizes every breaking change between PBL 7 and PBL 8. Use it as a quick reference, then read the detailed sections below for migration instructions.
Removed APIs and Their Replacements
queryPurchaseHistoryAsync() Removal
queryPurchaseHistoryAsync() returned the most recent purchase for each product the user had ever bought, including expired and consumed purchases. Google removed this API in PBL 8 because it encouraged patterns that led to incorrect entitlement logic. Developers often used it to "restore purchases" on new devices, but the results included purchases that were no longer valid, leading to users getting access they should not have.
The replacement is queryPurchasesAsync(), which returns only active, unconsumed purchases. This is what you should use for entitlement checks and purchase restoration.
Before (PBL 7):
After (PBL 8):
If you genuinely need historical purchase data (for analytics or auditing), query the Google Play Developer API from your backend using the Purchases.subscriptionsv2 or Purchases.products endpoints. The client library is no longer the right place for historical queries.
querySkuDetailsAsync() to queryProductDetailsAsync()
querySkuDetailsAsync() was the original API for fetching product information. It was deprecated in PBL 5 when Google introduced the new product model with base plans and offers. In PBL 8, it is gone entirely.
The migration is straightforward, but the return types are different. SkuDetails was a flat object with a single price. ProductDetails has a richer structure with subscriptionOfferDetails containing multiple base plans and offers, each with their own pricing phases.
Before (PBL 7):
After (PBL 8):
Note that queryProductDetailsAsync in PBL 8 returns a QueryProductDetailsResult rather than delivering results through a callback with a raw list. You access the product details through the result object. See the next section for details on this change.
enablePendingPurchases() Signature Change
In PBL 7, you called enablePendingPurchases() with no arguments to opt in to pending purchase support. In PBL 8, Google requires you to pass a PendingPurchasesParams object. This gives you explicit control over which types of pending purchases you support.
Before (PBL 7):
After (PBL 8):
The PendingPurchasesParams builder lets you specify enableOneTimeProducts() and enablePrepaidPlans() separately. You must call at least one of these methods. If your app supports both one time products and prepaid subscriptions, call both. If you only sell auto renewing subscriptions (no prepaid, no one time products), you still need to pass the params object, but you configure it for the product types you actually sell.
ProductDetailsResponseListener Signature Change
In PBL 7, queryProductDetailsAsync() accepted a ProductDetailsResponseListener that received a BillingResult and a nullable List<ProductDetails>. In PBL 8, the method returns a QueryProductDetailsResult object instead. This result object wraps both the billing result and the product details list, and also introduces handling for unfetched products (covered in the new features section below).
Before (PBL 7):
After (PBL 8):
The QueryProductDetailsResult object gives you three things: the billingResult with the response code, the productDetailsList containing the products that were successfully fetched, and the unfetchedProductList containing products that could not be retrieved. This is a cleaner pattern than the nullable list from PBL 7.
Alternative Billing API Renaming
Google renamed the entire alternative billing API surface to use "user choice" terminology instead of "alternative." This is not just cosmetic. The rename reflects Google's compliance framework and the legal terminology used in various markets. The functionality is identical, but every class, method, and interface name has changed.
Before (PBL 7):
After (PBL 8):
If you use alternative billing in your app, this is a find and replace migration. The callback behavior, the data you receive, and the flow you implement are all the same. Just update the names.
Replacement Mode Migration from ProrationMode
PBL 7 used ProrationMode constants to specify how plan changes should handle billing transitions. PBL 8 replaces this with ReplacementMode, which uses clearer names and adds a new mode (KEEP_EXISTING in PBL 8.1). The setter method on SubscriptionUpdateParams also changed from setReplaceProrationMode() to setSubscriptionReplacementMode().
Constant Mapping
Notice that Google dropped the IMMEDIATE_ prefix from most modes. The old names were verbose and somewhat misleading (for example, DEFERRED was never prefixed with IMMEDIATE_ even in PBL 7, creating an inconsistency). The new names are shorter and more consistent.
Before (PBL 7):
After (PBL 8.0):
SubscriptionUpdateParams Deprecation in PBL 8.1
In PBL 8.0, SubscriptionUpdateParams still works with the new ReplacementMode constants and the renamed setter. But starting in PBL 8.1, Google deprecated SubscriptionUpdateParams entirely in favor of SubscriptionProductReplacementParams. The new API attaches replacement configuration to the product parameters rather than to the billing flow parameters.
After (PBL 8.1, recommended):
This two step migration (ProrationMode to ReplacementMode in 8.0, then SubscriptionUpdateParams to SubscriptionProductReplacementParams in 8.1) can be done all at once if you are jumping straight from PBL 7 to PBL 8.1 or later. There is no reason to stop at the intermediate step.
New Features to Adopt
PBL 8 is not just about breaking changes. It introduces several new features that you should adopt during your migration to get the most out of the upgrade.
enableAutoServiceReconnection()
In PBL 7, when the connection to Google Play Services dropped (which happens when the system kills the Play Store process or the user's device switches networks), you had to detect the disconnection in onBillingServiceDisconnected() and manually call startConnection() again. This was error prone and led to a lot of boilerplate retry logic.
PBL 8 introduces enableAutoServiceReconnection() on the BillingClient.Builder. When enabled, the library automatically re establishes the connection when it drops, with built in exponential backoff. You no longer need to manage reconnection yourself.
After enabling this, your onBillingServiceDisconnected() callback still fires, but you do not need to call startConnection() inside it. The library handles that for you. You can use the callback purely for logging or UI updates (such as showing a temporary "reconnecting" indicator).
UnfetchedProduct Handling
When you call queryProductDetailsAsync() in PBL 8, the result may include an unfetchedProductList. This list contains products that you requested but that could not be retrieved. Reasons include a product being deactivated in the Play Console, a product ID typo, or a temporary server issue for a specific product.
In PBL 7, if one product out of ten failed to load, you got no information about which one failed. You just received a shorter list than expected. In PBL 8, the UnfetchedProduct object tells you exactly which product ID failed and why.
Use this information to build better diagnostics. If a product consistently appears in the unfetched list, it might be misconfigured in the Play Console or the product ID in your code might have a typo.
Sub Response Codes
PBL 8 introduces sub response codes that provide more granular information about why a purchase failed. The main BillingResponseCode tells you what happened (for example, the purchase failed). The sub response code tells you why (for example, the user had insufficient funds).
You access sub response codes through BillingResult.getOnPurchasesUpdatedSubResponseCode() inside your PurchasesUpdatedListener. See Appendix B for the full list of sub response codes and recommended handling strategies.
includeSuspendedSubscriptions (PBL 8.1)
PBL 8.1 adds the includeSuspendedSubscriptions option to QueryPurchasesParams. When enabled, queryPurchasesAsync() returns subscriptions that are in a suspended state (such as account hold or grace period) in addition to fully active subscriptions.
This is useful when you want to show users that their subscription has a payment issue and prompt them to update their payment method. Without this flag, suspended subscriptions are invisible to queryPurchasesAsync(), which means you cannot detect the problem on the client side and must rely entirely on your backend and push notifications to alert the user.
Step by Step Migration Checklist
Follow these steps in order when migrating from PBL 7 to PBL 8.
- Update your dependency. Change your
build.gradlefile to reference PBL 8.x. Set the version to the latest stable release (8.1 or later is recommended, since it includesSubscriptionProductReplacementParamsandincludeSuspendedSubscriptions). - Fix the BillingClient builder. Replace
enablePendingPurchases()withenablePendingPurchases(PendingPurchasesParams). AddenableAutoServiceReconnection(). If you use alternative billing, renameenableAlternativeBilling()toenableUserChoiceBilling()and update the listener type. - Remove queryPurchaseHistoryAsync() calls. Replace them with
queryPurchasesAsync(). If you need historical data, move that logic to your backend. - Remove querySkuDetailsAsync() calls. Replace them with
queryProductDetailsAsync(). Update your product display code to handle theProductDetailsmodel with its base plans and offers structure. - Update queryProductDetailsAsync() callbacks. Switch from the
ProductDetailsResponseListenercallback pattern to theQueryProductDetailsResultreturn type. Add handling forunfetchedProductList. - Replace ProrationMode with ReplacementMode. Find every usage of
ProrationModeconstants and replace them with the correspondingReplacementModeconstants. ReplacesetReplaceProrationMode()withsetSubscriptionReplacementMode(). - Migrate SubscriptionUpdateParams (if targeting PBL 8.1+). Replace
SubscriptionUpdateParamswithSubscriptionProductReplacementParams. Move the replacement configuration fromBillingFlowParamstoProductDetailsParams. - Rename alternative billing classes. Replace
AlternativeBillingListenerwithUserChoiceBillingListener,AlternativeChoiceDetailswithUserChoiceDetails, and update all references. - Adopt new PBL 8 features. Add
enableAutoServiceReconnection()to your builder. AddUnfetchedProducthandling to your product query flow. Consider adopting sub response codes in your purchase error handling. AddincludeSuspendedSubscriptionsif you want to detect payment issues on the client. - Simplify your reconnection logic. If you have manual reconnection code in
onBillingServiceDisconnected(), you can remove it (or reduce it to logging) now that auto reconnection is enabled. - Run your test suite. Verify that all billing flows work: new purchases, subscription renewals, plan changes (upgrades and downgrades), consumable purchases, and pending transactions. Test on a device with the latest Play Store version.
- Test with the Google Play Billing Library test tool. Use the
BillingClienttest methods and license testing accounts to verify each flow without spending real money. Pay special attention to plan change flows if you migrated fromProrationModetoReplacementMode. - Update your ProGuard/R8 rules if needed. PBL 8 may have different class names that need to be kept. Check the official migration guide for any updated ProGuard rules.
- Deploy to internal testing first. Roll out to your internal test track before pushing to production. Monitor crash reports and billing success rates for at least one full billing cycle.
Version Deprecation Timeline
Understanding Google's deprecation timeline helps you plan your migration schedule.
What "support ends" means in practice: After the deprecation date, Google does not immediately break your existing app. Users who already have your app installed will still be able to make purchases. However, Google may reject new APK/AAB uploads to the Play Console if they reference the deprecated library version. Google may also stop fixing bugs in deprecated versions, so any issues you encounter will not be patched.
The practical deadline: If you are currently on PBL 7, you have until August 31, 2026 to complete your migration. That might sound like plenty of time, but billing migrations deserve thorough testing across multiple billing cycles. Start your migration early enough that you have at least two to three months of production testing before the deadline. A good target is to have your PBL 8 migration in production by June 2026 at the latest.
If you are still on PBL 5 or 6: You are already past the recommended migration window. Prioritize upgrading to PBL 8 immediately. Skipping PBL 7 entirely is fine. You can migrate directly from PBL 5 or 6 to PBL 8 by following the same steps in this appendix, though you may have additional changes to make if you are still using the original SkuDetails based API throughout your codebase.
The migration from PBL 7 to PBL 8 is mechanical, not architectural. Your billing flow structure stays the same. Your backend integration stays the same. The changes are at the API surface level: renamed classes, updated method signatures, and a few removed methods. Budget a few days for the code changes and a few weeks for thorough testing, and you will be in good shape well before the deadline.