React-native-iap: finishTransactionIOS / finishTransaction não faz nada para iOS

Criado em 19 fev. 2020  ·  34Comentários  ·  Fonte: dooboolab/react-native-iap

Versão do react-native-iap

4.4.1

Versão do react-native

0,60,4

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

iOS

Comportamento esperado

Ao chamar finishTransactionIOS (purchaseId) ou finishTransaction (compra) para uma assinatura, a transação deve ser concluída e não ser emitida para o aplicativo novamente na próxima inicialização. Se houvesse um problema com isso, eu esperaria que algo fosse retornado ao listener de erro ou em uma promessa de uma das funções finishTransaction, nada acontece.

O Android parece funcionar bem.

Comportamento real

A transação é emitida em cada inicialização, a menos que clearTransactionIOS seja chamado, o que parece ter outros efeitos colaterais.

Ambiente testado (emulador? Dispositivo real?)

Dispositivo real

Passos para reproduzir o comportamento

  • Crie um produto de assinatura.
  • Compre a assinatura
  • Simular validação de back-end
  • Chame finishTransactionIOS ou finishTransaction nessa assinatura. Não tem efeito.

Eu tentei isso de várias maneiras e absolutamente nada parece funcionar, não importa o que eu faça, além de limpar as transações, a compra é emitida novamente para o aplicativo a cada lançamento. Alguma ideia do que está acontecendo ou o que eu poderia estar fazendo ou entendendo errado?

ℹ needs more info 📱 iOS 🙏 help wanted 🚶🏻 stale

Comentários muito úteis

@hyochan alguma atualização sobre este problema? Estamos parados há algum tempo e não conseguimos publicar nosso aplicativo.

Todos 34 comentários

Eu tenho o mesmo problema.

Você pode compartilhar ackResult ?

          try {
            const ackResult = await finishTransaction(purchase);
            console.log('ackResult', ackResult);
          } catch (ackErr) {
            console.warn('ackErr', ackErr);
          }

@hyochan
No meu caso:
ackResult undefined

@hyochan
No meu caso:
ackResult undefined

Mesmo aqui

Também no meu caso, o valor retornado é indefinido (dispositivo real com um usuário sandbox).

Parece relacionado com # 366. Também para ios finishTransaction não retornará nada.
Vocês podem se concentrar em # 366 e voltar para a atualização?

Parece relacionado com # 366. Também para ios finishTransaction não retornará nada.
Vocês podem se concentrar em # 366 e voltar para a atualização?

Apenas para esclarecer - uma vez que o nº 366 é sobre testes, você está sugerindo que isso deve funcionar bem na produção sem a necessidade de clearTransactionIOS?

@hyochan Quando estou testando a configuração de depuração com a conta sandbox, basicamente, estou recebendo notificações do Servidor para Servidor (Apple) sobre uma compra. O problema é que depois de algum tempo meu ouvinte de cliente recebe outra notificação sobre a compra, mas com transactionId diferente, mesmo quando a ação de compra aconteceu apenas uma vez, então parece que há um problema em concluir corretamente a transação anterior. No Android, tudo funciona bem e acho que não está relacionado ao # 366 porque não tenho nenhum problema para testar o processo de compra com uma conta sandbox.

