React-native-iap: purchaseUpdatedListener appelé plusieurs fois même après l'achat

Créé le 26 oct. 2020  ·  22Commentaires  ·  Source: dooboolab/react-native-iap

Version de react-native-iap

5.0.1

Version de react-native

0,63,3

Plateformes sur lesquelles vous avez rencontré l'erreur (IOS ou Android ou les deux?)

IOS

Comportement attendu

purchaseUpdatedListener n'est pas appelé plusieurs fois

Comportement réel

purchaseUpdatedListener est appelé au démarrage plusieurs fois, parfois même entre les deux

Environnement testé (émulateur? Real Device?)

Appareil réel (iOS 14, 13 en vol d'essai et dans l'AppStore)

Étapes pour reproduire le comportement

(Composant Navigateur)

const itemSkus = ['01', '02'];

const processNewPurchase = async (purchase) => {
  const { productId, transactionReceipt } = purchase;
  if (transactionReceipt !== undefined && transactionReceipt) {
       //backend call with fetch - validating receipt 
        if (data.ack === 'success') {
          console.log('finished');
          await finishTransaction(purchase);
      } else if (data.ack === 'failure') {
          props.setProcessing(false);
          console.log('error');
      }
    }
};


const getProductsIAP = useCallback(async () => {
  await clearProductsIOS();
  await clearTransactionIOS();

 try {
   const result = await initConnection();
   const products = await getProducts(itemSkus);
   props.setProducts(products);
   console.log('result', result);
 } catch (err) {
   console.warn(err.code, err.message);
 }

  purchaseUpdateSubscription = purchaseUpdatedListener(
    async (purchase) => {
      const receipt = purchase.transactionReceipt;
      console.log('purchaseUpdatedListener');
      if (receipt) {
        try {
          await processNewPurchase(purchase);
        } catch (ackErr) {
           console.log('ackErr', ackErr);
        }
      } else {
        console.log('purchaseUpdatedListener error: receipt');
      }
    },
  );

  purchaseErrorSubscription = purchaseErrorListener(
    (error: PurchaseError) => {
      console.log('purchaseErrorListener', error);
      console.log(JSON.stringify(error));
    },
  );

  setLoading(false);
}, []);


useEffect(() => {
  getProductsIAP();

  return () => {
    if (purchaseUpdateSubscription) {
      purchaseUpdateSubscription.remove();
      purchaseUpdateSubscription = null;
    }
    if (purchaseErrorSubscription) {
      purchaseErrorSubscription.remove();
      purchaseErrorSubscription = null;
    }
  };
}, []);

📱 iOS 🙏 help wanted

Commentaire le plus utile

Toujours le même problème. On dirait que c'est purchaseUpdatedListener est cassé. Veuillez corriger cela dès que possible. Aucune solution trouvée.

Tous les 22 commentaires

L'événement génère-t-il les mêmes résultats d'achat?

Oui, il se déclenche avec les résultats / réception réussis. S'il n'y a pas de reçu actuel, il se déclenche également, mais échoue sur le serveur de validation.

Pourriez-vous s'il vous plaît vérifier si votre composant n'est pas rendu? Il y avait beaucoup de problèmes lorsque le composant a été rendu et les écouteurs ont démarré plusieurs fois de temps en temps. Eh bien, cela devrait être publié dur mais nous devons reproduire votre cas.

Eh bien, c'est le "NavigatorContainer" signifie que je suis en train de définir différents états, de mettre à jour des choses et ainsi de suite. Mais ne rend qu'un seul puis "rerenders" pour la mise à jour des différents états, accessoires ...

Mais c'est à cela que sert - se désabonner des auditeurs / pour éviter cela.

useEffect(() => {
  getProductsIAP();

  return () => {
    if (purchaseUpdateSubscription) {
      purchaseUpdateSubscription.remove();
      purchaseUpdateSubscription = null;
    }
    if (purchaseErrorSubscription) {
      purchaseErrorSubscription.remove();
      purchaseErrorSubscription = null;
    }
  };
}, []);

Eh bien, c'est le "NavigatorContainer" signifie que je suis en train de définir différents états, de mettre à jour des choses et ainsi de suite. Mais ne rend qu'un seul et ensuite "rerenders" pour la mise à jour des différents états, accessoires ...

Mais c'est à cela que sert - se désabonner des auditeurs / pour éviter cela.

useEffect(() => {
  getProductsIAP();

  return () => {
    if (purchaseUpdateSubscription) {
      purchaseUpdateSubscription.remove();
      purchaseUpdateSubscription = null;
    }
    if (purchaseErrorSubscription) {
      purchaseErrorSubscription.remove();
      purchaseErrorSubscription = null;
    }
  };
}, []);

Oui, j'ai également mentionné cela. Je viens de partager le numéro précédent dont je me souviens. Nous avons besoin de la reproduction.

En outre, nettoyer la transaction ou essayer avec un autre compte testflight peut aider.

const getProductsIAP = useCallback(async () => {
  await clearProductsIOS();
  await clearTransactionIOS();

.....

Testé dans TestFlight, production et différents appareils.

Compte différent?

Oui, même résultat.

Étrange. Pourriez-vous également essayer l'extrait de code dans notre projet IapExample ?

J'ai le comportement suivant.

1) L'utilisateur achète IAP.
2) purchaseUpdatedListener est appelé.
3) L'utilisateur désinstalle l'application.
4) L'utilisateur revient sur l'application.
5) purchaseUpdatedListener est déclenché et se déroule en boucle.

J'utilise des classes, mais je ne vois aucun rendu.

Je suis sur "react-native-iap": "4.4.1" et "react-native": "0.63.3".

J'ai essayé de passer à la version 5.0.0, mais j'obtiens le même comportement.

