Search Unity

Subscriptions stopped working with 1.22.0

Discussion in 'Unity IAP' started by CaelumNoctis, May 2, 2019.

  1. CaelumNoctis

    CaelumNoctis

    Joined:
    Nov 3, 2016
    Posts:
    26
    Hi, we recently updated our Unity IAP to version 1.22.0 and suddenly got a lot of errors from our users concerning Google Play subscriptions.

    Code (Error):
    1. KeyNotFoundException: The given key was not present in the dictionary. System.Collections.Generic.Dictionary`2[System.String,System.Object].get_Item (System.String key) UnityEngine.Purchasing.SubscriptionManager.getGooglePlayStoreSubInfo (System.String payload) UnityEngine.Purchasing.SubscriptionManager.getSubscriptionInfo ()
    We also sometimes get this Play Store error when trying to purchase the same subscription: DF-DFERH-01.

    It seems to only happen to users who were already subscribed prior to the update. Purchasing the subscription as a fresh user works just fine.

    Anyone got any tips?
     
  2. JeffDUnity3D

    JeffDUnity3D

    Joined:
    May 2, 2017
    Posts:
    14,446
    A subscription is a non-consumable and should only be able to be purchased once.
     
  3. CaelumNoctis

    CaelumNoctis

    Joined:
    Nov 3, 2016
    Posts:
    26
    Still doesn't explain why our users suddenly "lost" their subscriptions when we updated.
    And prior to the update, you were able to click our in-game purchase button to receive a Play Store popup basically saying "You have already bought this" instead of getting a "Error while retrieving from server".

    So, obviously something changed with the 1.22.0 update.
     
  4. JeffDUnity3D

    JeffDUnity3D

    Joined:
    May 2, 2017
    Posts:
    14,446
    What do you mean, lost their subscriptions? Do you mean that ProcessPurchase is not called upon reinstall for this product?
     
  5. CaelumNoctis

    CaelumNoctis

    Joined:
    Nov 3, 2016
    Posts:
    26
    No, your internal IAP code can't seem to find the info about the subscription when we have initialized it.
    To activate the subscription effect in our game we check on startup if the subscription is still active and then we get the error mentioned above to our GameAnalytics dashboard.

    This error.
    Code (CSharp):
    1. KeyNotFoundException: The given key was not present in the dictionary. System.Collections.Generic.Dictionary`2[System.String,System.Object].get_Item (System.String key) UnityEngine.Purchasing.SubscriptionManager.getGooglePlayStoreSubInfo (System.String payload) UnityEngine.Purchasing.SubscriptionManager.getSubscriptionInfo ()
    Basically, it fails to retrieve SubInfo for players who already have active subscriptions.
     
  6. JeffDUnity3D

    JeffDUnity3D

    Joined:
    May 2, 2017
    Posts:
    14,446
    @CaelumNoctis Please show the code that you are using to check if the subscription is still active.
     
  7. CaelumNoctis

    CaelumNoctis

    Joined:
    Nov 3, 2016
    Posts:
    26
    This is what we do on startup. Hope it's readable.

    Code (CSharp):
    1. public void OnInitialized(IStoreController controller, IExtensionProvider extensions) {
    2.  
    3.             // Removes current subscriptions in local subscription manager
    4.             Player.instance.subscriptions.RemoveAllSubscriptions();
    5.  
    6.             // Overall Purchasing system, configured with products for this application.
    7.             m_StoreController = controller;
    8.             // Store specific subsystem, for accessing device-specific store features.
    9.             m_StoreExtensionProvider = extensions;
    10.  
    11.             m_AppleExtensions = extensions.GetExtension<IAppleExtensions>();
    12.  
    13. #if SUBSCRIPTION_MANAGER
    14.         Dictionary<string, string> introductory_info_dict = m_AppleExtensions.GetIntroductoryPriceDictionary();
    15. #endif
    16.  
    17.             foreach (Product item in controller.products.all) {
    18.  
    19. #if UNITY_IOS
    20.                 // enables promotional purchases on iOS
    21.                 if(item.availableToPurchase){
    22.                     m_AppleExtensions.SetStorePromotionVisibility(item, AppleStorePromotionVisibility.Show);
    23.                 }
    24. #endif
    25.                 if (item.hasReceipt) {
    26.                     // this is the usage of SubscriptionManager class
    27. #if SUBSCRIPTION_MANAGER
    28.                     if (item.definition.type == ProductType.Subscription) {
    29.                         if (checkIfProductIsAvailableForSubscriptionManager(item.receipt)) {
    30.                             string intro_json = (introductory_info_dict == null || !introductory_info_dict.ContainsKey(item.definition.storeSpecificId)) ? null : introductory_info_dict[item.definition.storeSpecificId];
    31.                             SubscriptionManager p = new SubscriptionManager(item, intro_json);
    32.                             SubscriptionInfo info = p.getSubscriptionInfo();
    33.  
    34.                             // Checks if this is an active subscription
    35.                             if (info.isExpired() == Result.False) {
    36.                                 Player.instance.subscriptions.AddNewSubscription(info.getRemainingTime().Days, item.definition.id);
    37.                             }
    38.                         } else {
    39.                             Debug.Log("This product is not available for SubscriptionManager class, only products that are purchase by 1.19+ SDK can use this class.");
    40.                         }
    41.                     } else {
    42.                         Debug.Log("the product is not a subscription product");
    43.                     }
    44. #endif
    45.                 }
    46.             }
    47.  
    48.         }
    49.  
    50. #if SUBSCRIPTION_MANAGER
    51.     private bool checkIfProductIsAvailableForSubscriptionManager(string receipt) {
    52.         var receipt_wrapper = (Dictionary<string, object>)MiniJson.JsonDecode(receipt);
    53.         if (!receipt_wrapper.ContainsKey("Store") || !receipt_wrapper.ContainsKey("Payload")) {
    54.             Debug.Log("The product receipt does not contain enough information");
    55.             return false;
    56.         }
    57.         var store = (string)receipt_wrapper ["Store"];
    58.         var payload = (string)receipt_wrapper ["Payload"];
    59.  
    60.         if (payload != null ) {
    61.             switch (store) {
    62.             case GooglePlay.Name:
    63.                 {
    64.                     var payload_wrapper = (Dictionary<string, object>)MiniJson.JsonDecode(payload);
    65.                     if (!payload_wrapper.ContainsKey("json")) {
    66.                         Debug.Log("The product receipt does not contain enough information, the 'json' field is missing");
    67.                         return false;
    68.                     }
    69.                     var original_json_payload_wrapper = (Dictionary<string, object>)MiniJson.JsonDecode((string)payload_wrapper["json"]);
    70.                     if (original_json_payload_wrapper == null || !original_json_payload_wrapper.ContainsKey("developerPayload")) {
    71.                         Debug.Log("The product receipt does not contain enough information, the 'developerPayload' field is missing");
    72.                         return false;
    73.                     }
    74.                     var developerPayloadJSON = (string)original_json_payload_wrapper["developerPayload"];
    75.                     var developerPayload_wrapper = (Dictionary<string, object>)MiniJson.JsonDecode(developerPayloadJSON);
    76.                     if (developerPayload_wrapper == null || !developerPayload_wrapper.ContainsKey("is_free_trial") || !developerPayload_wrapper.ContainsKey("has_introductory_price_trial")) {
    77.                         Debug.Log("The product receipt does not contain enough information, the product is not purchased using 1.19 or later");
    78.                         return false;
    79.                     }
    80.                     return true;
    81.                 }
    82.             case AppleAppStore.Name:
    83.             case AmazonApps.Name:
    84.             case MacAppStore.Name:
    85.                 {
    86.                     return true;
    87.                 }
    88.             default:
    89.                 {
    90.                     return false;
    91.                 }
    92.             }
    93.         }
    94.         return false;
    95.     }
    96. #endif
     
  8. JeffDUnity3D

    JeffDUnity3D

    Joined:
    May 2, 2017
    Posts:
    14,446
    Understood, you are explicitly calling getSubscriptionInfo which seems to be throwing the exception, can you confirm this single call causes the issue?
     
  9. CaelumNoctis

    CaelumNoctis

    Joined:
    Nov 3, 2016
    Posts:
    26
    Yes, the only errors we've seen are from this single method
     
  10. CaelumNoctis

    CaelumNoctis

    Joined:
    Nov 3, 2016
    Posts:
    26
    We'll try downgrading Unity IAP to what we had before and see if that helps.
     
  11. Kogamma

    Kogamma

    Joined:
    Feb 19, 2018
    Posts:
    19
    @JeffDUnity3D I'm currently having the same problem, would like to see some kind of answer for this
     
  12. JeffDUnity3D

    JeffDUnity3D

    Joined:
    May 2, 2017
    Posts:
    14,446
    Testing with a previous version would be a reasonable approach until we are able to get to this here.
     
  13. Kogamma

    Kogamma

    Joined:
    Feb 19, 2018
    Posts:
    19
    So we tried doing a rollback to IAP version 1.19 and the KeyNotFoundException error seems to have decreased. It's still there though. And when I check our GameAnalytics logs our errors seems to have started before we updated our IAP version. So maybe something on Google's side happened that you don't support?

    Just to clarify, the problems are only for the Google Play Store
     
  14. JeffDUnity3D

    JeffDUnity3D

    Joined:
    May 2, 2017
    Posts:
    14,446
    Sorry, decreased? So it works OK in your testing sometimes, and other times it does not?
     
  15. Kogamma

    Kogamma

    Joined:
    Feb 19, 2018
    Posts:
    19
    In our testing we never have any issues on our own devices. The errors have decreased overall based on error logs from all of our players
     
  16. Kogamma

    Kogamma

    Joined:
    Feb 19, 2018
    Posts:
    19
    @JeffDUnity3D To clarify, we have never gotten this error ourselves, we've only seen it in error logs on our GameAnalytics dashboard
     
  17. LuisJavierLopez

    LuisJavierLopez

    Joined:
    May 23, 2019
    Posts:
    8
    I used to have the same issue and eventually I managed to fix it:

    IN A NUTSHELL: check if your product type matches between the one configured in the app catalog with the one configured in the store.

    I don't know if this is your cases, but maybe you can double check it.

    In my case, I imported the product catalog from a CSV. Unfortunately, I had a subscription that was imported as a non consumable product. I kept that product in the store catalog but I changed the type in my local catalog, and I added a new subscription for testing. Then the error appeared, until I changed the type of the local product to non consumable.
     
  18. Kogamma

    Kogamma

    Joined:
    Feb 19, 2018
    Posts:
    19
    @LuisJavierLopez This is sadly not our issue since we manually add our subscription SKUs to the ConfigurationBuilder seperately
     
  19. mradel

    mradel

    Joined:
    Jan 16, 2018
    Posts:
    10
    Hi,

    We just updated from Unity purchasing 1.19.0 to 1.22.0 and released to Google Play and ran into the same issue when going live :

    ```
    Unity : Name: KeyNotFoundException: The given key was not present in the dictionary.
    Unity : Message: Exception
    Unity : StackTrace: System.Collections.Generic.Dictionary`2[TKey,TValue].get_Item (.TKey key)
    Unity : UnityEngine.Purchasing.SubscriptionManager.getGooglePlayStoreSubInfo (System.String payloa
    Unity : UnityEngine.Purchasing.SubscriptionManager.getSubscriptionInfo ()
    ```

    We have not changed anything related to getting the subscription information (we updated to get access to intro pricing support on GooglePlay). We were not able to see this issue before release in our sandbox subscription tests. This issue appeared when our version went live and we are now rolling back to 1.19.0 hoping it will fix the problem. We managed to reproduce with one of our devices on the live version but are unable to get more information on what exact steps are involved in the reproduction of the issue. It seems to affect a large majority of our existing customers with a subscription.

    Did you have any chance to look into this issue?
     
  20. JeffDUnity3D

    JeffDUnity3D

    Joined:
    May 2, 2017
    Posts:
    14,446
    @mradel Can you clarify, is this occurring in your Alpha/Beta testing too?
     
    Last edited: Jun 25, 2019
  21. mradel

    mradel

    Joined:
    Jan 16, 2018
    Posts:
    10
    This was occurring in our live version, affecting a very large portion of our customers. We just released a version rolling back to the IAP plugin 1.19.0 and while this have solved our issue, there are features we cannot access anymore (intro pricing).

    As far as I know we have not seen it in Alpha/Beta testing, we did not use the beta testing track for the first update where we had the issue or for the patch rolling back to the previous version as we only saw it live, we assumed any kind of sandbox testing could also produce false positives.

    Here is what we are trying to do on GooglePlay :

    Code (CSharp):
    1. var subscriptionManager = new SubscriptionManager(receipt, productId, null);
    2. var subscriptionInfo = subscriptionManager.getSubscriptionInfo(); //This call results in the issue
     
  22. JeffDUnity3D

    JeffDUnity3D

    Joined:
    May 2, 2017
    Posts:
    14,446
    I will be testing this today and will report my findings
     
  23. JeffDUnity3D

    JeffDUnity3D

    Joined:
    May 2, 2017
    Posts:
    14,446
    I am not able to reproduce. Before submitting to the IAP team, I need to reproduce first. I'm using IAP 1.22 and Unity 2019.1.4f1 and the Sample IAP project here, using the code from IAPDemo.cs. I'm on a device with US locale. https://forum.unity.com/threads/sample-iap-project.529555/

    This is my code:

    Code (CSharp):
    1. public void OnInitialized(IStoreController controller, IExtensionProvider extensions)
    2.     {
    3.         m_StoreController = controller;
    4.         m_StoreExtensionProvider = extensions;
    5.  
    6.         m_AppleExtensions = extensions.GetExtension<IAppleExtensions>();
    7.         Dictionary<string, string> introductory_info_dict = m_AppleExtensions.GetIntroductoryPriceDictionary();
    8.        
    9.         foreach (var item in controller.products.all)
    10.         {
    11.             if (item.availableToPurchase)
    12.             {
    13.                 if (item.receipt != null)
    14.                 {
    15.                     if (item.definition.type == ProductType.Subscription)
    16.                     {
    17.                         string intro_json = (introductory_info_dict == null || !introductory_info_dict.ContainsKey(item.definition.storeSpecificId)) ? null : introductory_info_dict[item.definition.storeSpecificId];
    18.                         SubscriptionManager p = new SubscriptionManager(item, intro_json);
    19.                         SubscriptionInfo info = p.getSubscriptionInfo();
    20.                         MyDebug("Called sub info for " + info.getProductId().ToString());
    21.                     }
    22.                 }
    23.             }
    24.         }
    25.  
    26.         MyDebug("OnInitialized: PASS");
    27.  
    28.     }