purchases-android icon indicating copy to clipboard operation
purchases-android copied to clipboard

Restore Purchase not working after updating to 9.1.1 in Android

Open mehul4795 opened this issue 4 months ago • 28 comments

I have been using the RevenutCat SDK for more than 2 years in my Android app. After upgrading the RevenueCat Android SDK from 8.x.x to 9.1.1, the restore purchase functionality has stoped working in my app and many of the users are complaining regarding the same. After upgrading the SDK version, I haven't made any code changes in the Restore Purchase functionality.

You can find the code that I am using below.

fun restorePurchase(): Flow<PlanPurchaseState> {
        return callbackFlow {
            trySend(PlanPurchaseState.PurchaseInProgress)

            Purchases.sharedInstance.restorePurchasesWith(onError = { error: PurchasesError ->
                trySend(PlanPurchaseState.PurchaseError(error.message))
                close()
            }, onSuccess = { customerInfo ->
                val entitlementInfo =
                    customerInfo.entitlements.all.values.firstOrNull { it.isActive }

                if (entitlementInfo == null) { // No previous purchase found
                    trySend(PlanPurchaseState.PurchaseError(context.getString(R.string.premium_plans_error_no_earlier_purchases)))
                } else {          
                    trySend(PlanPurchaseState.PurchaseSuccess(currentActivePlan))
                }

                close()
            })

            // Waits for the function to get finished and is only called after the close() function is invoked
            awaitClose()
        }
    }

When the above code is executed, I am getting entitlementInfo null every time even though a purchase was made earlier. Any help will be appreciated.

  1. Platform: Android
  2. SDK version: 9.1.1
  3. OS version: Android 16
  4. Android Studio version: Narhwal 2025.1.1 Patch 1
  5. How widespread is the issue. 100% devices affected.

mehul4795 avatar Jul 25 '25 12:07 mehul4795

👀 We've just linked this issue to our internal tracker and notified the team. Thank you for reporting, we're checking this out!

RCGitBot avatar Jul 25 '25 12:07 RCGitBot

Hi @mehul4795,

Are you perhaps using anonymous users and expired transactions or consumed one-time products?

There is a limitation in Billing Library 8 which our SDK v9 integrates:

Play Billing Library 8 removed functionality to query expired subscriptions or consumed one-time products. This means that, for users migrating from a non-RevenueCat implementation of the Play Billing Library, the SDK will not be able to send purchase information from these purchases. We can still ingest historical data from these purchases through a backend historical import. See docs. This doesn't affect developers that have all transactions in RevenueCat, which is true for the vast majority.

https://github.com/RevenueCat/purchases-android/releases/tag/9.0.0

Is this is your case your best option right now is downgrading to an SDK version v8 which uses Billing Library 7.

If this is not your case and you're still having problems, feel free to email me mark (at) revenuecat dot com with your project details and I'll take a look.

MarkVillacampa avatar Jul 26 '25 16:07 MarkVillacampa

I am using consumed one time purchase using anonymous users. But as you quoted

This doesn't affect developers that have all transactions in RevenueCat, which is true for the vast majority.

We have all of our transactions in RevenueCat. So as per my understanding, my app should not be affected by this change 🤔

mehul4795 avatar Jul 26 '25 18:07 mehul4795

+1 Seeing the exact same issue. Made a huge mistake by upgrading to V9 as we also have all our transactions in RC, so I thought we were not affected by the limitations as mentioned above. Now all anonymous users cannot restore their consumed one time purchases.

fanwgwg avatar Jul 28 '25 15:07 fanwgwg

Hey @fanwgwg,

Sorry to hear you're affected by this!

As Mark mentioned in his previous comment, the best option right now is to downgrade to SDK version v8, which uses Billing Library 7.

mawr92 avatar Jul 29 '25 15:07 mawr92

@mawr92 Thanks for the reply. Will the fix be included in the upcoming updates? If yes, any eta for the same?

mehul4795 avatar Jul 30 '25 07:07 mehul4795

@mehul4795, yes, the team is working on a workaround for this! I don't have an ETA at the moment, but we will keep you posted!