J'ai le comportement suivant.

  1. L'utilisateur achète IAP.
  2. PurchaseUpdatedListener est appelé.
  3. L'utilisateur désinstalle l'application.
  4. L'utilisateur retourne à l'application.
  5. purchaseUpdatedListener se déclenche et forme une boucle.

J'utilise des classes, mais je ne vois aucune répétition.

Je suis dans "react-native-iap": "4.4.1" et "react-native": "0.63.3".

J'ai essayé de mettre à jour vers 5.0.0, mais j'obtiens le même comportement.

Bonjour une question. avez-vous trouvé la solution à cela?

Toujours le même problème. On dirait que c'est purchaseUpdatedListener est cassé. Veuillez corriger cela dès que possible. Aucune solution trouvée.

Avoir le même problème avec cela. Comment est-il possible que ce ne soit pas corrigé pendant des mois? N'est-ce pas un problème critique ou quelqu'un a-t-il trouvé une solution de contournement pour cela? Aussi, si cela aide, je pense que je n'ai rencontré ce problème que lorsque j'ai mis à niveau mon appareil d'iOS 13 vers iOS 14. Autant que je me souvienne, cela fonctionnait bien sur iOS 13 mais je pouvais me tromper.

Même problème ici.
J'ai remarqué que cela se produit chaque fois que j'enregistre le code et que l'application est rechargée à chaud.
Dans ce scénario, le composant racine est démonté, puis remonté.

C'est mon useIap() hook

const useIap=()=>{

const purchaseUpdateSubscription = useRef<EmitterSubscription[]>([]);
    const purchaseErrorSubscription = useRef<EmitterSubscription[]>([]);

const init = useCallback(async () => {
        console.taggedLog(logTag, 'call iap init');

        await RNIap.initConnection();

        await RNIap.flushFailedPurchasesCachedAsPendingAndroid();

        purchaseUpdateSubscription.current.push(RNIap.purchaseUpdatedListener(async purchase => {
            console.taggedLog(logTag, 'purchaseUpdatedListener', purchase);

            try {
                await finalizePurchase(purchase);
            } catch (err) {
                handleFailedPurchase();
            }
        }));

        // use array to be sure to not accidentally overwrite subscriptions and lose access to them.
        // being an array I can always iterate and call `remove` on them
        purchaseErrorSubscription.current.push(RNIap.purchaseErrorListener(error => {
            handleFailedPurchase(error);
        }));

        updateProducts();
        return clear;
    }, []);

  const clear = useCallback(() => {
        console.taggedLog(logTag, 'clearing all');
        clearPurchaseEventListeners();
    }, [clearPurchaseEventListeners]);


 const clearPurchaseEventListeners = useCallback(() => {
        console.taggedLog(logTag, 'clearing purchase event listener', purchaseUpdateSubscription.current.length, purchaseErrorSubscription.current.length);
        purchaseUpdateSubscription.current.forEach(sub => sub.remove());
        purchaseErrorSubscription.current.forEach(sub => sub.remove());
        purchaseUpdateSubscription.current = [];
        purchaseErrorSubscription.current = [];
    }, []);

return {init,clear}
}

Et puis j'utilise ce code dans le composant racine

const iap=useIap();
 useEffect(() => {
        console.log("root component mounted");
        iap.init();

        return () => {
            console.log("root component unmounted");
            iap.clear();
        };
    }, []);

Le composant racine peut arriver à être démonté et remonté, la méthode clear est cependant appelée, et je vois également le journal 'clearing purchase event listener', 1,1, ce qui signifie qu'il va les effacer.

Même dans cette situation, l'achat purchaseUpdatedListener est encore appelé plusieurs fois, es

Toute solution?

J'ai le même problème.
Il a même appelé dans différents écrans.
Des mises à jour à ce sujet?

Même problème pour moi. Il appelle les anciens reçus lorsque l'iap s'initialise la prochaine fois.

J'ai eu le même problème et je me suis rendu compte que la même chose se produisait car je n'assemblais et ne démontais pas correctement le composant, rendant ainsi plusieurs écouteurs ouverts en même temps.

componentWillUnmount() { if (this.purchaseUpdateSubscription) { this.purchaseUpdateSubscription.remove(); this.purchaseUpdateSubscription = null; } }
Chaque fois qu'un achat est effectué et que le composant est démonté, il est nécessaire de supprimer l'auditeur / abonnement et d'en ajouter un nouveau lors de l'assemblage du composant.
Cette erreur ne se produit pas sur Android, pourquoi? Je ne sais pas, mais comme cela fonctionne sur Android, cela vous fait penser que l'erreur est dans iOS et non dans le code.

Une solution de contournement possible, en testant, j'ai remarqué qu'il montre parfois les reçus passés (envoyés au serveur), donc ce que j'ai fait est de stocker immédiatement le transactionId de chaque notification reçue, donc vous vérifiez toujours la base de données d'abord pour si ce transactionId a été traité et si alors ignorez (c'est un doublon) sinon vous pouvez continuer. Si la notification arrive en premier à l'application (plutôt qu'au serveur), vous pouvez mettre une logique pour contrôler cela, c'est-à-dire que vous ne devriez en recevoir qu'un après avoir cliqué sur un certain bouton, etc.
Après des tests approfondis, la duplication a cessé après un certain temps, quelque chose se passe là-bas.

Oui, il existe des solutions de contournement peu précises, mais elles sont toutes jolies ...
Veuillez simplement résoudre ce problème. Il s'agit certainement d'un bug critique et de rupture de projet depuis octobre 2020 ...

Il n'y a qu'une seule solution, c'est d'utiliser : Revenuecat;)

une solution quelconque?

Revenuecat

Cette page vous a été utile?
0 / 5 - 0 notes