Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. Dismiss Notice

IAP Restore - Android

Discussion in 'Unity IAP' started by a_deo_et_rege, Jun 20, 2018.

  1. JeffDUnity3D

    JeffDUnity3D

    Unity Technologies

    Joined:
    May 2, 2017
    Posts:
    14,446
  2. tmiller_vb

    tmiller_vb

    Joined:
    Dec 6, 2012
    Posts:
    19
    @JeffDUnity3D Switching to UnityIAP 1.19 did not seem to fix the issue at all, unfortunately. I was previously on 1.22.

    Is this how it will work for end users as well, or just for testers?
     
  3. JeffDUnity3D

    JeffDUnity3D

    Unity Technologies

    Joined:
    May 2, 2017
    Posts:
    14,446
    All users, for now.
     
  4. Altair10201

    Altair10201

    Joined:
    Dec 21, 2018
    Posts:
    1
    Hi, I'm just asking about something that makes me confused.
    As I know, Android Restore for non-consumable products processed by UnityIAP, when OnIntialized() called.
    But, you mentioned that "Restore only happens upon actual uninstall/reinstall". It makes me confused...

    Can you tell me When Android Restore happen? uninstall/reinstall, or OnIntialize()? (P.S. sorry for my bad English)
     
  5. JeffDUnity3D

    JeffDUnity3D

    Unity Technologies

    Joined:
    May 2, 2017
    Posts:
    14,446
    During install/reinstall, ProcessPurchase will be called for each restored item during IAP initialization. Use Debug.Log to confirm, as in the Sample IAP project.
     
  6. YasinJavaid_

    YasinJavaid_

    Joined:
    Jul 15, 2016
    Posts:
    3
  7. JeffDUnity3D

    JeffDUnity3D

    Unity Technologies

    Joined:
    May 2, 2017
    Posts:
    14,446
  8. Funtyx

    Funtyx

    Joined:
    May 3, 2017
    Posts:
    29
    Tell me how to restore a purchase on an android? After uninstalling the application and reinstalling it, I have certain data in the PlayerPrefs that will allow me to unlock the levels. In my game there is one non-consumable purchase that unlocks all levels. I put a script in the first scene in order to initialize the PlayerPrefs data but nothing works.

    Code (CSharp):
    1. #if UNITY_PURCHASING || UNITY_UNIFIED_IAP
    2. using UnityEngine.Events;
    3. using UnityEngine.UI;
    4. using System.IO;
    5. using System.Collections.Generic;
    6.  
    7. namespace UnityEngine.Purchasing
    8. {
    9.     public class IapRestore : MonoBehaviour
    10.     {
    11.         public UnityEvent RestoreSuccess;
    12.         public UnityEvent RestoreFail;
    13.  
    14.         public void RestorePurchase()
    15.         {
    16.             if (Application.platform == RuntimePlatform.WSAPlayerX86 ||
    17.                 Application.platform == RuntimePlatform.WSAPlayerX64 ||
    18.                 Application.platform == RuntimePlatform.WSAPlayerARM)
    19.             {
    20.                 CodelessIAPStoreListener.Instance.ExtensionProvider.GetExtension<IMicrosoftExtensions>()
    21.                     .RestoreTransactions();
    22.             }
    23.  
    24.             else if (Application.platform == RuntimePlatform.IPhonePlayer ||
    25.                 Application.platform == RuntimePlatform.OSXPlayer ||
    26.                 Application.platform == RuntimePlatform.tvOS)
    27.             {
    28.                 CodelessIAPStoreListener.Instance.ExtensionProvider.GetExtension<IAppleExtensions>()
    29.                     .RestoreTransactions(OnTransactionsRestored);
    30.             }
    31.  
    32.             else if (Application.platform == RuntimePlatform.Android &&
    33.                 StandardPurchasingModule.Instance().appStore == AppStore.SamsungApps)
    34.             {
    35.                 CodelessIAPStoreListener.Instance.ExtensionProvider.GetExtension<ISamsungAppsExtensions>()
    36.                     .RestoreTransactions(OnTransactionsRestored);
    37.             }
    38.  
    39.             else if(Application.platform == RuntimePlatform.Android)
    40.             {
    41.                 if (CodelessIAPStoreListener.Instance.StoreController != null
    42.                     && CodelessIAPStoreListener.Instance.StoreController.products.WithID("com.company.fullversion") != null
    43.                     && CodelessIAPStoreListener.Instance.StoreController.products.WithID("com.company.fullversion").hasReceipt)
    44.                 {
    45.                     RestoreSuccess.Invoke();
    46.                 }
    47.                 else
    48.                 {
    49.                     RestoreFail.Invoke();
    50.                 }
    51.  
    52.                 //if (CodelessIAPStoreListener.Instance.StoreController?.products.WithID("com.company.fullversion")?.hasReceipt)
    53.  
    54.                 //{
    55.                 //    RestoreSuccess.Invoke();
    56.                 //}
    57.  
    58.                 //else
    59.                 //{
    60.                 //    RestoreFail.Invoke();
    61.                 //}
    62.             }
    63.  
    64.             else
    65.             {
    66.                 Debug.LogWarning(Application.platform.ToString() + " is not a supported platform for the Codeless IAP restore button");
    67.             }
    68.         }
    69.  
    70.         void OnTransactionsRestored(bool success_apple)
    71.         {
    72.             if (success_apple)
    73.             {
    74.                 RestoreSuccess.Invoke();
    75.             }
    76.             else
    77.             {
    78.                 RestoreFail.Invoke();
    79.             }
    80.         }
    81.     }
    82. }
    83. #endif
     
  9. JeffDUnity3D

    JeffDUnity3D

    Unity Technologies

    Joined:
    May 2, 2017
    Posts:
    14,446
    Restore happens automatically on Android. If they already own a product like a Subscription, they will receive a ProcessPurchase upon IAP initialization. You don't need to add any code, your app will act just like the user auto-clicked your Buy button.
     
  10. Kupferrot

    Kupferrot

    Joined:
    Jan 19, 2014
    Posts:
    47
    Sorry for bringing up this old thread. But what you are writing there does not seem to be the case. I use the Codeless IAP and I checked the "automatically initialize unity purchasing" in the IAP Catalog.
    For the Code I only use:
    Code (CSharp):
    1. using System;
    2. using System.Collections;
    3. using System.Collections.Generic;
    4. using UnityEngine;
    5. using UnityEngine.Purchasing;
    6.  
    7. public class IAPManager : MonoBehaviour
    8. {
    9.     private string RemoveAds = "...";
    10.  
    11.     public void OnPurchaseComplete(Product product)
    12.     {
    13.         if(product.definition.id == RemoveAds)
    14.         {
    15.             PlayerPrefs.SetInt("Ads", 0); //0=false means no ads displayed anymore
    16.             Debug.Log("Ads have been removed");
    17.         }
    18.     }
    19.  
    20.     public void OnPurchaseFailed(Product product, PurchaseFailureReason failureReason)
    21.     {
    22.         Debug.Log(product.definition.id + "failed because" + failureReason);
    23.     }
    24.  
    25.  
    26. }
    I released the game to closed testing in Google Play and downloaded it. Pressed the buy button and received my non consumable item.

    I then uninstalled the app from my device and reinstalled it. However the purchase was not automatically restored. If I press the buy button again, it will tell me I already have the Item and then it will restore it. But nothing happens automatically.

    In unity I can see that the initialize is beeing called in the log because the Debug.Log("Initializing UnityPurchasing via Codeless IAP"); is called.


    I store the the information about the purchase in my playerpref which are reset after reinstall (of course). I would expect this automatic process that you are talking about to retrigger the OnPurchaseComplete function.

    I also added an IAP Listener to test if it would work then, but it was also without success
     
    Last edited: May 29, 2021
  11. Kupferrot

    Kupferrot

    Joined:
    Jan 19, 2014
    Posts:
    47
    I found a solution that is working in my case....
    At the beginning of my script I added:
    Code (CSharp):
    1.     public void Start()
    2.     {
    3.         if(CodelessIAPStoreListener.Instance.StoreController.products.WithID(RemoveAds).hasReceipt)
    4.         {
    5.             PlayerPrefs.SetInt("Ads", 0); //0=false means no ads displayed anymore
    6.             Debug.Log("Ads have been removed");
    7.         }
    8.     }
    This way I again receive the bought product after reinstalling. I have no clue why it is necessary because literally everywhere it is stated, that it would happen automatically.
    I also tested if a second, different device with the same google user would receive the purchase in the correct way. This was also working then.

    There is still one thing I don't fully understand though. If I uninstall, reinstall and before I start the app go into flight mode (no internet), with the code above I will still get the playerpref assigned correctly. Does that mean upon the installation, the information regarding the purchase is stored on my device even before I start the app? I was thinking it is sending a request to google or something like that.
     
  12. JeffDUnity3D

    JeffDUnity3D

    Unity Technologies

    Joined:
    May 2, 2017
    Posts:
    14,446
    I would not recommend to use OnPurchaseComplete or OnPurchaseFailed, these are the same names as our internal/scripted callbacks. Your listener needs to be on your first active scene when your game launches. I might recommend scripted IAP instead of Codeless where you won't see these types of issues. Perhaps start with the Sample IAP Project v2 here https://forum.unity.com/threads/sample-iap-project.529555/#post-6950270
     
  13. dormouse

    dormouse

    Joined:
    Mar 1, 2011
    Posts:
    82
    Hey Kupferrot,
    I had exactly the same issue that you got there. but my case is even worse, i added Start() func tthat the beginning of MyIAPManager.cs, but after re-installation, my receipt can not be restored, so my PlayerPrefs can not be set...
    do you know the reason ? you have an IAP listener as well?

    thanks

     
  14. JeffDUnity3D

    JeffDUnity3D

    Unity Technologies

    Joined:
    May 2, 2017
    Posts:
    14,446
    Please share your code. Also, I might recommend that you start with the Sample IAP Project v2 here https://forum.unity.com/threads/sample-iap-project.529555/#post-6950270
     
  15. dormouse

    dormouse

    Joined:
    Mar 1, 2011
    Posts:
    82
    hello Jeff,
    My IAP is simple enough, only one Non-Consumable item can be purchased to enable additional game levels.
    I tried with Sample IAP project v2, here is my code based on Sample V2:

    current issue:
    After a refresh installation (uninstall previous test app) via Internal-test, at the very beginning page of App, i have an Initialization Button calling 'InitializePurchasing' fucn. my UI debug shows:
    "Starting Initialized..."
    "OnInitialized: PASS"
    Validate =UnityEngine.Purchasing.Security IPurchaseReceipt[]
    Valid Receipt for com.xxxxx.yyyyy.purchasepro
    ProcessPurchase com.xxxxx.yyyyy.purchasepro

    It seems i have already successfully bought the item, which is weird that i haven't click on Purchase Button in the second page of app.

    google play store stored my previous bought item somewhere ? normally the test payment will be refund in few minutes.

    And, for the second time of using the same app, the Initialization Button only gives:
    "Starting Initialized..."
    "OnInitialized: PASS"

    Please help, thanks a lot.

    Code (CSharp):
    1. using System.Collections;
    2. using System;
    3. using System.Collections.Generic;
    4. using UnityEngine;
    5. using UnityEngine.UI;
    6. using UnityEngine.Purchasing;
    7. using UnityEngine.Purchasing.Security;
    8.  
    9. public class MyIAPManager : MonoBehaviour, IStoreListener
    10. {
    11.     private static IStoreController m_StoreController;          // The Unity Purchasing system.
    12.     private static IExtensionProvider m_StoreExtensionProvider; // The store-specific Purchasing subsystems.
    13.     private IAppleExtensions m_AppleExtensions;
    14.     private IGooglePlayStoreExtensions m_GoogleExtensions;
    15.  
    16.     //ProductID
    17.     private string myVersionPro = "com.xxxx.yyyyy.purchasepro"; //Google Play Console Non-Con product ID.
    18.     public Text myText;                                         // for Debug use.
    19.     public GameObject panelWelcome;                             //Once purchased, Object to display.
    20.  
    21.  
    22.     public void Awake()
    23.     {
    24.  
    25.     }
    26.     void Start(){
    27.         // If we haven't set up the Unity Purchasing reference
    28.         if (m_StoreController == null)
    29.         {
    30.             // Begin to configure our connection to Purchasing, can use button click instead
    31.             // InitializePurchasing();
    32.         }
    33.  
    34.     }
    35.     public void MyInitialize()
    36.     {
    37.         InitializePurchasing();
    38.     }
    39.  
    40.     public void InitializePurchasing()
    41.     {
    42.         if (IsInitialized())
    43.         {
    44.             return;
    45.         }
    46.  
    47.         var builder = ConfigurationBuilder.Instance(StandardPurchasingModule.Instance());
    48.  
    49.         builder.AddProduct(myVersionPro, ProductType.NonConsumable);
    50.  
    51.         MyDebug("Starting Initialized...");
    52.         UnityPurchasing.Initialize(this, builder);
    53.  
    54.     }
    55.  
    56.     private bool IsInitialized()
    57.     {
    58.         return m_StoreController != null && m_StoreExtensionProvider != null;
    59.     }
    60.  
    61.     public void BuyNonConsumable()
    62.     {
    63.         BuyProductID(myVersionPro);
    64.     }
    65.    
    66.     public void RestorePurchases()
    67.     {
    68.         m_StoreExtensionProvider.GetExtension<IAppleExtensions>().RestoreTransactions(result => {
    69.             if (result)
    70.             {
    71.                 MyDebug("Restore purchases succeeded.");
    72.             }
    73.             else
    74.             {
    75.                 MyDebug("Restore purchases failed.");
    76.             }
    77.          });
    78.     }
    79.  
    80.  
    81.     void BuyProductID(string productId)
    82.     {
    83.         if (IsInitialized())
    84.         {
    85.             UnityEngine.Purchasing.Product product = m_StoreController.products.WithID(productId);
    86.  
    87.             if (product != null && product.availableToPurchase)
    88.             {
    89.                 MyDebug(string.Format("Purchasing product:" + product.definition.id.ToString()));
    90.                 m_StoreController.InitiatePurchase(product);
    91.             }
    92.             else
    93.             {
    94.                 MyDebug("BuyProductID: FAIL. Not purchasing product, either is not found or is not available for purchase");
    95.             }
    96.         }
    97.         else
    98.         {
    99.             MyDebug("BuyProductID FAIL. Not initialized.");
    100.         }
    101.     }
    102.    
    103.     public void ListProducts()
    104.     {
    105.  
    106.         foreach (UnityEngine.Purchasing.Product item in m_StoreController.products.all)
    107.         {
    108.             if (item.receipt != null)
    109.             {
    110.                 MyDebug("Receipt found for Product = " + item.definition.id.ToString());
    111.             }
    112.         }
    113.     }
    114.  
    115.     public void OnInitialized(IStoreController controller, IExtensionProvider extensions)
    116.     {
    117.         MyDebug("OnInitialized: PASS");
    118.  
    119.         m_StoreController = controller;
    120.         m_StoreExtensionProvider = extensions;
    121.         m_AppleExtensions = extensions.GetExtension<IAppleExtensions>();
    122.         m_GoogleExtensions = extensions.GetExtension<IGooglePlayStoreExtensions>();
    123.  
    124.         m_GoogleExtensions?.SetDeferredPurchaseListener(OnPurchaseDeferred);
    125.  
    126.         Dictionary<string, string> dict = m_AppleExtensions.GetIntroductoryPriceDictionary();
    127.  
    128.         foreach (UnityEngine.Purchasing.Product item in controller.products.all)
    129.         {
    130.  
    131.             if (item.receipt != null)
    132.             {
    133.                 string intro_json = (dict == null || !dict.ContainsKey(item.definition.storeSpecificId)) ? null : dict[item.definition.storeSpecificId];
    134.  
    135.                 // if (item.definition.type == ProductType.Subscription)
    136.                 // {
    137.                 //     SubscriptionManager p = new SubscriptionManager(item, intro_json);
    138.                 //     SubscriptionInfo info = p.getSubscriptionInfo();
    139.                 //     MyDebug("SubInfo: " + info.getProductId().ToString());
    140.                 //     MyDebug("isSubscribed: " + info.isSubscribed().ToString());
    141.                 //     MyDebug("isFreeTrial: " + info.isFreeTrial().ToString());
    142.                 // }
    143.             }
    144.         }
    145.     }
    146.  
    147.     public void OnPurchaseDeferred(Product product)
    148.     {
    149.         MyDebug("Deferred product " + product.definition.id.ToString());
    150.      
    151.     }
    152.  
    153.     public void OnInitializeFailed(InitializationFailureReason error)
    154.     {
    155.         // Purchasing set-up has not succeeded. Check error for reason. Consider sharing this reason with the user.
    156.         MyDebug("OnInitializeFailed InitializationFailureReason:" + error);
    157.     }
    158.  
    159.  
    160.     public PurchaseProcessingResult ProcessPurchase(PurchaseEventArgs args)
    161.     {
    162.  
    163.         try
    164.         {
    165.             var validator = new CrossPlatformValidator(GooglePlayTangle.Data(), AppleTangle.Data(), Application.identifier);
    166.             var result = validator.Validate(args.purchasedProduct.receipt);
    167.             MyDebug("Validate = " + result.ToString());
    168.             //if receipt has been found:
    169.             PlayerPrefs.SetInt("Purchased", 1);
    170.             foreach (IPurchaseReceipt productReceipt in result)
    171.             {
    172.                 MyDebug("Valid receipt for " + productReceipt.productID.ToString());
    173.             }
    174.         }
    175.         catch (Exception e)
    176.         {
    177.             MyDebug("Error is " + e.Message.ToString());
    178.         }
    179.  
    180.         MyDebug(string.Format("ProcessPurchase: " + args.purchasedProduct.definition.id));
    181.         //if receipt has been found:
    182.         panelWelcome.SetActive(true);
    183.  
    184.         return PurchaseProcessingResult.Complete;
    185.      
    186.      }
    187.  
    188.  
    189.     public void OnPurchaseFailed(UnityEngine.Purchasing.Product product, PurchaseFailureReason failureReason)
    190.     {
    191.         MyDebug(string.Format("OnPurchaseFailed: FAIL. Product: '{0}', PurchaseFailureReason: {1}", product.definition.storeSpecificId, failureReason));
    192.     }
    193.    
    194.     private void MyDebug(string debug)
    195.     {
    196.        
    197.         Debug.Log(debug);
    198.         myText.text += "\r\n" + debug;
    199.     }
    200. }
     
  16. JeffDUnity3D

    JeffDUnity3D

    Unity Technologies

    Joined:
    May 2, 2017
    Posts:
    14,446
    Yes, when you reinstall the app, you will see ProcessPurchase called for any non-consumable you have bought previously, at any time, in any session. This is the Restore process
     
  17. dormouse

    dormouse

    Joined:
    Mar 1, 2011
    Posts:
    82
    So where is my error?
    To how solve my issue?
    So in any session i have to manually call InitializePurchasing function?
     
  18. dormouse

    dormouse

    Joined:
    Mar 1, 2011
    Posts:
    82
    how to revoke a purchased item? it seems my previous Internal Test (non-consumable item) has been permanently recorded by GooglePlay Store. I see all my previous test payment have been refounded already via PlayConsole->Order Management, but they are not update-to-date...weird.
     
  19. JeffDUnity3D

    JeffDUnity3D

    Unity Technologies

    Joined:
    May 2, 2017
    Posts:
    14,446
    For a new topic, please open a new thread.
     
  20. JeffDUnity3D

    JeffDUnity3D

    Unity Technologies

    Joined:
    May 2, 2017
    Posts:
    14,446
    Yes, you call InitializePurchasing every time the app starts. How else do you have purchasing enabled during game play? Please compare to the Sample IAP Project v2 as mentioned. Don't copy the code, actually get that project working and published into Closed Testing on Google
     
  21. Yiming075

    Yiming075

    Joined:
    Mar 24, 2017
    Posts:
    29
    It seems that the problem is caused by Android backup mechanism. The app will automatically restore the purchasing data causing Unity IAP thinks it has recovered it. After clearing the app data, the IAP works as excepted.

    The problem can be fixed by adding
    android:allowBackup="false"

    to the manifest
    <application>
    tag

    More details can see at here:
    https://stackoverflow.com/questions...embers-its-data-after-uninstall-and-reinstall
     
    Last edited: Apr 24, 2022
  22. EFFalcon2

    EFFalcon2

    Joined:
    Apr 28, 2014
    Posts:
    17
    Is it possible to determine if the ProcessPurchase is being triggered by the auto restore?