mawr92 avatar Aug 01 '25 17:08 mawr92

@mawr92 Thank you 👍

mehul4795 avatar Aug 02 '25 10:08 mehul4795

@mawr92 I just saw that an update with version 9.3.0 was published. Does it fix the issue?

mehul4795 avatar Aug 05 '25 14:08 mehul4795

@mawr92 any updates on this issue? Judging by the v9.3.0 change logs it doesn't seem to be addressed.

ssand avatar Aug 17 '25 14:08 ssand

Hey @mehul4795, @ssand,

Thank you for following up and for your patience! 🙌 The team is still working on a workaround for this issue. We will let you know as soon as this gets released!

mawr92 avatar Aug 18 '25 18:08 mawr92

Hi everyone! I wanted to give a summary and update on this issue.

Currently on major 9 of the RevenueCat Android SDK, which uses Google's Billing Client 8, it's impossible to obtain any consumed one time purchases from Google since they removed the methods to query for historical purchases.

Unfortunately, this means that the mechanisms we used to use to be able to restore these consumed purchases don't work anymore, and we can't securely know that the user purchased one of those products before. For developers using their own account system, they should be able to rely on that to recover purchases, by just having the customer log in. For those using anonymous users however, the situation is trickier since there is no such recovery system.

For these cases, we're currently testing with some backup functionality that Google provides, like Auto Backup and Block store, and we have a working prototype in the works. This option is not perfect however, since these backups don't work that well when a customer uses multiple devices simultaneously, but should limit the spread of this issue, by being able to restore a backup of the device in a different device, effectively recovering the anonymous user ids in some scenarios which would in turn recover these purchases. We plan to launch new versions in both major 8 and 9 with these changes.

We're still researching for other options we could use to recover these consumed purchases. In the meantime, we would suggest for all developers to:

  1. Review your product configuration and make sure that any products that should be purchased only once per customer are configured as non-consumables in the RevenueCat dashboard. This would avoid us from consuming these purchases, completely avoiding this problem. This won't fix purchases that have already been consumed though.
Image
  1. If you are in this situation, that is, you use anonymous users and have products consumed that need to be recovered, please stay in major 8 of our SDK, which still allows to query for historical purchases. Do make sure to follow step 1 to avoid this issue from spreading more in the future though.

Thank you for your patience while we try to figure out the best path forward.

tonidero avatar Aug 19 '25 07:08 tonidero

I hava same issue when updating to Revenuecat SDK 9.x.

  1. I use revenuecat SDK since 2022 year.
  2. I had config the product type as non-consumable.
Image

But the user can't restore the pruchase on SDK 9.X still even thought the user pruchase the product on 2025/7/12 by Revenuecat SDK

What should I do to avoid this issue from spreading more in the future though? I would like to provider some purchase order info if you want.

hanks-zyh avatar Aug 19 '25 07:08 hanks-zyh

Hi @hanks-zyh, could you contact us with your app and product identifier? We would like to check the configuration for your products.

One theory is that, we run a migration on August 6th that, by checking if the product was attached to a lifetime package and behaved like a non-consumable, converted some products configured as consumables to non-consumables, in order to fix this issue for these products. We believe your product might have been part of this script run. If this is the case, all purchases done before Aug 6th would have been consumed, and any after would remain unconsumed.

tonidero avatar Aug 19 '25 08:08 tonidero

Hi @hanks-zyh, could you contact us with your app and product identifier? We would like to check the configuration for your products.

One theory is that, we run a migration on August 6th that, by checking if the product was attached to a lifetime package and behaved like a non-consumable, converted some products configured as consumables to non-consumables, in order to fix this issue for these products. We believe your product might have been part of this script run. If this is the case, all purchases done before Aug 6th would have been consumed, and any after would remain unconsumed.

Thanks for reply, I hava send the infomation of product in RevenueCat Help center just now, please check it.

hanks-zyh avatar Aug 19 '25 08:08 hanks-zyh

Hi @tonidero, just a heads up, I am also experiencing the same issue as @hanks-zyh

