Search Unity

  1. Megacity Metro Demo now available. Download now.
    Dismiss Notice
  2. Unity support for visionOS is now available. Learn more in our blog post.
    Dismiss Notice

Unity IAP v1.20.0 upgrade questions

Discussion in 'Unity IAP' started by jason_yak, Jun 29, 2018.

  1. jason_yak

    jason_yak

    Joined:
    Aug 25, 2016
    Posts:
    514
    @nicholasr
    Hi there,

    I just got this update and was just wondering about change mentioned where some assets have been moved to Assets/Resources/

    I can see that the folder has been created after importing the updated plugin but it's empty. Is there any upgrade instructions if we already had the plugin installed? should i move any particular files over to this new location?

    Also I was just wondering if you have any details on the new method to inspect the sku details for Google Play products?

    Thanks!

    Jason
     
  2. JeffDUnity3D

    JeffDUnity3D

    Joined:
    May 2, 2017
    Posts:
    14,446
    No files should need to be moved. Are you using Codeless IAP and using the Catalog feature? This was the reason for the move, as your product Catalog would be reset. I have asked the IAP team if they have updated documentation regarding the new features.
     
  3. jason_yak

    jason_yak

    Joined:
    Aug 25, 2016
    Posts:
    514
    No I’ve implemented my own store controller and not using the codeless. I just wondered because there’s still two files sitting on the old Resources folder within the UnityPurchasing.... I wish Unity Packages had the ability to move or delete outdated files rather than being totally additive. It would make updating plugins cleaner.
     
  4. jason_yak

    jason_yak

    Joined:
    Aug 25, 2016
    Posts:
    514
    I seem to be getting an exception thrown when trying to call InitiatePurchase:

    Code (CSharp):
    1.  UnityIAP: JSONStore exception handling developerPayload: System.InvalidCastException: Unable to cast object of type 'Double' to type 'Dictionary`2'.
    2. W/Unity   ( 9261):   at UnityEngine.Purchasing.JSONStore.Purchase (UnityEngine.Purchasing.ProductDefinition product, System.String developerPayload) [0x00000] in <filename unknown>:0
    3. W/Unity   ( 9261):   at UnityEngine.Purchasing.PurchasingManager.InitiatePurchase (UnityEngine.Purchasing.Product product, System.String developerPayload) [0x00000] in <filename unknown>:0
    4. W/Unity   ( 9261):   at IAPManager+<purchaseRoutine>c__Iterator0.MoveNext () [0x00000] in <filename unknown>:0
    5. W/Unity   ( 9261):   at UnityEngine.SetupCoroutine.InvokeMoveNext (IEnumerator enumerator, IntPtr returnValueAddress) [0x00000] in <filename unknown>:0
    I'm sending a string for the payload in the InitiatePurchase method. I'm using v1.20.0. Any clues what's happening? would be happy to learn I'm doing something silly.. but can't see any obvious issues. The exception seems to be coming from an internal IAP purchase method.

    If it helps the OnPurchaseFailed event is called and sends: PurchaseFailureReason: DuplicateTransaction. On that note, I've been unable to find any decent info on how to handle a duplicate transaction. Why is that called? Earlier in the logs I'd also noticed a log that came in after the app was installed and the restore purchases process seemed to fail, it just logged this and didn't trigger any ProcessPurchase events:
    Already recorded transaction GPA.####.###.ID.HERE
     
  5. JeffDUnity3D

    JeffDUnity3D

    Joined:
    May 2, 2017
    Posts:
    14,446
    If you are trying to purchase an item a second time, you could get DuplicateTransaction. This would be expected for non-consumable items, or consumable items if they are left in Pending on the initial purchase. Please show your code that you are using regarding the payload issue, although this new issue would be better placed into a new thread. It seems from the error you are sending a numeric value and not a string value.
     
  6. jason_yak

    jason_yak

    Joined:
    Aug 25, 2016
    Posts:
    514
    Hi Jeff, thanks for the response. I’ll start a new thread after I investigate further to make sure I’m not doing anything wrong, but I’m definitely sending a string. The string is a hash value, the exception seems to be coming from an internal purchase method. Anyway... I’ll double check things first case there is something I’m doing wrong.... regarding the duplicate transaction, this was generated during a restore purchase process during first run of an android app ie. it’s not user initiated and not my code running a purchase attempt. The duplicate transaction fail happened instead of it running a process purchase event on first run.
     
  7. JeffDUnity3D

    JeffDUnity3D

    Joined:
    May 2, 2017
    Posts:
    14,446
    Can you elaborate regarding a "restore purchase process" ? What action did you take? A restore operation always implies there was an initial purchase. If this user purchased an item on a different Android device, you would still receive that error.
     
  8. jason_yak

    jason_yak

    Joined:
    Aug 25, 2016
    Posts:
    514
    An instance of the app was installed, my test account was whitelisted and a test purchase was made using the Google testing sandbox. I had uninstalled the app and then reinstalled a newer version of the app, when the restore process automatically ran on first run of the newly installed app gave the result I described in the previous message where it failed with a Duplicate Transaction error instead of Process Purchase event. So yes there had been an initial purchase but it was't on a different device that the restore was happening on.
     
  9. jason_yak

    jason_yak

    Joined:
    Aug 25, 2016
    Posts:
    514
    It sounds like the chain of events that caused this to happen are going to be very hard to track down and reproduce. It's made me wonder if the uninstallation process when working with alpha/beta/sideloaded builds are a little flaky and causing issues. But If this was to happen again, I'm thinking that a possible workaround might be that if receiving a duplicate transaction fail during a restore process (so first run of the app...ie. android restore, not iOS user initiated restore) that instead of treating this like an error that I instead manually force a receipt validation and process purchase event for the product that triggered the duplicate transaction so I end up in the same place as though the restore was triggered properly ie. that the previously purchased product was restored for the user. It's a bit of a bandaid solution though... I'll see if I can replicate the issue and figure out the circumstances that caused a duplicate transaction to happen instead of a normal restore process purchase event.
     
  10. JeffDUnity3D

    JeffDUnity3D

    Joined:
    May 2, 2017
    Posts:
    14,446
  11. jason_yak

    jason_yak

    Joined:
    Aug 25, 2016
    Posts:
    514
    Thanks but it's not been a problem with that. The first time I get a ProcessPurchase event I'm able to successfully validate the purchase and return a Complete response. But after uninstalling and reinstalling the ProcessPurchase event doesn't event fire so I'm not even able to return a Pending response. Anyway, I appreciate the suggestions, I'll keep testing and see what I can learn. Thanks =)
     
  12. hprnv

    hprnv

    Joined:
    Apr 23, 2014
    Posts:
    2
    looks like I have same problem. Yesterday I got a request from our QA engineer that he install build on Android, buy inapp, delete build, and install it again and game behaviour was like inapp was buyed already (at this point all was right and just some fixes at game logic I needed to do), but few hours later when I test new build with adb logcat I noticed that restoring process not triggering anymore. I decided that it was my mistake comewhere in code and install build which was tested by QA engineer earlier this day, but there was same problem.

    part of log when event fire correctly(change some private date):
    Code (CSharp):
    1.  
    2. UnityIAP StandardPurchasingModule Version: 1.15.0
    3.  
    4. (Filename: ./artifacts/generated/common/runtime/DebugBindings.gen.cpp Line: 51)
    5.  
    6. Receipt is valid. Contents:
    7.  
    8. (Filename: ./artifacts/generated/common/runtime/DebugBindings.gen.cpp Line: 51)
    9.  
    10. com.xxxxxxxxx.xxxxxxx.xxxxxxxxxxx
    11.  
    12. (Filename: ./artifacts/generated/common/runtime/DebugBindings.gen.cpp Line: 51)
    13.  
    14. 07/03/2018: 06:15:24
    15.  
    16. (Filename: ./artifacts/generated/common/runtime/DebugBindings.gen.cpp Line: 51)
    17.  
    18. GPA.3307-6422-1674-84027
    19.  
    20. (Filename: ./artifacts/generated/common/runtime/DebugBindings.gen.cpp Line: 51)
    21.  
    part of log when restore doesn't fire:
    Code (CSharp):
    1.  
    2. 07-04 13:11:56.917 16923 16956 I Unity   : UnityIAP StandardPurchasingModule Version: 1.15.0
    3. 07-04 13:11:56.917 16923 16956 I Unity   :
    4. 07-04 13:11:56.917 16923 16956 I Unity   : (Filename: ./artifacts/generated/common/runtime/DebugBindings.gen.cpp Line: 51)
    5.  
    But I use iap 1.15.0 version.

    My Store class:
    Code (CSharp):
    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4. using System;
    5. using DG.Tweening;
    6. using UnityEngine.Purchasing;
    7. using UnityEngine.Purchasing.Security;
    8.  
    9. public class Store : IStoreListener
    10. {
    11.     public static Action InitedEvent;
    12.     public static Action BuyProcedureEndEvent;
    13.     public static Action<int, TypeMoney> BuyCompliteEvent;
    14.     public static string SoundFine;
    15.     public static string SoundFatal;
    16.  
    17.     private static Store instance;
    18.  
    19.     private static IStoreController mStoreController; // The Unity Purchasing system.
    20.     private static IExtensionProvider mStoreExtensionProvider; // The store-specific Purchasing subsystems
    21.  
    22.     public static void Init()
    23.     {
    24.         if (IsInitialized())
    25.             return;
    26.  
    27.         instance = new Store();
    28.         var builder = ConfigurationBuilder.Instance(StandardPurchasingModule.Instance());
    29.         for (int i = 0; i < StoreItems.GetCountItems; i++)
    30.         {
    31.             var item = StoreItems.Get(i);
    32.             if (item.Product != TypeProduct.Free)
    33.                 builder.AddProduct(item.Key,
    34.                         (ProductType)Enum.Parse(typeof(ProductType), item.Product.ToString()));
    35.         }
    36.  
    37.         UnityPurchasing.Initialize(instance, builder);
    38.     }
    39.  
    40.     public static bool IsInitialized()
    41.     {
    42.         return mStoreController != null && mStoreExtensionProvider != null;
    43.     }
    44.  
    45.     public void OnInitialized(IStoreController controller, IExtensionProvider extensions)
    46.     {
    47.         mStoreController = controller;
    48.         mStoreExtensionProvider = extensions;
    49.         if (InitedEvent != null)
    50.             InitedEvent();
    51.     }
    52.  
    53.     public PurchaseProcessingResult ProcessPurchase(PurchaseEventArgs args)
    54.     {
    55.         Debug.Log("+++PROCESSPURCHASE+++");
    56.         bool validPurchase = true;
    57.  
    58. #if !UNITY_EDITOR
    59. #if UNITY_ANDROID || UNITY_IOS || UNITY_STANDALONE_OSX
    60.  
    61.         var validator = new CrossPlatformValidator(GooglePlayTangle.Data(),
    62.         AppleTangle.Data(), Application.identifier);
    63.  
    64.         try {
    65.            
    66.             // On Google Play, result has a single product ID.
    67.             // On Apple stores, receipts contain multiple products.
    68.             var result = validator.Validate(args.purchasedProduct.receipt);
    69.             // For informational purposes, we list the receipt(s)
    70.             Debug.Log("Receipt is valid. Contents:");
    71.             foreach (IPurchaseReceipt productReceipt in result) {
    72.                 Debug.Log(productReceipt.productID);
    73.                 Debug.Log(productReceipt.purchaseDate);
    74.                 Debug.Log(productReceipt.transactionID);
    75.             }
    76.         } catch (IAPSecurityException) {
    77.             Debug.Log("Invalid receipt, not unlocking content");
    78.             validPurchase = false;
    79.         }
    80. #endif
    81. #endif
    82.         if (validPurchase)
    83.         {
    84.             Wazzapps.WazzappsLocator.getPlugin().SendEvent("inapp_purchased", args.purchasedProduct.receipt);
    85.             BuyComplite(args.purchasedProduct.definition.id);
    86.         }
    87.         else
    88.         {
    89.             SoundController.Play2D(SoundFatal);
    90.             if (BuyProcedureEndEvent != null)
    91.                 BuyProcedureEndEvent();
    92.             var item = StoreItems.Get(args.purchasedProduct.definition.id);
    93.             Analitics.TryBuyInappDiamonds(args.purchasedProduct.definition.id, item.CountGoods, false);
    94.         }
    95.  
    96.         return PurchaseProcessingResult.Complete;
    97.     }
    98.  
    99.     public void OnInitializeFailed(InitializationFailureReason error)
    100.     {
    101.         DOVirtual.DelayedCall(1f, Init);
    102.     }
    103.  
    104.     public void OnPurchaseFailed(Product i, PurchaseFailureReason p)
    105.     {
    106.         if (BuyProcedureEndEvent != null)
    107.             BuyProcedureEndEvent();
    108.         var item = StoreItems.Get(i.definition.id);
    109.         Analitics.TryBuyInappDiamonds(i.definition.id, item.CountGoods, false);
    110.     }
    111.  
    112.     public static void BuyGoods(string key)
    113.     {
    114.         if (!IsInitialized()) return;
    115.  
    116.         Product product = mStoreController.products.WithID(key);
    117.         if (product != null && product.availableToPurchase)
    118.         {
    119.             mStoreController.InitiatePurchase(product);
    120.         }
    121.     }
    122.  
    123.     public static string GetProductLocalizedPriceById(string productId)
    124.     {
    125.         if (!IsInitialized()) return "";
    126.  
    127.         Product product = mStoreController.products.WithID(productId);
    128.         if (product != null && product.availableToPurchase)
    129.         {
    130.             return product.metadata.localizedPriceString + " " + product.metadata.isoCurrencyCode;
    131.         }
    132.  
    133.         return "";
    134.     }
    135.  
    136.     public static string GetProductLocalizedTitleById(string productId)
    137.     {
    138.         if (!IsInitialized()) return "";
    139.  
    140.         Product product = mStoreController.products.WithID(productId);
    141.         if (product != null && product.availableToPurchase)
    142.         {
    143.             return product.metadata.localizedTitle;
    144.         }
    145.  
    146.         return "";
    147.     }
    148.  
    149.     public static string GetProductLocalizedDescriptionById(string productId)
    150.     {
    151.         if (!IsInitialized()) return "";
    152.  
    153.         Product product = mStoreController.products.WithID(productId);
    154.         if (product != null && product.availableToPurchase)
    155.         {
    156.             return product.metadata.localizedDescription;
    157.         }
    158.  
    159.         return "";
    160.     }
    161.  
    162.     public static void BuyComplite(string key)
    163.     {
    164.         var premiumItems = StoreItems.AllKeys;
    165.         premiumItems.Remove("Diamond_Free");
    166.         if (premiumItems.Contains(key))
    167.         {
    168.             PlayerData.IsAdsActive = false;
    169.             Debug.Log("ADS DISABLED ON DIAMOND BUY");
    170.             Wazzapps.WazzappsLocator.getPlugin().DisableOnBuyDiamonds();
    171.             GuiController.Instance.MainGameWindow.StretchBorders(PlayerData.IsAdsActive);
    172.         }
    173.         Debug.Log("BuyComplite for: " + key);
    174.         var item = StoreItems.Get(key);
    175.         if (item != null)
    176.         {
    177.             int countGoods = 0;
    178.             SoundController.Play2D(SoundFine);
    179.             if (item.Product == TypeProduct.Free)
    180.             {
    181.                 switch (item.TypeMoney)
    182.                 {
    183.                     case TypeMoney.Coin:
    184.                         countGoods = item.CountGoods;
    185.                         break;
    186.                     case TypeMoney.Diamond:
    187.                         Analitics.AdsShowFreeDiamond();
    188.                         countGoods = item.CountGoods;
    189.                         break;
    190.                     default:
    191.                         countGoods = item.CountGoods;
    192.                         break;
    193.                 }
    194.             }
    195.             else
    196.             {
    197.                 countGoods = item.CountGoods;
    198.             }
    199.             Analitics.TryBuyInappDiamonds(key, countGoods, true);
    200.  
    201.             if (BuyCompliteEvent != null)
    202.                 BuyCompliteEvent(countGoods, item.TypeMoney);
    203.             if (BuyProcedureEndEvent != null)
    204.                 BuyProcedureEndEvent();
    205.         }
    206.         else
    207.         {
    208.             SoundController.Play2D(SoundFatal);
    209.             if (BuyProcedureEndEvent != null)
    210.                 BuyProcedureEndEvent();
    211.         }
    212.     }
    213.  
    214.     // Restore purchases previously made by this customer. Some platforms automatically restore purchases, like Google.
    215.     // Apple currently requires explicit purchase restoration for IAP, conditionally displaying a password prompt.
    216.     public static void RestorePurchases()
    217.     {
    218.         // If Purchasing has not yet been set up ...
    219.         if (!IsInitialized())
    220.         {
    221.             // ... report the situation and stop restoring. Consider either waiting longer, or retrying initialization.
    222.             Debug.Log("RestorePurchases FAIL. Not initialized.");
    223.             return;
    224.         }
    225.  
    226.         // If we are running on an Apple device ...
    227.         if (Application.platform != RuntimePlatform.IPhonePlayer &&
    228.             Application.platform != RuntimePlatform.OSXPlayer) return;
    229.  
    230.         // ... begin restoring purchases
    231.         Debug.Log("RestorePurchases started ...");
    232.  
    233.         // Fetch the Apple store-specific subsystem.
    234.         var apple = mStoreExtensionProvider.GetExtension<IAppleExtensions>();
    235.  
    236.         // Begin the asynchronous process of restoring purchases. Expect a confirmation response in
    237.         // the Action<bool> below, and ProcessPurchase if there are previously purchased products to restore.
    238.         apple.RestoreTransactions((result) =>
    239.                                   {
    240.                                       // The first phase of restoration. If no more responses are received on ProcessPurchase then
    241.                                       // no purchases are available to be restored.
    242.                                       //Debug.Log("RestorePurchases continuing: " + result + ". If no further messages, no purchases available to restore.");
    243.                                   });
    244.  
    245.         // Otherwise ...
    246.     }
    247. }
    248.  
     
    Last edited: Jul 4, 2018
  13. jason_yak

    jason_yak

    Joined:
    Aug 25, 2016
    Posts:
    514
    hi @JeffDUnity3D I'm seeing this issue on iOS as well. When calling:
    var someHashString = "552e21cd4cd9918678e3c1a0df491bc3";
    storeController.InitiatePurchase( product, someHashString );

    I pretty sure that iOS doesn't use the payload like android so my fix on iOS is to exclude a string for the payload:
    storeController.InitiatePurchase( product );

    But it's kind of proof that there's some internal exception being thrown in the InitiatePurchase method. Here's the log from xcode, so I've now seen this on iOS and Android using v1.20 of IAP:
    Code (CSharp):
    1. UnityIAP: JSONStore exception handling developerPayload: System.InvalidCastException: Unable to cast object of type 'Double' to type 'Dictionary`2'.
    2.   at UnityEngine.Purchasing.JSONStore.Purchase (UnityEngine.Purchasing.ProductDefinition product, System.String developerPayload) [0x00000] in <filename unknown>:0
    3.   at UnityEngine.Purchasing.PurchasingManager.InitiatePurchase (UnityEngine.Purchasing.Product product, System.String developerPayload) [0x00000] in <filename unknown>:0
    4.   at IAPManager+<purchaseRoutine>c__Iterator3.MoveNext () [0x00000] in <filename unknown>:0
    5.   at UnityEngine.SetupCoroutine.InvokeMoveNext (IEnumerator enumerator, IntPtr returnValueAddress) [0x00000] in <filename unknown>:0
    6.   at PaywallScreen+<purchaseFullGameAfterDelay>c__Iterator0.MoveNext () [0x00000] in <filename unknown>:0
    7.   at UnityEngine.SetupCoroutine.InvokeMoveNext (IEnumerator enumerator, IntPtr returnValueAddress) [0x00000] in <filename unknown>:0
     
  14. JeffDUnity3D

    JeffDUnity3D

    Joined:
    May 2, 2017
    Posts:
    14,446
    There are two issues being described in this thread, passing a string as the payload which may lead to an exception, and a possible issue with restore. Moving forward, lets discuss the restore issue only here, thank you for understanding. The payload issue should be in a separate thread.
     
  15. jason_yak

    jason_yak

    Joined:
    Aug 25, 2016
    Posts:
    514
    The thing is I saw this exception happen once during a restore process, and then more commonly through a user initiated purchase so the two issues are one and the same, both exhibiting the same exception.

    Also the method signature for the initiate purchase takes a string, and I am sending a string and if I wasn't the compiler wouldn't even let me compile the app. The above stack trace shows the method signature:
    InitiatePurchase (UnityEngine.Purchasing.Product product, System.String developerPayload)

    Internally there is an exception being thrown and it's nothing to do with my implementation. It appears that the JSONStore class is failing to properly cast a string and is trying to cast this as a Double. JSONStore must be something internally used by Unity IAP.
     
  16. JeffDUnity3D

    JeffDUnity3D

    Joined:
    May 2, 2017
    Posts:
    14,446
    I don't believe that is correct, unless you can confirm that it happens ONLY for restoring products that originally had a developer payload (which is possible). Can you test with and without a payload, to confirm? And yes, it is an internal JSON error. We need a fully reproducible scenario in order to begin work on an issue, thank you for understanding. I will test with a payload here, it should reproduce every time if it reproduces at all.
     
  17. jason_yak

    jason_yak

    Joined:
    Aug 25, 2016
    Posts:
    514
    I'll see if I can figure out some the reproducible steps, but have seen it a few times now and just been uninstalling and reinstalling an app that has a single non-consumable purchase.

    With Unity recently open sourcing the engine code, is Unity IAP open as well?
     
  18. JeffDUnity3D

    JeffDUnity3D

    Joined:
    May 2, 2017
    Posts:
    14,446
    The available source for IAP is included in the project when you import IAP in Assets/UnityPurchasing (but not the core purchase methods). And again, please limit this thread to a single topic.
     
  19. jason_yak

    jason_yak

    Joined:
    Aug 25, 2016
    Posts:
    514
    Again... it was relevant... I’m trying to find a result here because this is a show stopper bug for us and as I’m getting nowhere I wondered if I could look in to the code myself for you. I’m just trying propose ideas in search of answers. It’s not at all helpful or productive if we can’t exlore all avenues. I thought the context for my question was pretty clear why I was asking it.... I can’t imagine it would be hard to look for a line of code called after initiate purchase with a (Double) cast to know where to focus effort on looks for he problem, but as I can’t see the code then it’s another avenue closed off to me to find a resolution to the issue.
     
  20. jason_yak

    jason_yak

    Joined:
    Aug 25, 2016
    Posts:
    514
    Apologies for being short, I’m just under pressure and have a huge release meant to be out soon, so hoping to find a resolution ASAP. Will report back if I can narrow down reproducible steps.
     
  21. tayl0r

    tayl0r

    Joined:
    Jan 6, 2012
    Posts:
    85
    I just upgraded to Unity 2017 and the Unity IAP plugin and am getting this same exception, on Android:

    Code (CSharp):
    1. UnityIAP: JSONStore exception handling developerPayload: System.InvalidCastException: Cannot cast from source type to destination type.
    2. at UnityEngine.Purchasing.JSONStore.Purchase (UnityEngine.Purchasing.ProductDefinition product, System.String developerPayload) [0x00000] in :0
    my payload is the user id, which is a c# guid (xxxx-xxxx-xxxx-xxxx-xxxx-xxxx).

    Did you guys ever figure this out?

    Very frustrating because the method signature clearly takes a string, and I don't see anything in the Unity IAP docs that say the string needs to be in some specific format.
     
  22. JeffDUnity3D

    JeffDUnity3D

    Joined:
    May 2, 2017
    Posts:
    14,446
    @tayl0r Please show the code that you are using, and a specific value that you are setting. Yes, it should just take a string.
     
  23. tayl0r

    tayl0r

    Joined:
    Jan 6, 2012
    Posts:
    85
    Hi Jeff,

    Here is our purchasing code. It's basically straight from the documentation. I looked at your example app and you don't pass in a payload. I would think that if you changed your example app to pass in a payload, you'd get the same problem. Also, make sure you are testing on an actual Android or iOS device with real IAPs.

    The string value for payload in this case is the user id, which is a standard c# .net guid, like
    1ca560fb-5f2e-4de4-84d5-3342cafe2a0d


    Also, I'm using the new IAP release as of earlier this month, 1.20.1.
    And, I'm not using codeless.

    Code (CSharp):
    1.     public void PurchaseSku(string sku, string payload) {
    2.         // If Purchasing has been initialized ...
    3.         if (IsInitialized()) {
    4.             // ... look up the Product reference with the general product identifier and the Purchasing
    5.             // system's products collection.
    6.             Product product = _storeController.products.WithID(sku);
    7.  
    8.             // If the look up found a product for this device's store and that product is ready to be sold ...
    9.             if (product != null && product.availableToPurchase) {
    10.                 //Debug.Log("IapManager.PurchaseSku: " + product.definition.id);
    11.                 // ... buy the product. Expect a response either through ProcessPurchase or OnPurchaseFailed
    12.                 // asynchronously.
    13.  
    14.                 _storeController.InitiatePurchase(product, payload);
    This is what the warning looks like on Android:

    Code (CSharp):
    1. UnityIAP: JSONStore exception handling developerPayload: System.InvalidCastException: Cannot cast from source type to destination type.
    2. at UnityEngine.Purchasing.JSONStore.Purchase (UnityEngine.Purchasing.ProductDefinition product, System.String developerPayload) [0x00000] in :0
    This is what the warning looks like on iOS:

    Code (CSharp):
    1. UnityIAP: JSONStore exception handling developerPayload: System.InvalidCastException: Unable to cast object of type 'Double' to type 'Dictionary`2'.
    2. at UnityEngine.Purchasing.JSONStore.Purchase (UnityEngine.Purchasing.ProductDefinition product, System.String developerPayload) [0x00000] in :0
    3. at UnityEngine.Purchasing.PurchasingManager.InitiatePurchase (UnityEngine.Purchasing.Product product, System.String developerPayload) [0x00000] in :0
    4. at PromoV2.DoPurchase (.DynamicPromoText dpt, .ComboInfo combo) [0x00000] in :0
    5. at UnityEngine.Events.UnityEvent.Invoke () [0x00000] in :0
    6. at UnityEngine.EventSystems.ExecuteEvents.Execute[T] (UnityEngine.GameObject target, UnityEngine.EventSystems.BaseEventData eventData, UnityEngine.EventSystems.EventFunction`1 functor) [0x00000] in :0
    7. at UnityEngine.EventSystems.StandaloneInputModule.ProcessTouchPress (UnityEngine.EventSystems.PointerEventData pointerEvent, Boolean pressed, Boolean released) [0x00000] in :0
    8. at UnityEngine.EventSystems.StandaloneInputModule.ProcessTouchEvents () [0x00000] in :0
    9. at UnityEngine.EventSystems.StandaloneInputModule.Process () [0x00000] in :0
    10. UnityEngine.Purchasing.PurchasingManager:InitiatePurchase(Product, String)
    11. PromoV2:DoPurchase(DynamicPromoText, ComboInfo)
    12. UnityEngine.EventSystems.StandaloneInputModule:Process()
     
    Last edited: Oct 24, 2018
  24. JeffDUnity3D

    JeffDUnity3D

    Joined:
    May 2, 2017
    Posts:
    14,446
    We are aware of the warning, and it's on our plan to fix. You should be able to safely ignore the warning.
     
  25. tayl0r

    tayl0r

    Joined:
    Jan 6, 2012
    Posts:
    85
    Hi Jeff,

    Will that also fix the developer payload being correct in the response?

    Code (CSharp):
    1. json = {"Store":"GooglePlay","TransactionID":"GPA.xxxx-xxxx-xxxx-xxxxx","Payload":"{\"json\":\"{\\\"orderId\\\":\\\"GPA.xxxx-xxxx-xxxx-xxxx\\\",\\\"packageName\\\":\\\"com.xxxx.xxxx\\\",\\\"productId\\\":\\\"coin2\\\",\\\"purchaseTime\\\":1540507884261,\\\"purchaseState\\\":0,\\\"developerPayload\\\":\\\"{\\\\\\\"developerPayload\\\\\\\":\\\\\\\"ODA2M2JkYmUtZThjOC00NDdhLTgxMjYtMGE4YjQyNGEyZWU4\\\\\\\\n\\\\\\\",\\\\\\\"is_free_trial\\\\\\\":false,\\\\\\\"has_introductory_price_trial\\\\\\\":false,\\\\\\\"is_updated\\\\\\\":false}\\\",\\\"purchaseToken\\\":\\\"cadmhcfcomkfgohigbpmbbei.AO-J1Owd7066Jh4E9dKiTUvD71KhE__nWlpW2l2J83jloyNhbIYP_Ge7vT0ScqJ4fPz5JOJBthJbjNPcPutRyc3BCqOueJYrZclDVImyJT3w0QXYa-m_5Ec\\\"}\",\"signature\":\"mUxZFkiiKFhQ+FAvMBt\\/PhxJTkNx0wqEKc+R0d0z3mmCNMDFztTRLVt28FG4C+LlSQDhnfV9i6ukrIsX96iXZ1zSJs3OzKDgwIg9q+XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX\\/gDVmSoipYnoKJYMSEve4ZG\\/FJVE\\/oGecQnvUw3+1RXEHEBvtzbWwZqD0otFF4\\/4REoM0mJHhsQY187VoSj0wpUTkB92WsVkvX4wrVOngH5tlMrrtLKCNbRBkUthfnpu8GZ3RUAItJppSwpm9adiwiJCUpeWlPtqd65Dd\\/2PTpWpWTdZFtD0ChzIU9+FkwsDjw==\",\"skuDetails\":\"{\\\"productId\\\":\\\"coin2\\\",\\\"type\\\":\\\"inapp\\\",\\\"price\\\":\\\"$5.49\\\",\\\"price_amount_micros\\\":5490000,\\\"price_currency_code\\\":\\\"USD\\\",\\\"title\\\":\\\"Coin Pack 2 (XXXXXXXXXXXXXXXX)\\\",\\\"description\\\":\\\"Coin Pack 2\\\"}\",\"isPurchaseHistorySupported\":true}"}
    The developer payload there doesn't look anything like the user id I passed in. So I wouldn't say that it's just a "warning", it's actually preventing people from doing proper receipt verification by not allowing you to confirm that the receipt given to you by the user was actually created by that user and not someone else.

    Edit - looks like the developer payload is correct, its just base64 encoded.
    ODA2M2JkYmUtZThjOC00NDdhLTgxMjYtMGE4YjQyNGEyZWU4 = 8063bdbe-e8c8-447a-8126-0a8b424a2ee8, which is the user id I passed in. Very nice!
     
  26. JeffDUnity3D

    JeffDUnity3D

    Joined:
    May 2, 2017
    Posts:
    14,446
    Yes, it is indeed base64 encoded, glad you got it working!