Search Unity

I'm having a hard time understanding the Codeless IAP Listener

Discussion in 'Unity IAP' started by hibbygames, Nov 10, 2019.

  1. hibbygames

    hibbygames

    Joined:
    Mar 19, 2015
    Posts:
    49
    I have two IAP buttons in my first scene. I launched on the Google Play Store, and they are working successfully. I noticed that if I open a fresh install of the app, it restores the purchases, but if I quickly load a new scene (user pressing to launch a level immediately) the purchases fail to restore, until deleting the app and launching from a fresh install again.

    My understanding is that I can add the IAP Listener to that same first scene as my buttons, and then it will persist across other scenes, preventing the restore from failing.

    What I don't understand, is how does this thing know which purchase is which? Each IAP button has a specific Product ID linked to it, and then the event to call on purchase complete. This IAP listener just has a place to attach the On Purchase Complete event, but no Product IDs. How does it know which Product ID each event is tied to? This isn't making sense to me. It seems like it might give out the wrong purchase
     
  2. JeffDUnity3D

    JeffDUnity3D

    Joined:
    May 2, 2017
    Posts:
    14,446
    The On Purchase Complete method that you implement can take Product as the parameter. If this is for a commercial game and since you're starting to write code now anyway, I might not recommend Codeless, but use scripted instead. There is a Sample IAP project here https://forum.unity.com/threads/sample-iap-project.529555/
     

    Attached Files:

  3. hibbygames

    hibbygames

    Joined:
    Mar 19, 2015
    Posts:
    49
    Thanks for trying to help me. I still don't understand WHICH product. It's just a fulfilment, it doesn't know which product it's fulfilling. very vague and confusing. I'll try looking at that project. I'll probably still not understand, Thanks anyway
     
  4. JeffDUnity3D

    JeffDUnity3D

    Joined:
    May 2, 2017
    Posts:
    14,446
    The product is passed as the parameter from the Listener to the method that you write. You can tell WHICH product by inspecting the passed product parameter. I believe your definition of "fullfillment" is better known as Restore. During Restore, the specific product is passed as the parameter, as mentioned. Your purchase method may be fired several times, once for each non-consumable and/or subscription product that the user may have already purchased, and then Restored during a reinstall.
     
    Last edited: Nov 13, 2019
  5. hibbygames

    hibbygames

    Joined:
    Mar 19, 2015
    Posts:
    49
    Ok, so. For example. I have an IAP button. It has "com.companyName.game.100coins" and then it has a script attached that gives the player 100 coins. That makes perfect sense to me. Then I have another button with "com.companyName.game.noAds" and a script attached that sets a bool for whether to show ads, to false.

    If I used the IAP listener instead of buttons. I can attach those two scripts, but there is no "com.companyName.game.100coins" or "com.companyName.game.noAds" on it anywhere. Say it knows that I own "com.companyName.game.100coins" and I do not own "com.companyName.game.noAds". How does it know which attached script gives 100 coins, and which is for the ad? How does it know to run one script and not the other? How does it know which script is linked with which product id? I never told it "com.companyName.game.100coins" = "run this script" like I did with the buttons.
     
  6. JeffDUnity3D

    JeffDUnity3D

    Joined:
    May 2, 2017
    Posts:
    14,446
    You use a listener in addition to the buttons. Please show the code that you are using for On Purchase Complete as shown in the screenshot from the Listener documentation. Instead of GrantCredits() as shown in the screenshot, you would define the method as GrantCredits(Product myProduct) or similar. You then would check the myProduct properties for the actual product name https://docs.unity3d.com/2018.1/Documentation/ScriptReference/Purchasing.ProductDefinition-id.html Start with one of the sample projects that I linked to, the second is Codeless (but would need this additional Product parameter). I should add, you are correct that the product name isn't anywhere to be found. Google passes the product information via their store API during restore, and we get the product name from that. So it is indeed hidden. Then we pass that to the Product object. Granted, it's a bit confusing, and is why I recommend the scripted approach. Codeless seems to always take more time to implement than scripted, and you still have to write some code anyway.
     
    Last edited: Nov 14, 2019
  7. hibbygames

    hibbygames

    Joined:
    Mar 19, 2015
    Posts:
    49
    Ohh, it's starting to come together in my slow head, thank you man :)

    So I would write the code like this? And I would leave that field empty instead of writing the product, because Google handles it?
    Annotation 2019-11-14 122109.png Annotation 2019-11-14 122108.png
     
  8. hibbygames

    hibbygames

    Joined:
    Mar 19, 2015
    Posts:
    49
    but I would write product instead of string.. durr
     
  9. JeffDUnity3D

    JeffDUnity3D

    Joined:
    May 2, 2017
    Posts:
    14,446
    You leave that field blank, the listener can pass any product not just one. And the parameter must be of type Product, not string.
     
  10. hibbygames

    hibbygames

    Joined:
    Mar 19, 2015
    Posts:
    49
    Thank you, I think I understand how it works now, but still not quite there. When I make the parameter type Product, I am no longer able to attach the script. As you can see in this picture, it's not listed in the drop down, but when I comment out "Product", it suddenly appears again
    iap.png
     
  11. JeffDUnity3D

    JeffDUnity3D

    Joined:
    May 2, 2017
    Posts:
    14,446
    Because it's not compiling, there is no _product.productID member in the Product class. Where are you seeing productId as a member of the Product class? You should be seeing Intellisense show what members are available. Are you using Visual Studio as your editor? I believe you want _product.definition.id
     
  12. hibbygames

    hibbygames

    Joined:
    Mar 19, 2015
    Posts:
    49
    I have "using UnityEngine.Monetization;" at the top, which makes 'Product' work, and it seems it is compiling successfully without any errors. I see productId here in this dropdown when I start writing. Yes, I'm using Visual Studio on Windows 10.

    I can't write _product.definition.id instead, because it says 'Product' does not contain a definition for 'definition'
    productID.png
     
  13. JeffDUnity3D

    JeffDUnity3D

    Joined:
    May 2, 2017
    Posts:
    14,446
    For IAP you want

    using UnityEngine.Purchasing;
     
  14. hibbygames

    hibbygames

    Joined:
    Mar 19, 2015
    Posts:
    49
    Jeff, it's working! Is it ok that the button processes the purchase, and then the listener processes it again? Could there be a double spend?
    output.png
     
  15. JeffDUnity3D

    JeffDUnity3D

    Joined:
    May 2, 2017
    Posts:
    14,446
    Yeah, I've noticed that too. My sample IAP codeless project does the same thing. That happens when you have the listener in the same scene as the buttons. It wouldn't create double spend and would probably work just fine, but it just seems a bit off that it behaves that way.
     
  16. hibbygames

    hibbygames

    Joined:
    Mar 19, 2015
    Posts:
    49
    Awesome, thank you for all your help, I really appreciate it! I tested an android build and tried as hard as I could to break it. Immediately going to different scenes without the IAP buttons, launching without internet and then turning it on later etc. Things that made the IAP recovery fail before. It's doing an excellent job of recovering the purchases now!
     
  17. Merlin_ua

    Merlin_ua

    Joined:
    Nov 16, 2016
    Posts:
    2
    Sorry for reopening this old thread, but how exactly it wouldn't create a double spend? ProcessPurchase both in buttons and listener should grant the purchase to the user (as "grant this item to the player" log message suggests for this particular case).
     
  18. SamOYUnity3D

    SamOYUnity3D

    Unity Technologies

    Joined:
    May 12, 2019
    Posts:
    626
    After clicking the purchase button, there will be only one spend, and IAPButton.ProcessPurchase and IAPListener.ProcessPurchase will be called after the purchase is successful. Generally, you only need to process the purchase in one of them.
     
  19. Merlin_ua

    Merlin_ua

    Joined:
    Nov 16, 2016
    Posts:
    2
    If I process the purchase only in one of them, say IAPButton.ProcessPurchase, then why do I need IAPListener at all?

    I'll actually go ahead and describe my problem, which lead me to this thread. I have a single scene, but IAPButtons are only instantiated (from prefab) when a store pop-up is created. I'm concerned that any purchase events that were not processed in previous runs (say, because the app was forcibly closed) will be missed, as there are no button handlers at the moment of the scene load. Therefore I added an IAPListener. However, now I have the problem that when buttons are actually there I receive purchase events both in listener and buttons.

    Now I see the following solutions to this:
    1) Create a database of purchases indexed by transactionID and only grant in-game products once per transaction.
    2) Disable IAPListener every time buttons are instantiated and enable it as soon as they are destroyed.

    Which way is the suggested way for Codeless IAP implementation? Or am I doing anything completely wrong? I'd appreciate any help with this...
     
  20. SamOYUnity3D

    SamOYUnity3D

    Unity Technologies

    Joined:
    May 12, 2019
    Posts:
    626
    If you implement the IAPListener interface, then you only need to process purchases in IStoreListener.ProcessPurchase and not need to process purchases in IAPButton.ProcessPurchase.
     
  21. JeffDUnity3D

    JeffDUnity3D

    Joined:
    May 2, 2017
    Posts:
    14,446
    You need a Listener to listen for events when IAP initializes, like at app launch. There may not be an IAP Button active at this time, so without the listener, this message would be lost.
     
  22. breban1

    breban1

    Joined:
    Jun 7, 2016
    Posts:
    194
    @JeffDUnity3D Start IAP on my project, does your statement about NOT using Codeless still hold true? If so, why? And is the link you provide up-to-date? Thanks!
     
  23. JeffDUnity3D

    JeffDUnity3D

    Joined:
    May 2, 2017
    Posts:
    14,446
    It's more work than scripted, and doesn't support receipt validation without additional scripting anyway.