`` `tsx
useEffect (() => {
purchaseUpdateSubscription.current = purchaseUpdatedListener (
assíncrono (compra: InAppPurchase | SubscriptionPurchase) => {
const recibo = compra.transactionReceipt;
if (recibo) {
tentar {
aguarde myBackendHandler ({
do utilizador,
compra,
});
resultado const = espera RNIap.finishTransaction (compra, falso);
console.log ('resultado', resultado);
} catch (e) {
// FAÇAM
}
}
}
);
return () => {
if (purchaseUpdateSubscription.current) {
purchaseUpdateSubscription.current.remove ();
purchaseUpdateSubscription.current = null;
}
};
}, []);
`` ``

Ei equipe, obrigado por todo o trabalho duro nesta biblioteca, ela realmente torna nossas vidas mais fáceis. Estou comentando porque esse problema não foi resolvido e não vejo como as transações antigas não compensadas têm algo a ver com # 366.

Meu caso de uso é

  1. O usuário 1 faz a compra e o benefício premium aplicado à sua conta.
  2. O usuário 1 sai
  3. O usuário 2 faz login

Resultado esperado:

  • As transações do usuário 1 não são emitidas por meio do listener de compra

Resultado atual

  • As transações do usuário 1 NÃO são compensadas e são emitidas novamente. Mesmo que um novo usuário tenha feito login, levando a dados incorretos.

Esse é apenas um exemplo de por que é importante priorizar essa questão. Obrigado por ler este longo comentário ...

mesmo aqui

exatamente o mesmo problema acima, parece que a pilha não foi limpa, mesmo depois de desconectar a conta sandbox / itunes, reiniciar o dispositivo, reinstalar o aplicativo ..: /

Você poderia verificar se finishTransaction está realmente fazendo nada no arquivo RNIapIos.m ?
Acabei de verificar e ele conclui corretamente sua transação.

Verifique o método clearTransaction ou finishTransactionWithIdentifier e tente colocar todos os logs dentro dele. Tente encontrar os códigos abaixo.

RCT_EXPORT_METHOD(clearTransaction) {
    NSArray *pendingTrans = [[SKPaymentQueue defaultQueue] transactions];
    NSLog(@"\n\n\n  ***  clear remaining Transactions. Call this before make a new transaction   \n\n.");
    for (int k = 0; k < pendingTrans.count; k++) {
        [[SKPaymentQueue defaultQueue] finishTransaction:pendingTrans[k]];
    }
-(void)finishTransactionWithIdentifier:(NSString *)transactionIdentifier {
    SKPaymentQueue *queue = [SKPaymentQueue defaultQueue];
    for(SKPaymentTransaction *transaction in queue.transactions) {
        if([transaction.transactionIdentifier isEqualToString:transactionIdentifier]) {
            [[SKPaymentQueue defaultQueue] finishTransaction:transaction];
        }
    }
}

Não posso levar este problema adiante porque não estou tendo nenhuma ideia do que está acontecendo. Espero que alguém possa depurar coisas em seu ambiente e compartilhar 🙏

alguma atualização sobre este problema?

também estou tendo esse problema.

mesmo aqui finishTransactionIOS / finishTransaction isso não está funcionando em IOS

Parece que experimento a mesma coisa.
A compra do IAP é concluída com sucesso, mas a chamada final para finishTransaction retorna undefined .

// Finish transaction
const ackResult = await finishTransaction(purchase);
console.log("Ack result: ", ackResult); // Ack result: undefined

_Dispositivo real "iPhone 6s" usando ambiente Sandbox da App Store._

Qual é o resultado esperado de finishTransaction? undefined um bom resultado ou devo esperar outra coisa?

Também estou tendo esse problema no iOS, em que nem finishTransactionIOS nem finishTransaction estão realmente removendo essas transações, o que está me impedindo de lançar o aplicativo. Qualquer atualização?

@hyochan Embora eu definitivamente não seja fluente em Objective-C, adicionei alguns logs em finishTransaction e finishTransactionWithIdentifier e parece que eles estão funcionando corretamente (a id está sendo passada e combinada). A única coisa que consigo pensar é que [[SKPaymentQueue defaultQueue] finishTransaction:transaction]; não está realmente se conectando ao StoreKit e concluindo a transação? Eu realmente não sei para onde ir a partir daqui, mas espero que isso seja útil para depurar

Você também está lidando com notificações de status de assinatura da App Store? No meu caso, tive esse problema até ter certeza de que meu servidor tratou todos os eventos de notificação corretamente e respondeu com um 200.

@espenjanson Interessante. Não, não estava. Mas, na verdade, acabei de mudar para o módulo InAppPurchases da Expo ontem e está tudo funcionando muito bem agora.

@lachlanglen Nevermind. Foi apenas um único golpe de sorte (?) Ou outra coisa. Construiu outra versão do aplicativo e o problema está lá novamente. Pensando em fazer o mesmo, mas um pouco preocupado que o expo repo não esteja muito bem mantido.

Eu estava dando uma olhada nele e é correto que ele retorna indefinido, uma vez que a chamada de finishTransactions não se destina a retornar nada em obj-c.
Fiz alguns testes e no meu caso, parece que os conclui corretamente (teste simples, puxar transações pendentes, se não houver nada, está funcionando bem)

@espenjanson Não sei o que o faz pensar que os pacotes da Expo não são bem mantidos. Acho que a Expo lançou seu módulo iap recentemente. Se eu soubesse que o expo tinha um módulo iap no momento em que estava implementando o iap, teria escolhido o expo sem pensar duas vezes.

Existe alguma maneira de resolver isso?
Eu testei isso no TestFlight mesmo com um novo usuário sandbox e ainda continua acontecendo.

Meu purchaseUpdateListener continua recebendo transações "antigas".
Estou ligando para finishTransaction(purchase,false) porque estou usando assinaturas autorenováveis.
O resultado da chamada é undefined .

Alguém pode oferecer uma solução alternativa ou uma explicação de por que isso está acontecendo?

@zatloeri Esse é aparentemente um comportamento desejado. Levei um tempo para perceber isso. A App Store colocará novo recibo para renovação no StoreKit que aciona o observador. Você deve processar esse recibo e concluir a transação.

https://developer.apple.com/videos/play/wwdc2018/705/
https://developer.apple.com/videos/play/wwdc2020/10671

(assista no Safari para qualidade HD)

@ziyoshams Obrigado pela resposta rápida e pelos recursos que analisarei em breve.

Mas primeiro eu só quero apontar uma coisa, porque eu não acho que fiz isso aparente.
O que você está descrevendo parece lógico para mim e esperava que isso acontecesse, mas o que me intriga é o seguinte:

O produto A expirou conforme verificado por receipt validation .
Recebo uma transação antiga (do dia anterior) para o produto A quando inicio ou coloco o aplicativo em primeiro plano.
Então, da próxima vez que coloco o aplicativo em primeiro plano, recebo outra transação antiga (talvez um pouco mais recente que a anterior, não tenho certeza) para o produto A.
Em outro primeiro plano, possivelmente outro e assim por diante.

Eu esperaria colocar todos eles em um aplicativo se a assinatura já tiver expirado.
Este também é um comportamento esperado ou algo estranho está acontecendo?

@zatloeri Toda transação que não for finalizada aparecerá na fila, mesmo que tenha expirado. Para assinaturas mensais, você deve receber esse recibo a cada 5 minutos ou mais. Por exemplo, se você comprar algo e voltar ao aplicativo no dia seguinte, receberá de 5 a 6 recibos ao mesmo tempo. Eu recomendo fortemente assistir a esses vídeos WWDC. Eles são muito úteis.

Estou tendo o mesmo problema no iOS, a transação não está sendo concluída, a fila não está ficando vazia e o recibo reaparece sempre que o aplicativo é iniciado. Verifiquei o recebimento no servidor e concluí a transação. Por favor, dê uma olhada no meu código abaixo

Eu mencionei alguns trechos do meu código abaixo ...

`` `
import RNIap, {
InAppPurchase,
Produtos,
PurchaseError,
Inscrição,
SubscriptionPurchase,
terminarTransação,
terminarTransactionIOS,
purchaseErrorListener,
purchaseUpdatedListener,
clearTransactionIOS,
} de 'react-native-iap';

componente asyncDidMount () {
resultado const = espera RNIap.initConnection ();
const itemSubs = Platform.select ({
android: [
get (this.props, 'subscriptionStore.subscription.android_product_id', null)
],
ios: [
get (this.props, 'subscriptionStore.subscription.ios_product_id', null)
]
});

const subscriptions = await RNIap.getSubscriptions (itemSubs);
purchaseUpdateSubscription = purchaseUpdatedListener (
assíncrono (compra) => {
const recibo = compra.transactionReceipt;
deixe assinatura = {
subscription_id: get (this.props, 'subscriptionStore.subscription.id', null)
};
if (Platform.OS === 'ios') {
assinatura = {
...inscrição,
order_id: get (purchase, 'transactionId', null),
purchase_token: get (purchase, 'originalTransactionIdentifierIOS', null),
recibo: get (purchase, 'transactionReceipt', null), // (armazená-lo em TEXT)
os: 'IOS'
}
} else if (Platform.OS === 'android') {
dados const = JSON.parse (recibo);
assinatura = {
...inscrição,
order_id: get (data, 'orderId', null),
purchase_token: get (data, 'purchaseToken', null),
os: 'ANDROID'
}
}
tentar {
terminarTransação (compra, falso); // isto está aqui para temp, ele será colocado com sucesso mais tarde.
this.props.notificationStore.showToast ('Inscrito com sucesso. Aguarde ...', 'sucesso', 5000);
const resp = espera por this.props.subscriptionStore.onPurchase (assinatura); // verificar transações na extremidade do servidor.
if (resp.success) {// resposta de sucesso
this.props.userStore.setUser ({is_premium: 1});
this.props.navigation.replace ('FixFooter');
}
} catch (erro) {
console.log ("ERRO API onPurchase:", erro);
}
}
)

purchaseErrorSubscription = purchaseErrorListener (
assíncrono (erro) => {
console.log ('erro de compra:', erro);
}
)
}

/ * botão onPurchase * /
async onPurchase () {
tentar {
const request = await RNIap.requestSubscription (Platform.OS === 'android'?
get (this.props, 'subscriptionStore.subscription.android_product_id', null):
get (this.props, 'subscriptionStore.subscription.ios_product_id', null))
} catch (erro) {
console.log ('erro:', erro)
}
}

@ sufyan297 você pode compartilhar um snippet de seu código do lado do servidor? Também estou tentando configurar uma verificação de recibo, mas o documento da Apple é extremamente confuso

@ziyoshams Você está obtendo null também usando o método finishTransaction do módulo expo iap?

@bcbcbcbcbcl Não estou usando o módulo expo.

+1 para mim retorna indefinido

@hyochan alguma atualização sobre este problema? Estamos parados há algum tempo e não conseguimos publicar nosso aplicativo.

Olá, parece que não houve nenhuma atividade sobre este problema recentemente. O problema foi corrigido ou ainda requer a atenção da comunidade? Este problema pode ser resolvido se nenhuma outra atividade ocorrer. Você também pode rotular esse problema como "Para discussão" ou "Bom primeiro problema" e eu o deixarei em aberto. Obrigado por suas contribuições.

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