Restore purchase (DEF-2738) SOLVED

As far as I know, you should always call finish() on non-consumable items. On Android they are called “managed” products.
As for consumables and unmanaged products, I think you should also call finish() and manage them yourself in your app. That is if a user reinstalls the game, it’s not required to restore such products. On iOS you instead use iCloud to restore the game progress and consumables.
On Android you can use Google Play Game Services or maybe Firebase to do the same thing for unmanaged products.

Again, as far as I know, if you don’t call finish(), after some time the purchase would be cancelled and the money would be returned to the user.

Calling finish() supposed to protect users from crashes during the purchase. You get the transaction, update internal values and only after that you call finish(). That way if the app crashes during update of the internal values - the user receives unfinished transactions when the app starts again.

1 Like

Cool, thank you!!!
But I use only non-consumable items on ios and managed items on android. And if I call finish() on Android i can’t restore them later.
That mean a non-consumable purchase doesn’t work in defold at all =(

If you can’t restore managed products - that’s definitely a bug.

1 Like

Yes, I can’t =(

Than mean that my “workflow” on android is wrong!
And Defold do not support restore purchase and non-consumable products.

I’ve tested iap.restore() on iOS now and it works as expected. I have an app setup on iTunes Connect with a bunch of Consumable products and one Non-Consumable product:

My app is set to auto finish transactions in game.project.

I buy the consumable and non-consumable products as normal using iap.buy(). If I try to buy the non-consumable product again I get the “You’ve already purchased this” message:

And when I do a call to iap.restore() I get a callback to my iap.set_listener() defined function, with the state iap.TRANS_STATE_RESTORED. I can do this any number of times without any problems.

As for iap.restore() on Android you need to do as you’ve already outlined in your post above:

  1. Don’t use auto finish transactions
  2. Consume consumable products manually using iap.finish()
  3. Use iap.restore() to get iap.set_listener() callbacks for any product that hasn’t been manually consumed yet

So I guess in order for you to get non consumable products to work in your game you need to do a couple of things:

  1. Don’t use auto finish transactions
  2. Always call iap.finish() on iOS
  3. Call iap.finish() on Android for consumable products
1 Like

Let’s try to understand step by step…

  1. It means that I did everything right there?
  1. And the purchase will not be canceled?

  2. Now I have next result on ios (i did not make a finish() of this purchase):
    https://www.youtube.com/watch?v=qbibMMqhf6w
    (I forgot to show console, but I have no callback)
    Should I create new account for tests? I have no possibility to continue work with this non finished purchaises?

Hmm, so that’s a consumable product that you haven’t called iap.finish() on? This is what happens for me:

iOS app auto_finish_transactions disable

  • Starting app:
  • iap.set_listener()
  • iap.buy() on a consumable product
  • NO call to iap.finish()
  • iap.buy() on the same product yields “You’ve already bought this”. No callback when I click through the popup (bug? maybe)
  • iap.restore() does nothing. This is expected since it only works for non-consumable products.
  • Restarting app:
  • iap.set_listener()
  • Callback for the unfinished purchase is invoked
  • NOTE: Multiple callbacks will be invoked if I have tried purchasing the same product multiple times. transaction.trans_ident is the same though.

So, from what I can tell there might be a bug with “You’ve already bought this” not resulting in any callback to the listener, but otherwise everything looks ok to me.

no, it’s non-consumable product (I told only about non-consumable all time)
But when I made ios it was the same logic as on android without finish().

How to reproduce bug on video:

  • buy non-consumable product without finish();
  • reinstall app;
  • try to buy this item one more time.
    (restore purchase doesn’t work in this case too.)

Pls, can you answer me by my points 1-3. I want to be sure that i understand right.

I try to finish purchase on ios and it works good (restore and repeat of purchase)
And now I have next unsolved questions:

  1. Did I make everything right on android (I shouldn’t finish() purchaise)?
  1. The purchase will not be canceled if i did not use finish() (android has only consume() method and has no method for confirm purchaise)?

  2. What can I do with purchases that user already bouth, but not finished on ios (problem on video)? (I made it by mistake, but it posible after crash or some connection problem)

Yes. Do not call iap.finish() for non-consumable purchases. Google will keep track of this purchase for you. Every time you call iap.set_listener() or iap.restore() you’ll get a callback with any purchase that hasn’t been finished.

As far as I know the purchase will not be canceled by Google. This is supported by the official documentation: https://developer.android.com/google/play/billing/api.html#consume

On iOS (and Android) you should get a callback to the iap.set_listener() function for every product that you haven’t called iap.finish() for. Aren’t you getting a callback? It works in my test app.

1 Like
  1. Done!
  2. Done!
  3. No, I did not receive callback to the iap.set_listener on ios if I did not finish() purchase (on android all good).

Strange. Which iOS version and device are you testing on? Let me double check with my test app.

it is iPad 4 with 10.3.2 ios version

Works in my test app. I’m actually getting multiple callbacks. Once for every time I’ve tried to buy the product (same transaction.trans_ident though).

I receive Purchasing state of transaction and that all

The same code works good on android, and on ios (if purchase was finished) too. But doesn’t work if I did not finish() a purchase.
Now I have 2 sandbox accounts where I cant buy or restore anything.

You mean that you’re getting the purchasing state as a result of the iap.buy() call and then nothing at all for iap.set_listener() (not even when the app is restarted and iap.set_listener is called again)?

It looks like (you can see it on video starting point 3):

  • buy non-consumable product without finish();
  • reinstall app;
  • try to buy this item and receive Purchasing callback;
  • tap buy and ok in ios windows (last is “already bought”);
  • and have no any other messages (in my case it is spin screen without callback), I checked it using DefCon and print in first line of callback (before all conditions).

I can restart an app and repeat all this points and receive the same result.

Google billing API has getPurchaseHistory() method.
https://developer.android.com/google/play/billing/billing_reference.html#getPurchaseHistory

I guess in the engine it’s not used and getPurchases() is used instead.

I’m unable to reproduce this. On an iPhone 5s (iOS 8.3!):

  • Create non-consumable product in iTunes Connect
  • Set auto_finish_transactions to false
  • Install app and run app the first time:
  • Call iap.set_listener()
  • Buy product using iap.buy()
  • Don’t call iap.finish()
  • Uninstall app
  • Install app and run it again:
  • Call iap.set_listener() -> I get a callback as expected!
  • Reinstall any number of times shows the same behavior.

I will try on another phone with a newer iOS version. Which iOS version are you testing on?