Review your product configuration and make sure that any products that should be purchased only once per customer are configured as non-consumables in the RevenueCat dashboard. This would avoid us from consuming these purchases, completely avoiding this problem. This won't fix purchases that have already been consumed though.

I’m using anonymous users, and my product has been set as non-consumable for as long as I can remember. However, some users are still unable to restore their lifetime purchase.

I will downgrade for now.

Let me know if you need more info.

wes-nz avatar Aug 24 '25 21:08 wes-nz

Hello,

it's been more than a month since the issue was created. We are getting a lot of requests of the Restore Purchase not working in our production apps. I hope the team is taking this issue seriously and we can get the fix as soon as possible.

Thanks

mehul4795 avatar Sep 08 '25 11:09 mehul4795

Hi everyone, we wanted to give another update on this issue after the previous update.

We found there is no good way to recover these purchases with Billing client 8. Currently the only way to lower the number of times users will run into issues restoring is by being able to recover the RevenueCat anonymous user id used in previous installations. In order to do that, we recommend that you make sure you have configured backups correctly for your apps. We have written some documentation on the topic here.

So right now, for everyone affected by this issue we would recommend to:

  • Stay in major 8 of our SDK which uses Billing client 7. We will continue to bring important fixes to the major 8 until Billing client 8 is mandatory next year.
  • Make sure backups for your app work and include the RevenueCat Shared Preferences file as mentioned in the docs

For users that have a backup performed in this situation and have a device compatible with backups, they would recover the RevenueCat anonymous user id, effectively recovering all their purchases as well.

Note that this solution won't fix all cases, since there are situations and devices where backups won't work. For those, the only solution right now is a manual approach by asking them for proof of purchase to obtain the order Id of their purchase (Starting in GPA.....), then search the user for that orderId in the RevenueCat dashboard, which will allow you to find the user that did that purchase, then, from the RevenueCat dashboard itself, transfer those purchases to the new anonymous user the user is using. So for this manual approach you would need both the Order ID of the purchase and the anonymous user ID that the customer is using.

Sorry for the inconvenience with this. We will continue working on this in search of more automatic ways of dealing with this and seeing if we can get a way to recover these purchases from Google.

tonidero avatar Sep 10 '25 11:09 tonidero

@tonidero Thanks for providing possible solutions. We will try to implement them for now in our apps. Hope your team finds the solution and the process gets automated. Anyway, thanks again :)

mehul4795 avatar Sep 11 '25 06:09 mehul4795

@tonidero Could RevenueCatSDK expose an API that accepts an order number (starting with GPA...) to transfer the purchase entitlement to a new anonymous user? like Purchase.transformOrder(GPA_order_info) So we could then build an in-app submission form: users who need to restore their purchase could look up their order history, enter the GPA number, and submit the form to move the entitlement.

hanks-zyh avatar Sep 17 '25 06:09 hanks-zyh

@tonidero Thanks for the detailed explanation earlier. I have a follow-up question regarding product configuration.

Historically, our one-time products were created before RevenueCat introduced the consumable vs. non-consumable option for Google Play, so by default all of our older purchases were marked as consumed.

Now that I’ve run into this restore issue, I understand that I should reconfigure these products as non-consumable in the RevenueCat dashboard. Before making this change, are there any implications I should be aware of, or is it safe to update the product type directly?

Specifically, what happens in the following cases after changing the product type:

  • A user tries to restore a purchase that was already consumed under the old (consumable) configuration. Will the entitlement still be unrecoverable, or will the new non-consumable setting allow RevenueCat to restore it?
  • A user’s earlier (consumed) purchase was later refunded through Google Play, and the same user then buys the new non-consumable version of the product. Will RevenueCat correctly grant the entitlement in this scenario, or could the old refund status interfere?

Thanks in advance for clarifying!

fanwgwg avatar Sep 17 '25 06:09 fanwgwg

Could RevenueCatSDK expose an API that accepts an order number (starting with GPA...) to transfer the purchase entitlement to a new anonymous user?

