Skip to content
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

Errors with calling 'await RNIap.prepare();' for in app purchase in Android #168

Closed
SlZeroth opened this issue May 23, 2018 · 26 comments
Closed
Labels
🤖 android Related to android

Comments

@SlZeroth
Copy link

Version of react-native-iap

the lastest version for now

Platforms you faced the error (IOS or Android or both?)

Android

Expected behavior

calling 'await RNIap.prepare();' succesfully

Actual behavior

I got an error message saying 'Illegal callback invocation from native module. This callback type only permits a single invocation from native code'

I have an error at the 347 line of RnlapModule.java when I use RNIapModule.java
'resolvePromisesForKey(PROMISE_PREPARE, null);' This is 347 line. I mean that when I try to implement it, I have an error.

Tested environment (Emulator? Real Device?)

Real Device

Steps to reproduce the behavior

@hyochan hyochan added the 🤖 android Related to android label May 23, 2018
@hyochan
Copy link
Member

hyochan commented May 23, 2018

@SlZeroth What is the version of your react-native? I'll watch this one tommorow. It's 12 PM in here, need to go to sleep :(

@SlZeroth
Copy link
Author

@dooboolab my react native version is 0.54 . thank you :)

@hyochan
Copy link
Member

hyochan commented May 24, 2018

@SlZeroth I've just tested in my environment and couldn't reproduce this one. Are you calling any native modules when trying to use react-native-iap method? Also, this might be helpful to you.

@pehagg
Copy link

pehagg commented Jun 1, 2018

I'm having the same issue, appears to happen when we reset the app state (user signs in or out of Facebook and we need to re-sync all data). We essentially end up in a state where we call prepare for a second time.

The error in the console is: The callback prepare() exists in module RNIapModule, but only one callback may be registered to a function in a native module..

Running RN 0.55.3 and RN IAP 1.0.5.

EDIT: This is on the simulator, where the first call to prepare fails due to Store not being properly set up.

@pehagg
Copy link

pehagg commented Jun 1, 2018

On a real device this results in a slightly different error, Error: The service is disconnected (check your internet connection.). The device is connected to the internet.

@pehagg
Copy link

pehagg commented Jun 1, 2018

Workaround, track if prepare has been called at all and call endConnection before any subsequent calls to prepare.

@SlZeroth
Copy link
Author

SlZeroth commented Jun 1, 2018

@pehagg hmm .... I got same error The callback prepare() exists in module RNIapModule, but only one callback may be registered to a function in a native module.. on real device , where console did you saw "Error: The service is disconnected (check your internet connection." error ??

@SlZeroth
Copy link
Author

SlZeroth commented Jun 1, 2018

@pehagg I tested react native iap module after upload alpha APP.

@hyochan
Copy link
Member

hyochan commented Jun 7, 2018

@SlZeroth Are you still suffering? Have you read this? Also check if you are using other native modules when you are using react-native-iap.

@wasedaigo
Copy link
Contributor

wasedaigo commented Jun 8, 2018

I am getting the same error:
Error Logs

java.lang.RuntimeException: Illegal callback invocation from native module. This callback type only permits a single invocation from native code.
        at com.facebook.react.bridge.CallbackImpl.invoke(CallbackImpl.java:28)
        at com.facebook.react.bridge.PromiseImpl.resolve(PromiseImpl.java:30)
        at com.dooboolab.RNIap.RNIapModule.resolvePromisesForKey(RNIapModule.java:476)
        at com.dooboolab.RNIap.RNIapModule.access$300(RNIapModule.java:44)
        at com.dooboolab.RNIap.RNIapModule$6.onBillingSetupFinished(RNIapModule.java:357)
        at com.android.billingclient.api.BillingClientImpl$BillingServiceConnection.onServiceConnected(BillingClientImpl.java:887)
        at android.app.LoadedApk$ServiceDispatcher.doConnected(LoadedApk.java:1208)
        at android.app.LoadedApk$ServiceDispatcher$RunConnection.run(LoadedApk.java:1225)
        at android.os.Handler.handleCallback(Handler.java:739)
        at android.os.Handler.dispatchMessage(Handler.java:95)
        at android.os.Looper.loop(Looper.java:135)
        at android.app.ActivityThread.main(ActivityThread.java:5273)
        at java.lang.reflect.Method.invoke(Native Method)
        at java.lang.reflect.Method.invoke(Method.java:372)
        at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:903)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:698)

