Search Unity

[Closed] How should Codeless IAP "Restore" work.

Discussion in 'Unity IAP' started by gtzpower, Mar 16, 2018.

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

    gtzpower

    Joined:
    Jan 23, 2011
    Posts:
    318
    Hello everyone. I am trying to implement iOS purchase restoration using codeless IAP. have been trying to figure out how the purchase is fulfilled with a restore and I cannot really find any documentation on it (the Unity manual doesn't even seem to mention the IAP Button having restore option).

    I think what should happen is that the IAP restore button will ask iOS what the user owns with regard to non-consumable purchases, a list of owned products is returned, and if an IAP Button exists for one of those products, it's onPurchaseComplete event is invoked for that product. I assume this kind of invocation is what happens because it seems to be how non-consumables are fulfilled if the user is logged in to their App Store account when the app is launched.

    My problem is that if they a user is not logged in to the App Store (as seen in Settings -> iTunes & App Store) and they try to restore using the IAP Button, the restore succeeds (after logging in, OnTransactionsRestored prints "Transactions restored: true" to the debug log), but ProcessPurchase is never called on any of the IAP Buttons. If I kill the app and re-launch it, now that the user has logged in, the purchase is fulfilled automatically when an IAP Button is enabled.

    Thoughts?

    Unity 2017.3.1
    Purchasing 1.17.0
     
  2. JeffDUnity3D

    JeffDUnity3D

    Joined:
    May 2, 2017
    Posts:
    14,446
    Does it work correctly for you if the user is already logged into the App Store? For previous versions of Codeless IAP to work, the buttons need to be in scope in the current/active scene and enabled. The most recent version of Codeless IAP provides a Listener so the buttons do not need to be active. The Create IAP Listener is under the Unity IAP menu in the Editor.
     
  3. gtzpower

    gtzpower

    Joined:
    Jan 23, 2011
    Posts:
    318
    Thanks for the fast response. If the user is already logged in, I don't even get to the restore button because the purchase is restored as soon as the IAP Button is enabled.

    The buy and restore buttons are on the same screen. If the user is logged in when they go to that screen, the restore just happens before they can hit the button.

    I added the IAP Listener to my scene and configured the events to point to my shop script for fulfillment, but my fulfillment/failed functions are still not being called. I also noticed that the IAP Listeners own internal ProcessPutchase and OnPurchaseFailed functions are not being called (no output in the Debug.Log for them). Here is my debug log:

    Edit: Cleaned up log to only include relevant info to the post.
     
    Last edited: Mar 17, 2018
  4. JeffDUnity3D

    JeffDUnity3D

    Joined:
    May 2, 2017
    Posts:
    14,446
    You are stating that products are automatically restored on iOS for you? That would imply that ProcessPurchase is being called. Also please limit your log listings to contain "unity" only for readability. Are you testing via TestFlight?
     
  5. gtzpower

    gtzpower

    Joined:
    Jan 23, 2011
    Posts:
    318
    The purchases are automatically restored ONLY when the application is loaded while the user is logged in. If the user is not logged in to iTunes, the purchase is not automatically restored when the application is opened (as expected). This is when the user must hit the restore button in order to prompt the login. After logging in, ProcessPurchase is not being called from this point (this is where the problem lies, I cannot restore their purchases once they do login). If I exit the application and re-open, now that I am logged in, the purchase restores automatically when the button becomes visible.
     
    Last edited: Mar 17, 2018
  6. gtzpower

    gtzpower

    Joined:
    Jan 23, 2011
    Posts:
    318
    Sorry, forgot to mention, this happens on TestFlight and on a manual USB deployment.

    Edit: In an attempt to make the problem as clear as possible, I started an empty scene so that I can give you reproduction steps. This is assuming your iTunes account already owns a non-consumable IAP to be restored.

    1. Add an IAP Button to a new scene, and set it the button type to "restore".

    2. Add an IAP Listener to the scene (we will just rely on it's Debug.Log calls to see whats happening).

    3. On your iOS device, sign out of iTunes (Settings -> iTunes & App Store -> Tap your username and choose "Sign out")

    4. Build and run to the device.

    5. Once the app launches, tap the restore button and then login when prompted by iOS.

    6. After you have logged in, search the in the output/console in Xcode for "ProcessPurchase". You should see that there was no Debug.Log from the IAPListener. This is where I need the callback so I can process the restoration and give the user their goods. But it doesn't work.

    7. Exit the application and redeploy it (or simply run it again with Xcode attached for the console output).

    8. Once running, search the Xcode log again, and you will see that "ProcessPurchase" was called during initialization (now that you have already logged in during step 6).


    I hope it clarifies the problem I am having. Restoring only works if the user is logged in before the app is loaded. I am not sure how else to explain it.
     
    Last edited: Mar 17, 2018
  7. unityjingyao

    unityjingyao

    Unity Technologies

    Joined:
    Feb 20, 2017
    Posts:
    220
    Hi @gtzpower
    I followed your steps to build a sample project. I tried several times but still couldn't reproduce your issue.
    Every time I logged in my sandbox account, I got my subscription product restored.
    This is my log.
    Could you please open a ticket and send us a demo project which could reproduce this issue? Thank you.
    https://analytics.cloud.unity3d.com/support/
     
  8. gtzpower

    gtzpower

    Joined:
    Jan 23, 2011
    Posts:
    318
    Hi @unityjingyao, thanks for giving this a try. Thank you for showing your log as well so I can see what it should look like. I have opened a ticket but do not see a ticket number to reference in the Zendesk notification. I don't see any way to upload the demo project through the ticketing system either, so I will post it here.

    I created a brand new project just to reproduce this (see attached). I also created this video for you so you can see exactly what is happening on my end. You will see that the restore reported that it succeeded, but ProcessPurchase was never called. I tried this with an IapListener as well with the same result, but in the demo project attached, just the IapButton is setup to process purchases.


    I suspect that the key to reproduction in my case is that the device is in a state where I am asked to "Sign in with an existing Apple ID" after I hit the restore button.
     

    Attached Files:

  9. JeffDUnity3D

    JeffDUnity3D

    Joined:
    May 2, 2017
    Posts:
    14,446
    @gtzpower Do you mean that your PurchaseSucceeded method is not being called when you are not logged in with your AppleID? Sorry, I was not quite able to follow your video.
     
  10. gtzpower

    gtzpower

    Joined:
    Jan 23, 2011
    Posts:
    318
    Yes, PurchaseSucceeded is not being called. But this is because IAPButton.ProcessPurchase is not being called. If you look at the log from unityjingyao, you see a sequence of these events:
    I don't get that ProcessPurchase call to invoke my PurchaseSucceeded method. You can see that in the output log in the video. Restore is requested, transactions are restored, but no ProcessPurchase to allow me to handle the restoration.
     
    Last edited: Mar 19, 2018
  11. unityjingyao

    unityjingyao

    Unity Technologies

    Joined:
    Feb 20, 2017
    Posts:
    220
    Hi @gtzpower ,
    I still couldn't reproduce this issue with your project.
    I only replaced the bundle id and product id to my own.

    I noticed a small difference between your first log and the log in the video.
    The product id of "no_ads" in your first log is "com.funonium.gravityshots.no_ads", but I found it was "com.funonium.orbiting_ballz.no_ads" in the video.
    I also found this error log.
    Is this expected?
    Which is the right one?
    Could you please send me the log of "00:40" in the video? It was the initialization result log but it was jumped. And I guess the initialization of UnityIAP was failed.
     
  12. gtzpower

    gtzpower

    Joined:
    Jan 23, 2011
    Posts:
    318
    The product ID is different indeed. I wanted to make sure that there wasn't something wrong with the project in iTunes Connect, so I setup the test application using another product ID and bundle ID entirely to eliminate the possibility of some issue with the project in iTunes connect. Both products experience the same issue, either one is the right one.

    Regarding the "PurchaseFailed : Unknown", this is not normal. I am glad you mentioned it because this proves that my IAPButton is listening. I don't normally get that, but this is my PanelMessageScropt.PurchaseFailed() function calling Debug.Log(). This "unknown" error actually popped up on the screen, and then the overlay disappears afterwards. This shows that my handler runs when the restore fails at least. However, this is followed by an error saying that I need to "Add an active IAPButton to handle the error".... What?

    Unfortunately I do not have the window up anymore to get the log that you see in the video. I ran the app again and captured the log for you. It is a little different because I didn't get that unusual "unknown" error, but still no ProcessPurchase() call:
    I can capture another video for you if you want without that "Unknown" error, and provide the accompanying log output. I can add you to my TestFlight internal testing team and upload a non-functioning build if that might help testing on your end (I haven't tried building from a different Mac because I don't have another one, but this would reveal if it is a build issue).

    I really want to be able to use this. I can always go back to the P31 Storekit plugin I normally use, but this system is so much easier to use... If I can just get it working.

    Thanks so much for looking at this with me. I am really at my wits end.
     
    Last edited: Mar 21, 2018
  13. JeffDUnity3D

    JeffDUnity3D

    Joined:
    May 2, 2017
    Posts:
    14,446
    While we continue to investigate, have you considered the non-Codeless approach to IAP? It would give you more control on the event handling. Granted Restore on Codeless IAP should work as expected, we are still looking into this.
     
  14. gtzpower

    gtzpower

    Joined:
    Jan 23, 2011
    Posts:
    318
    Sure thing. Tonight, I opened up the IAP Demo scene and modified the non-consumable ID to match my application's ID in order to do a non-Codeless IAP test. I see the same behavior. Here are 2 logs.

    The first one is what happens when I am not logged into iTunes when I launch the app. Notice that there is no ProcessPurchase() call to fulfill the restoration:
    I also get the same behavior as the codeless IAP if I am logged into iTunes when I open the app. The purchase is restored (ProcessPurchase is called) as the IAP system initializes since I am logged in, but ProcessPurchase is not called when I try to restore.
     
  15. unityjingyao

    unityjingyao

    Unity Technologies

    Joined:
    Feb 20, 2017
    Posts:
    220
    Hi @gtzpower
    I tested on another iPad with WiFi and 4G network, still can't make it happen.
    You can find the Objective-C source code of Unity IAP at "/Assets/Plugins/UnityPurchasing/iOS/UnityPurchasing.m".
    When a purchase is done, onTransactionSucceeded(Line 155) will be fired from updatedTransactions(Line 377).
    The first line of updatedTransactions prints out a log which is "UpdatedTransactions".
    You can find it in my log, but I couldn't find it in any of your logs.
    I guess that Apple didn't call this method. You can add code to print more logs in this file.

    Could you please try it with a different network, like 4G?
    If it still doesn't work, please add me as a TestFlight tester. I'll PM you an email. Thank you.
     
  16. JeffDUnity3D

    JeffDUnity3D

    Joined:
    May 2, 2017
    Posts:
    14,446
    We are hearing from various users that this may be an issue on the Apple side which hopefully is now resolved. Is this still occurring for you? Other posts mention that users need to confirm the Terms and Conditions if they recently updated iTunes, or the Unknown error may be returned.
     
  17. gtzpower

    gtzpower

    Joined:
    Jan 23, 2011
    Posts:
    318
    Hey there! So, I managed to get this working with some fidgeting, and I think a combination of your suggestions above may have led me to the solution. I first got com.funonium.orbiting_ballz.no_ads to start working properly, and I wasn't sure what exactly fixed it. So I checked with the other product id (com.funonium.gravityshots.no_ads) and it was still having the issue. So I started a recording as I played around with it just in case it would reveal something useful. Let me know if you are interested in seeing the video.

    Anyway, in the end, purchasing the item again using the non-codeless IAP, on LTE, seemed to clear things up. I got the prompt that the IAP was already owned and that it would be restored. This isn't the first time I have "re-purchased" the IAP, but it is possibly the first time I have tried it on LTE and probably the first time that I have tried it with the non-codeless IAP system.

    So I pulled out my iPad which had the codeless IAP build on it that I sent to you a few days ago. With no changes, I was now able to login and restore the purchase. So it seems like something was messed up on Apple's end since it was resolved on my iPad by repurchasing on my iPhone. How those IAP's got into that state in the first place is still a mystery to me, but I will be keeping an eye on it just in case it happens again.

    Lastly, I was also having problems with iTunes constantly prompting me to enter my password. (https://forum.unity.com/threads/constantly-being-asked-for-itunes-password-on-ios.519002/). This was popping up every time my network connection changed (leaving home and arriving at work would prompt me 2 times for my sandbox password). I was able to disable wireless and re-enable wireless to get prompts constantly. After doing the above process, those prompts that have been driving me crazy for 3 weeks now have also gone away. WOOHOO!

    I am going to return to codeless IAP system in my application and keep an eye on things. Hopefully the problems don't return, but if they do, I will try to find a rhythm to it.

    Thank you so much for your help everyone!
     
  18. gtzpower

    gtzpower

    Joined:
    Jan 23, 2011
    Posts:
    318
    Welp, that was a short lived victory. I am back to this problem:
    https://forum.unity.com/threads/log...ted-app-purchase-still-being-restored.521238/

    I think I want to continue in this thread since the example project I am using is attached above and there is more context here (let me know if you want me to switch threads.)

    I have discovered that if I restore my purchase using Codeless IAP, something odd happens and I run into the problem linked above. The purchase is constantly being restored when IAP is initialized, even when I am not signed into iTunes and I have deleted/redeployed the app. It's like the purchase was never fully processed or something and iOS is trying to give it to the app. I also begin getting prompted for my password constantly again in this state.

    Here is a log of what a launch looks like with codeless IAP (the scene is provided in the sample project I uploaded above). I can launch multiple times, and ProcessPurchase keeps getting called on initialization (this was a second run after restoring using codeless IAP):
    However, if I switch to the non-codeless IAP Demo scene, ProcessPurchase gets called on the first run:
    But on the second run, it appears to have been cleaned up by the non-codeless IAP initialization during the first run. It does not get called again:
    I am deleting the application off of the device in between runs. This was the problem I had right before restores stopped working last time, and I think it may be related to the entire issue since it seems to be a problem with the state of the IAP. It really feels like the codeless IAP is not handling the restore fully and iOS thinks it wasn't delivered. As soon as non-codeless IAP has a chance to handle it, all seems to be happy and I don't hear about the purchase again on initialization.

    I know there is a lot to take in here and I may not be detailing it clearly. If it would help, I can put together a video to show the issue for you.

    I will do some more digging tomorrow in the Codeless IAP source that I can see and compare it to the non-codeless solution incase that reveals something.
     
    Last edited: Mar 23, 2018
  19. unityjingyao

    unityjingyao

    Unity Technologies

    Joined:
    Feb 20, 2017
    Posts:
    220
    Hi @gtzpower
    I found the cause finally.
    upload_2018-3-23_12-36-52.png
    You didn't check the "Consume Purchase" checkbox in your sample project.
    So the restore transaction wasn't finished.

    It's a little bit weird when it's called "Consume Purchase" since the product is non-consumable.
    But it means "finish transaction" on iOS platform.
    It's used for determining the return value of ProcessPurchase. You can find the code at Line 320 of IAPButton.cs.
    This option should be ignored for non-consumable/subscription products.
    I'll pass this issue to IAP team.
    Please try it in your project.
     
  20. gtzpower

    gtzpower

    Joined:
    Jan 23, 2011
    Posts:
    318
    Bingo! That solved the issue where purchases were being restored on initialization even though I was logged out. Thanks a lot!

    My next question after this was resolved was going to be how the IAPListener is useful for applications that contain consumable AND non-consumable purchases when you can only mark to consume or not consume with no filter on which products are being consumed and which are not. I didn't want to flood too many questions at once, but I suppose asking this question would have helped with the other issues.

    Now, my final question is, do I want to uncheck this on Android so the non-consumable isn't available for purchase again, or does this not relate to actual "consumption" of the product as far as google uses the term? This is the entire reason it was unchecked in the first place; "Consuming" means the user no-longer owns the product on Android: https://developer.android.com/google/play/billing/api.html. (See the "Consuming In-app Products" section).

    If this parameter is entirely unrelated to consumption of the product as Google uses the term, would the dev team consider renaming it? Or, are you having the IAP team look at it because it should be related to consumption, it should be able to be unchecked on all platforms, and a future update will fix the issue?
     
    Last edited: Mar 23, 2018
  21. unityjingyao

    unityjingyao

    Unity Technologies

    Joined:
    Feb 20, 2017
    Posts:
    220
    Hi @gtzpower ,

    The callbacks attached to IAPButton will be fired only when the button is active.
    If your IAPButtons are not in the first scene, some IAP callbacks will not be handled.
    In this situation, you can add an IAPListener to deal with these callbacks.
    Please find more information about IAPListener in the changelogs of Unity IAP.
    https://forum.unity.com/threads/unity-iap-store-package-1-17-0-is-now-available.415517/#post-3236313
    https://forum.unity.com/threads/unity-iap-store-package-1-17-0-is-now-available.415517/#post-3410305

    You can find the type(Consumable/Non-Consumable) of a product via Product.definition.type of IAPListener callbacks.

    As I said in my last reply, this option is used for determining the return value of ProcessPurchase. It will not get a Non-Consumable product consumed on Google Play.
    Unity IAP will check the type of a product before consuming it. If it's Non-Consumable, Unity IAP will not consume it.

    To keep consistent with iOS platform, I suggest you check this option.

    Regarding renaming this option, I need to discuss it with the IAP team. Now I'm not sure how we will deal with this issue. Anyway, thank you so much for your feedback and suggestions!
     
  22. gtzpower

    gtzpower

    Joined:
    Jan 23, 2011
    Posts:
    318
    Thanks a lot for helping me understand this feature of the codeless IAP system. I believe leaving the box checked will resolve the issues I have been having. I will post back if I run into further problems.

    Again, thank you both for all of your help. :)
     
Thread Status:
Not open for further replies.