@hanks-zyh We thought about this option, but it would mean the order ID would become "secret information" that would allow users to redeem purchases. Additionally, we would need to add a new endpoint using the public API key to perform the transfer. We consider this to be a security loophole, (specially to be performed in the SDK) so we didn't implement this option. If you still want to allow users to redeem the purchases using the order ID, accepting the risk that there could be users sharing the order ID, you could potentially implement a server-side approach using our API endpoint to obtain a purchase based on the order ID and then the endpoint to transfer purchases to a new user

tonidero avatar Sep 17 '25 07:09 tonidero

are there any implications I should be aware of, or is it safe to update the product type directly?

@fanwgwg, changing from consumable to non-consumable will only have the implication that the purchases of that product won't be consumed anymore by RevenueCat, remaining "active" in the Google account that made the purchase. This does mean that that Google account won't be able to purchase that same product again, Google will disallow buying the same product multiple times until it's been consumed. So it should be safe if your intention is to make the product to be only purchasable once per Google account.

A user tries to restore a purchase that was already consumed under the old (consumable) configuration. Will the entitlement still be unrecoverable, or will the new non-consumable setting allow RevenueCat to restore it?

Unfortunately it will still be unrecoverable. Once consumed, you can't "unconsume" it, and the purchase won't be able to be obtained by using Google's Billing client, and due to this, we won't be able to restore it. This will however allow purchases performed AFTER changing the setting to non-consumable to be restored so we still highly recommend you to do this if your intention is for these products to only be purchased once per Google account and be able to be restored just based on the Google account.

A user’s earlier (consumed) purchase was later refunded through Google Play, and the same user then buys the new non-consumable version of the product. Will RevenueCat correctly grant the entitlement in this scenario, or could the old refund status interfere?

Yes, in this scenario, RevenueCat would be able to restore the new non-consumed purchase. This does mean that the user that made the initial refunded purchase would lose access unless they buy again.

tonidero avatar Sep 17 '25 07:09 tonidero

@tonidero Thanks for the quick reply. A quick follow up on this:

A user tries to restore a purchase that was already consumed under the old (consumable) configuration. Will the entitlement still be unrecoverable, or will the new non-consumable setting allow RevenueCat to restore it?

We’ll be staying on SDK v8 for now, but plan to switch our products in RC from consumable → non-consumable so future purchases are restorable when we upgrade to V9.

For existing users with already consumed purchases, will restores still work on v8 (since Billing Client 7 can query history) after we make the change to non-consumable?

Just want to be sure we don’t break restores for existing customers when making the change.

fanwgwg avatar Sep 17 '25 07:09 fanwgwg

@fanwgwg, yes, changing from consumable to non-consumable will still allow purchases to be restored in SDK v8 (whether they were previously consumed or not). As I said, this would only mean that moving forward, users will only be able to purchase that product once per Google account (before, users were able to purchase multiple times).

So basically, after changing to non-consumable, some users might start seeing an error message from Google saying they already own that product if they try to purchase after uninstalling/reinstalling without restoring purchases. But then, they can just restore purchases and entitlements would be transferred to the new account.

tonidero avatar Sep 17 '25 07:09 tonidero

Reopening issue. It was closed due to a failing automation. Sorry about that.

vegaro avatar Sep 22 '25 07:09 vegaro

Hi everyone! I have an update on this issue. We've found a workaround for this issue, which consists on communicating with the Google Play service directly instead of through the Billing library by using an AIDL file.

It's been working great in our internal tests, and now we've launched a new beta release 9.16.0-beta02 with those changes in case you want to try it out. It would be greatly appreciated 🙏

Edit: Please use 9.16.0-beta02 instead of beta01 since it contains a fix to actually use the AIDL

tonidero avatar Dec 05 '25 14:12 tonidero

Hi again everyone! We've just released 9.16.0 with the change mentioned above which should fix this issue for the foreseeable future. Please let us know if you still have this issue after updating or if you have any other feedback! We will release this fix in the other SDKs (RN, Flutter, KMP, Capacitor, Cordova, Unity) soon.

tonidero avatar Dec 15 '25 15:12 tonidero