Iap, Android, And Restores

I just remembered, on iOS I think there’s also an OS-driven popup if the device is having trouble connecting with the store due to network delays.  I think that’s a case that would happen on a real device, where calling store.restore() immediately after the app starts could, a few seconds later, result in a popup that the user would be surprised to see.

  • Andrew

exactly, that would be strange and suspicious.

I’m also encountering something like this right now… I wonder with a managed product if it’s possible somehow to catch the info of an already purchased product with the Google Android version? For example: In my app you can unlock levels via one in-app purchase (managed product). Successfully purchased I save the information in a file on the device. Now, if a user is deleting the app and reinstalls it or want to get the levels on another device he tries to purchase and gets the information he already has purchased this product… and I don’t get back an event I can use in the app to save this information again. So the user can’t restore AND can’t buy again. Is that right?

What is the right way to do this?

Can anyone help me please?

Thank you!

Daniela

That’s correct.

I think there’s something missing in Corona when it comes to in app purchases/billing.

It’s even worse on iOS, where if you try to restore a purchase, which was never made, you will not receive any notification…

I handle it this way:

In main menu I’ve got a button, which can be used to remove ads/unlock multiplayer.

When Android user uses this button to restore the purchase, he will get an error from Play store, and then I will display an error message, as part of which I tell user that if they made the purchase before, they can restore it in options.

Then in options I have a restore button.

Because of a bug in Corona [yup… both Google and Apple provide calls to handle that], restore is screwed and you won’t get response from server, if user cannot restore a purchase. So I simply have a ‘please wait’ popup, and if popup stays open for longer than 10 seconds I allow user to close it (I just show them a cancel button after 10 seconds).

Terrible solution if you ask me, but I haven’t figured out any better way to handle it.

I’ve been following this thread, but I haven’t been able to replicate the problems others seem to be facing.  Things seem to be working fine for me on both iOS and Google Play.

Here’s my setup.  I have some managed products that the user can purchase, and I have a separate button to restore purchases.  I don’t call store.restore() until the user actually presses the restore purchases button.

Here’s what I’m seeing.

On Android, let’s say the user buys a product, deletes the app, and then reinstalls the app.  If they try to buy the product again, my transaction callback receives a “failed” transaction.  The user also sees an automatic popup from Google Play telling them they already own the product.  True, it’s up to the user to then realize they need to go and select restore purchases, but I don’t think that’s a big issue.  If it’s something you’re worried about, then in response to a failed state, you can show your own popup reminding the user to try restoring purchases, which, Krystian, it sounds like is what you’re doing, and is what I do too.  Either way, if the user then does hit the restore purchases button, initiating a call to store.restore(), my transaction callback gets a “purchased” transaction for each restored product as expected.  So that all seems fine to me.

On iOS, let’s again say the user buys a product, deletes the app, and then reinstalls the app.  If they try to buy the product again, the user gets a popup from iOS indicating that they already own the product and can download it again for free.  If they say yes, then my transaction callback gets a “purchased” transaction.  If they so no, it gets a “cancelled” transaction.  If instead of all this, they happen to go directly to the restore purchases button, initiating a call to store.restore(), my transaction callback gets a “restored” transaction.  So again, that all seems fine to me.

It sounds like many on this thread are calling store.restore() immediately after store.init() and not receiving any transactions in your transaction listener that you think should be restored.  Just a thought, but try calling store.restore() after a few second delay or by making a separate button for it.  Notwithstanding that the sample code in the API documentation shows store.restore() being called immediately after store.init(), I wonder if there’s something asynchronous about store.init() that could prevent it from working.

Separately, @krystian6, you raised a separate point about what should happen when store.restore() is called if the user hasn’t previously purchased anything and there’s nothing to restore.  It sounds like you’re saying that you expect your transaction callback to receive an event indicating that there was nothing to restore, but instead your callback isn’t receiving anything.  First, from a user standpoint, I don’t see that as a big problem, since a user wouldn’t be expected to press a restore purchases button unless they had something in mind that they were trying to restore.  Second, on the question of whether this behavior is a Corona bug or not, I don’t think it is.  For iOS, according to the bottom of this page – http://developer.apple.com/library/ios/#documentation/NetworkingInternet/Conceptual/StoreKitGuide/MakingaPurchase/MakingaPurchase.html – “the App Store generates a new restore transaction for each transaction that was previously completed”.  This implies to me that it generates nothing if no products were previously purchased.  The Android documentation isn’t quite as clear, but this page – http://developer.android.com/google/play/billing/v2/api.html – does say that RESTORE_TRANSACTIONS “retrieves a user’s transaction status for managed purchases and subscriptions”, which again implies to me that if the user has no managed purchases or subscriptions, it retrieves nothing.

