Search Unity

Resolved How to know if a user canceled or refunded an IAP item?

Discussion in 'Unity IAP' started by ronchito, Aug 24, 2022.

  1. ronchito

    ronchito

    Joined:
    Dec 4, 2013
    Posts:
    12
    I currently keep all purchases validation local.
    Say, a user purchased IAP on android/iOS (I'm talking about non-consumable items only), I save a player-pref for that purchase so I know whether the user bought this item or not.

    But I see a bug that when users ask for refunds from google, they get the money back but the process was not made through the game loop and I keep showing them the item even though they canceled it outside of the game environment.

    I thought that there would be a way to use android and iOS API to know whether the user has this item active currently or not. But I couldn't find those endpoints.

    Even if I use a backend server (such as firebase) as a more appropriate way of validating purchases, how do I know if the purchase was canceled/refunded if it doesn't go through the game loop? How do I then disable the item to that user?

    Since I make games for android and iOS I would be happy to address both platforms.
     
    Last edited: Aug 24, 2022
  2. Baroni

    Baroni

    Joined:
    Aug 20, 2010
    Posts:
    3,260
    Cancelled and refunded (voided) purchases are removed from Unity IAP's product.hasReceipt property - it then returns false. It might get cached for a while (up to 3 days) though. If you have a limited amount of IAP items, you can unlock your game content by checking that property directly, instead of writing to PlayerPref.

    Otherwise, Google has a Voided Purchases API, but that is more complex to setup and in addition to a backend server, requires user authentication too.
     
    JeffDUnity3D likes this.
  3. ronchito

    ronchito

    Joined:
    Dec 4, 2013
    Posts:
    12
    Thanks for your fast response! The Unity IAP's product.hasReceipt property sounds perfect for me.

    But both on Android and iOS it throughs a null exception.
    Here's the code I used:

    Code (CSharp):
    1.             Product cproduct = m_StoreController.products.WithID(productID);
    2.             if (cproduct != null && cproduct.hasReceipt)
    3.             {
    4.                 Debug.Log("Bought: " + productID);
    5.             }
    6.             else
    7.             {
    8.                 Debug.Log("Didn't buy: " + productID);
    9.             }

    In the Unity editor, it doesn't through any errors, but on mobile devices, it does through a null exception error.
    Could it be that m_StoreController or m_StoreController.products aren't initialized on time?
     
  4. JeffDUnity3D

    JeffDUnity3D

    Joined:
    May 2, 2017
    Posts:
    14,446
    Yes, you need to initialize IAP first. Where is the null reference exception? Are you able to purchase that productID?
     
  5. ronchito

    ronchito

    Joined:
    Dec 4, 2013
    Posts:
    12
    Yes, the IAP works great on both Android and iOS for that product id.
    m_StoreController is null, but now I see that if I wait long enough it works!

    Now I have 2 questions:

    1. When would be the ideal time (function wise) to check about the purchased products so m_StoreController won't be null?

    2. If that property can replace the player prefs, that means that there must be internet access when using the app. Does it cache locally the "hasPurchased" property for 3 days every time there's an Internet connection?
     
  6. JeffDUnity3D

    JeffDUnity3D

    Joined:
    May 2, 2017
    Posts:
    14,446
    You would check after OnInitialized is called. Or in that method. IAP won't initialize without the internet.
     
    ronchito likes this.
  7. Gamivision

    Gamivision

    Joined:
    Apr 23, 2021
    Posts:
    6
    I have an issue about IAP in my game. whenever user click on IAP product and then cancel it not purchasing successful then an exception throws on this following method :
    UnityPurchaser.OnPurchaseFailed() and its a
    NullReferenceException · Object reference not set to an instance of an object.
     
  8. Yannick_D

    Yannick_D

    Unity Technologies

    Joined:
    Feb 21, 2022
    Posts:
    231
    Hello,

    Would you be able to provide us with a call stack for your Null Reference Exception so we can help you better?
     
  9. Gamivision

    Gamivision

    Joined:
    Apr 23, 2021
    Posts:
    6
    upload_2022-11-21_18-5-13.png upload_2022-11-21_18-5-13.png
     
  10. Yannick_D

    Yannick_D

    Unity Technologies

    Joined:
    Feb 21, 2022
    Posts:
    231
    This seems to be the product being null. You will want to null check this on your script handling the OnPurchaseFailed.

    The only case where we explicitly return a null product is when Purchasing was not initialized correctly since in that case, we are unable to match it to an existing product.
     
  11. Gamivision

    Gamivision

    Joined:
    Apr 23, 2021
    Posts:
    6
    Product is not null and i have checked it. when user cancel inapp purchasing process then this happened.
     
  12. Yannick_D

    Yannick_D

    Unity Technologies

    Joined:
    Feb 21, 2022
    Posts:
    231
    Does your IAP Listener have a script attached to the On Purchase Failed?

    Also, is UnityPurchaser part of your own code? If so, would you be able to provide that code and the GVIAPListener.purchaseFailed so I can help you better?
     
  13. Gamivision

    Gamivision

    Joined:
    Apr 23, 2021
    Posts:
    6
    yes i have attached both files
     

    Attached Files:

  14. Yannick_D

    Yannick_D

    Unity Technologies

    Joined:
    Feb 21, 2022
    Posts:
    231
    Based on the callstack and your files, I believe it's the purchaseFail in GVIAPListener that's null.

    Could you double check the purchaseFail event?
     
  15. Gamivision

    Gamivision

    Joined:
    Apr 23, 2021
    Posts:
    6
    yes i have checked there is no nothing to be null but i have attached file that have purchaseFail callback
     

    Attached Files:

  16. Yannick_D

    Yannick_D

    Unity Technologies

    Joined:
    Feb 21, 2022
    Posts:
    231
    From what I can see, it might be that purchaseFail is called twice, the first call will deregister the callback, and the second call will attempt to call null.

    You could add a trace in purchaseFailed to see how often it's being called to confirm this.

    Could it be that you have both Coded and Codeless IAP at the same time?
    In the IAP Catalog, make sure "Automatically initialize UnityPurchasing (recommended)" is turned off to disable Codeless.

    If this doesn't work, could you try to only do the registerCallbacks once and never deregisterCallbacks to see if that works?
     
    Gamivision likes this.