· 7 min read · Nadeera Silva
Play Billing Library Deadline: Updating Flutter IAP Apps
Google Play's extension for Billing Library 7 and target API 36 ends on 1 Nov 2026. Here's how to upgrade Flutter in_app_purchase and retest it.

Google Play's Play Billing Library deadline lands on 1 November 2026, the last day of the extension window, and Flutter apps that sell subscriptions or one-time products are the ones most likely to miss it. If your app still uses in_app_purchase 3.2.x or older, it ships Play Billing Library 7, and after the extension ends Play won't accept updates built with it. The same date ends the extension for the Android 16 (API level 36) target requirement.
This post covers what Google requires, how to check which Billing Library your Flutter app actually ships, the code changes between versions, and what to retest before you upload.

The Play Billing Library deadline and target API 36
Two separate policies share the same dates this year.
Target API level. According to Google's target API requirements, since 31 August 2026 new apps and app updates must target Android 16 (API level 36). Wear OS and Android Automotive OS apps need API 35, and Android TV and Android XR apps need API 34. Existing apps must target at least API 35 to stay visible to new users on newer Android versions. Developers could request an extension to 1 November 2026.
Play Billing Library version. Every Billing Library version has a two-year deprecation cycle. Google's deprecation FAQ (last updated 20 October 2026) gives these dates for new apps and updates:
| Billing Library | Deadline for new apps and updates | Extension deadline |
|---|---|---|
| 7 | 31 August 2026 | 1 November 2026 |
| 8 | 31 August 2027 | 1 November 2027 |
| 9 | 31 August 2028 | 1 November 2028 |
Which Billing Library your Flutter package uses
Most Flutter apps don't depend on the Billing Library directly. They get it through a plugin, so the plugin version decides your Billing Library version. From the in_app_purchase_android changelog and the pub.dev package data:
| Flutter package | Version | Play Billing Library |
|---|---|---|
in_app_purchase | 3.2.x and older | 7.1.1 (through in_app_purchase_android 0.4.x) |
in_app_purchase | 3.3.0 and later (from 3 June 2026) | 8.0.0 (through in_app_purchase_android 0.5.0 to 0.5.4) |
in_app_purchase_android | 0.5.5 (19 October 2026) | 9.1.0 |
purchases_flutter (RevenueCat) | 9.0.0 and later | 8 or newer |
in_app_purchase 3.3.x depends on in_app_purchase_android ^0.5.0, so a normal flutter pub upgrade picks up 0.5.5 if your Flutter version allows it. 0.5.0 needs Flutter 3.38 or later, and every release from 0.5.0+1 onward needs Flutter 3.44 or later.
RevenueCat moved to Billing Library 8 in purchases_flutter 9.0.0, released in July 2025. If you use RevenueCat, the main job is getting onto a current major version and reading its migration notes.
Step 1: find the version you actually ship
Don't guess from pubspec.yaml. Check the resolved dependencies and the Gradle graph:
flutter pub deps --style=compact | grep -E "in_app_purchase|purchases_flutter"
cd android
./gradlew :app:dependencies --configuration releaseRuntimeClasspath \
| grep "com.android.billingclient"If the Gradle output shows com.android.billingclient:billing:7.x, you need to upgrade. If you already upgraded and Play Console still shows the warning, Google's FAQ says to check that the merged AndroidManifest.xml still contains com.google.android.play.billingclient.version and that manifest merging hasn't removed it.
Step 2: upgrade the plugin
In pubspec.yaml:
dependencies:
in_app_purchase: ^3.3.1
# Only if you import Android-specific APIs such as
# GooglePlayPurchaseParam or InAppPurchaseAndroidPlatformAddition:
in_app_purchase_android: ^0.5.5Then:
flutter upgrade
flutter pub upgrade in_app_purchase in_app_purchase_android
flutter pub deps --style=compact | grep in_app_purchaseStep 3: replace purchase history queries
Billing Library 8 removed queryPurchaseHistory(), and the plugin's 0.5.0 release removed its Dart wrapper. If your restore or "already purchased" logic called it, that code won't compile after the upgrade:
// in_app_purchase_android 0.4.x: removed in 0.5.0
final history = await billingClient.queryPurchaseHistory(ProductType.subs);Use the purchases the user owns now instead:
import 'package:in_app_purchase/in_app_purchase.dart';
import 'package:in_app_purchase_android/in_app_purchase_android.dart';
Future<List<GooglePlayPurchaseDetails>> ownedGooglePlayPurchases() async {
final addition = InAppPurchase.instance
.getPlatformAddition<InAppPurchaseAndroidPlatformAddition>();
final response = await addition.queryPastPurchases();
if (response.error != null) {
throw Exception('Play query failed: ${response.error!.message}');
}
return response.pastPurchases;
}Despite its name, queryPastPurchases() calls queryPurchases for one-time products and subscriptions. It returns what the user owns now, not a full history. Google's query purchase history guidance is clear about the replacements: use queryPurchasesAsync for purchases you still need to process, use the Voided Purchases API on your server for refunds and cancellations, and keep purchase history on your own backend.
This is the same principle as on iOS: the server decides who has access, not the device. I covered the Apple side in Flutter subscriptions with StoreKit and webhooks. If your Android backend doesn't record purchases yet, this upgrade is a good time to add it.
Step 4: handle the new billingUnavailable response
in_app_purchase_android 0.5.5 moves to Billing Library 9.1.0, which brings two changes you should know about:
- minSdk 23. The plugin now requires Android 6.0. Flutter's own default minimum is already 24, so this only affects apps that lowered
minSdkby hand. - New response code. When the system blocks the Play Store app (for example in an OEM kids mode), Billing Library 9 returns
BILLING_UNAVAILABLEwhere it used to returnERROR(release notes).
In the plugin, a failed purchase arrives as an IAPError whose source is google_play and whose message is the response code as text. If you branch on that text, add the new case:
bool isPlayBillingUnavailable(IAPError? error) =>
error != null &&
error.source == 'google_play' &&
error.message.contains('billingUnavailable');
Future<void> buy(ProductDetails product) async {
final iap = InAppPurchase.instance;
if (!await iap.isAvailable()) {
showStoreUnavailableMessage();
return;
}
final started = await iap.buyNonConsumable(
purchaseParam: PurchaseParam(productDetails: product),
);
if (!started) showStoreUnavailableMessage();
}buyNonConsumable (also used for subscriptions) returns false when Play doesn't start the purchase flow, so check the return value as well as the purchase stream. Show a calm message rather than a retry loop.
Step 5: target API 36 in Gradle
Flutter 3.35 and later already set flutter.targetSdkVersion to 36 (source). Check android/app/build.gradle.kts for a hard-coded value:
android {
compileSdk = flutter.compileSdkVersion
defaultConfig {
minSdk = flutter.minSdkVersion
// Use Flutter's default (36 on Flutter 3.35+) or set 36 explicitly.
targetSdk = flutter.targetSdkVersion
}
}Targeting API 36 turns on some Android 16 behaviour changes that Flutter apps notice:
- Edge-to-edge can't be disabled.
windowOptOutEdgeToEdgeEnforcementis ignored, so check that headers and bottom bars respect system insets (SafeArea). - Predictive back is on by default on Android 16 devices, and
onBackPressedis no longer called. Test any screen that intercepts back withPopScope, plus native code that overridesonBackPressed. - Orientation locks are ignored on large screens. On displays with a smallest width of 600dp or more, portrait-only apps fill the screen in landscape too. Check tablet and foldable layouts.
Pre-upload checklist
Upload to an internal testing track first and test with license testers:
- The Gradle graph shows
com.android.billingclient:billing:8.xor9.x, not7.x. targetSdkresolves to 36 in the built bundle.- Products and subscription offers load, and
notFoundIDsin the product response is empty. - A new subscription purchase completes and is acknowledged. Google refunds purchases that aren't acknowledged within three days.
- Restore on a fresh install finds an active subscription.
- Your server still receives and verifies the purchase token.
- An upgrade or downgrade between subscription plans works, if you offer one.
- The "store unavailable" message shows instead of a crash when Play is missing or blocked.
- Screens look right edge-to-edge and back navigation behaves on an Android 16 device or emulator.
- Play Console's Policy status page no longer shows the Billing Library warning after upload.
Frequently asked questions
Is Play Billing Library 8 enough, or do I need 9?
Version 8 is enough for this deadline. It's accepted for new apps and updates until 31 August 2027. Moving straight to 9 (plugin 0.5.5) saves you another upgrade next year if your Flutter version allows it.
My app doesn't sell anything. Does the billing deadline apply?
No. Google's FAQ says the Billing Library only appears in APKs that request the com.android.vending.BILLING permission. The target API 36 requirement still applies to every app update.
What happens if I upload after 1 November with Billing Library 7?
Play Console won't accept the update until it uses a supported version. Your existing release stays live, and users who already have it can keep using it.
Can I still request an extension?
The extension window itself ends on 1 November 2026, so there's nothing left to extend to. Treat the upgrade as due now.
Sources
- Target API level requirements for Google Play apps (Google)
- Google Play Billing Library version deprecation FAQ (Google, 2026-10-20)
- Google Play Billing Library release notes (Google)
- Migrate to Google Play Billing Library 9 from versions 7 or 8 (Google)
- Integrate the Google Play Billing Library into your app (Google)
- Query purchase history (Google)
- Behaviour changes: apps targeting Android 16 (Google)
- in_app_purchase_android changelog (pub.dev, 2026-10-19)
- in_app_purchase (pub.dev, 2026-09-18)
- RevenueCat purchases-flutter changelog (GitHub)
- Flutter Gradle plugin defaults (FlutterExtension.kt, Flutter 3.47.7) (GitHub)
Keep reading
- Flutter UIScene Migration: Fix iOS 27 Launch CrashesFlutter apps built with Xcode 27 quit at launch without the UIScene lifecycle. Here's how to migrate a custom AppDelegate, Info.plist and plugins.
- StoreKit 2 Subscriptions in Flutter: Restore Purchases and Server NotificationsStoreKit 2 subscriptions in a Flutter app: server verification, App Store Server Notifications V2, restore purchases and one access record across gateways.
- Taking Over an Existing Flutter App and PHP Backend: My PlaybookHow I take over a live Flutter app with a legacy PHP backend: a first-week checklist, reproducing bugs, PHP security fixes and a safe move to token auth.
Comments
Share a question or note about this article.
Loading comments…