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. We have updated the language to the Editor Terms based on feedback from our employees and community. Learn more.
    Dismiss Notice

Question In the sample project v3, where is the right place to write what the product actually does?

Discussion in 'Unity IAP' started by SwathedOrange, Aug 30, 2022.

  1. SwathedOrange

    SwathedOrange

    Joined:
    Feb 20, 2017
    Posts:
    18
    So I've added the
    buyPremium
    product and wrote its function in the
    Code (CSharp):
    1. public PurchaseProcessingResult ProcessPurchase(PurchaseEventArgs args)
    I was wondering if that is right? Also, if the player reinstalls the game, or reopens the game, does the nonconsumable product remain purchased and active?
     
  2. JeffDUnity3D

    JeffDUnity3D

    Unity Technologies

    Joined:
    May 2, 2017
    Posts:
    14,446
    Please show all your code, but sounds right. You will need to check the receipt to see if it's active, you won't get ProcessPurchase each time you start the game.
     
  3. SwathedOrange

    SwathedOrange

    Joined:
    Feb 20, 2017
    Posts:
    18
    Code (CSharp):
    1.     private static IStoreController m_StoreController;          // The Unity Purchasing system.
    2.     private static IExtensionProvider m_StoreExtensionProvider; // The store-specific Purchasing subsystems.
    3.     private static UnityEngine.Purchasing.Product test_product = null;
    4.     private IAppleExtensions m_AppleExtensions;
    5.     private IGooglePlayStoreExtensions m_GoogleExtensions;
    6.  
    7.     public static string buyPremium = "buy_premium";
    8.  
    9.     public Text myText;
    10.  
    11.     public Button btnPremium;
    12.  
    13.     private Boolean return_complete = true;
    14.  
    15.     private ConfigurationBuilder builder;
    16.  
    17.     public Button[] myButtons;
    18.     public GameObject purchaseScreen;
    19.  
    20.     void Awake()
    21.     {
    22.  
    23.     }
    24.  
    25.     void Start()
    26.     {
    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
    31.             InitializePurchasing();
    32.         }
    33.         MyDebug("In Start...");
    34.     }
    35.  
    36.     public void MyInitialize()
    37.     {
    38.         InitializePurchasing();
    39.     }
    40.  
    41.     public void InitializePurchasing()
    42.     {
    43.         if (IsInitialized())
    44.         {
    45.             return;
    46.         }
    47.  
    48.         builder = ConfigurationBuilder.Instance(StandardPurchasingModule.Instance());
    49.  
    50.         builder.AddProduct(buyPremium, ProductType.NonConsumable);
    51.  
    52.         builder.Configure<IGooglePlayConfiguration>().SetObfuscatedAccountId("test1");
    53.  
    54.         builder.Configure<IGooglePlayConfiguration>().SetDeferredPurchaseListener(OnDeferredPurchase);
    55.  
    56.         MyDebug("Starting Initialized...");
    57.         UnityPurchasing.Initialize(this, builder);
    58.  
    59.         //ProductCatalog catalog = ProductCatalog.LoadDefaultCatalog();
    60.  
    61.         //foreach (ProductCatalogItem product in catalog.allProducts)
    62.         // {
    63.         //MyDebug("Product = " + product.id);
    64.         //}
    65.     }
    66.  
    67.     void OnDeferredPurchase(Product product)
    68.     {
    69.         MyDebug($"Purchase of {product.definition.id} is deferred");
    70.         btnPremium.enabled = false;
    71.  
    72.     }
    73.     private bool IsInitialized()
    74.     {
    75.         return m_StoreController != null && m_StoreExtensionProvider != null;
    76.     }
    77.  
    78.     public void BuyNonConsumable()
    79.     {
    80.         BuyProductID(buyPremium);
    81.     }
    82.  
    83.     public void BuyNoAds()
    84.     {
    85.         //BuyProductID(NO_ADS);
    86.     }
    87.  
    88.     public void CompletePurchase()
    89.     {
    90.         if (test_product == null)
    91.             MyDebug("Cannot complete purchase, product not initialized.");
    92.         else
    93.         {
    94.             m_StoreController.ConfirmPendingPurchase(test_product);
    95.             //FetchProducts();
    96.  
    97.             MyDebug("Completed purchase with " + test_product.definition.id.ToString());
    98.         }
    99.     }
    100.  
    101.     public void FetchProducts()
    102.     {
    103.         var myHashSet = new HashSet<ProductDefinition>();
    104.  
    105.         ProductDefinition item = new ProductDefinition(buyPremium, ProductType.NonConsumable);
    106.  
    107.         myHashSet.Add(item);
    108.  
    109.         m_StoreController.FetchAdditionalProducts(myHashSet, OnProductsFetched, OnInitializeFailed);
    110.     }
    111.  
    112.     public void ToggleComplete()
    113.     {
    114.         return_complete = !return_complete;
    115.         MyDebug("Complete = " + return_complete.ToString());
    116.  
    117.     }
    118.  
    119.     public void ChangeUser()
    120.     {
    121.         builder.Configure<IGooglePlayConfiguration>().SetObfuscatedAccountId("test2");
    122.     }
    123.  
    124.     public void OnProductsFetched()
    125.     {
    126.         MyDebug("In fetch...");
    127.     }
    128.  
    129.     public void RestorePurchases()
    130.     {
    131.         m_StoreExtensionProvider.GetExtension<IAppleExtensions>().RestoreTransactions(result => {
    132.             if (result)
    133.            {
    134.                MyDebug("Restore purchases succeeded.");
    135.            }
    136.             else
    137.             {
    138.                MyDebug("Restore purchases failed.");
    139.             }
    140.         });
    141.     }
    142.  
    143.     public void BuyProductID(string productId)
    144.     {
    145.         if (IsInitialized())
    146.         {
    147.             UnityEngine.Purchasing.Product product = m_StoreController.products.WithID(productId);
    148.  
    149.             if (product != null && product.availableToPurchase)
    150.             {
    151.                 MyDebug(string.Format("Purchasing product:" + product.definition.id.ToString()));
    152.                 m_StoreController.InitiatePurchase(product);
    153.  
    154.  
    155.             }
    156.             else
    157.             {
    158.                 MyDebug("BuyProductID: FAIL. Not purchasing product, either is not found or is not available for purchase");
    159.             }
    160.         }
    161.         else
    162.         {
    163.             MyDebug("BuyProductID FAIL. Not initialized.");
    164.         }
    165.     }
    166.  
    167.     public void ListProducts()
    168.     {
    169.         foreach (UnityEngine.Purchasing.Product item in m_StoreController.products.all)
    170.         {
    171.             if (item.receipt != null)
    172.             {
    173.                 MyDebug("Receipt found for Product = " + item.definition.id.ToString());
    174.             }
    175.         }
    176.     }
    177.     public void OnInitialized(IStoreController controller, IExtensionProvider extensions)
    178.     {
    179.         MyDebug("OnInitialized: PASS");
    180.  
    181.         m_StoreController = controller;
    182.         m_StoreExtensionProvider = extensions;
    183.         m_AppleExtensions = extensions.GetExtension<IAppleExtensions>();
    184.         m_GoogleExtensions = extensions.GetExtension<IGooglePlayStoreExtensions>();
    185.  
    186.         Dictionary<string, string> dict = m_AppleExtensions.GetIntroductoryPriceDictionary();
    187.  
    188.         foreach (UnityEngine.Purchasing.Product item in controller.products.all)
    189.         {
    190.             if (item.receipt != null)
    191.             {
    192.                 string intro_json = (dict == null || !dict.ContainsKey(item.definition.storeSpecificId)) ? null : dict[item.definition.storeSpecificId];
    193.  
    194.                 if (item.definition.type == ProductType.Subscription)
    195.                 {
    196.                     SubscriptionManager p = new SubscriptionManager(item, null);
    197.                     SubscriptionInfo info = p.getSubscriptionInfo();
    198.                     MyDebug("SubInfo: " + info.getProductId().ToString());
    199.                     MyDebug("getExpireDate: " + info.getExpireDate().ToString());
    200.                     MyDebug("isSubscribed: " + info.isSubscribed().ToString());
    201.                 }
    202.             }
    203.         }
    204.     }
    205.  
    206.     public void OnPurchaseDeferred(Product product)
    207.     {
    208.  
    209.         MyDebug("Deferred product " + product.definition.id.ToString());
    210.     }
    211.  
    212.     public void OnInitializeFailed(InitializationFailureReason error)
    213.     {
    214.         // Purchasing set-up has not succeeded. Check error for reason. Consider sharing this reason with the user.
    215.         MyDebug("OnInitializeFailed InitializationFailureReason:" + error);
    216.     }
    217.  
    218.  
    219.     public PurchaseProcessingResult ProcessPurchase(PurchaseEventArgs args)
    220.     {
    221.         test_product = args.purchasedProduct;
    222.  
    223.         //var validator = new CrossPlatformValidator(GooglePlayTangle.Data(), AppleTangle.Data(), Application.identifier);
    224.  
    225.         // var result = validator.Validate(args.purchasedProduct.receipt);
    226.  
    227.         // foreach (IPurchaseReceipt productReceipt in result)
    228.         // {
    229.         //     MyDebug("Valid receipt for " + productReceipt.productID.ToString());
    230.         // }
    231.  
    232.         //MyDebug("Validate = " + result.ToString());
    233.  
    234.  
    235.  
    236.         MyDebug("Receipt:" + test_product.receipt.ToString());
    237.  
    238.         if (return_complete)
    239.         {
    240.             MyDebug(string.Format("ProcessPurchase: Complete. Product:" + args.purchasedProduct.definition.id + " - " + test_product.transactionID.ToString()));
    241.  
    242.             SetPremiumState();
    243.  
    244.             return PurchaseProcessingResult.Complete;
    245.         }
    246.         else
    247.         {
    248.             MyDebug(string.Format("ProcessPurchase: Pending. Product:" + args.purchasedProduct.definition.id + " - " + test_product.transactionID.ToString()));
    249.             return PurchaseProcessingResult.Pending;
    250.         }
    251.  
    252.     }
    253.  
    254.     private void SetPremiumState()
    255.     {
    256.         foreach (var button in myButtons)
    257.         {
    258.             button.gameObject.SetActive(false);
    259.         }
    260.         purchaseScreen.SetActive(false);
    261.     }
    262.  
    263.     public void OnPurchaseFailed(UnityEngine.Purchasing.Product product, PurchaseFailureReason failureReason)
    264.     {
    265.         MyDebug(string.Format("OnPurchaseFailed: FAIL. Product: '{0}', PurchaseFailureReason: {1}", product.definition.storeSpecificId, failureReason));
    266.     }
    267.  
    268.     private void MyDebug(string debug)
    269.     {
    270.         Debug.Log(debug);
    271.         myText.text += "\r\n" + debug;
    272.     }
    Also, in the
    public PurchaseProcessingResult ProcessPurchase(PurchaseEventArgs args)
    , I can leave the validator and the result commented out?
     
  4. JeffDUnity3D

    JeffDUnity3D

    Unity Technologies

    Joined:
    May 2, 2017
    Posts:
    14,446
    Perfect! Nicely done. And yes, you can comment out that portion for now. But I believe Apple requires receipt validation when you submit your app, they will try to inject a fake receipt to see if your game detects and rejects it. On Android, there is significant fraud including cheat apps, you definitely want some form of receipt validation to help fight it.
     
  5. SwathedOrange

    SwathedOrange

    Joined:
    Feb 20, 2017
    Posts:
    18
    How would you prefer to do it? I was thinking
    PlayerPrefs
    . Don't need any strong data because my game doesn't really have anything that can put you in advantage if you hack it, premium just unlocks more categories. But then again, if the player uninstalls the game, wont
    PlayerPreft
    data go away and the game will ask for purchasing again?
     
  6. JeffDUnity3D

    JeffDUnity3D

    Unity Technologies

    Joined:
    May 2, 2017
    Posts:
    14,446
    If you want receipt validation, then uncomment out the corresponding code, it will check the receipt. If the user purchases a non-consumable and reinstalls the app, ProcessPurchase will be triggered automatically and you'll have the receipt in the parameter object. After that, the receipt is stored in the device cache and will be available in the product controller as demonstrated in the Sample code and you can check it in or after OnInitialized is called. That is what the ListProducts method does.
     
  7. SwathedOrange

    SwathedOrange

    Joined:
    Feb 20, 2017
    Posts:
    18
    Okay thanks a lot for that. One more question tho, do I have to change anything in the AppleTangle and GooglePlayTangle cs files(the order or the key)?
     
  8. JeffDUnity3D

    JeffDUnity3D

    Unity Technologies

    Joined:
    May 2, 2017
    Posts:
    14,446
    Yes, you do need to use your own Google Play Store Public key from your Google dashboard https://docs.unity3d.com/Manual/UnityIAPValidatingReceipts.html
     
  9. SwathedOrange

    SwathedOrange

    Joined:
    Feb 20, 2017
    Posts:
    18
    Okay I've done that. It is giving me this error when i try to purchase the product tho.
    NotImplementedException: The method or operation is not implemented.
     
  10. JeffDUnity3D

    JeffDUnity3D

    Unity Technologies

    Joined:
    May 2, 2017
    Posts:
    14,446
    What line is the error on? That would likely be expected if running in the Editor, it is only meant for the device. You could wrap it in a try/catch block otherwise.
     
  11. SwathedOrange

    SwathedOrange

    Joined:
    Feb 20, 2017
    Posts:
    18
    It is on line cs:239, which is
    var validator = new CrossPlatformValidator(GooglePlayTangle.Data(), AppleTangle.Data(), Application.identifier);

    Okay, Ill try doing that
     
  12. SwathedOrange

    SwathedOrange

    Joined:
    Feb 20, 2017
    Posts:
    18
    I tried testing the app but when I purchase the product nothing happens. Let me get something clear, in GooglePlayTangle.cs, I changed the data to what is required. What about the order and the key?
     
  13. John_Corbett

    John_Corbett

    Unity Technologies

    Joined:
    May 17, 2019
    Posts:
    151
    Hi @SwathedOrange.

    Do you mean you changed the data manually? Normally that file is generated using the Obfuscator tool in the Editor.
     
  14. SwathedOrange

    SwathedOrange

    Joined:
    Feb 20, 2017
    Posts:
    18
    Yea I can't find the tool, it's not under Services > In-App Purchasing > IAP Receipt Validation Obfuscator for me for some reason.
     

    Attached Files:

  15. John_Corbett

    John_Corbett

    Unity Technologies

    Joined:
    May 17, 2019
    Posts:
    151
    It should be in your top menus, not the Project Settings.

    2022-09-14 09_28_45-.png

    Which IAP package version are you using? If you don't see this it might be under "Unity IAP" instead of "Services"
     
  16. SwathedOrange

    SwathedOrange

    Joined:
    Feb 20, 2017
    Posts:
    18
    O wow I can't believe it. I've been literally searching for hours, online too before i asked here only to find out now its been there this whole time. Didn't know I was THAT blind. Thanks a lot for that image, it actually helped, wow
     
  17. John_Corbett

    John_Corbett

    Unity Technologies

    Joined:
    May 17, 2019
    Posts:
    151
    Don't feel so bad. We've done work to make this available in the Project Settings as well for easier discovery, but unfortunately we can only add this control in Unity 2022 and later. If you're still on 2021 or older, we can't control the project settings from the package and have to publish them independently to the editor, which is why that update isn't present there.
     
  18. SwathedOrange

    SwathedOrange

    Joined:
    Feb 20, 2017
    Posts:
    18
    Ive managed to get it all working nicely, thanks for all the help, you guys are great!
     
    John_Corbett likes this.
  19. SwathedOrange

    SwathedOrange

    Joined:
    Feb 20, 2017
    Posts:
    18
    Sorry to bother you again, but I've stumbled on a problem. When purchased it all works as it should, the problem appears when the user re-enters the app. The content becomes locked again
     
  20. JeffDUnity3D

    JeffDUnity3D

    Unity Technologies

    Joined:
    May 2, 2017
    Posts:
    14,446
    So how are you unlocking the content? Please share your code. In the Sample project, you can look at the ListProducts method and see if the product has a receipt.
     
  21. SwathedOrange

    SwathedOrange

    Joined:
    Feb 20, 2017
    Posts:
    18
    Okay so I've used the sample code 4.
    I've added in the start
    Code (CSharp):
    1. if (m_StoreController == null)
    2.         {
    3.             // Begin to configure our connection to Purchasing
    4.             InitializePurchasing();
    5.         }
    for some reason, is that why its not working as it should?

    Code (CSharp):
    1. using System;
    2. using UnityEngine;
    3. using UnityEngine.Purchasing;
    4. using UnityEngine.UI;
    5. using UnityEngine.Purchasing.Security;
    6.  
    7. using Unity.Services.Core;
    8. using Unity.Services.Analytics;
    9. using System.Collections.Generic;
    10.  
    11. public class IAPManager : MonoBehaviour, IStoreListener
    12. {
    13.     static public IAPManager Instance;
    14.  
    15.     private static IStoreController m_StoreController;          // The Unity Purchasing system.
    16.     private static IExtensionProvider m_StoreExtensionProvider; // The store-specific Purchasing subsystems.
    17.     private static UnityEngine.Purchasing.Product test_product = null;
    18.  
    19.     IGooglePlayStoreExtensions m_GooglePlayStoreExtensions;
    20.  
    21.     public static string buy_premium = "buy_premium";
    22.  
    23.     public Button[] myButtons;
    24.     public GameObject purchasingScreen;
    25.     public static bool isPurchased = false;
    26.  
    27.     private Boolean return_complete = true;
    28.  
    29.     public Button restorePurchases;
    30.  
    31.     async void Start()
    32.     {
    33.         Instance = this;
    34.         if (Application.platform == RuntimePlatform.Android)
    35.         {
    36.             restorePurchases.gameObject.SetActive(false);
    37.         }
    38.         if (m_StoreController == null)
    39.         {
    40.             // Begin to configure our connection to Purchasing
    41.             InitializePurchasing();
    42.         }
    43.         try
    44.         {
    45.             await UnityServices.InitializeAsync();
    46.             List<string> consentIdentifiers = await AnalyticsService.Instance.CheckForRequiredConsents();
    47.         }
    48.         catch (ConsentCheckException e)
    49.         {
    50.             MyDebug("Consent: :" + e.ToString());  // Something went wrong when checking the GeoIP, check the e.Reason and handle appropriately.
    51.         }
    52.  
    53.         MyAction += myFunction;
    54.     }
    55.  
    56.  
    57.  
    58.     public void InitializePurchasing()
    59.     {
    60.         if (IsInitialized())
    61.         {
    62.             return;
    63.         }
    64.  
    65.         var builder = ConfigurationBuilder.Instance(StandardPurchasingModule.Instance());
    66.  
    67.         builder.Configure<IGooglePlayConfiguration>().SetDeferredPurchaseListener(OnDeferredPurchase);
    68.         builder.Configure<IGooglePlayConfiguration>().SetQueryProductDetailsFailedListener(MyAction);
    69.  
    70.         builder.AddProduct(buy_premium, ProductType.NonConsumable);
    71.  
    72.         UnityPurchasing.Initialize(this, builder);
    73.     }
    74.  
    75.     private event Action<int> MyAction;
    76.  
    77.     void myFunction(int myInt)
    78.     {
    79.         MyDebug("Listener = " + myInt.ToString());
    80.     }
    81.     private bool IsInitialized()
    82.     {
    83.         return m_StoreController != null && m_StoreExtensionProvider != null;
    84.     }
    85.  
    86.     void OnDeferredPurchase(UnityEngine.Purchasing.Product product)
    87.     {
    88.         MyDebug($"Purchase of {product.definition.id} is deferred");
    89.     }
    90.  
    91.     public void BuyPremium()
    92.     {
    93.         BuyProductID(buy_premium);
    94.     }
    95.  
    96.     public void CompletePurchase()
    97.     {
    98.         if (test_product == null)
    99.             MyDebug("Cannot complete purchase, product not initialized.");
    100.         else
    101.         {
    102.             m_StoreController.ConfirmPendingPurchase(test_product);
    103.             MyDebug("Completed purchase with " + test_product.transactionID.ToString());
    104.         }
    105.  
    106.     }
    107.  
    108.     public void ToggleComplete()
    109.     {
    110.         return_complete = !return_complete;
    111.         MyDebug("Complete = " + return_complete.ToString());
    112.  
    113.     }
    114.     public void RestorePurchases()
    115.     {
    116.         m_StoreExtensionProvider.GetExtension<IAppleExtensions>().RestoreTransactions(result =>
    117.         {
    118.  
    119.             if (result)
    120.             {
    121.                 MyDebug("Restore purchases succeeded.");
    122.                 Dictionary<string, object> parameters = new Dictionary<string, object>()
    123.                 {
    124.                     { "restore_success", true },
    125.                 };
    126.                 AnalyticsService.Instance.CustomData("myRestore", parameters);
    127.             }
    128.             else
    129.             {
    130.                 MyDebug("Restore purchases failed.");
    131.                 Dictionary<string, object> parameters = new Dictionary<string, object>()
    132.                 {
    133.                     { "restore_success", false },
    134.                 };
    135.                 AnalyticsService.Instance.CustomData("myRestore", parameters);
    136.             }
    137.  
    138.             AnalyticsService.Instance.Flush();
    139.         });
    140.  
    141.     }
    142.  
    143.     void BuyProductID(string productId)
    144.     {
    145.         if (IsInitialized())
    146.         {
    147.             UnityEngine.Purchasing.Product product = m_StoreController.products.WithID(productId);
    148.  
    149.             if (product != null && product.availableToPurchase)
    150.             {
    151.                 MyDebug(string.Format("Purchasing product:" + product.definition.id.ToString()));
    152.                 m_StoreController.InitiatePurchase(product);
    153.             }
    154.             else
    155.             {
    156.                 MyDebug("BuyProductID: FAIL. Not purchasing product, either is not found or is not available for purchase");
    157.             }
    158.         }
    159.         else
    160.         {
    161.             MyDebug("BuyProductID FAIL. Not initialized.");
    162.         }
    163.     }
    164.  
    165.     public void OnInitialized(IStoreController controller, IExtensionProvider extensions)
    166.     {
    167.         MyDebug("OnInitialized: PASS");
    168.  
    169.         m_StoreController = controller;
    170.         m_StoreExtensionProvider = extensions;
    171.         m_GooglePlayStoreExtensions = extensions.GetExtension<IGooglePlayStoreExtensions>();
    172.  
    173.     }
    174.  
    175.     public void OnInitializeFailed(InitializationFailureReason error)
    176.     {
    177.         // Purchasing set-up has not succeeded. Check error for reason. Consider sharing this reason with the user.
    178.         MyDebug("OnInitializeFailed InitializationFailureReason:" + error);
    179.     }
    180.  
    181.     public PurchaseProcessingResult ProcessPurchase(PurchaseEventArgs args)
    182.     {
    183.         test_product = args.purchasedProduct;
    184.  
    185.         var validator = new CrossPlatformValidator(GooglePlayTangle.Data(), AppleTangle.Data(), Application.identifier);
    186.         var result = validator.Validate(args.purchasedProduct.receipt);
    187.         MyDebug("Validate = " + result.ToString());
    188.  
    189.         if (m_GooglePlayStoreExtensions.IsPurchasedProductDeferred(test_product))
    190.         {
    191.             //The purchase is Deferred.
    192.             //Therefore, we do not unlock the content or complete the transaction.
    193.             //ProcessPurchase will be called again once the purchase is Purchased.
    194.             return PurchaseProcessingResult.Pending;
    195.         }
    196.         if (return_complete)
    197.         {
    198.             PremiumState();
    199.             Debug.Log("Complete");
    200.             MyDebug(string.Format("ProcessPurchase: Complete. Product:" + args.purchasedProduct.definition.id + " - " + test_product.transactionID.ToString()));
    201.             return PurchaseProcessingResult.Complete;
    202.         }
    203.         else
    204.         {
    205.             MyDebug(string.Format("ProcessPurchase: Pending. Product:" + args.purchasedProduct.definition.id + " - " + test_product.transactionID.ToString()));
    206.             return PurchaseProcessingResult.Pending;
    207.         }
    208.  
    209.     }
    210.  
    211.     void PremiumState()
    212.     {
    213.         foreach(var button in myButtons)
    214.         {
    215.             button.gameObject.SetActive(false);
    216.         }
    217.         purchasingScreen.SetActive(false);
    218.         isPurchased = true;
    219.     }
    220.  
    221.     public void OnPurchaseFailed(UnityEngine.Purchasing.Product product, PurchaseFailureReason failureReason)
    222.     {
    223.         MyDebug(string.Format("OnPurchaseFailed: FAIL. Product: '{0}', PurchaseFailureReason: {1}", product.definition.storeSpecificId, failureReason));
    224.     }
    225.  
    226.     public void ListPurchases()
    227.     {
    228.         foreach (UnityEngine.Purchasing.Product item in m_StoreController.products.all)
    229.         {
    230.             if (item.hasReceipt)
    231.             {
    232.                 MyDebug("In list for  " + item.receipt.ToString());
    233.             }
    234.             else
    235.                 MyDebug("No receipt for " + item.definition.id.ToString());
    236.         }
    237.     }
    238.     private void MyDebug(string debug)
    239.     {
    240.         Debug.Log(debug);
    241.     }
    242. }
    243.  
     
  22. JeffDUnity3D

    JeffDUnity3D

    Unity Technologies

    Joined:
    May 2, 2017
    Posts:
    14,446
    @SwathedOrange So you know, ProcessPurchase is only triggered when the user purchases the product. The next time they start the app, it won't be triggered and your PremiumState won't be called. You'll want to go through your product controller like in ListProducts and see if the user has a receipt, and call PremiumState if so (for example).
     
  23. SwathedOrange

    SwathedOrange

    Joined:
    Feb 20, 2017
    Posts:
    18
    Okay that explains it. Do I just put ListProducts in the Start() or after Initialization?
     
  24. JeffDUnity3D

    JeffDUnity3D

    Unity Technologies

    Joined:
    May 2, 2017
    Posts:
    14,446
    After IAP initialization. You can use item.hasReceipt
     
  25. SwathedOrange

    SwathedOrange

    Joined:
    Feb 20, 2017
    Posts:
    18
    Like this?

    Code (CSharp):
    1. async void Start()
    2.     {
    3.         Instance = this;
    4.         if (Application.platform == RuntimePlatform.Android)
    5.         {
    6.             restorePurchases.gameObject.SetActive(false);
    7.         }
    8.         if (m_StoreController == null)
    9.         {
    10.             // Begin to configure our connection to Purchasing
    11.             InitializePurchasing();
    12.         }
    13.         ListPurchases();
    14.         try
    15.         {
    16.             await UnityServices.InitializeAsync();
    17.             List<string> consentIdentifiers = await AnalyticsService.Instance.CheckForRequiredConsents();
    18.         }
    19.         catch (ConsentCheckException e)
    20.         {
    21.             MyDebug("Consent: :" + e.ToString());  // Something went wrong when checking the GeoIP, check the e.Reason and handle appropriately.
    22.         }
    23.  
    24.         MyAction += myFunction;
    25.     }
     
  26. JeffDUnity3D

    JeffDUnity3D

    Unity Technologies

    Joined:
    May 2, 2017
    Posts:
    14,446
    @SwathedOrange No, you probably want to place it in OnInitialized (that's where IAP initialization succeeds). And please show your updated ListProducts code.
     
  27. SwathedOrange

    SwathedOrange

    Joined:
    Feb 20, 2017
    Posts:
    18
    Okay.

    Code (CSharp):
    1. public void OnInitialized(IStoreController controller, IExtensionProvider extensions)
    2.     {
    3.         MyDebug("OnInitialized: PASS");
    4.  
    5.         m_StoreController = controller;
    6.         m_StoreExtensionProvider = extensions;
    7.         m_GooglePlayStoreExtensions = extensions.GetExtension<IGooglePlayStoreExtensions>();
    8.  
    9.         ListPurchases();
    10.     }
    ListPurchases:

    Code (CSharp):
    1. public void ListPurchases()
    2.     {
    3.         foreach (UnityEngine.Purchasing.Product item in m_StoreController.products.all)
    4.         {
    5.             if (item.hasReceipt)
    6.             {
    7.                 MyDebug("In list for  " + item.receipt.ToString());
    8.                 PremiumState();
    9.             }
    10.             else
    11.                 MyDebug("No receipt for " + item.definition.id.ToString());
    12.         }
    13.     }
     
  28. JeffDUnity3D

    JeffDUnity3D

    Unity Technologies

    Joined:
    May 2, 2017
    Posts:
    14,446
    Looks good!
     
    SwathedOrange likes this.
  29. SwathedOrange

    SwathedOrange

    Joined:
    Feb 20, 2017
    Posts:
    18
    New problem appeared! The premium content unlocks after user re-enters the game now as it should. But after returning to the menu scene after playing one category, the content locks itself again.
     
  30. SwathedOrange

    SwathedOrange

    Joined:
    Feb 20, 2017
    Posts:
    18
    Okay I've solved it by putting ListPurchases() in the Start() but you told me to not do it. Now I don't know if I should leave it like that because of what u've said or no?

    Code (CSharp):
    1.     async void Start()
    2.     {
    3.         Instance = this;
    4.         if (Application.platform == RuntimePlatform.Android)
    5.         {
    6.             restorePurchases.gameObject.SetActive(false);
    7.         }
    8.         if (m_StoreController == null || PlayerPrefs.GetInt("premium") == 40)
    9.         {
    10.             // Begin to configure our connection to Purchasing
    11.             InitializePurchasing();
    12.         }
    13.         ListPurchases();
    14.         try
    15.         {
    16.             await UnityServices.InitializeAsync();
    17.             List<string> consentIdentifiers = await AnalyticsService.Instance.CheckForRequiredConsents();
    18.         }
    19.         catch (ConsentCheckException e)
    20.         {
    21.             MyDebug("Consent: :" + e.ToString());  // Something went wrong when checking the GeoIP, check the e.Reason and handle appropriately.
    22.         }
    23.  
    24.         MyAction += myFunction;
    25.     }
     
  31. JeffDUnity3D

    JeffDUnity3D

    Unity Technologies

    Joined:
    May 2, 2017
    Posts:
    14,446