I guess the problem is if, after a user presses the button and you call store.restore(), you activate the system’s activity indicator or some modal dialog that prevents the user from using the app.  You’d turn it off in your transaction callback, but since it never gets called, the user is stuck.  The solution is to either use a timeout, which, @krystrian6, it sounds like is what you do, or you not show an activity indicator / modal dialog in the first place.  But again, it seems to me like this would be the same thing on iOS or Android natively.

  • Andrew

Hi Andrew,

thanks for taking interest in this thread and for sharing your thoughts.

Indeed, the issue I have is with the negative scenarios, when user tries to restore a product they don’t own.

I think we read the same docs and get different ideas on how this should work.

For iOS I would expect to receive “paymentQueueRestoreCompletedTransactionsFinished” call, which indicates that app store finished sending restores. When getting this call without any restores I would assume user purchased nothing.

For Android I would expect to receive a response for RESTORE_TRANSACTIONS, with a response code indicating user didn’t purchase any products.

Never mind that, I do have this work around in place and for now this have to suffice. I will be starting a new project within a week or two, and as a preparation exercise I will simply build a sample app for android and ios to check and see how this works natively :slight_smile:

Again, thanks for taking part in this thread.

Hi Krystian,

Sounds good, I’ll be interested to hear what you find out when you try the “nothing to restore” scenario in native code

  • Andrew

Is there a reason you simply don’t call store.restore() when your app boots up and unlock things automatically?  If there’s nothing to restore, you simply go on with your app.  If you get call backs, unlock them and go on (downloading content as necessary).

The reason I don’t call store.restore() as soon as the app starts is that iOS will sometimes prompt the user to re-enter their password.  I thought that would be a strange user experience for a user running the app for the first time after having uninstalled it previously or after their device was wiped.

  • Andrew

I think that may just be a testing behavior.  Have we seen that happen on live apps?  If they are logged into iTunes on their device, they should only be prompted for their PW when they go to buy things… I think.  

You might be right that it’s only something that happens in the sandbox with test accounts.  But the … is what worries me.  :-)

  • Andrew

I just remembered, on iOS I think there’s also an OS-driven popup if the device is having trouble connecting with the store due to network delays.  I think that’s a case that would happen on a real device, where calling store.restore() immediately after the app starts could, a few seconds later, result in a popup that the user would be surprised to see.

  • Andrew

exactly, that would be strange and suspicious.

I’m having a similar issue to this and wondering if anyone can shed light on it:

  1. I make a test purchase of an unmanaged item, which is successful.
  2. I delete the app and re-install it.
  3. I call restore (the user taps a restore button) but the callback handler never gets called.

So in my case, I’m calling restore for a test user who’s already successfully purchased the item.

In iOS, when testing, if I try purchasing something I’ve already purchased, I get a warning and can download it again. Also on iOS, the restore works properly.

On google play, when testing, it seems that I can make the purchase multiple times and google doesn’t warn, and it seems that restore doesn’t work in test mode? Am I correct in these two assumptions?

If that’s the case, I’ll try testing a beta version through google play to see if that behaves properly.

Hi

the callback handler for restore will not be called for a user who does not own the IAP.

It will get called for user who previously bought the addon.

Beta/alpha testing is the way to go, however you should know that you WILL be charged when getting the iap during alpha/beta test.

@zrm310: If it’s an unmanaged item, it’s not possible to restore the transaction.  So store.restore() won’t do anything.  Only managed items can be restored.

  • Andrew

Thanks. Yep I noticed late last night as i was typing my post that I mistakenly set up one item as unmanaged and happened to always be testing with that one, which was causing all my confusion. All the other differences I see on google vs ios are luckily being handled by my code ( timeout handling for restore not calling the handler if they’ve never purchased something; and failed transaction message suggesting they tap on restore for if they are trying to purchase something twice.)

I’m having a similar issue to this and wondering if anyone can shed light on it:

  1. I make a test purchase of an unmanaged item, which is successful.
  2. I delete the app and re-install it.
  3. I call restore (the user taps a restore button) but the callback handler never gets called.

So in my case, I’m calling restore for a test user who’s already successfully purchased the item.

In iOS, when testing, if I try purchasing something I’ve already purchased, I get a warning and can download it again. Also on iOS, the restore works properly.

On google play, when testing, it seems that I can make the purchase multiple times and google doesn’t warn, and it seems that restore doesn’t work in test mode? Am I correct in these two assumptions?

If that’s the case, I’ll try testing a beta version through google play to see if that behaves properly.

Hi

the callback handler for restore will not be called for a user who does not own the IAP.

It will get called for user who previously bought the addon.

Beta/alpha testing is the way to go, however you should know that you WILL be charged when getting the iap during alpha/beta test.

@zrm310: If it’s an unmanaged item, it’s not possible to restore the transaction.  So store.restore() won’t do anything.  Only managed items can be restored.

  • Andrew