Search Unity

Resolved IAP Caching products price

Discussion in 'Unity IAP' started by savala02, Jan 14, 2022.

Thread Status:
Not open for further replies.
  1. savala02

    savala02

    Joined:
    Jan 28, 2020
    Posts:
    8
    Hello guys!

    We are having many problems with price caching from products, getting the product from Google Play using In App Purchasing.

    We can get the price in the first time when app open, but when need update the price in Google Play Console, the price don't update in game. But when click to buy the game, the Google Play modal show the correct price.

    We try use IAP version 2 with Unity 2019.3.0.f6 and now using IAP 4.1.2 and using Unity 2021.2.7.f1. Both versions have the same problem.

    We tried force update the price getting again the product using the method "FetchAdditionalProducts" but this doesn't work too.

    Even opening and closing the game many times this make no difference.

    We think this problem is with IAP caching, specifically in "product.metadata".


    The code:

    Code (CSharp):
    1.  
    2. using System;
    3. using UnityEngine;
    4. using UnityEngine.Purchasing;
    5. using TMPro;
    6.  
    7. public class Purchaser : Singleton<Purchaser>, IStoreListener
    8. {
    9.         [SerializeField]
    10.         private TextMeshProUGUI fullPrice_android;
    11.         [SerializeField]
    12.         private TextMeshProUGUI price_android;
    13.         [SerializeField]
    14.         private TextMeshProUGUI fullPrice_iOS;
    15.         [SerializeField]
    16.         private TextMeshProUGUI price_iOS;
    17.         private Product product;
    18.         private static IStoreController m_StoreController;          // The Unity Purchasing system.
    19.         private static IExtensionProvider m_StoreExtensionProvider; // The store-specific Purchasing subsystems.
    20.  
    21.         public static string removeAds = "com.dinitystudios.crashdashone.removeads";
    22.  
    23.         // Apple App Store-specific product identifier for the subscription product.
    24.         private static string kProductNameAppleSubscription = "com.unity3d.subscription.new";
    25.  
    26.         // Google Play Store-specific product identifier subscription product.
    27.         private static string kProductNameGooglePlaySubscription = "com.unity3d.subscription.original";
    28.  
    29.         void Start()
    30.         {
    31.             // If we haven't set up the Unity Purchasing reference
    32.             if (m_StoreController == null)
    33.             {
    34.                 // Begin to configure our connection to Purchasing
    35.                 InitializePurchasing();
    36.             }
    37.         }
    38.  
    39.         public void InitializePurchasing()
    40.         {
    41.             // If we have already connected to Purchasing ...
    42.             if (IsInitialized())
    43.             {
    44.                 // ... we are done here.
    45.                 return;
    46.             }
    47.  
    48.             // Create a builder, first passing in a suite of Unity provided stores.
    49.             var builder = ConfigurationBuilder.Instance(StandardPurchasingModule.Instance());
    50.  
    51.             // Add a product to sell / restore by way of its identifier, associating the general identifier
    52.             builder.AddProduct(removeAds, ProductType.NonConsumable);
    53.  
    54.             UnityPurchasing.Initialize(this, builder);
    55.         }
    56.  
    57.  
    58.         private bool IsInitialized()
    59.         {
    60.             // Only say we are initialized if both the Purchasing references are set.
    61.             return m_StoreController != null && m_StoreExtensionProvider != null;
    62.         }
    63.  
    64.         private void SetAdPrice()
    65.         {
    66.             product = m_StoreController.products.WithID(removeAds);
    67.  
    68.             var price = product.metadata.localizedPrice;
    69.             var isoCurrencyCode = product.metadata.isoCurrencyCode;
    70.  
    71.             price_android.text = $"{price} {isoCurrencyCode}";
    72.  
    73.             price_iOS.text = $"{price} {isoCurrencyCode}";
    74.         }
    75.  
    76.         public void BuyRemoveAds()
    77.         {
    78.             BuyProductID(removeAds);
    79.         }
    80.  
    81.         void BuyProductID(string productId)
    82.         {
    83.             // If Purchasing has been initialized ...
    84.             if (IsInitialized())
    85.             {
    86.                 // ... look up the Product reference with the general product identifier and the Purchasing
    87.                 // system's products collection.
    88.                 Product product = m_StoreController.products.WithID(productId);
    89.  
    90.                 // If the look up found a product for this device's store and that product is ready to be sold ...
    91.                 if (product != null && product.availableToPurchase)
    92.                 {
    93.                     Debug.Log(string.Format("Purchasing product asychronously: '{0}'", product.definition.id));
    94.                     // ... buy the product. Expect a response either through ProcessPurchase or OnPurchaseFailed
    95.                     // asynchronously.
    96.                     m_StoreController.InitiatePurchase(product);
    97.                 }
    98.                 // Otherwise ...
    99.                 else
    100.                 {
    101.                     // ... report the product look-up failure situation
    102.                     Debug.Log("BuyProductID: FAIL. Not purchasing product, either is not found or is not available for purchase");
    103.                 }
    104.             }
    105.             // Otherwise ...
    106.             else
    107.             {
    108.                 // ... report the fact Purchasing has not succeeded initializing yet. Consider waiting longer or
    109.                 // retrying initiailization.
    110.                 Debug.Log("BuyProductID FAIL. Not initialized.");
    111.             }
    112.         }
    113.  
    114.  
    115.         // Restore purchases previously made by this customer. Some platforms automatically restore purchases, like Google.
    116.         // Apple currently requires explicit purchase restoration for IAP, conditionally displaying a password prompt.
    117.         public void RestorePurchases()
    118.         {
    119.             // If Purchasing has not yet been set up ...
    120.             if (!IsInitialized())
    121.             {
    122.                 // ... report the situation and stop restoring. Consider either waiting longer, or retrying initialization.
    123.                 Debug.Log("RestorePurchases FAIL. Not initialized.");
    124.                 return;
    125.             }
    126.  
    127.             // If we are running on an Apple device ...
    128.             if (Application.platform == RuntimePlatform.IPhonePlayer ||
    129.                 Application.platform == RuntimePlatform.OSXPlayer)
    130.             {
    131.                 // ... begin restoring purchases
    132.                 Debug.Log("RestorePurchases started ...");
    133.  
    134.                 // Fetch the Apple store-specific subsystem.
    135.                 var apple = m_StoreExtensionProvider.GetExtension<IAppleExtensions>();
    136.                 // Begin the asynchronous process of restoring purchases. Expect a confirmation response in
    137.                 // the Action<bool> below, and ProcessPurchase if there are previously purchased products to restore.
    138.                 apple.RestoreTransactions((result) =>
    139.                 {
    140.                     // The first phase of restoration. If no more responses are received on ProcessPurchase then
    141.                     // no purchases are available to be restored.
    142.                     Debug.Log("RestorePurchases continuing: " + result + ". If no further messages, no purchases available to restore.");
    143.                 });
    144.             }
    145.             // Otherwise ...
    146.             else
    147.             {
    148.                 // We are not running on an Apple device. No work is necessary to restore purchases.
    149.                 Debug.Log("RestorePurchases FAIL. Not supported on this platform. Current = " + Application.platform);
    150.             }
    151.         }
    152.  
    153.  
    154.         //
    155.         // --- IStoreListener
    156.         //
    157.  
    158.         public void OnInitialized(IStoreController controller, IExtensionProvider extensions)
    159.         {
    160.             // Purchasing has succeeded initializing. Collect our Purchasing references.
    161.             Debug.Log("OnInitialized: PASS");
    162.  
    163.             // Overall Purchasing system, configured with products for this application.
    164.             m_StoreController = controller;
    165.             // Store specific subsystem, for accessing device-specific store features.
    166.             m_StoreExtensionProvider = extensions;
    167.  
    168.             SetAdPrice();
    169.         }
    170.  
    171.  
    172.         public void OnInitializeFailed(InitializationFailureReason error)
    173.         {
    174.             // Purchasing set-up has not succeeded. Check error for reason. Consider sharing this reason with the user.
    175.             Debug.Log("OnInitializeFailed InitializationFailureReason:" + error);
    176.         }
    177.  
    178.  
    179.         public PurchaseProcessingResult ProcessPurchase(PurchaseEventArgs args)
    180.         {
    181.             if (String.Equals(args.purchasedProduct.definition.id, removeAds, StringComparison.Ordinal))
    182.             {
    183.                 Debug.Log(string.Format("ProcessPurchase: PASS. Product: '{0}'", args.purchasedProduct.definition.id));
    184.  
    185.                 AdManager.Instance.SetAdsRemoved();
    186.             }
    187.             // Or ... an unknown product has been purchased by this user. Fill in additional products here....
    188.             else
    189.             {
    190.                 Debug.Log(string.Format("ProcessPurchase: FAIL. Unrecognized product: '{0}'", args.purchasedProduct.definition.id));
    191.             }
    192.  
    193.             // Return a flag indicating whether this product has completely been received, or if the application needs
    194.             // to be reminded of this purchase at next app launch. Use PurchaseProcessingResult.Pending when still
    195.             // saving purchased products to the cloud, and when that save is delayed.
    196.             return PurchaseProcessingResult.Complete;
    197.         }
    198.  
    199.  
    200.         public void OnPurchaseFailed(Product product, PurchaseFailureReason failureReason)
    201.         {
    202.             // A product purchase attempt did not succeed. Check failureReason for more detail. Consider sharing
    203.             // this reason with the user to guide their troubleshooting actions.
    204.             Debug.Log(string.Format("OnPurchaseFailed: FAIL. Product: '{0}', PurchaseFailureReason: {1}", product.definition.storeSpecificId, failureReason));
    205.         }
    206.     }
    207.  
     
  2. JeffDUnity3D

    JeffDUnity3D

    Joined:
    May 2, 2017
    Posts:
    14,446
    @savala0202 You should not change the price of a product once you publish, and definitely don't change the price after a customer makes a purchase. If you make a price change, I suspect you'll need to publish a new version of your game also.
     
  3. savala02

    savala02

    Joined:
    Jan 28, 2020
    Posts:
    8
    Hello @JeffDUnity3D


    We have no intention of change the price once this is defined, our concern is that in google play console the price is automatically generated in local currency accordingly with the region, the exchange rate can vary along of time so the price in the Google Play modal and the in-game price will no be the same in this case.
     
  4. JeffDUnity3D

    JeffDUnity3D

    Joined:
    May 2, 2017
    Posts:
    14,446
    Got it! I see what you mean, makes perfect sense. I will pass this along to engineering. Agreed, it has the appearance of an issue at our end that we would need to address, hopefully in a future release. We have quite a back log of tasks at the moment and a bit resource constrained, so likely no immediate solution. Can you share a specific example of such a price change that isn't reflected in the localizedPrice? A screenshot of the Google popup on the device, the device locale, the account locale, a screenshot of the price defined your dashboard, the countries you've selected on the Google dashboard and the corresponding Debug.Log output in the logcat logs showing the localizedPrice would help to speed things along. Please let me know if you need help with any of those.
     
  5. JeffDUnity3D

    JeffDUnity3D

    Joined:
    May 2, 2017
    Posts:
    14,446
    Just checking, are you able to provide the requested information?
     
  6. savala02

    savala02

    Joined:
    Jan 28, 2020
    Posts:
    8
    Hello, sorry for taking too long to answer.
    I was trying to get an android account setup with a different country to make adicional tests but I didn't manage to so the best I can do for now is simulate the issue.

    Test condition:
    google play console account locale - Brazil
    google play console currency - BRL
    device locale - Brazil
    android account locale - Brazil
    android account currency - BRL

    Replicating the issue:

    The precification was defined to 10 BRL on google play console.


    The application was runned in the device for the first time. The value is shown as 10 BRL in the game IU and in the google play IU


    The precification was defined to 5 BRL on google play console to simulate a price variation due exchange rate fluctuation.



    The application was runned in the device for the second time. The value is shown as 10 BRL in the game IU and 5 BRL in the google play IU.


    The test was conducted in 2 different devices and on an android emulator, the 3 tests had the same result.
    There is no log error to report

    note: the green text field is received product.metadata.localizedPrice + product.metadata.localizedPrice.
     

    Attached Files:

    JeffDUnity3D likes this.
  7. JeffDUnity3D

    JeffDUnity3D

    Joined:
    May 2, 2017
    Posts:
    14,446
    Can you share your full purchasing code (or send it to me in a private message)? Specifically, when and where are you retrieving product.metadata.localizedPrice.
     
  8. savala02

    savala02

    Joined:
    Jan 28, 2020
    Posts:
    8
    The full code is in the first post, the product.metadata.localized Price is retrieved in line 68 inside the function "SetAdPrice". The script is attached to a gameobject in the first scene that loads when the game starts (this gameobject is not destroyed when a new scene loads).
    the code execution sequence is: function "Start"(line 29) calls "InitializePurchasing"(line 39), if the callback "OnInitialized"(line 158) occur the "SetAdPrice"(line 68) is called, "SetAdPrice" retrieved product.metadata.localized and pass the value to a UI text field.
     
  9. JeffDUnity3D

    JeffDUnity3D

    Joined:
    May 2, 2017
    Posts:
    14,446
    Got it! I will follow up. What is the localizedPrice in ProcessPurchase, does that match the dashboard? Granted too late there to set the price in the UI, just troubleshooting so far. Is it then set correctly the next time the app launches?
     
  10. savala02

    savala02

    Joined:
    Jan 28, 2020
    Posts:
    8
    In general once the app get the price from the dashboard the price will always be the same and will not be correct the next time the app launches, but in all the tests i saw the app update the price for the new value on two occasions although i was not able to identify what has triggered this update.
     
  11. JeffDUnity3D

    JeffDUnity3D

    Joined:
    May 2, 2017
    Posts:
    14,446
    I suspect a purchase triggered the update.
     
  12. savala02

    savala02

    Joined:
    Jan 28, 2020
    Posts:
    8
    It could be but at the time i'm sure there was no purchase to trigger this update. We were not testing purchase just trying to retrieve the localizedPrice correctly
     
  13. JeffDUnity3D

    JeffDUnity3D

    Joined:
    May 2, 2017
    Posts:
    14,446
    I am asking you to confirm, not as a solution but to troubleshoot. Does the price get set correctly in the cache after a purchase? Please refer to my "granted too late" comment previously also.
     
  14. savala02

    savala02

    Joined:
    Jan 28, 2020
    Posts:
    8
    The price do not get set correctly in the cache after a purchase. Once the cache gets a value, it will never change.
     
  15. JeffDUnity3D

    JeffDUnity3D

    Joined:
    May 2, 2017
    Posts:
    14,446
    Again, what is the price in ProcessPurchase? Is it correct there? Check the product object passed into ProcessPurchase. If it is not correct there, unfortunately there is nothing we (Unity IAP) can do, we are getting the price directly via the Google Billing API.
     
  16. JeffDUnity3D

    JeffDUnity3D

    Joined:
    May 2, 2017
    Posts:
    14,446
    @savala0202 Have you had a chance to check the price within the ProcessPurchase method?
     
  17. savala02

    savala02

    Joined:
    Jan 28, 2020
    Posts:
    8
    I haven't had a chance to test it yet. I will do it as soon as possible.
     
  18. UDN_8c755132-93e7-49e2-92cc-7373e26b874c

    UDN_8c755132-93e7-49e2-92cc-7373e26b874c

    Joined:
    Mar 19, 2017
    Posts:
    2
    Same issue here.
    And I just found that changing language in phone will trigger updating the prcie text.
     
  19. ianwong_unity

    ianwong_unity

    Joined:
    Apr 12, 2022
    Posts:
    7
    Hi, I got the similar issue, the price in Japan iOS appstore is adjusted but the localised price string is not reflected. Is there anyway to refresh the price?
     
  20. KrcKung

    KrcKung

    Joined:
    Jul 18, 2017
    Posts:
    56
    We seem to notice the same issue as well on the iOS, the price didn't change in game after we change the price at Appstore there
     
  21. Yannick_D

    Yannick_D

    Unity Technologies

    Joined:
    Feb 21, 2022
    Posts:
    235
    Hello,
    The price and region is sent from Apple, we only format the string on our end.

    If this is related to localizedPriceString, make sure the region is properly set so that Apple returns the correct information.
    When testing on Sandbox, the Sandbox Apple ID's region is used instead of the device's region which might affect the price displayed.

    If the price was changed recently and the price is also wrong in the purchase UI that pops-up, then this might the price change delay on Apple's side. Give it some more time and the change should work.
     
    KrcKung likes this.
  22. KrcKung

    KrcKung

    Joined:
    Jul 18, 2017
    Posts:
    56
    Thanks for the reply, i will report it back to our developers then, thanks again.
     
  23. Arnaud_Gorain

    Arnaud_Gorain

    Unity Technologies

    Joined:
    Jun 28, 2022
    Posts:
    183
    This thread is now closed. Feel free to reach out via a new thread if you encounter further issues.
    Thanks!
     
Thread Status:
Not open for further replies.