dooboolab-community / react-native-iap

In App Purchase module for React Native!
https://react-native-iap.dooboolab.com
MIT License
2.74k stars 631 forks source link

[Android] purchaseUpdatedListener doesn't receive notification of successful purchase on device startup #2487

Open kesha-antonov opened 12 months ago

kesha-antonov commented 12 months ago

Hi!

Description

Use case:

Expected Behavior

Screenshots

photo_2023-07-12 17 35 46 photo_2023-07-12 17 35 49 photo_2023-07-12 17 35 51

Environment:

usmanabid94 commented 12 months ago

check this link: https://usmanabid477.medium.com/in-app-purchase-integration-in-react-native-app-96c0acd13649

maulik54e commented 12 months ago

Facing same issue

usmanabid94 commented 11 months ago

Are you using the latest in app billing SDK?

kesha-antonov commented 11 months ago

Are you using the latest in app billing SDK?

I use latest rn-iap I guess there used latest billing sdk

usmanabid94 commented 11 months ago

@kesha-antonov check sometimes we need to add the billing library manually also or check if the library is updated int he build.gradle file in the android studio

kesha-antonov commented 11 months ago

@kesha-antonov check sometimes we need to add the billing library manually also or check if the library is updated int he build.gradle file in the android studio

Hmm This lib uses 5.1.0 https://github.com/dooboolab-community/react-native-iap/blob/main/android/gradle.properties#L9 https://github.com/dooboolab-community/react-native-iap/blob/main/android/build.gradle#L166C23-L166C60

Latest is 6.0.1 https://developer.android.com/google/play/billing/release-notes

maulik54e commented 11 months ago

same i'm using 5.1.0

radko93 commented 11 months ago

This used to work in the past, seems like it's broken now.

radko93 commented 11 months ago

I made a workaround for this. After initConnection I call getAvailablePurchases on Android only and then filter the ones you need:

export const filterPendingPurchasesAndroid = (purchases: ProductPurchase[]) => {
  return purchases.filter(
    (purchase) =>
      purchase.purchaseStateAndroid === PurchaseStateAndroid.PURCHASED &&
      !purchase.isAcknowledgedAndroid,
  )
}

You can then verify those same as in the listener.

maulik54e commented 11 months ago

getAvailablePurchases returns empty list in my case even though there is purchase which is not acknowledged

kesha-antonov commented 11 months ago

I made a workaround for this. After initConnection I call getAvailablePurchases on Android only and then filter the ones you need:


export const filterPendingPurchasesAndroid = (purchases: ProductPurchase[]) => {

  return purchases.filter(

    (purchase) =>

      purchase.purchaseStateAndroid === PurchaseStateAndroid.PURCHASED &&

      !purchase.isAcknowledgedAndroid,

  )

}

You can then verify those same as in the listener.

Are your products consumable or non-consumable?

radko93 commented 11 months ago

My products are non-consumable. I tested this on Android 13 phone. Somehow I got that purchase in getAvailablePurchases. Maybe it's not reliable 100%, I haven't tested it in production yet.

kesha-antonov commented 11 months ago

For non-consumable it's correct logic I guess. You'll get them either way in that method as I understand

I'll check for consumable tomorrow

kesha-antonov commented 11 months ago

@radko93 You're right, thanks!! I can get purchased product on app startup with getAvailablePurchases even when they're consumable.

kesha-antonov commented 11 months ago

On iOS it works as expected. purchaseUpdatedListener fires on app startup when there're unfinished purchases, whilst on android it does not and we should use getAvailablePurchases

usmanabid94 commented 11 months ago

@kesha-antonov can you share an example for this.

kesha-antonov commented 11 months ago

@usmanabid94

async setPurchaseListener () {
  if (this.isPurchaseListenerSet) return

  this.isPurchaseListenerSet = true

  this.purchaseUpdateSubscription = purchaseUpdatedListener(this.processPurchase)

  this.purchaseErrorSubscription = purchaseErrorListener(
    ...
  )
}

async processUnfinishedPurchases () {
  try {
    // ON IOS IT WORKS AS EXPECTED: UNFINISHED PURCHASES LOADED ON APP STARTUP IN purchaseUpdatedListener
    if (CONSTANTS.IS_IOS)
      return

    const purchases = await getAvailablePurchases()
    for (const purchase of purchases)
      await this.processPurchase(purchase)
  } catch (e) {
    logException('processUnfinishedPurchases e', e)
  } 
}

...

await this.setPurchaseListener()
await this.processUnfinishedPurchases()
zachariast commented 11 months ago

@kesha-antonov I encountered a similar problem, and during my search for a solution, I stumbled upon a noteworthy pull request in the GitHub repository. This pull request involves the removal of the startListening() method. Intrigued, I decided to give it a shot by reintroducing this method, and lo and behold, the issue was resolved 🧐. While this might not be the ultimate solution, it serves as a solid foundation for me to build upon 😊.

radko93 commented 11 months ago

@zachariast that's a good find! Seems like this is causing the regression. But if the workaround is OK then it should actually be fine

gabrielareia commented 9 months ago

Same problem is happening to me. I did the same as @zachariast and it also worked in my case, the android app starts to listen to events again when we open the app. But I wouldn't call it a workaround since I had to mess with the lib's code. This should be reviewed and brought back, or a better solution should be implemented if this part was indeed causing problems before. Also, startListening is marked as deprecated.

Screenshot 2023-09-12 at 18 24 19

kesha-antonov commented 9 months ago

Same problem is happening to me. I did the same as @zachariast and it also worked in my case, the android app starts to listen to events again when we open the app.

But I wouldn't call it a workaround since I had to mess with the lib's code.

This should be reviewed and brought back, or a better solution should be implemented if this part was indeed causing problems before.

Also, startListening is marked as deprecated.

Screenshot 2023-09-12 at 18 24 19

Thanks for the info! Yeah, it doesn't look as easy workaround since we have to patch the package. It should be fixed in the repo itself

pencilcheck commented 4 months ago

On Android, it seems like using "getAvailablePurchases" is the workaround for startListening since the purchaseUpdatedListener wouldn't work.

MarkCSmith commented 3 months ago

For what it is worth, I still see this problem even though the call to startListening() has been reinstated. Calling getAvailablePurchases() as an attempted workaround does return pending purchases but it never returns a completed (successful or failed) purchase. I am testing using Google's "Slow test card" payment methods.