Search Unity

InApp auto restore

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

  1. developHTS

    developHTS

    Joined:
    Nov 3, 2017
    Posts:
    2
    A problem appeared while testing an app using a test account.
    The app was deleted after an InApp purchase had been made, then was re-installed and the purchase was restored. After these manipulations, further deletions and re-installations make the purchase restore on its own will, without using the restore function, no matter what test account is used.

    How can this problem be solved?
     
  2. JeffDUnity3D

    JeffDUnity3D

    Unity Technologies

    Joined:
    May 2, 2017
    Posts:
    5,180
    Is this on Google or iOS? On Google, purchases will automatically be restored.
     
  3. developHTS

    developHTS

    Joined:
    Nov 3, 2017
    Posts:
    2
    Platform IOS
     
  4. JeffDUnity3D

    JeffDUnity3D

    Unity Technologies

    Joined:
    May 2, 2017
    Posts:
    5,180
  5. vabster

    vabster

    Joined:
    May 12, 2013
    Posts:
    13
    I can't post steps to reproduce or device logs, just wanted to let you know this is happening to me too on an iOS device in Unity 2018.1.5f1
     
  6. JeffDUnity3D

    JeffDUnity3D

    Unity Technologies

    Joined:
    May 2, 2017
    Posts:
    5,180
    @vabster Any reason you are not able to provide device logs or steps to reproduce? Perhaps it is because it was reported by a user, and you're not seeing it yourself? We have not seen this behavior here, the logs would be necessary to troubleshoot, thanks.
     
  7. oskar-szulc

    oskar-szulc

    Joined:
    Feb 22, 2013
    Posts:
    6
    @JeffDUnity3D
    The same problem appears in 2017.4.3f1
    Here are log excerpts that might be useful:
    UnityIAP Version: 1.18.0
    UnityEngine.Purchasing.StandardPurchasingModule:Instance(AppStore)
    PurchaseHandler:InitializePurchasing()

    /var/mobile/Containers/Data/Application/283CEC00-98EB-4C64-951B-857FFED8466E/Documents/Unity/0f11d761-1a2e-4742-a98c-f061d7bd6925/IAP/store.json
    UnityEngine.Purchasing.PurchasingManager:Initialize(IInternalStoreListener, HashSet`1)
    PurchaseHandler:InitializePurchasing()

    Log: Fetching optimized store details from https://ecommerce.iap.unity3d.com/catalog?appid=[removed]&deviceid=[removed]&userid=[removed]
    UnityEngine.Purchasing.StoreCatalogImpl:FetchProducts(Action`1)
    PurchaseHandler:InitializePurchasing()

    Log: Failed to fetch IAP catalog due to unexpected http status code, attempting to use cache


    and then comes the list of restore purchase calls in form of


    PurchaseHandler:ProcessPurchase(PurchaseEventArgs)
    UnityEngine.Purchasing.Extension.UnityUtil:Update()
     
  8. JeffDUnity3D

    JeffDUnity3D

    Unity Technologies

    Joined:
    May 2, 2017
    Posts:
    5,180
    @oskar-szulc Those calls are expected in the normal initialization flow (the http status code mention is an incorrect error and can be safely ignored). Please provide steps to reproduce.
     
  9. msureka83

    msureka83

    Joined:
    Nov 28, 2014
    Posts:
    35
    Same here. After the last update of Unity IAP, is is automatically restoring purchases. I thought this was a feature of the new IAP update to make it function like android. Is this a bug and not suppose to happen??
     
  10. JeffDUnity3D

    JeffDUnity3D

    Unity Technologies

    Joined:
    May 2, 2017
    Posts:
    5,180
    @msureka83 please provide steps to reproduce.
     
  11. reigota

    reigota

    Joined:
    Mar 23, 2010
    Posts:
    62
    This is happening to me with subscription. Each time I uninstall and re-install,
    PurchaseProcessingResult is called several times! All tests I did in this itunes account is returning as a different transaction. The behaviour is exactly the expected one as described here: https://developer.apple.com/library...g.html#//apple_ref/doc/uid/TP40008267-CH8-SW1 except thet NO CONFIRMATION from the user is asked. The receipt is becaming bigger and bigger.. No steps to reproduce, this is happening in my project and I cannot assembly a setup to test it independently. But I am pretty sure you guys can do it.
    btw, unity 2017.4.15 and IAP 1.20.1
     
  12. reigota

    reigota

    Joined:
    Mar 23, 2010
    Posts:
    62
    @JeffDUnity3D Any news about this problem? My restore button is useless and apple is complaining about it. I can't approve my game with subscription now.
     
  13. lolaswift

    lolaswift

    Joined:
    Jan 10, 2019
    Posts:
    37
    I'm running into the same issue. Unity restore my purchase without even asking for my apple sandbox id.

    Anyone of you has found out what's going on?
     
  14. JeffDUnity3D

    JeffDUnity3D

    Unity Technologies

    Joined:
    May 2, 2017
    Posts:
    5,180
    @lolaswift Please elaborate. Are you calling the restore method, and it's not prompting for a password? We have no control over the password prompts, we are a pass through service for the store API's.
     
  15. JeffDUnity3D

    JeffDUnity3D

    Unity Technologies

    Joined:
    May 2, 2017
    Posts:
    5,180
    Sorry, no steps to reproduce? So this occurs without even a single purchase occurring?
     
  16. lolaswift

    lolaswift

    Joined:
    Jan 10, 2019
    Posts:
    37
    No, I am not calling the restore method. Just exit the app and open it again. When I checked the xcode debug window, the receipt was downloaded by IAP and validated. I asked some senior unity developer yesterday and he said now, it's the designed behavior that unity did it like that just as it did it on android. Is that true?
     
  17. JeffDUnity3D

    JeffDUnity3D

    Unity Technologies

    Joined:
    May 2, 2017
    Posts:
    5,180
    No, auto restore on iOS is not expected. It may only be occurring in their Sandbox environment, we have no control over that. And sorry, without steps to reproduce, I would not be able to assist. So you are receiving a Restore operation, without even having purchased anything previously? Please confirm these steps:

    * Install the app, but do not purchase any product
    * Delete and reinstall the app
    * You begin to receive product receipts in ProcessPurchase immediately.
     
  18. zackblack

    zackblack

    Joined:
    May 17, 2015
    Posts:
    23
    I would also like to note this is occurring on my apps as well. Unity 2018.2.20f1. When the publicly released app is downloaded from the iOS App Store, the purchases are automatically restored, but my app never gets any notification or callbacks that this is happening. It is "unlocked", but slipped through detection events.
     
  19. JeffDUnity3D

    JeffDUnity3D

    Unity Technologies

    Joined:
    May 2, 2017
    Posts:
    5,180
    What type of product is being restored? Is it a non-consumable or a recurring subscription? And what do you mean, no callbacks? ProcessPurchase would be expected to be called during a restore operation, that is the core issue here. How are you making the determination that restore is occurring?
     
    Last edited: Jan 30, 2019
  20. JeffDUnity3D

    JeffDUnity3D

    Unity Technologies

    Joined:
    May 2, 2017
    Posts:
    5,180
    I've been informed by the IAP team that 1.20.1 fixes the auto restore behavior on iOS.
     
  21. zackblack

    zackblack

    Joined:
    May 17, 2015
    Posts:
    23
    Sorry, 1.20.1? Do you mean Unity or IAP system?
     
  22. zackblack

    zackblack

    Joined:
    May 17, 2015
    Posts:
    23
    My apologies, yes - The "no callbacks" was a race condition on my end. :-/
     
  23. lolaswift

    lolaswift

    Joined:
    Jan 10, 2019
    Posts:
    37
    Hi Jeff,I encountered the issue when testing in their Sandbox environment. My IAP items are going through their review process at the moment. I will report it back when they are approved.
     
  24. lolaswift

    lolaswift

    Joined:
    Jan 10, 2019
    Posts:
    37
    What do you mean exactly? Since IAP 1.20.1, auto restore should not happen, correct?
     
  25. JeffDUnity3D

    JeffDUnity3D

    Unity Technologies

    Joined:
    May 2, 2017
    Posts:
    5,180
    Yes, that is correct, when in production.
     
  26. mbrindic

    mbrindic

    Joined:
    Mar 13, 2014
    Posts:
    13
    Hi guys, I have same issue about auto-restore purchases on iOS platform with sandbox. I'm wondering is this going to be fixed in the future or not?

    You said that it's fixed in production, but it's really unreliable for me to publish code that is not properly tested...

    I'm using Unity 2017.4.15 with Unity IAP 1.21.
     
    Last edited: Apr 30, 2019
  27. JeffDUnity3D

    JeffDUnity3D

    Unity Technologies

    Joined:
    May 2, 2017
    Posts:
    5,180
    Please provide the device logs https://forum.unity.com/threads/how-to-capturing-device-logs-on-ios.529920/
     
  28. mbrindic

    mbrindic

    Joined:
    Mar 13, 2014
    Posts:
    13
  29. Plummers

    Plummers

    Joined:
    Oct 29, 2015
    Posts:
    14
    Hi, I think I have the same iOS testing bug. So initially I put an app in development on my phone through iTunes (drag and drop), bought my darkmode IAP. Now I want to test it again with an updated version of the app.

    1. I deleted the old version from the phone and restarted the phone.
    2. I logged out of all iTunes / sandbox accounts on the phone
    3. I installed new version of my app (drag and drop iTunes)
    4. Started app and it seems on starting up it restores the purchases! I don't press anything.

    Is this correct? It seems however I go about uninstalling/reinstalling/switching accounts that the purchase is stored on the phone somehow.
     
  30. JeffDUnity3D

    JeffDUnity3D

    Unity Technologies

    Joined:
    May 2, 2017
    Posts:
    5,180
    You mention "seems on starting up", can you confirm? How are you debugging? Can you provide the device logs?
     
    Last edited: Sep 11, 2019
  31. ViniciusZeroUm

    ViniciusZeroUm

    Joined:
    Jun 12, 2019
    Posts:
    5
    Hello,

    I'm experiencing the same issue. The steps to reproduce are the same as Plummers:

    1. Installed the app and make a purchase
    2. I deleted the old version from the phone and restarted the phone.
    3. I logged out of all iTunes / sandbox accounts on the phone
    4. I installed new version of my app from xcode
    5. Started app and it seems on starting up it restores the purchases! I don't press anything.
    6. If I try to use
    storeExtensionProvider.GetExtension<IAppleExtensions>(). void RestoreTransactions (Action<bool> callback);
    the callback is never called

    The devices log are:

    ----------------------------------------------------------------------------------------------------
    2019-09-10 18:29:27.838533-0300 AppName[748:1814511] [DYMTLInitPlatform] platform initialization successful
    2019-09-10 18:29:27.850113-0300 AppName[748:1814462] UnityIAP UnityEarlyTransactionObserver: Created
    2019-09-10 18:29:27.852284-0300 AppName[748:1814462] UnityIAP UnityEarlyTransactionObserver: Registered for lifecycle events
    CrashReporter: initialized
    2019-09-10 18:29:27.895077-0300 AppName[748:1814462] Built from '2019.1/staging' branch, Version '2019.1.7f1 (f3c4928e5742)', Build type 'Release', Scripting Backend 'il2cpp'
    2019-09-10 18:29:27.898847-0300 AppName[748:1814462] -> registered mono modules 0x1017b35b0
    -> applicationDidFinishLaunching()
    2019-09-10 18:29:28.019856-0300 AppName[748:1814462] Metal GPU Frame Capture Enabled
    2019-09-10 18:29:28.020965-0300 AppName[748:1814462] Metal API Validation Disabled
    2019-09-10 18:29:28.215617-0300 AppName[748:1814462] UnityIAP UnityEarlyTransactionObserver: Added to the payment queue
    -> applicationDidBecomeActive()
    GfxDevice: creating device client; threaded=1
    Initializing Metal device caps: Apple A9 GPU
    Initialize engine version: 2019.1.7f1 (f3c4928e5742)
    CrashReporter: No pending report exists at /var/mobile/Containers/Data/Application/725F41BC-8A6E-4430-BAC1-E9A8CF602F4A/Library/Caches/CrashReports/crash-pending.plcrashUnloadTime: 0.701625 ms
    ************** IAPManager -> InitializePurchasing
    UnityIAP Version: 1.22.0
    2019-09-10 18:29:29.173034-0300 AppName[748:1814462] [MC] System group container for systemgroup.com.apple.configurationprofiles path is /private/var/containers/Shared/SystemGroup/systemgroup.com.apple.configurationprofiles
    2019-09-10 18:29:29.173317-0300 AppName[748:1814462] [MC] Reading from public effective user settings.
    2019-09-10 18:29:29.405607-0300 AppName[748:1814562] [BoringSSL] nw_protocol_boringssl_get_output_frames(1301) [C1.1:2][0x110929b70] get output frames failed, state 8196
    2019-09-10 18:29:29.405701-0300 AppName[748:1814562] [BoringSSL] nw_protocol_boringssl_get_output_frames(1301) [C1.1:2][0x110929b70] get output frames failed, state 8196
    2019-09-10 18:29:29.405880-0300 AppName[748:1814562] TIC Read Status [1:0x0]: 1:57
    2019-09-10 18:29:29.405893-0300 AppName[748:1814562] TIC Read Status [1:0x0]: 1:57
    2019-09-10 18:29:29.448516-0300 AppName[748:1814507] [BoringSSL] nw_protocol_boringssl_get_output_frames(1301) [C2.1:2][0x110930150] get output frames failed, state 8196
    2019-09-10 18:29:29.448603-0300 AppName[748:1814507] [BoringSSL] nw_protocol_boringssl_get_output_frames(1301) [C2.1:2][0x110930150] get output frames failed, state 8196
    2019-09-10 18:29:29.448775-0300 AppName[748:1814507] TIC Read Status [2:0x0]: 1:57
    2019-09-10 18:29:29.448788-0300 AppName[748:1814507] TIC Read Status [2:0x0]: 1:57
    Using configuration builder objects
    2019-09-10 18:29:29.489880-0300 AppName[748:1814462] UnityIAP: Requesting 21 products
    2019-09-10 18:29:29.507441-0300 AppName[748:1814462] UnityIAP: Requesting product data...
    Unloading 5 Unused Serialized files (Serialized files now loaded: 0)
    UnloadTime: 2.225625 ms

    Unloading 16 unused Assets to reduce memory usage. Loaded Objects now: 401.
    Total: 2.129625 ms (FindLiveObjects: 0.315667 ms CreateObjectMapping: 0.029125 ms MarkObjects: 1.437333 ms DeleteObjects: 0.346667 ms)

    Unloading 2 Unused Serialized files (Serialized files now loaded: 0)
    2019-09-10 18:29:32.300686-0300 AppName[748:1814462] UnityIAP: Received 20 products
    productID: br.com.CompanyName.AppName.subs.365.1
    transactionID: 1000000566868347
    originalTransactionIdentifier: 1000000566868347
    purchaseDate: 8/28/2019 2:17:19 PM
    originalPurchaseDate: 9/10/2019 6:44:41 PM
    subscriptionExpirationDate: 8/28/2019 3:17:19 PM
    productID: br.com.CompanyName.AppName.subs.365.1
    transactionID: 1000000566868341
    originalTransactionIdentifier: 1000000566868341
    purchaseDate: 8/28/2019 3:17:19 PM
    originalPurchaseDate: 9/10/2019 6:44:34 PM
    subscriptionExpirationDate: 8/28/2019 4:17:19 PM
    productID: br.com.CompanyName.AppName.subs.365.1
    transactionID: 1000000566868343
    originalTransactionIdentifier: 1000000566868343
    purchaseDate: 8/28/2019 4:17:19 PM
    originalPurchaseDate: 9/10/2019 6:44:37 PM
    subscriptionExpirationDate: 8/28/2019 5:17:19 PM
    productID: br.com.CompanyName.AppName.subs.365.1
    transactionID: 1000000566868334
    originalTransactionIdentifier: 1000000566868334
    purchaseDate: 8/28/2019 5:17:19 PM
    originalPurchaseDate: 9/10/2019 6:44:34 PM
    subscriptionExpirationDate: 8/28/2019 6:17:19 PM
    productID: br.com.CompanyName.AppName.subs.365.1
    transactionID: 1000000566868348
    originalTransactionIdentifier: 1000000566868348
    purchaseDate: 8/28/2019 6:17:19 PM
    originalPurchaseDate: 9/10/2019 6:44:41 PM
    subscriptionExpirationDate: 8/28/2019 7:17:19 PM
    productID: br.com.CompanyName.AppName.subs.365.1
    transactionID: 1000000566868349
    originalTransactionIdentifier: 1000000566868349
    purchaseDate: 8/28/2019 7:17:19 PM
    originalPurchaseDate: 9/10/2019 6:44:41 PM
    subscriptionExpirationDate: 8/28/2019 8:17:19 PM
    productID: br.com.CompanyName.AppName.subs.365.1
    transactionID: 1000000566868344
    originalTransactionIdentifier: 1000000566868344
    purchaseDate: 8/28/2019 10:15:40 PM
    originalPurchaseDate: 9/10/2019 6:44:37 PM
    subscriptionExpirationDate: 8/28/2019 11:15:40 PM
    productID: br.com.CompanyName.AppName.subs.365.1
    transactionID: 1000000566868338
    originalTransactionIdentifier: 1000000566868338
    purchaseDate: 9/10/2019 5:21:20 PM
    originalPurchaseDate: 9/10/2019 6:44:34 PM
    subscriptionExpirationDate: 9/10/2019 6:21:20 PM
    productID: br.com.CompanyName.AppName.subs.365.1
    transactionID: 1000000566868339
    originalTransactionIdentifier: 1000000566868339
    purchaseDate: 9/10/2019 6:21:20 PM
    originalPurchaseDate: 9/10/2019 6:44:34 PM
    subscriptionExpirationDate: 9/10/2019 7:21:20 PM
    productID: br.com.CompanyName.AppName.subs.365.1
    transactionID: 1000000566875543
    originalTransactionIdentifier: 1000000566875543
    purchaseDate: 9/10/2019 7:21:20 PM
    originalPurchaseDate: 9/10/2019 7:20:22 PM
    subscriptionExpirationDate: 9/10/2019 8:21:20 PM
    productID: br.com.CompanyName.AppName.subs.365.1
    transactionID: 1000000566890931
    originalTransactionIdentifier: 1000000566890931
    purchaseDate: 9/10/2019 8:21:20 PM
    originalPurchaseDate: 9/10/2019 8:20:25 PM
    subscriptionExpirationDate: 9/10/2019 9:21:20 PM
    productID: br.com.CompanyName.AppName.subs.365.1
    transactionID: 1000000566907845
    originalTransactionIdentifier: 1000000566907845
    purchaseDate: 9/10/2019 9:21:20 PM
    originalPurchaseDate: 9/10/2019 9:20:43 PM
    subscriptionExpirationDate: 9/10/2019 10:21:20 PM
    Unavailable product br.com.CompanyName.AppName.pt.collection.5 -br.com.CompanyName.AppName.pt.collection.5
    (Filename: ./Runtime/Export/Debug/Debug.bindings.h Line: 48)

    ****************** IAPManager -> OnInitialized()
    ******************** IAPManager -> ProcessPurchase() - productId = br.com.CompanyName.AppName.subs.365.1
    Uploading Crash Report
    NullReferenceException: Object reference not set to an instance of an object.
    at IAPManager.ProcessPurchase (UnityEngine.Purchasing.PurchaseEventArgs args) [0x00000] in <00000000000000000000000000000000>:0
    at UnityEngine.Purchasing.PurchasingManager.ProcessPurchaseIfNew (UnityEngine.Purchasing.Product product) [0x00000] in <00000000000000000000000000000000>:0
    at UnityEngine.Purchasing.PurchasingManager.OnProductsRetrieved (System.Collections.Generic.List`1[T] products) [0x00000] in <00000000000000000000000000000000>:0
    at UnityEngine.Purchasing.AppleStoreImpl.OnProductsRetrieved (System.String json) [0x00000] in <00000000000000000000000000000000>:0
    at System.Action.Invoke () [0x00000] in <00000000000000000000000000000000>:0
    at UnityEngine.Purchasing.Extension.UnityUtil.Update () [0x00000] in <00000000000000000000000000000000>:0
    (Filename: currently not available on il2cpp Line: -1)

    2019-09-10 18:29:32.578942-0300 AppName[748:1814517] [BoringSSL] nw_protocol_boringssl_get_output_frames(1301) [C3.1:2][0x1109674e0] get output frames failed, state 8196
    2019-09-10 18:29:32.579251-0300 AppName[748:1814517] [BoringSSL] nw_protocol_boringssl_get_output_frames(1301) [C3.1:2][0x1109674e0] get output frames failed, state 8196
    2019-09-10 18:29:32.582608-0300 AppName[748:1814517] TIC Read Status [3:0x0]: 1:57
    2019-09-10 18:29:32.582684-0300 AppName[748:1814517] TIC Read Status [3:0x0]: 1:57
    UnloadTime: 2.081083 ms

    Unloading 11 unused Assets to reduce memory usage. Loaded Objects now: 1778.
    Total: 12.678167 ms (FindLiveObjects: 0.602583 ms CreateObjectMapping: 0.097042 ms MarkObjects: 11.816292 ms DeleteObjects: 0.161333 ms)

    Unloading 0 Unused Serialized files (Serialized files now loaded: 0)
    UnloadTime: 8.282917 ms

    Unloading 1 unused Assets to reduce memory usage. Loaded Objects now: 1778.
    Total: 12.111375 ms (FindLiveObjects: 0.590833 ms CreateObjectMapping: 0.097875 ms MarkObjects: 11.382125 ms DeleteObjects: 0.039542 ms)

    ################ RestorePurchases
    ################ Application.platform == RuntimePlatform.IPhonePlayer
    ################ apple == null ? False
    ################ apple == UnityEngine.Purchasing.AppleStoreImpl
    2019-09-10 18:29:39.182405-0300 AppName[748:1814462] UnityIAP: Restore transactions
    2019-09-10 18:29:39.182614-0300 AppName[748:1814462] UnityIAP: RestorePurchase
    ################ END
    ************** IAPManager -> InitializePurchasing
    ************** IAPManager -> InitializePurchasing - IsInitialized = true

    ----------------------------------------------------------------------------------------------------

    From the log:
    - Apparently "ProcessPurchase(PurchaseEventArgs args)" is being called once the app is started up.
    - The "productID: br.com.CompanyName.AppName.subs.365.1" is added only once in code, it works well on android.

    This is my IAP code (it is messy because of the amount of testing I am doing):

    Code (CSharp):
    1. using System;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4. using UnityEngine.Purchasing;
    5.  
    6.  
    7. public class IAPManager : MonoBehaviour, IStoreListener
    8. {
    9.     [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSplashScreen)]
    10.     public static void OnRuntimeMethodLoad()
    11.     {
    12.         //Debug.Log("########### OnRuntimeMethodLoad() ###########");
    13.  
    14.         UnityEngine.Analytics.Analytics.initializeOnStartup = false;
    15.         UnityEngine.Analytics.Analytics.enabled = false;
    16.         UnityEngine.Analytics.Analytics.deviceStatsEnabled = false;
    17.         UnityEngine.Analytics.Analytics.limitUserTracking = true;
    18.         UnityEngine.Analytics.PerformanceReporting.enabled = false;
    19.     }
    20.  
    21.  
    22.     private static IStoreController storeController;          // The Unity Purchasing system.
    23.     private static IExtensionProvider storeExtensionProvider; // The store-specific Purchasing subsystems.
    24.  
    25.     public Action onInitialize;
    26.     public Action onInitializeFailure;
    27.  
    28.     public Action onPurchaseSuccess;
    29.     public Action onPurchaseFailure;
    30.  
    31.     public bool IsPlayServicesUpToDate
    32.     {
    33.         get;
    34.         private set;
    35.     }
    36.  
    37.     private void Start()
    38.     {
    39.         //Debug.Log("IAPManager -> Start");
    40.         DontDestroyOnLoad(gameObject);
    41.  
    42.         _instance = this;
    43.  
    44.         IsPlayServicesUpToDate = true;
    45.  
    46.         //----ios
    47.         var builder = ConfigurationBuilder.Instance(StandardPurchasingModule.Instance());
    48.         bool canMakePayments = builder.Configure<IAppleConfiguration>().canMakePayments;
    49.  
    50.         //Debug.Log("IAPManager -> canMakePayments = " + (canMakePayments));
    51.     }
    52.  
    53.     public void InitializePurchasing(List<string> promosList, List<string> subsList)
    54.     {
    55.         Debug.Log("************** IAPManager -> InitializePurchasing");
    56.         if (Application.internetReachability == NetworkReachability.NotReachable)
    57.         {
    58.             //Debug.Log("IAPManager -> InitializePurchasing -> Application.internetReachability == NetworkReachability.NotReachable");
    59.             IsPlayServicesUpToDate = true;
    60.             onInitializeFailure();
    61.             return;
    62.         }
    63.  
    64.         if (IsInitialized())
    65.         {
    66.             Debug.Log("**************  IAPManager -> InitializePurchasing - IsInitialized = true");
    67.             IsPlayServicesUpToDate = true;
    68.             return;
    69.         }
    70.  
    71.         var builder = ConfigurationBuilder.Instance(StandardPurchasingModule.Instance());
    72.  
    73.         promosList.ForEach(promoId => { builder.AddProduct(promoId, ProductType.NonConsumable); Debug.Log("promos list -> " + promoId); });
    74.  
    75.         subsList.ForEach(subId => { builder.AddProduct(subId, ProductType.Subscription); Debug.Log("subs list -> " + subId); });
    76.  
    77.         UnityPurchasing.Initialize(this, builder);
    78.     }
    79.  
    80.  
    81.     public bool IsInitialized()
    82.     {
    83.         //Debug.Log("IAPManager -> IsInitialized()");
    84.         return storeController != null && storeExtensionProvider != null;
    85.     }
    86.  
    87.     public string GetItemPrice(string productId)
    88.     {
    89.         //Debug.Log("IAPManager -> GetItemPrice");
    90.         return storeController.products.WithID(productId).metadata.localizedPriceString;
    91.     }
    92.  
    93.     public void BuyProduct(string promoId, Action onSuccess, Action onFailure)
    94.     {
    95.         //Debug.Log("IAPManager -> BuyProduct - promoId = " + promoId);
    96.         onPurchaseSuccess += onSuccess;
    97.         onPurchaseFailure += onFailure;
    98.         BuyProductID(promoId);
    99.     }
    100.  
    101.     void BuyProductID(string productId)
    102.     {
    103.         //Debug.Log("IAPManager -> BuyProductID = " + productId);
    104.         if (IsInitialized())
    105.         {
    106.             Product product = storeController.products.WithID(productId);
    107.  
    108.             if (product != null && product.availableToPurchase)
    109.             {
    110.                 storeController.InitiatePurchase(productId);
    111.             }
    112.             else
    113.             {
    114.                 //Debug.Log("BuyProductID: FAIL. Not purchasing product, either is not found or is not available for purchase");
    115.             }
    116.         }
    117.         else
    118.         {
    119.             //Debug.Log("BuyProductID FAIL. Not initialized.");
    120.         }
    121.     }
    122.  
    123.     public void RestorePurchases(Action onSuccess, Action onFailure)
    124.     {
    125.         Debug.Log("################ RestorePurchases");
    126.  
    127.         if (!IsInitialized())
    128.         {
    129.             onFailure();
    130.             Debug.Log("################ IsInitialized() = " + IsInitialized());
    131.  
    132.             return;
    133.         }
    134.  
    135.         if (Application.platform == RuntimePlatform.IPhonePlayer)
    136.         {
    137.             Debug.Log("################ Application.platform == RuntimePlatform.IPhonePlayer");
    138.             var apple = storeExtensionProvider.GetExtension<IAppleExtensions>();
    139.  
    140.             Debug.Log("################ apple == null ? " + (apple == null));
    141.             Debug.Log("################ apple == " + apple);
    142.  
    143.             apple.RestoreTransactions((result) => {
    144. //                Debug.Log("################ apple.RestoreTransactions");
    145.                 if (result)
    146.                 {
    147.                     onSuccess();
    148.                     Debug.Log("################ SUCCESS restoreTransactions((result) = " + result);
    149.                 }
    150.                 else
    151.                 {
    152.                     onFailure();
    153.                     Debug.Log("################ FAILURE restoreTransactions((result) = " + result);
    154.                 }
    155.             });
    156.         }
    157.         else
    158.         {
    159.             onFailure();
    160.             Debug.Log("################ RestorePurchases FAIL. Not supported on this platform. Current = " + Application.platform);
    161.         }
    162.  
    163.         Debug.Log("################ END");
    164.     }
    165.  
    166.     //
    167.     // --- IStoreListener
    168.     //
    169.  
    170.     public void OnInitialized(IStoreController controller, IExtensionProvider extensions)
    171.     {
    172.         Debug.Log("****************** IAPManager -> OnInitialized()");
    173.         //Debug.Log("IAPManager -> OnInitialized() - controller = " + controller);
    174.         IsPlayServicesUpToDate = true;
    175.  
    176.         storeController = controller;
    177.         storeExtensionProvider = extensions;
    178.  
    179.         onInitialize?.Invoke();
    180.     }
    181.  
    182.  
    183.     public void OnInitializeFailed(InitializationFailureReason error)
    184.     {
    185.         Debug.Log("****************** IAPManager -> OnInitializeFailed() error:\n" + error.ToString());
    186. #if UNITY_ANDROID
    187.         IsPlayServicesUpToDate &= !error.Equals(InitializationFailureReason.PurchasingUnavailable);
    188. #endif
    189.         onInitializeFailure?.Invoke();
    190.     }
    191.  
    192.  
    193.     public PurchaseProcessingResult ProcessPurchase(PurchaseEventArgs args)
    194.     {
    195.         string productId = args.purchasedProduct.definition.id;
    196.  
    197.         Debug.Log("******************** IAPManager -> ProcessPurchase() - productId = " + productId);
    198.  
    199.         onPurchaseSuccess();
    200.  
    201.         return PurchaseProcessingResult.Complete;
    202.     }
    203.  
    204.  
    205.     public void OnPurchaseFailed(Product product, PurchaseFailureReason failureReason)
    206.     {
    207.         //Debug.Log("IAPManager -> OnPurchaseFailed:\nproduct = " + product + "\nfailureReason = " + failureReason);
    208.  
    209.         if(onPurchaseFailure != null)
    210.         {
    211.             onPurchaseFailure();
    212.         }
    213.     }
    214.  
    215.     public void RestorePurchase(string prodId, Action onSuccess, Action onFailure)
    216.     {
    217.         //Debug.Log("IAPManager -> RestorePurchase - prodId = " + prodId);
    218.         Product product = storeController.products.WithID(prodId);
    219.  
    220.         if (product != null && product.hasReceipt)
    221.         {
    222.             onSuccess();
    223.         }
    224.         else
    225.         {
    226.             onFailure();
    227.         }
    228.     }
    229.  
    230.     public bool IsPurchased(string prodId)
    231.     {
    232.         //Debug.Log("IAPManager -> IsPurchased: prodId = " + prodId);
    233.         Product product = storeController.products.WithID(prodId);
    234.  
    235.         if (product != null && product.hasReceipt)
    236.         {
    237.             return true;
    238.         }
    239.  
    240.         return false;
    241.     }
    242.  
    243.     private static IAPManager _instance;
    244.     private static readonly object padlock = new object();
    245.  
    246.     private IAPManager() { }
    247.  
    248.     public static IAPManager instance
    249.     {
    250.         get
    251.         {
    252.             lock (padlock)
    253.             {
    254.                 if (_instance == null)
    255.                 {
    256.                     GameObject go = new GameObject();
    257.                     _instance = go.AddComponent<IAPManager>();
    258.                 }
    259.                 return _instance;
    260.             }
    261.         }
    262.     }
    263. }
    264.