Skip to main content
Question

Non-consumable (one-time) product can't be restored across devices - Google Play returns ITEM_ALREADY_OWNED but restorePurchases() returns empty

  • September 7, 2026
  • 1 reply
  • 12 views

HanyAbbas
Forum|alt.badge.img

Hi everyone,

I'm running into the known Billing Library 8 limitation around consumed one-time products, and want to confirm the recommended path forward, plus flag one detail that seems inconsistent.

Setup:

  • Flutter app, no login system (anonymous app_user_ids)
  • purchases_flutter 10.11.0 / purchases-android (via hybrid common)
  • Product sfl_pro is a non-consumable one-time product (not a subscription), correctly configured as non-consumable in the RevenueCat dashboard
  • Transfer Purchases setting is enabled correctly
  • Testing via Open Testing track with a licensed tester account (sandbox/test purchases, not production charges)

Issue:

  1. User purchases sfl_pro on Device A → works, entitlement active.
  2. On Device B (same Google account, same license tester, confirmed identical "preferred billing account" identifier in logcat), restorePurchases() returns successfully but with an empty result:
   activeEntitlements: [],
nonSubscriptionTransactions: [],
  1. However, if the user tries to purchase sfl_pro again from Device B, Google Play immediately returns "You already own this item" (ITEM_ALREADY_OWNED), confirming Google Play does consider the purchase owned by this account.

So restorePurchases() can't see the purchase, but Google Play's purchase flow clearly can.

Questions:

  1. Is Order ID transfer (via dashboard or Restore by Order ID API) really the only supported path for this scenario given the Billing Library 8 constraints, or is there a more automated way to sync this (e.g., triggering a sync when we detect ITEM_ALREADY_OWNED during a purchase attempt)?
  2. Is there a recommended way to catch ITEM_ALREADY_OWNED in the Flutter SDK specifically and automatically kick off a recovery flow (grant entitlement, or prompt for Order ID) without the user needing to dig up their Order ID manually?
  3. Product was never configured as consumable at any point (verified in dashboard history) - is there any other known cause for restorePurchases() returning empty here, or is this purely a byproduct of BC8's removed query capability for one-time products with anonymous users?

Note: All purchases described above (Device A purchase, Device B restore attempt, and repurchase attempt) were made using a Google Play licensed tester account, so these are sandbox/test transactions, not real production purchases.

 

Any guidance appreciated - want to make sure I'm not missing a supported automatic recovery path before building a manual Order ID fallback UI.

1 reply

HanyAbbas
Forum|alt.badge.img
  • Author
  • New Member
  • September 8, 2026

Update / Resolution:

Just wanted to follow up in case this helps anyone else hitting the same issue.

Root cause turned out to be related to the device having multiple Google accounts (7 accounts) added, even though only one was set as the "active/default" account visible in the Play Store UI. Logcat showed a different "preferred billing account" identifier (Billing preferred account via installer for <package>: [...]) between Device A (original purchase) and Device B (restore attempt), despite both showing the same visible/default Google account.

After removing the extra Google accounts on Device B (down to just the one used for the original purchase) and reinstalling the app, the billing identifier matched Device A's, and restorePurchases() / the ITEM_ALREADY_OWNED flow started working correctly.

I'm honestly not 100% sure the multi-account cleanup is the actual root cause versus a coincidental fix (e.g., a background sync/cache refresh on Google's or RevenueCat's side around the same time), since the visibly "active" account never changed. But documenting the steps that immediately preceded the fix in case the pattern helps someone else debugging similar restore issues with multiple Google accounts on device.

For anyone hitting the same "restorePurchases() returns empty but ITEM_ALREADY_OWNED fires on purchase" issue with a non-consumable/one-time product and no login system - this does line up with the known Billing Library 8 limitation around querying consumed one-time products for anonymous users, so the Order ID transfer (or manual entitlement grant) remains the documented fallback if account cleanup doesn't resolve it.