React-native-iap: purchaseUpdatedListener chamado várias vezes, mesmo após a compra

Criado em 26 out. 2020  ·  22Comentários  ·  Fonte: dooboolab/react-native-iap

Versão do react-native-iap

5.0.1

Versão do react-native

0.63.3

Plataformas em que você enfrentou o erro (IOS ou Android ou ambos?)

IOS

Comportamento esperado

purchaseUpdatedListener não é chamado várias vezes

Comportamento real

purchaseUpdatedListener sendo chamado na inicialização várias vezes, às vezes até entre

Ambiente testado (emulador? Dispositivo real?)

Dispositivo real (iOS 14, 13 em Test Flight e na AppStore)

Passos para reproduzir o comportamento

(Componente Navigator)

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

Comentários muito úteis

Ainda é o mesmo problema. Parece que o purchaseUpdatedListener está quebrado. Corrija isso o mais rápido possível. Nenhuma solução encontrada.

Todos 22 comentários

O evento dispara os mesmos resultados de compra?

Sim, ele dispara com os resultados / recebimento bem-sucedidos. Se não houver um recibo atual, ele também dispara, mas falha no servidor de validação.

Você poderia verificar se o seu componente não foi renderizado novamente? Havia muitos problemas quando o componente era renderizado novamente e os ouvintes eram iniciados várias vezes ocasionalmente. Bem, isso deve ser difícil de lançar, mas precisamos reproduzir o seu caso.

Bem, é o "NavigatorContainer" significa que estou definindo diferentes estados, atualizando coisas e assim por diante. Mas apenas renderiza um e depois "renderiza novamente" para a atualização de diferentes estados, adereços ...

Mas é para isso - cancelar a assinatura dos ouvintes / evitar isso.

useEffect(() => {
  getProductsIAP();

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

Bem, é o "NavigatorContainer" significa que estou definindo diferentes estados, atualizando coisas e assim por diante. Mas apenas renderiza um e depois "renderiza" para a atualização de diferentes estados, adereços ...

Mas é para isso - cancelar a assinatura dos ouvintes / evitar isso.

useEffect(() => {
  getProductsIAP();

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

Sim, eu também mencionei isso. Acabei de compartilhar a edição anterior de que me lembro. Precisamos da reprodução.

Além disso, limpar a transação ou tentar uma conta diferente do testflight pode ajudar.

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

.....

Testado em TestFlight, produção e dispositivos diferentes.

Conta diferente?

Sim, mesmo resultado.

Estranho. Você também poderia tentar o snippet de código em nosso projeto IapExample ?

Eu tenho o seguinte comportamento.

1) O usuário compra IAP.
2) purchaseUpdatedListener é chamado.
3) O usuário desinstala o aplicativo.
4) O usuário volta ao aplicativo.
5) purchaseUpdatedListener é acionado e entra em um loop.

Estou usando classes, mas não vejo nenhuma re-renderização.

Estou em "react-native-iap": "4.4.1" e "react-native": "0.63.3".

Tentei atualizar para 5.0.0, mas estou obtendo o mesmo comportamento.

Eu tenho o seguinte comportamento.

  1. O usuário compra IAP.
  2. PurchaseUpdatedListener é chamado.
  3. O usuário desinstala o aplicativo.
  4. O usuário retorna ao aplicativo.
  5. purchaseUpdatedListener é acionado e forma um loop.

Estou usando aulas, mas não vejo nenhuma repetição.

Estou em "react-native-iap": "4.4.1" e "react-native": "0.63.3".

Tentei atualizar para 5.0.0, mas obtenho o mesmo comportamento.

Olá, uma pergunta. você encontrou a solução para isso?

Ainda é o mesmo problema. Parece que o purchaseUpdatedListener está quebrado. Corrija isso o mais rápido possível. Nenhuma solução encontrada.

Tendo o mesmo problema com isso. Como é possível que não seja corrigido por meses? Este não é um problema crítico ou alguém encontrou uma solução alternativa para isso? Além disso, se ajudar, acho que só encontrei esse problema quando atualizei meu dispositivo do iOS 13 para o iOS 14. Pelo que me lembro, estava funcionando bem no iOS 13, mas posso estar enganado.

Mesmo problema aqui.
Percebi que isso acontece sempre que salvo o código e o aplicativo é recarregado a quente.
Nesse cenário, o componente raiz é desmontado e, em seguida, remontado.

Este é o meu gancho useIap()

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}
}

E então eu uso este código no componente raiz

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

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

O componente raiz pode ser desmontado e remontado, mas o método clear é chamado e também vejo o log 'limpando ouvinte de evento de compra' 1,1, o que significa que ele vai limpá-los.

Mesmo nessa situação, a compra purchaseUpdatedListener ainda é chamada muitas vezes, es

qualquer solução?

Tem o mesmo problema.
Ele até chamou em telas diferentes.
Alguma atualização sobre isso?

O mesmo problema para mim. Ele chama as receitas anteriores quando o iap for inicializado na próxima vez.

Tive o mesmo problema e percebi que o mesmo estava acontecendo porque não estava montando e desmontando o componente corretamente, fazendo com que vários ouvintes abrissem ao mesmo tempo.

componentWillUnmount() { if (this.purchaseUpdateSubscription) { this.purchaseUpdateSubscription.remove(); this.purchaseUpdateSubscription = null; } }
Cada vez que uma compra é feita e o componente é desmontado é necessário remover o listener / assinatura e adicionar um novo ao montar o componente.
Esse erro não ocorre no android, por quê? Não sei, mas como funciona no Android faz pensar que o erro está no iOS e não no código.

Uma possível solução, testando, percebi que às vezes mostra os recibos anteriores (enviados para o servidor), então o que fiz foi armazenar imediatamente o transactionId de cada notificação recebida, então você sempre verifica o banco de dados primeiro para saber se esse transactionId foi processado e se então ignore (é uma duplicata); se não, você pode prosseguir. Se a notificação chegar primeiro ao aplicativo (em vez do servidor), você pode colocar alguma lógica para controlar isso, ou seja, você só deve receber 1 após um determinado botão ter sido clicado, etc.
Depois de testar exaustivamente, ele parou de duplicar depois de um tempo, algo está acontecendo lá.

Sim, existem algumas soluções alternativas, mas são todas bonitas ...
Apenas corrija este problema. Este é definitivamente um bug crítico e de quebra de projeto desde outubro de 2020 ....

Só existe uma solução, é usar : Revenuecat;)

Alguma solução?

Revenuecat

Esta página foi útil?
0 / 5 - 0 avaliações

Questões relacionadas

jvandenaardweg picture jvandenaardweg  ·  4Comentários

iutin picture iutin  ·  4Comentários

jvandenaardweg picture jvandenaardweg  ·  4Comentários

schumannd picture schumannd  ·  3Comentários

bakedbean picture bakedbean  ·  5Comentários