React-native-iap: Falha de compra no aplicativo iOS quando o método de pagamento adicionado ao vivo quando não há método de pagamento já presente

Criado em 31 out. 2018  ·  27Comentários  ·  Fonte: dooboolab/react-native-iap

Versão do react-native-iap

react-native-iap": "^2.3.17

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

iOS

Comportamento esperado

O pagamento só deve ser efetuado quando finishTransaction está sendo chamado

Comportamento real

O valor está sendo detectado depois que um método de pagamento, como cartão de crédito, é adicionado.
Mas o método RNIap.buyProductWithoutFinishTransaction(sku) alcançado.

Ambiente testado (emulador? Dispositivo real?)

Dispositivo real - iPhone 6s

Passos para reproduzir o comportamento

  • Verifique se a conta da Apple no dispositivo a forma de pagamento está definida como nenhuma ou alguns detalhes de pagamento inválidos
  • Faça a compra no aplicativo por meio do aplicativo
  • O usuário é levado à página de configurações da conta para adicionar um método de pagamento válido
  • A forma de pagamento é cobrada, mas o recibo da transação não é recebido no código abaixo mencionado
ComponentDidMount(){
     await RNIap.initConnection();
     await RNIap.consumeAllItems();
     const prod = await RNIap.getProducts(product);

} 

  async componentWillUnmount() {
       RNIap.endConnection()
}

