-
-
Notifications
You must be signed in to change notification settings - Fork 151
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Restore from redeemed codes does not work (iOS) #51
Comments
So I can create this what types of products did you create in iTunes connect? It sounds like you did this: 1.) Create 2 Non-Consumables Assumption is that you get both of them, but you seem to be getting a duplicate. May be related to: #29 ? |
1 - 4: Yes exactly this way. Yes I get a duplicate - in this case both with the PurchaseState "Purchased". So I don't see the relation to #29 because I don't get a Deffered result. The strange thing is that it worked on some devices like my own but fails with this result on others. I got the reports from my customers and decided to log the response with my web API. |
Is there any update on this? I got more and more requests from my users about failed restores because the restore functions gets the same product id multiple times instead of each purchased product by itself (iOS). |
I haven't had time to look at it at all to be honest as I don't use coupons, but do plan to look into it. So if you have: If I buy them out right from the app then I get back 2 purchases for "productid1" and "productid2" If I redeem both of them as promo codes then it gets back |
It seems not to happen on all devices. I implement a web log so that when a user hit's restore I see what is restored and there are about 2-3% from my users that gets the last purchase multiple times instead of each once. Because of the high amount of this error I guess it is not because of the gift codes (I only give about 30 away and have about 482 different devices in my log with this problem yet). The purchase or redeem "Product1", "Product2" and "Product3". Now they want to restore and sometimes it get's 3x "Product3" - the last purchase / redeem - instead of each once. If it happens on a device it would not change (two of the users contacted me directly and tried it multiple times). I don't know what happens exactly there but for me it looks like a problem where the ReSharper would say "access to modified closure". |
Got it hmmmmm interesting. I am looking through the code now and not seeing anything. I just do this to convert:
Looks valid |
I'm looking through this... https://developer.xamarin.com/guides/ios/platform_features/in-app_purchasing/transactions_and_verification/ The only thing I see is a switch for restored. Which I have added... But still strange. |
The code is small, so maybe just add that to your IOS app and try it out and see what comes from it. I only have apps with a single item currently so hard for me to test. |
I've tried to create this problem on any of my devices (13 in total for iOS) with about 30 accounts and I was not able to create the issue at least once... I just hear it in the reports and see it in the logs that the user gets for a single restore multiple times the same product id. I will implement the code tomorrow to my app and let one user test it where I know the problem occurred. |
gotcha, i also deployed a new pre-release that has my switch casing on the restored... maybe that was being ignored before? |
Also, are these your apps: https://itunes.apple.com/us/app/breath-companion/id1218936949?mt=8 https://play.google.com/store/apps/details?id=com.SoftwareNotion.BreathCompanion? Very cool! We are all huge zelda fans :) Let's make sure we get this working for sure. I can install the iOS app and try to reproduce too. Feel free to email me motz (at) microsoft.com |
Thats exactly the app where I have the issue (and a few other issues on Android as well since the latest version but thats not because of this plugin ^^). Just uploaded a test version with the changes to TestFlight. As soon as the test version is approved by Apple, I can give you feedback if this helps to resolve the issue. |
sounds good! Let me know :) |
I got the first responses from my beta users about the issue. So far, there is no problem on iOS for users that worked before as well. But the users that got before multiple times the last purchase id, not gets an NullReferenceException on restore. I just added a push to my web logging to get the entire stack trace. All the information I currently have is that the Exception occurs somewhere since the update on the restore function - but only for users with the problem before. |
Interesting.... yeah would like to see the stack trace, maybe it is just something when i am converting it and it could be a possible fix. these things are so hard to test. |
So far still no exception from iOS in my log... But I got one from Android that seems to be the same (?) - a NullReferenceException when the restore is triggered:
|
hmmmm, i haven't changed any Android code lately to be honest with you. I just ran through this code in my sample app and had it working on android just fine:
The best way to diagnose this is to: 1.) Remove the plugin from your android project This will let you step through the code. |
I've already tried a few things but can't get this error yet. I will try your suggestion, thanks for the response. I'm a bit confused that there is no exception from iOS logged yet. It has already installations on 17 devices refered to itunesconnect and the testers know that the restore needs to be tested. |
Just found out where the exception comes from. It happens if you try to restore without any purchase on this account before. Maybe that was the same for iOS? I will monitor this in the next few days. |
Yeah, so that method will return null if there are no purchases.. I could change to a blank list, but I just check against null. I bet that was the issue. Let's hope my other fix for iOS works |
So I finally git some results... this time the apple review process was a little bit longer... Here are all exception that don't say "Canceled by User", "Cannot connect to Store", "Unable to process purchase" or "Purchase canceled": iOS - 2017-07-06 13:21:34, 2017-07-06 05:34:41 and 2017-07-06 05:34:32 - NullReferenceException
iOS - 2017-07-06 07:40:35 and 2017-07-06 00:49:46 - Restore failed
|
So, it looks like:
|
|
It looks like for the Restore failed it used to throw just a generic exception, but 2 months ago we changed it to return a normal exception. Basically when you catch that exception you need to know if it really was an error because they purchased something before and it couldn't be restored or was it because they haven't purchased yet. Very tricky. |
Just released the new version of my app (including 1.2.2 of you plugin) and still got some reports about this issue (I've shared ~50 codes per platform per IAP so there are now way more users that could have this problem). So here is the list of exceptions I get so far, maybe some of them help you to improve the plugin because I think this plugin is great (after I implemented it one time by myself I know how much trouble this is even if you just implement one platform as I did with Swift): iOS - Restore - 2017-07-22 15:28:403 - this happens multiple times, so far I see 37 in my log from the 50 users who uses the codes - at least it looks like this is the only restore exception I got:
And then there are also this exception but I don't know how relevant they are: Android - Purchase - 2017-07-22 15:45:14 - this one looks like a cancel from the user for me but it happens two times from the same user:
iOS - Purchase - 2017-07-22 - I guess this is the same as last time? So it might be irrelevant, happens two times for the same user:
And thats it. All others are "Cannot connect to store"-erros. |
I'm having the same trouble with in-apps on iOS, and I can add some info that can bring some light to this problem:
|
I can confirm that this also happens with real purchases. I got two reports about exactly this problem (it must be a never ending task because I see the "Purchase start" log entry but neither a failure or a success response). |
And it never happens in a sandbox or TestFlight. |
I got more and more users who are reporting this issue but I cannot see anything is the log except that they started the purchase but it never goes into the "success" or "exception" state. Looks a bit like a deadlock in some cases for iOS? |
I am also reading through this for promo codes: https://stackoverflow.com/questions/41931584/ios-detecting-promo-code-in-app-purchases What does your code snippet look like for making the purchase? How about Android? Any issues there? |
Android seems to have no problems at all. This is my code for the purchase (Forms / Shared):
|
All looks relatively alright, I don't really ever dispose of mine though, i see no reason to disconnect the observer at all. https://developer.apple.com/library/content/technotes/tn2387/_index.html is what I am reading and it is pretty much spot on to my code and implementation. The only thing is that you should be able to query all of the purchases , which will also finish any pending transactions that are there. Apple makes you implement a restore button so this should also do it. |
A Restore button is also implemented. The dispose was added just to see if it makes any difference. I will check if the usage of SKPaymentQueue change something. |
Some more info. |
Ahhh that is interesting... if you gave out the promo code and it is trying to be purchased again then it could get stuck.... That is interesting.... but very strange.... maybe we need to check for a specific error code. |
It gets stuck with regular purchases as well sometimes. The PurchaseAsync method is called but the callback never comes. Just got new reports in the last few days from people with this problem and I have not create new codes yet (also I see in my log the entry that I do before "PurchaseAsync" but neither the success or the exception log is triggered). |
OK, guys, now I know what's the problem. |
From the first two days testing this it seems to work what overzealus mentioned. Are there any plans to include it directly to the Plugin? Because it would be much cleaner if the IAP code is in one place. |
Yes, we should be able to, i am open to pull requests if this is fixing things up for everyone. Please help out :) |
I will need a PR from you guys who have found some solutions to help out. since i can't reproduce. |
I guess the solution from overzealus would be better. I cannot test it on my own too and can only relate on the response of my users (and even if I trust most of them, I cannot confirm it 100% by myself). |
@jamesmontemagno, the solution is really simple:
PS. I don't know, what should we do with consumables or subscriptions, since I don't use them, and I'm not sure, that bug happens with them and this code can give unpredictable results with consumables! |
Sebastian1989101 my issues is I can purchase my auto renew subscription get a purchaseID of 100003456789 as an example and the sandbox renews up to 5 times or whatever... Then when I check to see if I purchased the subscription already using var purchases = await billing.GetPurchasesAsync(ItemType.Subscription); it seems I get the same purchaseID over and over again. So I purchase again (after it stopped renewing) and I get a new purchaseID 10000345555 for instance, and then I update the server with the new purchaseID I run the var purchases = await billing.GetPurchasesAsync(ItemType.Subscription); and I never get the new purchaseID, it's always the first one. Is this correct? Should I not update the server code with the new purchaseID, just keep the original? |
@jamesmontemagno Is there a chance that this getting fixed soon? I'm currently working on 3 new projects and fixing my old ones for iOS 11 / watchOS 4. Would be great to just use a updated plugin instead of inserting this workaround in each project. |
I am not sure exactly what the workaround is to be honest.... @Sebastian1989101 do you have a workaround? I am looking at what @overzealus did, but literally looping through each of the items I don't understand that at all. |
I'm using a pretty similar solution as @overzealus did. There are a few transactions sometimes in the queue that can be grabbed from there. It's possible that a transaction / a purchase gets a timeout (in this case the Plugin.InAppBilling never comes back from a Async call). Thats exactly what happend if someone had this bug where only the first purchase is restored. |
So do you add those transactions to the returned ones? I'll post a snippet with what I think should happen. |
@ jamesmontemagno the main solution is not just looping through transaction, but checking transaction queue (SKPaymentQueue.DefaultQueue.Transactions) before you call SKPaymentQueue.DefaultQueue.RestoreCompletedTransactions(); And about looping inside transactions. Each transaction have a property of the same type, called OriginalTransaction. It's a transaction, that happened before and connected with the inspectable transation. So, if we check status of the one, that is on the "top" - we'll have failed status (we don't know the reason, may be user had some internet connection problems). But, if we check inner transaction, we can see, that user have already purchased it and restored it (may be on previous installation of the app). |
So.... would it be something like this:
|
I guess sometimes the Payment also stays in Pending status for some reason. But maybe @overzealus could be more specific than me. Edit: Can you maybe roll out a beta version with this changes? So that we can test it if it has the same result as the custom implementation. |
I receive more and more complains from users about the problem too. @jamesmontemagno, the implementation of suggested solution from @overzealus looks good to me and I'll be happy to test it with the next update. Any plans to release the update soon? @overzealus Do we need to use FindOriginalTransaction for Purchase method too? |
Check: 1.2.3.98-beta |
Just received feedback from users - the new update works perfectly. Thanks so much! |
Got the exact same feedback as @desunit from my TestFlight users so far. I hope it works as well on the release version it is currently in review by apple. |
yay! i am glad we were able to all work together on this one. I wish that Apple's API was better :) <3 |
If you are creating an issue for a BUG please fill out this information. If you are asking a question or requesting a feature you can delete the sections below.
Failure to fill out this information will result in this issue being closed. If you post a full stack trace in a bug it will be closed, please post it to http://gist.github.com and then post the link here.
Bug Information
Version Number of Plugin: 1.1.0.35-beta
Device Tested On: Multiple iOS devices
Simulator Tested On: -
Version of VS: 15.2 (26430.12) Release
Version of Xamarin: 2.3.4.247
Versions of other things you are using: Mapsui 1.0.9
Steps to reproduce the Behavior
Redeem multiple an IAP codes on iTunes and try to restore it on the iOS device (does not happend all the time. It happens in 1/20 tries).
Expected Behavior
On restore there should be each product that was redeemed with a code or purchased.
Actual Behavior
The last redeemed code returns multiple times. So if you have a code for "Unlock XY" and one for "Remove Ads" the last redeemed code is returned for each redeemed code. In this case if you redeem "Unlock XY" first and "Remove Ads" than, you got 2x "Remove Ads" on the restore function.
Code snippet
Screenshotst
Here is entry in my DB from the PurchaseReport-Method I built. The customer gets 2x the last product he restored. http://puu.sh/wdD3q/55a9cd9e74.png
The text was updated successfully, but these errors were encountered: