IAP Android: detect non-consumable purchase restored (Android equivalent of Product.appleProductIsRestored)

In my game, for a non-consumable product, we intend to give the user some secondary benefit when they purchase it for the first time but not on restore. When the restore calls ProcessPurchase and passes in the PurchaseEventArgs, I saw that the Product sent with it has a flag appleProductIsRestored, which I have been using for iOS to differentiate between a new purchase vs. restore. Is there an equivalent flag or other method for Android that I can use to differentiate between a new purchase and a restore?

Hi @JHSV !
Thank you for your question!
You are absolutely right that on iOS, Unity IAP in versions 4.x sets the appleProductIsRestored flag on the Product passed in PurchaseEventArgs when using StoreKit1.
However, starting with IAP version 5.x and StoreKit 2, Apple handles purchases and restorations differently. Unity IAP no longer sets the appleProductIsRestored flag. Instead, all successful transactions, whether new or restored, are processed the same way through ProcessPurchase.

Unfortunately, for Android (Google Play Billing), there is no built-in equivalent flag like appleProductIsRestored. Google Play does not explicitly distinguish a restored purchase from a new one when returning data during initialization. All owned products come back the same way when queried from the purchase history or inventory.

Here are some ideas/approaches to differentiate initial purchase vs restore on Android:

1. Track locally or on your backend

  • When the user makes a purchase, record that product ID as “purchased” either:
    In local persistent storage (e.g. PlayerPrefs, Application.persistentDataPath, etc.)
    Or in your backend service

  • When ProcessPurchase is called, check if you have already marked this product ID as purchased.
    If not, treat it as a new purchase (grant the bonus).
    If yes, treat it as a restore (skip the bonus).

2. Check purchase timing (if possible)

If your backend tracks purchase timestamps, you can compare the current purchase time with the recorded one to determine whether this is a first-time purchase or a restore.

3. Use Google’s purchaseToken

Each purchase has a unique purchaseToken. When a non-consumable is restored, Google still returns the original purchaseToken (unless it has been replaced).

If you track the tokens of all purchases your user has made:

  • A new purchase will have a new token.
  • A restored purchase will reuse the existing token.

This requires persistent tracking and syncing of tokens though.

Here is a sample approach you can use in ProcessPurchase if you e.g decide to use first suggestion with player prefs:

public PurchaseProcessingResult ProcessPurchase(PurchaseEventArgs args)
{
    string productId = args.purchasedProduct.definition.id;

    if (!IsPreviouslyGranted(productId))
    {
        // First-time grant
        GrantProduct(productId);
        GrantBonus(productId);
        MarkAsGranted(productId);
    }
    else
    {
        // Already granted previously
        GrantProduct(productId); // but no bonus
    }

    return PurchaseProcessingResult.Complete;
}

bool IsPreviouslyGranted(string productId)
{
    return PlayerPrefs.GetInt("Granted_" + productId, 0) == 1;
}

void MarkAsGranted(string productId)
{
    PlayerPrefs.SetInt("Granted_" + productId, 1);
    PlayerPrefs.Save();
}

We hope this helps!

Thank you for your reply. We’ve been kind of doing the first approach already, so I was wondering if there was something entirely on Google or Apple’s end that can distinguish them without having to manage it in our code, but that doesn’t seem to be the case. It seems to me that in IAP 5.x, if we can’t use appleProductIsRestored, we would have to use a similar check against persistent state for iOS as well, right?

When you buy non_consumable product, the ProcessPurchase is called the first time, in product receipt (which is google receipt) there is acknowledged property that set to false. Obviously its the first purchase so its not acknowledge yet. if its false then give first time reward, and after deleting the app and gets restored, the ProcessPurchase will be called automatically, then if you see acknowledged property, it is set to true. I distinguish first purchase and restore with this

Thank you for your reply. Does this only work on Android though?

Hey @JHSV and @kimsungjinDC ,
Those are great points!

@JHSV, you are right → within IAP 5.x and StoreKit 2, since appleProductIsRestored is gone, we do need to track first-time purchases vs restores ourselves on iOS now too, just like on Android.

@kimsungjinDC → thanks for the tip! The ‘acknowledged’ flag in the Google Play receipt is definitely helpful.
If it is false, it is likely a brand new purchase; and if it is true, it is probably a restore. But to your point, that is Android-only, as far as I know so far iOS receipts do not have anything similar.

So for now, tracking state yourself (locally or with a backend) is still the most reliable cross-platform solution.

Thanks again for the insights!