buyProduct(sku){
await RNIap.clearTransaction();

RNIap.buyProductWithoutFinishTransaction(sku)
.then(purchase => {
  // not reached
 if(calltoserverisSuccess){
    RNIap.finishTransaction();
 }
})
.catch(error => {
 // code enters catch case if ever
}}
📱 iOS 🙏 help wanted

Comentários muito úteis

Acho que precisamos entrar em contato com apple para isso e parece realmente terrível que haja um caso de teste diferente que não é reproduzível no ambiente sandbox . Para quem quiser entender o problema, gravei a tela e vocês podem ver o clipe aqui . @anandwahed Você também poderia entrar em contato com a apple para isso? Porque eu sei que eles não são tão apoiadores, então é melhor que mais pessoas tenham contato com eles. Vamos nos reunir para resolver esse problema.

Todos 27 comentários

Desculpe dizer isso, mas seu código parece ter muitos erros de sintaxe. Consulte nosso projeto de exemplo em nosso repositório e compare com seu código primeiro.

@dooboolab
Obrigado pela resposta, o código postado aqui é apenas um exemplo, não é exatamente o que está sendo usado no aplicativo real.
vou tentar explicar qual é o problema

  1. componentDidMount

    • conexão iniciada chamando RNIap.initConnection();

    • então os produtos são buscados e armazenados no estado chamando RNIap.getProducts(product)

  1. quando o usuário clica no botão de compra

    • RNIap.clearTransaction(); é chamado apenas para garantir que não haja mais transações pendentes.
    • RNIap.buyProductWithoutFinishTransaction(sku) é chamado em caso de sucesso, uma chamada para o servidor de aplicativos é acionada se o servidor fornecer caso de sucesso verdadeiro, então RNIap.finishTransaction(); é chamado para completar o pagamento.
  2. componentWillUnmount ()

    • RNIap.endConnection() para encerrar a conexão.

Este processo funciona bem se o usuário já tiver adicionado a forma de pagamento. Quando o usuário não tem uma forma de pagamento adicionada, é necessário adicionar a página da forma de pagamento e o valor é cobrado antes de atingir RNIap.finishTransaction();

Obrigado pelo detalhe. Seu problema parece claro agora. cc @JJMoon

@JJMoon

@ zohaibahmed-22 Este é um caso de teste sandbox?

@JJMoon Não, é um caso de ambiente real.

Entendo que este erro ocorre quando o usuário está desconectado ou sem informações de cartão de crédito.
Se os métodos funcionarem bem, a causa desse erro estará em outro lugar.
Precisamos preparar esta ação, que deixa o aplicativo, viewDidDisappear etc.
No modo sandbox, esse sintoma ocorre de maneira um pouco diferente.
Quando eu faço uma nova conta sandbox e a primeira compra não funciona.
Na segunda vez, o dispositivo está conectado e funciona bem.
Não tenho a menor ideia desse erro.

@dooboolab e @JJMoon também acho que encontrei o problema ao examinar nosso código iOS. No método updatedTransactions , quando recebemos uma mensagem de falha no SKPaymentTransactionStateDeferred ou SKPaymentTransactionStateFailed , não devemos finishTransaction .

Visto que em muitos casos o resultado SKPaymentTransactionStateFailed é seguido por um SKPaymentTransactionStatePurchased como mencionado no tópico 6431 acima. Não tenho certeza sobre o código Objective-C, portanto, verifique e confirme se liberamos a transação em caso de falha.

@anandwahed eu concordo com você. É minha culpa. Desculpe por isso.
Vou dar uma olhada no tópico da Apple e cuidar do problema.

@anandwahed Acho que é o jeito certo de finish transaction quando falha. Por favor, procure este tópico. https://stackoverflow.com/questions/11008636/inapp-purchase-skpaymentqueue-finish-transaction-doesnt-work
Quando falhar, não haverá recibo, portanto você não aplica o produto adquirido. E a transação final nem sempre significa 'compra'.
Enquanto isso, investigarei o tópico 6431.

Acabei de ler o tópico 6431 (https://forums.developer.apple.com/thread/6431#14562)
O resultado final. Problema não resolvido desde 2015. Uau ..
Existem duas maneiras diferentes de lidar com esse efeito de 'fluxo de kit de armazenamento'.

  1. Não mostra nenhum alerta de usuário.
  2. Mostre o resultado.

E aprendi duas coisas.
A. Temos que finishTransaction quando ele falhou. (Acho que com ou sem opção, sempre)
B. O fluxo do Storekit faz com que haja falha e sucesso com o recebimento. (é mau)

Sugiro a todos que leiam este tópico e retornem a este assunto.

Eu acho que o caminho que você escolher (1 ou 2), é por sua conta.
Podemos precisar de uma chamada de volta para resposta de falha.

@JJMoon significa que não devemos usar buyProductWithoutFinishTransaction?

@ maxs15 Eu não quis dizer isso. Desculpe por confundir.
O fluxo do StoreKit pode acontecer em qualquer compra. É um problema do iOS, não este módulo.
Por enquanto, eu não tenho nenhuma pista também. Isso acontece em qualquer aplicativo iOS nativo. Certo?
Vamos cavar mais em nosso tempo livre.

Hoje, tentei depurar esse problema porque o gerei. O pagamento finishTransanction e é concluído, mas não recebe callback quando payMethod foi alterado. Tentei depurar escrevendo console.log mas não consegui testar o faturamento real no ambiente de dev . Alguém poderia me sugerir como depurar esse processo para que eu possa debug isso por real purchase ? Devo ter que usar isso no modo sandbox ? Está funcionando perfeitamente no sandbox, então não tenho ideia de como depurar isso. Isso é muito relutante.

Vamos reunir algumas ideias porque acho que é muito importante consertar isso.

Sempre recebo esse erro quando tento fazer a compra com um usuário que não é do sandbox.

@hyochan Se você conectar o servidor real (o seu), não importa se você está no modo de depuração ou no modo de liberação. Eu acho que você tem 2 opções.

  1. Você executa no dispositivo real em modo de depuração no Xcode. Use o log do console JS.
  2. Você executa no dispositivo real em modo de liberação no Xcode. Use NSLog no código objetivo-c.
    Ambos os métodos devem funcionar.

@JJMoon Sim, já percebi isso, mas ainda não consegui fazer testes de compra ao vivo no iOS. Este stackoverflow é verdadeiro? Então, como posso resolver esse problema? Temos que testar a compra ao vivo.

Qualquer pessoa que esteja enfrentando esse problema, tenho certeza que todos enfrentam, dê-nos uma ideia de como encontrar esse problema. in-app purchase fail when payment method added live conforme descrito no título do problema. Como posso depurar isso? @anandwahed Você já

@hyochan Não, não contatamos o suporte da Apple.

Acho que precisamos entrar em contato com apple para isso e parece realmente terrível que haja um caso de teste diferente que não é reproduzível no ambiente sandbox . Para quem quiser entender o problema, gravei a tela e vocês podem ver o clipe aqui . @anandwahed Você também poderia entrar em contato com a apple para isso? Porque eu sei que eles não são tão apoiadores, então é melhor que mais pessoas tenham contato com eles. Vamos nos reunir para resolver esse problema.

Hoje recebemos resposta da Apple. Pode haver retorno de chamada retornado novamente após failure . Estamos trabalhando em uma solução alternativa em # 348, mas temo que isso possa ser muito desagradável.

Eu libertei para 2.4.0-beta1 , tentando encontrar uma solução alternativa para esse problema. O PR # 348 foi adicionado a esta versão e você também pode ver o readme deste recurso . Observe que isso está em teste.

Eu testei isso em uma compra ao vivo e parece estar funcionando. No entanto, lembre-se de que você só deve adicionar ouvinte quando houver uma falha ou ele poderá ser duplicado com um resultado bem-sucedido.

Pessoal, obrigado por investirem tempo nesse problema! 🙏

Acho que a documentação atual precisa ser mais clara sobre o uso de addAdditionalSuccessPurchaseListenerIOS . Isso é algo que tentei resolver em # 414.

Mas também acho que isso deixa espaço para melhorias na API. Vocês estariam abertos para discutir uma mudança na API que pode acomodar melhor o chamado 'fluxo de kit de loja'? Algo que talvez use RxJS, por exemplo:

const observable = RNIap.buyProduct('com.example.coins100')
                        .subscribe(
                            purchase => console.log(purchase), // successful payment
                            err => console.log(err) // err.code and err.message are available
                        )

Ou algo diferente que esconda melhor a assinatura adicional para iOS?

@Edgpaez Isso é ótimo, mas mudará o comportamento da compra sendo feita em android . Espero poder investigar version 3 deste módulo em 2019 .

oi @hyochan , AFAICT, não precisamos alterar o comportamento interno do pacote, apenas a interface voltada para o público. Podemos manter o método buyItemByType resolvendo promessas e simplesmente adicionar um pouco de Rx em index.js .

por exemplo, teríamos isso em index.js:

export const buyProduct = (sku) => Platform.select({
  android: () => Observable.of(RNIapModule.buyItemByType(ANDROID_ITEM_TYPE_IAP, sku, null, 0)), // returns an observable that emits when the RNIapModule.buyItemByType promise resolves
  ios: ... // ios would do the same but taking into account the usage of addAdditionalSuccessPurchaseListenerIOS
})();

Meu objetivo não é essa implementação específica, mas ocultar detalhes específicos para o 'fluxo de kit de loja'.
Você acha que isso é uma boa ideia?
Você estaria interessado em um PR?

@Edgpaez Ok. Eu entendo o detalhe agora. No entanto, acho que adicionar RxJS é um pouco demais para implementar feature pois acho que isso pode ser resolvido sem ele.

Além disso, acho que a seguir seria algo diferente.

RNIap.buyProduct('com.example.coins100')
                        .subscribe(
                            purchase => console.log(purchase), // successful payment
                            err => console.log(err) // err.code and err.message are available
                        )

Se você chamar buyProduct para dois itens como abaixo,

RNIap.buyProduct('com.example.coins100');
RNIap.buyProduct('com.example.coins200');

Não podemos garantir qual terminaria primeiro, então acho que devemos lidar com isso no nativo para sendEvent para JS .

Eu sinto que a implementação deve ser semelhante a

RNIap.buyProduct('com.example.coins100');
RNIap.buyProduct('com.example.coins200');

// receiving events
const subs =  RNIap.purchaseUpdateListener(purchase => {
  ...
});

Diga-me se há algo que perdi.

Vamos lidar com mais discussões em # 423

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