dependencies

"dependencies": {
    "bluebird": "^3.4.7",
    "lodash": "^4.17.4",
    "npm": "^6.1.0",
    "react": "^16.3.1",
    "react-native": "^0.55.3",
    "react-native-device-info": "^0.21.5",
    "react-native-exit-app": "^1.0.0",
    "react-native-firebase": "^4.2.0",
    "react-native-global-props": "^1.1.3",
    "react-native-iap": "^1.0.8",
    "react-native-sound": "^0.10.9",
    "react-native-store-view": "^3.1.1"
  }

According to this:
https://www.bountysource.com/issues/42940399-illegal-callback-invocation-from-native-module

It's a common thing with RN on Android, when a callback is invoked more than once. The callback needs to be replaced with an event that can be fired multiple times.

@wasedaigo
Copy link
Contributor

wasedaigo commented Jun 8, 2018

ok, basically there is a pass that a promise can be called multiple times. In case it hits this line, the same promise will be invoked twice. This needs to be fixed it seems?
https://github.com/dooboolab/react-native-iap/blob/master/android/src/main/java/com/dooboolab/RNIap/RNIapModule.java#L119

It seems I need to call endConnection()? I did not know this is a requirement. Why don't we just call it internally every time before we start billing?

@hyochan
Copy link
Member

hyochan commented Jun 8, 2018

@wasedaigo Sounds good if not calling endConnection was an issue. I felt like starting and finishing services in every transaction would result in some side effects. Therefore, I left this one as other developers' taste. I've suggested to some of them to start billing service when componentDidMount and end it when componentWillUnmount. I'll be more specific in the readme sooner.

@wasedaigo
Copy link
Contributor

As long as it is clearly state in the docs calling endConnection is fine. I saw other libraries did open/close for this as well.

As long as we can avoid this magical error from appearing we should be able to close this issue

@nkov
Copy link

nkov commented Oct 4, 2018

I am still having this issue and not much luck fixing it by calling endConnection. I have tried starting the service and calling endConnection on each billing action:

  1. Verify purchases
import * as RNIap from 'react-native-iap'

await RNIap.initConnection()
await RNIap.getProducts([SKU])
const products = await RNIap.getAvailablePurchases()
// some logic with the purchases
await RNIap.endConnection()
  1. Make purchase
await RNIap.initConnection()
await RNIap.getProducts([SKU])
await RNIap.buyProduct(SKU)
await RNIap.endConnection()

And I have also tried calling endConnection on componentWillUnmount as described in the README.

In both cases, everything works, but later on there is always a crash (ie. when the app is backgrounded), and the stack is the exact same as posted above. Using version 2.2.2 on Android.

@dooboolab can we re-open this or point me in the right direction please?

@hyochan
Copy link
Member

hyochan commented Oct 4, 2018

@nkov If you have some scenes which screen is holding iap service for long time, I prefer you to try more frequent lifecycle such as onResume and onPause in android. You may refer to use these lifecycle in AppState. Therefore, you may use initConnection when it is in foreground and endConnection when it isn't.
Could you try this and comeback?

@punksta
Copy link

punksta commented Oct 17, 2018

I am having the same issue too. I coded in similar way as @nkov in Make purchase. It's still not clear what is proper way of calling initConnection and endConnection

@hyochan
Copy link
Member

hyochan commented Oct 18, 2018

@punksta initConnection checks the availability for purchase in ios which is somewhat trivial. However, it is critical in Android because the BillingService in Android is not always running. To use the billing feature in Android you need to start BillingService which is what initConnection does.

@punksta
Copy link

punksta commented Oct 18, 2018

I understand it, it's clear. however calling initConnection second time after endConnection sometimes crashes the app. I meant that the flow is not very clear.
When you are done with the billing is not very clear.
Should I call initConnection before each purchase and endConnection just after each? Or better to use AppState to close connection when app in background? Users can use billing few times per session

