Restore purchase (DEF-2738) SOLVED

Ok, and are you in your code explicitly calling iap.finish()? If you for some reason didn’t call iap.finish() (app crashed or you simply forgot to in your code) then you should get a callback every time you set the IAP listener using iap.set_listener() until you finish the transaction.

1 Like

no (
I forgot to uncomment it after experiments with android restore purchase and so on…
But how I can to finish it now if I have no callback? By auto_finish checkbox?

Ok. I am woken up.
I shouldn’t finish non-consumable products because if I finish it I can’t restore them.
And now I did not called finish() method for my products.
On android it works good, and I can to restore all purchaise (I rechecked using finish() method too, and with finish() I can’t to restore purchaises)

But on iOS I have an issue when callback did not invoked.

Hmm. Ok so on iOS nothing happens when you call iap.restore() and you get no callback in iap.set_listener()?

Could you please provide a step by step description of what you do, what happens, expected behavior and how your app and products are configured?

1 Like

What I want to do:
I want to have non consumable products and possibility to restore them without server.

Just first example with android, when all works fine. I just want to understand that I made all right.

Part 1, Let’s try to understand how it works on Android (and how it should work)

Products configuration:
On Android I have no option to set my product as non-consumable.
I just create new product and activate it.

###Steps how it works

Buy
1.

function M.buy(self, id)
  iap.set_listener(iap_listener)
  iap.buy(id)
end
local function iap_listener(self, transaction, error)
 if transaction.state == iap.TRANS_STATE_PURCHASED then
        msg.post(...)
--no finish!!!
 end
end

Reinstall an apk

Restore
1.

function M.restore_purchases()
    iap.set_listener(iap_listener)
    iap.restore()
end

2 Same code as in Buy part

local function iap_listener(self, transaction, error)
if transaction.state == iap.TRANS_STATE_PURCHASED then
        msg.post(...)
--no finish!!!
end
end

###Steps how it didn’t work:

Buy

  1. Same code for a buy
function M.buy(self, id)
  iap.set_listener(iap_listener)
  iap.buy(id)
end
local function iap_listener(self, transaction, error)
 if transaction.state == iap.TRANS_STATE_PURCHASED then
     msg.post(...)
     iap.finish(transaction)
 end
end

Reinstall an apk

Restore
1.

function M.restore_purchases()
    iap.set_listener(iap_listener)
    iap.restore()
end

2 . iap listener did not invoked at all (I have no non consumed items, because i finish them all)


All right there?

Part 2, iOS issue

Products configuration:

I use the same code as for android.
And buying works good.
But restoring…

When I try to restore using the same code:

function M.restore_purchases()
    iap.set_listener(iap_listener)
    iap.restore()
end

iap_listener do not invoked.

When I try to buy item one more time:


If I tap to ok iap_listener do not invoked. And I don’t know that user finish work with ios interface.

When I try to buy item one more time on Android it just invoke iap_listener with needed transaction and TRANS_STATE_PURCHASED state (as it shoud be, as i think)

Ok, hmm, you might have come across a bug. I’ve created a ticket to investigate further: DEF-2738

1 Like

Any estimates when somebody can look into it?
It’s fun, but “restore purchase” was our last task before soft launch. =)))

Approximately, just for understand what we can say to our potential publishers.

You should be able to implement basic IAP features with native extensions if you want to soft launch without waiting.

Yes, but If it’s not a long term, I can spend this time for next tasks, that we should to do regardless of soft launch results.

Not sure. @Sara, @sven, @Andreas_Tadic?

I can take a look later this week.

1 Like

IAP_TRANS_RESTORED is invoked on platforms supporting it, which I think is only iOS (need to check that) so if the iap.restore true on Android and it doesn’t support restoring items, it’s a bug. I would need to investigate the code to give correct answers, but it does seem like a bug.
And to be clear, the IAP_TRANS_RESTORED can only be invoked on platforms supporting restoring items since one need to know what item was restored (which is not possible otherwise).

2 Likes

But Android is supporting restore purchase for non-consumable products (products that you did not consume using consumePurchase). And iap.restore should work on android too.

What do you think about second part of issue this and this.

Ok, I misread and thought you meant it was an error it returing true on iap.restore on Android. Ok, so that’s good.
Regarding the rest, I won’t personally have time to look into this straight away.
It does seem like a bug but before reporting it, it would need to be investigated further.
@saracglaser, @Bjorn_Ritzl maybe you can prioritise this correctly.

1 Like

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?