Android: iap.restore(), iap.finish() and iap.buy() in sequence

Hello!

In my game I have a situation when I need to restore() item, then finish() it, then buy() and so on.
It looks like the case isn’t implemented yet because after reloading the app I have strange behaviour like loosing ability to restore item or to buy it or I catch an exception.

Here is a stack trace of the exception:

01-21 20:00:33.975 19469 19469 E AndroidRuntime: FATAL EXCEPTION: main
01-21 20:00:33.975 19469 19469 E AndroidRuntime: Process: ..., PID: 19469
01-21 20:00:33.975 19469 19469 E AndroidRuntime: java.lang.IndexOutOfBoundsException: Index: 0, Size: 0
01-21 20:00:33.975 19469 19469 E AndroidRuntime: at java.util.ArrayList.get(ArrayList.java:437)
01-21 20:00:33.975 19469 19469 E AndroidRuntime: at com.defold.iap.IapGooglePlay$5.onSkuDetailsResponse(IapGooglePlay.java:343)
01-21 20:00:33.975 19469 19469 E AndroidRuntime: at com.defold.iap.IapGooglePlay$6.onSkuDetailsResponse(IapGooglePlay.java:376)
01-21 20:00:33.975 19469 19469 E AndroidRuntime: at com.android.billingclient.api.zzj.run(com.android.billingclient:billing@@3.0.0:8)
01-21 20:00:33.975 19469 19469 E AndroidRuntime: at android.os.Handler.handleCallback(Handler.java:873)
01-21 20:00:33.975 19469 19469 E AndroidRuntime: at android.os.Handler.dispatchMessage(Handler.java:99)
01-21 20:00:33.975 19469 19469 E AndroidRuntime: at android.os.Looper.loop(Looper.java:201)
01-21 20:00:33.975 19469 19469 E AndroidRuntime: at android.app.ActivityThread.main(ActivityThread.java:6864)
01-21 20:00:33.975 19469 19469 E AndroidRuntime: at java.lang.reflect.Method.invoke(Native Method)
01-21 20:00:33.975 19469 19469 E AndroidRuntime: at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:547)
01-21 20:00:33.975 19469 19469 E AndroidRuntime: at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:873)

Steps to reproduce the behavior:

  1. Make function which calls iap.restore(), then iap.finish(), then iap.buy(), then iap.acknowledge() on purchased
  2. Call the function
  3. Restart app
  4. Call the function
  5. And so on
  6. See behaviour

Defold version:

  • Version 1.2.178

IAP Extension version

  • Version 3.0.0

Platforms:

  • Android

And please, fix this:

static int IAP_Restore(lua_State* L)
{
    // TODO: Missing callback here for completion/error
    // See iap_ios.mm

I will take a look at your report on Monday (tomorrow). Please note that there is no concept of restoring purchases on Android like there on iOS though.

Yes, I understand, I’m using it for restoring non-completed items.

I don’t know your exact use case, but maybe my experience with the IAP extension on Android will help?

My game has non-consumable IAPs (remove ads, and a different weapon).

The code to purchase goes like this:

  1. My code calls iap.buy(item_id)
  2. The IAP listener function is then called with transation.state == iap.TRANS_STATE_PURCHASED.
  3. I add to my save file that they purchased the item.
  4. I then call iap.acknowledge, since this item is not consumable.

The interesting thing is what happens the next time the user opens my game:

  1. The IAP listener is called automatically with the same transation.state == iap.TRANS_STATE_PURCHASED as when they first purchased! No need to call iap.restore to make this happen. It’s just part of the IAP initialization, and each of your non-consumable items will do this.
  2. You can then ensure your save file is up to date with their purchases.
2 Likes

GitHub issue: https://github.com/defold/extension-iap/issues/31

@nomind I was not able to reproduce the crash myself.

Could you please share exactly what this function or series of function calls look like?

I also took a look at the code based on the stack trace you shared and added a check that the returned skuDetails list isn’t null and isn’t empty:

Perhaps you can give the PR a try and let me know if it solved the problem for you. Add this as your project dependency:

https://github.com/defold/extension-iap/archive/Issue-32-android-crash-on-restore-buy-finish.zip

1 Like

Hi!
Looks like the PR fixed the issue.

BTW, could you please implement this:

static int IAP_Restore(lua_State* L)
{
    // TODO: Missing callback here for completion/error
    // See iap_ios.mm

It’s very useful in my case, thank you!

That’s great! I’ve merged the PR and made a new release.

Added this as https://github.com/defold/extension-iap/issues/33

I might be able to take a look at that tomorrow.

I now realise that the same comment exists in the restore code for both iOS and Android. What are you specifically looking for that the API isn’t providing today? I believe you will be getting a call to the IAP listener (set through iap.set_listener()) for any non-consumed product (Android) or purchased product (iOS).

In my case I’m looking for ability to understand, if non-completed items exists or not.
Right now I’m calling iap.restore() several times during to application lifecycle and each time I have to wait few seconds before iap.buy() to understand that there are no non-completed items, because in this case listener is not called.
It will be great to have ability to handle this situation, for example, call listener with empty transaction or send any signal.