@pehagg
Copy link

pehagg commented Oct 31, 2018

We're still experiencing issues with 2.3.12. The only way for me to get rid of the random errors we got because the connection was not open anymore or wasn't prepared, was to init the connection every time a purchase is being made and close it once the purchase is completed. However, this has now lead us to see these crashes in our logs:

Illegal callback invocation from native module. This callback type only permits a single invocation from native code.

Before making this change we initialised the connection every time the user arrived in the IAP screen, but as said, this wasn't enough, we got all kinds of random exceptions from RNIap. I'm running out of options and will need to start to look for another library if this cannot be sorted out.

@hyochan
Copy link
Member

hyochan commented Oct 31, 2018

@pehagg You're missing endConnection. Please read the apis once more.

@pehagg
Copy link

pehagg commented Nov 2, 2018

I call endConnection in the finally block of a try-catch-finally, where the init, purchase, and consumption is all done in the try block.

@hyochan
Copy link
Member

hyochan commented Nov 3, 2018

show us the code so we can figure out

@pehagg
Copy link

pehagg commented Nov 6, 2018

Here's the code. The only plausible explanation I can come up with is that there's an exception that is swallowed up by RNIap, which would explain that the finally block doesn't get called at all, or then the actual endConnection call fails.

try {
    await RNIap.initConnection();
    const iap = <redacted> // find the IAP matching productId
    if (iap) {
      const purchase = await RNIap.buyProduct(productId);
      await RNIap.consumePurchase(purchase.purchaseToken);
      <redacted>
    }
  } catch (error) {
     <redacted>
  } finally {
    try {
      await RNIap.endConnection();
    } catch (error) {
      <redacted>
    }
  }

@pehagg
Copy link

pehagg commented Nov 7, 2018

Just noticed that some of the crashes are coming from another part of the code, where we initialise the IAPs for the first time. The structure is the same, without the purchase and consumption. Here's a stack trace of such a case:

com.facebook.react.bridge.CallbackImpl.invoke CallbackImpl.java:28
com.facebook.react.bridge.PromiseImpl.reject PromiseImpl.java:68
com.facebook.react.bridge.PromiseImpl.reject PromiseImpl.java:36
com.dooboolab.RNIap.RNIapModule.rejectPromiseWithBillingError RNIapModule.java:448
com.dooboolab.RNIap.RNIapModule.access$200 RNIapModule.java:44
com.dooboolab.RNIap.RNIapModule$3.onBillingSetupFinished RNIapModule.java:125
com.android.billingclient.api.BillingClientImpl$BillingServiceConnection.onServiceConnected BillingClientImpl.java:908
android.app.LoadedApk$ServiceDispatcher.doConnected LoadedApk.java:1818
android.app.LoadedApk$ServiceDispatcher$RunConnection.run LoadedApk.java:1847
android.os.Handler.handleCallback Handler.java:808
android.os.Handler.dispatchMessage Handler.java:101
android.os.Looper.loop Looper.java:166
android.app.ActivityThread.main ActivityThread.java:7425
java.lang.reflect.Method.invoke Method.java
com.android.internal.os.Zygote$MethodAndArgsCaller.run Zygote.java:245
com.android.internal.os.ZygoteInit.main ZygoteInit.java:921

In this case we got E_SERVICE_ERROR from initConnection. What happens if the initConnection call fails and you call endConnection, as this would be the case with this crash?

@phillmill
Copy link

phillmill commented Nov 8, 2018

I'm having this issue as well.

I'm not sure if this is helpful or not, but after looking at quite a few logs from crashlytics, it seems to occur after a user abandons the app for a while - not exiting, but maybe leaving it running in the background.

I'm using the componentWillUnmount method for calling RNIap.endConnection(). I wonder if I should use a different approach because of what I explained ?

edit: I'm realizing now that they could just be exiting the app at a later time

@dominichadfield
Copy link

dominichadfield commented Nov 20, 2018

Hi, this is a very interesting issue and one that I am having also that I have referenced here: #325

I was just wondering if this discussion was still open or if anyone has a solution.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
🤖 android Related to android
Projects
None yet
Development

No branches or pull requests

8 participants