Search Unity

IAP restore issues (Android)

Discussion in 'Unity IAP' started by DanWeston, Mar 31, 2019.

  1. DanWeston

    DanWeston

    Joined:
    Sep 9, 2014
    Posts:
    19
    I'm having a lot of alarming issues with restore purchases on Android. I am testing my IAP restore using two phones, both are on the same Android version and only contain the same Google account logged in.

    When an IAP is purchased on device A, I clear the app data, uninstall and reinstall the app. Upon opening the game after the reinstall the IAP I purchased on device A is restored.

    When I perform a clean install and open the game on device B (using the same Google/Google Play account) the purchase I made on device A is not restored.

    If I start to purchase IAPs on device B and the perform a clean install on device A, none of the IAPs from device B are restoring but the initial purchase made on device A will become restored. This is the same if I were to flip device A and B around.

    What is alarming about this is that I am able to buy the same non-consumable twice across two devices . Looking at the purchase history on each device I can see the purchases made across both of them.

    I am using the most recent version of the Unity IAP package (1.22.0) and my IAP restore on iOS is functioning as expected. I have also tried implementing the new restore method on Android but this performs the same as when the app is loaded for the first time.
     
    Last edited: Apr 1, 2019
    justinbartoo likes this.
  2. JeffDUnity3D

    JeffDUnity3D

    Unity Technologies

    Joined:
    May 2, 2017
    Posts:
    5,158
    Thanks for reporting this, I will check.
     
  3. DanWeston

    DanWeston

    Joined:
    Sep 9, 2014
    Posts:
    19
    Thanks for the response, I am getting a lot of feedback from my Android users that purchases aren't being carried over. I am out of ideas on the issue. Just to provide further information, all of my Products are marked as Non Consumable when I initialise the store.
     
  4. DanWeston

    DanWeston

    Joined:
    Sep 9, 2014
    Posts:
    19
    Is there any update on this? I set up another two devices (different from my initial post ) with two new Google accounts and the issue I described is still occurring!
     
  5. JeffDUnity3D

    JeffDUnity3D

    Unity Technologies

    Joined:
    May 2, 2017
    Posts:
    5,158
    No updates yet, I will be testing this week.
     
  6. JeffDUnity3D

    JeffDUnity3D

    Unity Technologies

    Joined:
    May 2, 2017
    Posts:
    5,158
    So far in my testing, I am seeing the same thing. I purchased a subscription on one Android device, and it didn't Restore on another. I have let the IAP team know.
     
  7. DanWeston

    DanWeston

    Joined:
    Sep 9, 2014
    Posts:
    19
    Thanks for the update, is there an ETA on getting a fix?
     
  8. BeeSmart

    BeeSmart

    Joined:
    Dec 6, 2013
    Posts:
    14
    Hi Dan - I encountered a recent problem on android that I wonder if you have as well, it deals with restore purchases.
    https://forum.unity.com/threads/and...-non-consumable-restore-per-app-start.671506/
    I have installed and re-installed the app on multiple devices and the restores happen but I have to open and close the app multiple times until all the non-consumable purchases are received. It seems the purchases are queued in the order that they are purchased. When the first one gets send and the purchase gets completed i.e. BuyComplete method. It appears that there is no callback to send this next purchase receipt but it happens once the app gets reinitialized. Without seeing the source code, I am unsure this is what is happening but is what is appears to me. Hopefully, you have some in sight.

    Thanks
    Chris
     
  9. JeffDUnity3D

    JeffDUnity3D

    Unity Technologies

    Joined:
    May 2, 2017
    Posts:
    5,158
    No ETA yet at this point
     
  10. DanWeston

    DanWeston

    Joined:
    Sep 9, 2014
    Posts:
    19
    Hi Chris, I've not experienced this issue with my app and implementation. Although my purchases aren't being shared between devices my restores will get all the purchases made on the device in one go! I'd suggest on your post to maybe share some code if you can? My initial reaction to your issue is that there could be an issue with your ProcessPurchase method, is PurchaseProcessingResult.Complete always being returned? Best of luck with your issue, I hope you find an conclusive answer/fix!
     
  11. BeeSmart

    BeeSmart

    Joined:
    Dec 6, 2013
    Posts:
    14
    Hi Dan, Thanks for the reply and hope you had a great holiday. I agree that I think the issue is in the ProcessPurchase method but I cannot seem to figure a way around the issue.

    Here is the code and it is really simple, at this point. As a reminder, I am running Codeless IAP so I am not even sure I need the ProcessPurchase Method unless I plan on deploying on iOS (which I plan on doing) once I get beyond this hurdle. I created a ProcessPurchase method but I is not even called. When i look at the e argument, it is for a single product does not appear to be called again until the next time the app is opened.

    e appears to be a single product and ProcessPurchase only is called once

    Code (CSharp):
    1. public PurchaseProcessingResult ProcessPurchase(PurchaseEventArgs e)
    2. {
    3.         tbDebugMg.text = tbDebugMg.text + "\n##ProcessPurchase " + e.purchasedProduct.definition.id;
    4.  
    5.         return PurchaseProcessingResult.Complete;
    6. }
     
  12. BeeSmart

    BeeSmart

    Joined:
    Dec 6, 2013
    Posts:
    14
    Dan - I just had a thought that I will test and reply the result. I have 5 non-consumables as stated in earlier posts. I am using the same method BuyComplete(Product) method for the On Purchase Complete Event for all of the products. I have thus far purchased 3 on the non-consumables that I need restored on re-install. What I am seeing as of now, the BuyComplete method is only called once.

    This code snippet is from the CodelessIAPStoreListener.cs. It should be looping through the activeListeners but I think there is only 1 activeListener for all 5 non-consumable buttons. I am going to see if I create multiple BuyComplete methods and assign them to each of the non-consumable buttons On Purchase Complete Events to see if during the restore these separate BuyComplete[1-5] methods get called.

    Code (CSharp):
    1. public PurchaseProcessingResult ProcessPurchase(PurchaseEventArgs e)
    2.         {
    3.             PurchaseProcessingResult result;
    4.  
    5.             // if any receiver consumed this purchase we return the status
    6.             bool consumePurchase = false;
    7.             bool resultProcessed = false;
    8.  
    9.             foreach (IAPButton button in activeButtons)
    10.             {
    11.                 if (button.productId == e.purchasedProduct.definition.id)
    12.                 {
    13.                     result = button.ProcessPurchase(e);
    14.  
    15.                     if (result == PurchaseProcessingResult.Complete) {
    16.  
    17.                         consumePurchase = true;
    18.                     }
    19.  
    20.                     resultProcessed = true;
    21.                 }
    22.             }
    23.  
    24.             foreach (IAPListener listener in activeListeners)
    25.             {
    26.                 result = listener.ProcessPurchase(e);
    27.  
    28.                 if (result == PurchaseProcessingResult.Complete) {
    29.  
    30.                     consumePurchase = true;
    31.                 }
    32.  
    33.                 resultProcessed = true;
    34.             }
    35.  
    36.             // we expect at least one receiver to get this message
    37.             if (!resultProcessed) {
    38.  
    39.                 Debug.LogError("Purchase not correctly processed for product \"" +
    40.                                  e.purchasedProduct.definition.id +
    41.                                  "\". Add an active IAPButton to process this purchase, or add an IAPListener to receive any unhandled purchase events.");
    42.  
    43.             }
    44.  
    45.             return (consumePurchase) ? PurchaseProcessingResult.Complete : PurchaseProcessingResult.Pending;
    46.         }
     
  13. JeffDUnity3D

    JeffDUnity3D

    Unity Technologies

    Joined:
    May 2, 2017
    Posts:
    5,158
  14. BeeSmart

    BeeSmart

    Joined:
    Dec 6, 2013
    Posts:
    14
    Bad news to report with using separate BuyComplete methods. During the restore, the BuyComplete methods are still called one at a time on app start. I have posted my entire IAPmgr in hope that someone spots the issue.

    I removed proprietary code and I know I do not need multiple BuyComplete methods but I was testing

    Code (CSharp):
    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4. using UnityEngine.UI;
    5. using UnityEngine.Purchasing;
    6.  
    7. public class IAPMgr : MonoBehaviour //, IStoreListener
    8. {
    9.     //IStoreController controller;
    10.     //IExtensionProvider extensions;
    11.  
    12.     // Start is called before the first frame update
    13.     void Start()
    14.     {
    15.         tbDebugMg.text = "Start...";
    16.        
    17.         //Need to review playerprefs to see if there are any playerprefs, if so take action
    18.         PurchasedItemsActions();
    19.        
    20.     }
    21.  
    22.     // Update is called once per frame
    23.     void Update()
    24.     {
    25.        
    26.     }
    27.  
    28.     //This is not called
    29.     bool IsInitialized()
    30.     {
    31.         // Only say we are initialized if both the Purchasing references are set.
    32.         tbDebugMg.text = tbDebugMg.text + "\nIsInitialized...";
    33.         return controller != null && extensions != null;
    34.     }
    35.    
    36.     public void BuyComplete(Product product)
    37.     {
    38.         string msg = "Purchase Complete [" + product.definition.id + "]  Store Specific ID [" + product.definition.storeSpecificId + "]";
    39.         Debug.Log(msg);
    40.         tbDebugMg.text = tbDebugMg.text + msg;
    41.  
    42.         PurchaseActions(product.definition.id);
    43.     }
    44.  
    45.     public void BuyFailed(Product product, PurchaseFailureReason purchaseFailureReason)
    46.     {
    47.         string msg = product.definition.id + " " +  "Purchase failed";
    48.         Debug.Log(msg);
    49.         tbDebugMg.text = msg;      
    50.     }
    51.  
    52.     public void PurchasedItemsActions()
    53.     {
    54.        
    55.     }
    56.  
    57.     public void PurchasedItemsActions(string sKey)
    58.     {
    59.        
    60.     }
    61.  
    62.     private void PurchaseActions(string productID)
    63.     {
    64.         //Here is a switch statement
    65.     }
    66.  
    67.     private void Actions_NoAds()
    68.     {
    69.         tbYourAds.text = tbYourAds.text + "STOP AND HIDE ALL ADS";
    70.         GameObject.FindGameObjectWithTag("PurchBtn1").SetActive(false);
    71.     }
    72.  
    73.     private void Actions_FullAccess()
    74.     {
    75.         tbDebugMg.text = tbDebugMg.text = "/n FULL ACCESS TO EVERYTHING";
    76.         tbYourAds.text = tbYourAds.text + "STILL STOP AND HIDE [ALL] ADS";
    77.         GameObject.FindGameObjectWithTag("PurchBtn2").SetActive(false);
    78.     }
    79.  
    80.     private void Actions_TestPackage1()
    81.     {
    82.         tbTestPkg1.text = "access to TEST PKG1";
    83.         GameObject.FindGameObjectWithTag("PurchBtn3").SetActive(false);
    84.     }
    85.  
    86.     private void Actions_TestPackage2()
    87.     {
    88.         tbTestPkg2.text = "access to TEST PKG2";
    89.         GameObject.FindGameObjectWithTag("PurchBtn4").SetActive(false);
    90.     }
    91.  
    92.     private void Actions_TestPackage3()
    93.     {
    94.         tbTestPkg3.text = "access to TEST PKG3";
    95.         GameObject.FindGameObjectWithTag("PurchBtn5").SetActive(false);
    96.     }
    97.  
    98.     //This is never called.
    99.     public void OnInitializeFailed(InitializationFailureReason error)
    100.     {
    101.         Debug.Log("OnInitializeFailed InitializationFailureReason:" + error);
    102.  
    103.         //throw new System.NotImplementedException();
    104.     }
    105.  
    106.    //THis is never called
    107.     public PurchaseProcessingResult ProcessPurchase(PurchaseEventArgs e)
    108.     {
    109.        
    110.         tbDebugMg.text = tbDebugMg.text + "\n##ProcessPurchase " + e.purchasedProduct.definition.id;
    111.         //Debug.Log("ProcessPurchase " + e.purchasedProduct.definition.id);
    112.        
    113.        
    114.  
    115.         return PurchaseProcessingResult.Complete;
    116.  
    117.         //throw new System.NotImplementedException();
    118.  
    119.         /*
    120.           bool validPurchase = true; // Presume valid for platforms with no R.V.
    121.  
    122.         // Unity IAP's validation logic is only included on these platforms.
    123. #if UNITY_ANDROID || UNITY_IOS || UNITY_STANDALONE_OSX
    124.         // Prepare the validator with the secrets we prepared in the Editor
    125.         // obfuscation window.
    126.         var validator = new CrossPlatformValidator(GooglePlayTangle.Data(),
    127.             AppleTangle.Data(), Application.identifier);
    128.  
    129.         try
    130.         {
    131.             // On Google Play, result has a single product ID.
    132.             // On Apple stores, receipts contain multiple products.
    133.             var result = validator.Validate(e.purchasedProduct.receipt);
    134.             // For informational purposes, we list the receipt(s)
    135.             Debug.Log("Receipt is valid. Contents:");
    136.             foreach (IPurchaseReceipt productReceipt in result)
    137.             {
    138.                 if (productReceipt.productID != e.purchasedProduct.definition.id)
    139.                 {
    140.                     Debug.Log("Invalid receipt data");
    141.                     validPurchase = false;
    142.                 }
    143.             }
    144.         }
    145.         catch (IAPSecurityException)
    146.         {
    147.             Debug.Log("Invalid receipt, not unlocking content");
    148.             validPurchase = false;
    149. #if UNITY_EDITOR
    150.             validPurchase = true;
    151. #endif
    152.         }
    153. #endif
    154.  
    155.         //apply the purchasing in case if the transaction is valid
    156.         if (validPurchase)
    157.         {
    158.             switch (e.purchasedProduct.definition.id)
    159.             {
    160.                 //Use your own products
    161.                 case (gameID + "vip1"):
    162.                     //Do your product logic
    163.                     break;
    164.                 case (gameID + "lives1"):
    165.                     //Do your product logic
    166.                     break;
    167.                 case (gameID + "2x"):
    168.                     //Do your product logic
    169.                     break;
    170.                 case (gameID + "gems1"):
    171.                     //Do your product logic
    172.                     break;
    173.             }
    174.         }
    175.  
    176.         return PurchaseProcessingResult.Complete;
    177.          */
    178.     }
    179.  
    180.     public void OnPurchaseFailed(Product p, PurchaseFailureReason r)
    181.     {
    182.         tbDebugMg.text = tbDebugMg.text + string.Format("OnPurchaseFailed: FAIL. Product: '{0}', PurchaseFailureReason: {1}", p.definition.storeSpecificId, r);
    183.         Debug.Log(string.Format("OnPurchaseFailed: FAIL. Product: '{0}', PurchaseFailureReason: {1}", p.definition.storeSpecificId, r));
    184.         //throw new System.NotImplementedException();
    185.     }
    186.  
    187.     public void OnInitialized(IStoreController controller, IExtensionProvider extensions)
    188.     {
    189.         tbDebugMg.text = tbDebugMg.text = "/n In OnInitalized";
    190.         throw new System.NotImplementedException();
    191.     }
    192.  
    193.     // Restore purchases previously made by this customer. Some platforms automatically restore purchases, like Google.
    194.     // Apple currently requires explicit purchase restoration for IAP, conditionally displaying a password prompt.
    195.     public void RestorePurchases()
    196.     {
    197.         if (!IsInitialized())
    198.         {
    199.             tbDebugMg.text = tbDebugMg.text + "\nRestorePurchases FAIL. Not initialized.";
    200.             Debug.Log("RestorePurchases FAIL. Not initialized.");
    201.             return;
    202.         }
    203.  
    204.         // If we are running on an Apple device ...
    205.         if (Application.platform == RuntimePlatform.IPhonePlayer
    206.                 || Application.platform == RuntimePlatform.OSXPlayer
    207.                 || Application.platform == RuntimePlatform.tvOS)
    208.         {
    209.             tbDebugMg.text = tbDebugMg.text + "\nRestorePurchases started ...";
    210.             Debug.Log("RestorePurchases started ...");
    211.  
    212.             var apple = extensions.GetExtension<IAppleExtensions>();
    213.             // Begin the asynchronous process of restoring purchases. Expect a confirmation response in
    214.             // the Action below, and ProcessPurchase if there are previously purchased products to restore.
    215.             apple.RestoreTransactions(OnTransactionsRestored);
    216.         }
    217.         else
    218.         {
    219.             // We are not running on an Apple device. No work is necessary to restore purchases.
    220.             tbDebugMg.text = tbDebugMg.text + "\nRestorePurchases FAIL. Not supported on this platform. Current = " + Application.platform;
    221.             Debug.Log("RestorePurchases FAIL. Not supported on this platform. Current = " + Application.platform);
    222.         }
    223.  
    224.         void OnTransactionsRestored(bool success)
    225.         {
    226.             tbDebugMg.text = tbDebugMg.text + "\nTransactions restored " + success.ToString();
    227.             Debug.Log("Transactions restored " + success.ToString());
    228.         }
    229.     }
    230.  
     
  15. BeeSmart

    BeeSmart

    Joined:
    Dec 6, 2013
    Posts:
    14
    If you are deploying on iOS even though using the Codeless IAP, my IAPMgr will require the interface of IStoreListener correct to implement RestorePurchases() properly. Correct?
     
  16. JeffDUnity3D

    JeffDUnity3D

    Unity Technologies

    Joined:
    May 2, 2017
    Posts:
    5,158
    No. The only thing that an IAP Manager script should do is grant the user the product, like unlocking 50 Gold Coins. During the restore, the listener will internally call ProcessPurchase, etc and then should fire the method you have connected to your Restore button, which again, should ONLY unlock the IAP product. From the docs "If the restore succeeds, Unity IAP invokes the On Purchase Complete (Product) function on the IAP Button associated with that Product." https://docs.unity3d.com/Manual/UnityIAPCodelessIAP.html . The sample project demonstrates this.
     
  17. BeeSmart

    BeeSmart

    Joined:
    Dec 6, 2013
    Posts:
    14
    Hi Jeff - I used the sample-iap project and added 4 non-consumables(NC) similar to my demo project and ironically they restore just fine (all on the initial start of the app). I then started going through my demo to make sure I have not missed anything. What I have discovered but not solved yet, is if I remove the method that grants/unlocks the non-consumable (which does a little more than unlocks, it unlocks, stores a cryptic key in PlayerPrefs and hides the buy button) the restore of all non-consumable happens. This proven by the various Purchase Complete messages but if I leave that PurchaseAction method in place, it hangs after the first restore and will not restore the second NC until the next app open. I am now in the process of running through that method and child methods to determine what might be causing the problem. I thought a coroutine around the main PurchaseAction method would solve the problem but did not work either. Will keep you posted?
     
  18. BeeSmart

    BeeSmart

    Joined:
    Dec 6, 2013
    Posts:
    14
    Hurrah!!! The solution came in my sleep last night. Originally, prior to the exit of myBuyComplete method, I was setting the IAPButton to SetActive(false). This obviously does more than just hides the button; it was not allowing the purchase complete to finish properly. After changing the SetActive call to interactable = false the restore on Android works like a champ. I will handle the hiding of "purchased item buttons" during the restore another way. I still want to hide those buttons after the user makes a purchase..

    BTW.. the SetActive(false) works just fine on the initial purchase of the item, just not on the restore of multiple non-consumables.

    Hope this helps others.

    Chris

    Now onto testing in iOS. Still stumped on using the Restore buttons for iOS. Is there a way to determine if the user has installed the app before so I can show the button in restore mode instead of purchase mode. Just asking, but I will keep searching.

    Thanks for your help Jeff & Dan. It definitely helped me look for alternative solutions.
     
  19. JeffDUnity3D

    JeffDUnity3D

    Unity Technologies

    Joined:
    May 2, 2017
    Posts:
    5,158
    One thought, you could check PlayerPrefs for an installed flag. If empty, it's a first install, and set it then. This assumes PlayerPrefs is reset upon reinstall, usually the case, but you'll want to test.
     
  20. AhmadGhodrati

    AhmadGhodrati

    Joined:
    Jan 19, 2019
    Posts:
    8
    hi there.
    any update on this?
    (still can't restore on multiple device for google play!)
    or some tutorial on restoring between multiple device while we have codeless IAP implemented ?
     
  21. JeffDUnity3D

    JeffDUnity3D

    Unity Technologies

    Joined:
    May 2, 2017
    Posts:
    5,158
    Sorry, no updates yet. It's still on our task list.
     
    AhmadGhodrati likes this.
  22. Wolar

    Wolar

    Joined:
    Sep 25, 2014
    Posts:
    24
    Hello,
    just wanted to say that we are having the same issue (we are not using codeless IAP), we haven't find any workaround to this so far it's kind of problem for us.
    Martin
     
  23. Berno

    Berno

    Joined:
    Oct 29, 2014
    Posts:
    15
    I have also started hearing from customers that are not getting non consumable products restored across devices on Android.
    My App is not running codeless IAP and has been working since Unity 5.
    Currently running Unity 2018.4.0f1, IAP Package 2.0.6.
     
    Last edited: Jul 19, 2019
  24. JeffDUnity3D

    JeffDUnity3D

    Unity Technologies

    Joined:
    May 2, 2017
    Posts:
    5,158
    Hopefully this will be addressed in the next release in a few weeks. I have asked the IAP team to include this update.
     
  25. Lisan

    Lisan

    Joined:
    Jun 17, 2009
    Posts:
    145
    I have the same issue and a lots of complains from customers.
    The purchase always restored after reinstalling on the same device it was done, but on the other device with the same account it behave different, and i can't find any logical explanation yet.

    1) On the launch the game registers iap product, and sometimes previous purchase discovered here (always for the same device), but mostly not, no in-apps found at this point.
    2) When user tries to buy the product again, sometimes IAP found previous purchase here, and return "DuplicateTransaction" status, preventing the duplicate purchase. But just sometimes, otherwise previous purchase not found even here, and user purchasing the same product again, whitch is totally unacceptable behaviour.

    Our testers are fighting this this thing for several days, and they have all sorts of scenarios. Two days ago they had "duplicate transaction" error on the first run, and repeated purchase on the second run. After that they somehow managed to restore purchase by wiping Google Play cache, but i couldn't reproduce it. Today they don't have "duplicate transaction" error at all, and on the second device it's always repeated purchase of the same product. But, when tried on 3-d and 4-d device after that, purchase is restored, so Google allows to do it only twice, whitch is still too much.

    So, for now we are still confused, is anything can be done about it on our side, or it's just sitting and waiting for update from Unity, retuning money to angry costomers.

    Our current game made in Unity 2017 (last build in 2017.4.30), IAP 1.22.0
     
  26. JeffDUnity3D

    JeffDUnity3D

    Unity Technologies

    Joined:
    May 2, 2017
    Posts:
    5,158
    @Lisan Correct, that is the issue we are working on. I don't think it will be in the next release due out very soon, but likely the next release after that, likely a month or so out.
     
  27. Lisan

    Lisan

    Joined:
    Jun 17, 2009
    Posts:
    145
    Just wonderfull. As if Unity IAP team has something more important to attend than users, spending money twice for the same product and voting down the apps by setting 1 star in rating. I have complains almost everyday.
     
  28. JeffDUnity3D

    JeffDUnity3D

    Unity Technologies

    Joined:
    May 2, 2017
    Posts:
    5,158
    I am personally pushing to get this fix out, we had higher priority fixes we needed to get out in the current release. It's an issue with priorities vs resources. We have to coordinate the release among hundreds of developers and test across many Android and iOS versions. The code fixes themselves are generally easy, it's the coordination of the release process and the huge test matrix that takes time. We choose not to publish beta IAP releases, and only release when the integration testing is complete.
     
  29. pertz

    pertz

    Joined:
    Jan 8, 2015
    Posts:
    61
    I've been getting several email complains and bad reviews due to this, took me weeks to find this topic, that's why I didnt complain before.

    This is a major policy issue, my app could be REMOVED from Play Store for this problem. It's impossible this is known for several months and still isnt fixed!

    I thought it was something related to code, or device type, as it's not happening to all players, but only some devices, now with this explanation it makes sense: it only works on the device that originally made the purchase. Since my game is very old, some purchases were made in 2015, 2016, so all those loyal old players are not getting their purchase fulfilled, since they certainly changed their devices since then.

    Please at least provide a temporary workaround, a beta client, a hack - anything!
     
  30. pertz

    pertz

    Joined:
    Jan 8, 2015
    Posts:
    61
    Does anyone know if this is a problem of the current IAP (1.22) or a problem of Unity 2019? If it's a IAP problem we could try to roll back to IAP 1.21? @DanWeston have you found a workaround?
     
  31. pertz

    pertz

    Joined:
    Jan 8, 2015
    Posts:
    61
    @JeffDUnity3D can you provide some kind of workaround? Or maybe confirm if it's a problem exclusively on IAP 1.22, and if I roll back to IAP 1.21 or some other version (which?) the restore would work?

    Or maybe this is a problem related to the current "In App Purchasing" package (2.0.6) inside Package Manager? I dont know how to roll that back to previous versions, but I could try if that's the culprit.
     
  32. adbourdages

    adbourdages

    Joined:
    Jul 8, 2012
    Posts:
    44
    I'm tagging along for future updates on this issue since our app also has had this problem for month now and I just found this topic (I thought there was some bug on our side instead). Thankfully we have automated cloud saves so that players can get their data back.
     
  33. xmarleauFFG

    xmarleauFFG

    Joined:
    Mar 28, 2017
    Posts:
    3
    Hi,

    I'll jump in the wagon on this one. I've been looking at this thread for a few weeks now, waiting for the fix to happen. Like the others, restoring non-consumable purchases on a second device with the same account does not work.

    Also, a small percentage of users can't restore their purchases on the same device they bought it. I give these users codes so they can redeem the IAP. Using the code unlocks the IAP, but as soon as they close and reopen the app, they don't have the IAP unlocked anymore... it is very problematic. The issue with this is that I don't have a way to reproduce and test this faulty behaviour, since its working with my testing devices/accounts. This means I can't even work on a fix myself.

    Unless there is a new version of Unity IAP coming out in the next week, is there not another temporary solution? Is there not a stable version I can roll back to?
     
  34. JeffDUnity3D

    JeffDUnity3D

    Unity Technologies

    Joined:
    May 2, 2017
    Posts:
    5,158
    You can try with IAP 1.19, the restore issue looks to have been possibly introduced in a more recent release. We hope to have this addressed in release soon, no official time frame but I might expect 1-2 months, hopefully sooner. https://forum.unity.com/threads/unity-iap-previous-versions.527432/#post-4737377 And you mention "they don't have the IAP unlocked anymore". How are unlocking products and tracking user purchases?
     
    Last edited: Aug 16, 2019
    xmarleauFFG likes this.
  35. xmarleauFFG

    xmarleauFFG

    Joined:
    Mar 28, 2017
    Posts:
    3
    Here's our logic, we use the same logic for 2 of our other apps and we have not experienced this problem (using unity Purchasing 1.11.4)
    I've strip out some debugging and other platform specific lines (Steam, iOS) to keep only the GPlay logic.

    Code (CSharp):
    1. public void Initialize(IEnumerable<string> productSKUs) {
    2.  
    3.     _purchasedSKUs = new List<string>(4);
    4.  
    5.     // initialize the Unity IAP
    6.     _allProducts = new List<ProductDefinition>();
    7.     foreach (string sku in productSKUs) {
    8.         _allProducts.Add(new ProductDefinition(sku, sku, ProductType.NonConsumable));
    9.     }
    10.  
    11.     _builder = ConfigurationBuilder.Instance(StandardPurchasingModule.Instance());
    12.  
    13.     if (_allProducts != null) {
    14.         _builder.AddProducts(_allProducts);
    15.     }
    16.  
    17.     _crossPlatformValidator = new CrossPlatformValidator(GooglePlayTangle.Data(), AppleTangle.Data(), Application.identifier);
    18.  
    19.     UnityPurchasing.Initialize(this, _builder);
    20. }
    21.  
    22. public void PurchaseProduct(string sku) {
    23.     try {
    24.         if (IsInitialized() && !HasPurchased(sku)) {
    25.             if (!string.IsNullOrEmpty(sku)) {
    26.                 Product product = _controller.products.WithID(sku);
    27.                 if (product != null && product.availableToPurchase) {
    28.                     Debug.LogFormat("IAPManager: Attempting to purchase {0}...", sku);
    29.                     _pendingPurchase = product;
    30.                     _controller.InitiatePurchase(sku);
    31.                 }
    32.             } else {
    33.                 // no product id
    34.                 Debug.LogError("IAPManager: SKU cannot be null/empty");
    35.             }
    36.         } else {
    37.             // controller not initiailzed
    38.             Debug.LogError("IAPManager is not initialized");
    39.         }
    40.     } catch (System.Exception e) {
    41.         Debug.LogException(e);
    42.     }
    43. }
    44.  
    45. private void CompletePurchase(string sku) {
    46.  
    47.     if (_purchasedSKUs != null) {
    48.         _purchasedSKUs.AddUniqueItem(sku);
    49.     }
    50.  
    51.     if (PurchaseProcessFinished != null) {
    52.         PurchaseProcessFinished(sku);
    53.     }
    54.  
    55.     _pendingPurchase = null;
    56. }
    57.  
    58. public bool HasPurchased(string sku) {
    59.  
    60.     if (_purchasedSKUs != null && _purchasedSKUs.Contains(sku)) {
    61.         return true;
    62.     }
    63.     bool isValidReceipt;
    64.  
    65.     if (_controller == null) {
    66.         return false;
    67.     }
    68.  
    69.     // use the IAP receipt validation with information queried from the respective store
    70.     Product product = _controller.products.WithID(sku);
    71.     if (product == null || !product.hasReceipt || string.IsNullOrEmpty(product.receipt)) {
    72.         // no product/receipt information
    73.         return false;
    74.     }
    75.  
    76.     isValidReceipt = IsValidReceipt(product.receipt);
    77.     return isValidReceipt && _purchasedSKUs.Contains(sku);
    78. }
    79.  
    80. private bool IsValidReceipt(string receipt) {
    81.    
    82.     bool validPurchase = true;
    83.  
    84.     try {
    85.         IPurchaseReceipt[] result = _crossPlatformValidator.Validate(receipt);
    86.  
    87.         // For informational purposes, we list the receipt(s)
    88.         Debug.Log("IAPManager: Receipt is valid. Contents:");
    89.         for (int i = 0; i < result.Length; i++) {
    90.             IPurchaseReceipt productReceipt = result[i];
    91.  
    92.             if (_controller == null) {
    93.                 // we're offline, IAP was never initialized. Validate via developer defined product ids
    94.                 bool isValidProductId = false;
    95.                 for (int j = 0; j < _allProducts.Count; j++) {
    96.                     if (_allProducts[j] != null && _allProducts[j].id == productReceipt.productID) {
    97.                         // product id is valid
    98.                         isValidProductId = true;
    99.                         break;
    100.                     }
    101.                 }
    102.                 if (!isValidProductId) {
    103.                     // unable to find associated product id (receipt could be invalid)
    104.                     throw new IAPSecurityException();
    105.                 }
    106.             } else {
    107.                 // validate with the IAP controller initialized via the store server
    108.                 Product associatedProduct = _controller.products.WithID(productReceipt.productID);
    109.                 if (associatedProduct == null) {
    110.                     // receipt is for product id not associated with this app
    111.                     throw new IAPSecurityException();
    112.                 }
    113.             }
    114.  
    115.             if (_purchasedSKUs != null) {
    116.                 _purchasedSKUs.AddUniqueItem(productReceipt.productID);
    117.             }
    118.  
    119.         }
    120.     } catch (IAPSecurityException e) {
    121.         Debug.LogException(e);
    122.         Debug.LogError("Receipt: " + receipt);
    123.         Debug.LogError("IAPManager: Invalid receipt, not unlocking content");
    124.         validPurchase = false;
    125.     }
    126.  
    127.     return validPurchase;
    128. }
    129.  
    130.  
     
    JeffDUnity3D likes this.
  36. Berno

    Berno

    Joined:
    Oct 29, 2014
    Posts:
    15
    Do you have any advice on how to roll back to this version?
    I have imported the 1.19 package you linked to. Is there a particular version of IAP I should install from the package manager along side with this? In the package manager in Unity 2018 I can install as far back as the v2.0 package.
    I have to admit I have been confused since we have the IAP installed from the services tab and the package manager.
     
  37. JeffDUnity3D

    JeffDUnity3D

    Unity Technologies

    Joined:
    May 2, 2017
    Posts:
    5,158
    You don't need to be concerned with the Package Manager version. You can follow the directions here https://forum.unity.com/threads/iap-troubleshooting-remove-and-reinstall-unity-iap.511747/
     
  38. Lisan

    Lisan

    Joined:
    Jun 17, 2009
    Posts:
    145
    Did anyone here tried to downgrade from 1.22 to 1.19 already? I am not sure, it's the more than year ago, i believe there were much functionaluty added since then. And will it 100% fix the issue?
     
  39. pertz

    pertz

    Joined:
    Jan 8, 2015
    Posts:
    61
    @Lisan @Berno I have tried and it seems it did not work. I got feedback from 2 players and both said same problems continued after updating to the new version (which contained IAP 1.19).

    I have not tried messing with the Package Manager version, so it's still the latest version there.

    EDIT: also, a player recently mentioned that if he completely kills the app and reopens it the purchase will be properly restored (using latest IAP, this has nothing to do with the 1.19 rollback). Maybe we can force-kill the app if he reopens it after having it a few minutes closed?
     
  40. JeffDUnity3D

    JeffDUnity3D

    Unity Technologies

    Joined:
    May 2, 2017
    Posts:
    5,158
    That is a separate issue. Make sure you are NOT calling Application.Quit. It does not properly unload components. I've heard that the issue is addressed in 2018.4.6f1 and above. You'll want to stay away from Unity 2019.* for IAP for now.
     
  41. pertz

    pertz

    Joined:
    Jan 8, 2015
    Posts:
    61
    Unfortunately I have already migrated to Unity 2019 months ago.

    On Android/Google Play I must close the app when the user presses Back on the home screen, it's a mandatory core design function, UX-B1. My app risks losing editor's choice/similar awards if I remove it.

    Is there any kind of workaround? For example can I somehow unload those components via code before I call Application.Quit?
     
  42. JeffDUnity3D

    JeffDUnity3D

    Unity Technologies

    Joined:
    May 2, 2017
    Posts:
    5,158
    Try the latest release of 2019, the fix is rolling out now. I would suggest trying with a separate/empty project and add IAP, or making sure to make a backup, prior to upgrading. Or you could use the Sample IAP project https://forum.unity.com/threads/sample-iap-project.529555/
     
  43. pertz

    pertz

    Joined:
    Jan 8, 2015
    Posts:
    61
    Thanks.

    Do you have an expected deadline for that fix to come to 2019.1? I'm afraid of migrating to 2019.2 and get new/unrelated problems.
     
  44. JeffDUnity3D

    JeffDUnity3D

    Unity Technologies

    Joined:
    May 2, 2017
    Posts:
    5,158
  45. pertz

    pertz

    Joined:
    Jan 8, 2015
    Posts:
    61
    Thanks but I had already checked that page, and saw that for 2019.1 it's "in review", that's why I asked if you have any expected date for that.
    For 2019.2 I saw it's already released (2019.2.2) but I didnt want to risk going to 2019.2 as it's still a relatively new release.
     
  46. JeffDUnity3D

    JeffDUnity3D

    Unity Technologies

    Joined:
    May 2, 2017
    Posts:
    5,158
    Separate topic from this thread https://forum.unity.com/threads/sol...e-after-application-quit.665497/#post-4732853
     
  47. pertz

    pertz

    Joined:
    Jan 8, 2015
    Posts:
    61
    Yes, sorry for changing the subject this thread. That thread is closed tho, but I guess in a couple weeks the fix for that problem should reach 2019.1, and if it doesnt I will just migrate to 2019.2 and hope for the best.

    But in any case this thread's problem seems to be the major problem for my app, and returning to 1.19 didnt work for me. Is there any other suggestion of possible workaround (involving code or library setup etc)?
     
  48. JeffDUnity3D

    JeffDUnity3D

    Unity Technologies

    Joined:
    May 2, 2017
    Posts:
    5,158
    Sorry no, we are hoping to address this in an upcoming release
     
  49. DanWeston

    DanWeston

    Joined:
    Sep 9, 2014
    Posts:
    19
    It's great news to hear an update is rolling out for 2019, however, is there a time frame for the current LTS?

    I tried tentatively updating my project 2019 a few weeks ago but this broke a couple of things.

    I'd prefer to keep the project on LTS but if it's not seeing this fix for a while I may reconsider and spend the resource and time on updating... the reviews and feedback I get on this issue is harmful to the overall rating of the game which is frustrating as it's an issue out of my control.
     
  50. DanWeston

    DanWeston

    Joined:
    Sep 9, 2014
    Posts:
    19
    Unfortunately not, if you guys figure out anything I'd be happy to know!