React-native-iap: purchaseUpdatedListener is repeatedly called

Created on 3 Feb 2020  ·  6Comments  ·  Source: dooboolab/react-native-iap

Version of react-native-iap

4.3.0

Version of react-native

0.60.5

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

Both

Expected behavior

Following the example I want to call a custom method after my subscription has been purchased. It is my understanding that after acknowledging the purchase the listener should be removed? I have tried explicitly calling remove but had no success.

Example

 const purchaseListener = purchaseUpdatedListener(purchase => {
      const receipt = purchase.transactionReceipt;
      if (receipt) {
        callCustomEndpoint().then(result => {
          if(result){
            // Subscription is valid
            finishTransaction(purchase);
          }
        }) 
      };
      finishTransaction(purchase);
    });

Actual behavior

The callCustomEndpoint() method is being called multiple times. How can I stop this from happening? As I only need to hit this endpoint once.

I'm using a functional component instead of class component if it's important.

Tested environment (Emulator? Real Device?)

Real Device

Steps to reproduce the behavior

🙏 help wanted

Most helpful comment

I am going to put down my solution for hooks for those who will need it later on.

outside your state component (though probably works also inside):

var purchaseUpdateSubscription;
var purchaseErrorSubscription;
const loadIAPListeners = () => {
  initConnection(); // important, or else it won't trigger before a random state change
  purchaseUpdateSubscription = purchaseUpdatedListener(
    async (
      purchase: InAppPurchase | SubscriptionPurchase | ProductPurchase,
    ) => {
      console.log('purchaseUpdatedListener', purchase);
      let receipt = purchase.transactionReceipt;
      if (receipt) {
        apis.checkreceipt({data: purchase, platform: Platform.OS}); // I personally don't care about callback
        if (Platform.OS === 'ios') {
          await RNIap.finishTransactionIOS(purchase.transactionId);
        } else if (Platform.OS === 'android') {
          await RNIap.acknowledgePurchaseAndroid(purchase.purchaseToken);
        }
        await RNIap.finishTransaction(purchase, true);
        await RNIap.finishTransaction(purchase, false);
      } else {
        // Retry / conclude the purchase is fraudulent, etc...
      }
    },
  );
  purchaseErrorSubscription = purchaseErrorListener((error: PurchaseError) => {
    console.log('purchaseErrorListener', error);
  });
};

inside your state component:

useEffect(() => {
    loadIAPListeners();
    return () => {
      purchaseUpdateSubscription.remove();
      purchaseErrorSubscription.remove();
    };
  }, []);

All 6 comments

I am suspecting you've registered purchaseListener multiple times when component rerenders.

Thanks, I have since rewritten this using hooks so that it isn't called on each render. My mistake was trying to take the provided example in the docs of a class component and port it to a functional component. Great work on the library!

@cgathergood I'm having this issue too. Do you mind posting your event handler code using react hooks?

@cgathergood i would like to see code sample as well. i am little bit struggling with hooks.

@wootwoot1234 @osmantuna Can you please provide the event handler codes if you managed to fix it?

I am going to put down my solution for hooks for those who will need it later on.

outside your state component (though probably works also inside):

var purchaseUpdateSubscription;
var purchaseErrorSubscription;
const loadIAPListeners = () => {
  initConnection(); // important, or else it won't trigger before a random state change
  purchaseUpdateSubscription = purchaseUpdatedListener(
    async (
      purchase: InAppPurchase | SubscriptionPurchase | ProductPurchase,
    ) => {
      console.log('purchaseUpdatedListener', purchase);
      let receipt = purchase.transactionReceipt;
      if (receipt) {
        apis.checkreceipt({data: purchase, platform: Platform.OS}); // I personally don't care about callback
        if (Platform.OS === 'ios') {
          await RNIap.finishTransactionIOS(purchase.transactionId);
        } else if (Platform.OS === 'android') {
          await RNIap.acknowledgePurchaseAndroid(purchase.purchaseToken);
        }
        await RNIap.finishTransaction(purchase, true);
        await RNIap.finishTransaction(purchase, false);
      } else {
        // Retry / conclude the purchase is fraudulent, etc...
      }
    },
  );
  purchaseErrorSubscription = purchaseErrorListener((error: PurchaseError) => {
    console.log('purchaseErrorListener', error);
  });
};

inside your state component:

useEffect(() => {
    loadIAPListeners();
    return () => {
      purchaseUpdateSubscription.remove();
      purchaseErrorSubscription.remove();
    };
  }, []);
Was this page helpful?
0 / 5 - 0 ratings

Related issues

sanilcgs picture sanilcgs  ·  3Comments

bakedbean picture bakedbean  ·  5Comments

makarsky picture makarsky  ·  3Comments

jvandenaardweg picture jvandenaardweg  ·  4Comments

Gribadze picture Gribadze  ·  4Comments