React-native-iap: Échec d'achat dans l'application iOS lorsque le mode de paiement est ajouté en direct alors qu'aucun mode de paiement n'est déjà présent

Créé le 31 oct. 2018  ·  27Commentaires  ·  Source: dooboolab/react-native-iap

Version de react-native-iap

react-native-iap": "^2.3.17

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

iOS

Comportement attendu

Le paiement ne doit être effectué que lorsque finishTransaction est appelé

Comportement réel

Le montant est détecté après l'ajout d'un mode de paiement tel qu'une carte de crédit.
Mais la méthode RNIap.buyProductWithoutFinishTransaction(sku) arrivée.

Environnement testé (émulateur? Real Device?)

Appareil réel - iPhone 6s

Étapes pour reproduire le comportement

  • Assurez-vous que le compte Apple de l'appareil que le mode de paiement est défini sur aucun ou certains détails de paiement non valides
  • Effectuez l'achat intégré via l'application
  • L'utilisateur est redirigé vers la page des paramètres du compte pour ajouter un mode de paiement valide
  • Le mode de paiement est facturé mais le reçu de transaction n'est pas reçu dans le code mentionné ci-dessous
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

Commentaire le plus utile

Je pense que nous devons contacter apple pour cela et cela semble vraiment terrible qu'il y ait un cas de test différent qui n'est pas reproductible dans l'environnement sandbox . Pour ceux qui veulent comprendre le problème, j'ai enregistré l'écran et vous pouvez voir le clip ici . @anandwahed Pourriez-vous également contacter Apple à ce sujet? Parce que je sais qu'ils ne les soutiennent pas, il est donc préférable que plus de gens aient des contacts avec eux. Rassemblons-nous pour résoudre ce problème.

Tous les 27 commentaires

Désolé de le dire, mais votre code semble comporter de nombreuses erreurs de syntaxe. Veuillez vous référer à notre exemple de projet dans notre repo et comparer d'abord avec votre code.

@dooboolab
Merci pour la réponse, le code affiché ici n'est qu'un exemple, ce n'est pas l'exact qui est utilisé dans l'application réelle.
je vais essayer d'expliquer quel est le problème

  1. componentDidMount

    • connexion initiée en appelant RNIap.initConnection();

    • puis les produits sont récupérés et stockés dans l'état en appelant RNIap.getProducts(product)

  1. lorsque l'utilisateur clique sur le bouton d'achat

    • RNIap.clearTransaction(); est appelé juste pour s'assurer qu'aucune transaction en attente ne reste.
    • RNIap.buyProductWithoutFinishTransaction(sku) est appelé en cas de succès, un appel au serveur d'applications est déclenché si le serveur donne le cas de succès true, puis RNIap.finishTransaction(); est appelé pour effectuer le paiement.
  2. composantWillUnmount ()

    • RNIap.endConnection() pour mettre fin à la connexion.

Ce processus fonctionne bien si l'utilisateur a déjà ajouté un mode de paiement. Lorsque l'utilisateur n'a pas ajouté de mode de paiement, il faut ajouter la page du mode de paiement et le montant est facturé avant d'atteindre le RNIap.finishTransaction();

Merci pour le détail. Votre problème semble clair maintenant. cc @JJMoon

@JJMoon

@ zohaibahmed-22 Est-ce un cas de test sandbox?

@JJMoon Non, c'est un vrai cas d'environnement.

Je comprends que cette erreur se produit lorsque l'utilisateur est déconnecté ou qu'il n'y a aucune information de carte de crédit.
Si les méthodes fonctionnent correctement, la cause de cette erreur se trouve ailleurs.
Nous devons préparer cette action, qui quitte l'application, viewDidDisappear etc.
En mode sandbox, ce symptôme se produit de manière quelque peu différente.
Lorsque je crée un nouveau compte sandbox et que le premier achat ne fonctionne pas.
À la deuxième fois, l'appareil est connecté et fonctionne correctement.
Je n'ai aucune idée de cette erreur.

@dooboolab et @JJMoon aussi je pense avoir trouvé le problème en parcourant notre code iOS. Dans la méthode updatedTransactions , lorsque nous recevons un message d'échec dans le SKPaymentTransactionStateDeferred ou SKPaymentTransactionStateFailed nous ne sommes pas supposés finishTransaction .

Puisque dans de nombreux cas, le résultat SKPaymentTransactionStateFailed est suivi d'un SKPaymentTransactionStatePurchased comme mentionné dans le thread 6431 ci-dessus. Je ne suis pas sûr du code Objective-C, veuillez donc vérifier et confirmer si nous effaçons la transaction en cas d'échec.

@anandwahed Je suis d'accord avec vous. C'est de ma faute. Désolé.
Je vais examiner le fil d'Apple et m'occuper du problème.

@anandwahed Je suppose que c'est la bonne façon de finish transaction cas d'échec. Veuillez rechercher ce fil. https://stackoverflow.com/questions/11008636/inapp-purchase-skpaymentqueue-finish-transaction-doesnt-work
En cas d'échec, il n'y aura pas de reçu, vous n'appliquez donc pas le produit acheté. Et la transaction finale ne signifie pas toujours «acheter».
En attendant, j'étudierai le fil 6431.

Je viens de lire le fil 6431 (https://forums.developer.apple.com/thread/6431#14562)
La ligne du bas. Problème non résolu depuis 2015. Wow ..
Il existe deux manières différentes de gérer cet effet de «flux de kit de magasin».

  1. N'affichez aucune alerte utilisateur.
  2. Montrez le résultat.

Et j'ai appris deux choses.
A. Nous devons finishTransaction cas d'échec. (Je suppose avec ou sans option, toujours)
B. Le flux de Storekit échoue et réussit à la fois. (c'est mauvais)

Je vous suggère à tous de lire ce fil et de revenir sur ce numéro.

Je suppose que, de quelle manière vous choisissez (1 ou 2), c'est tout seul.
Nous pourrions avoir besoin d'un rappel pour une réponse en cas de panne.

@JJMoon cela signifie-t-il que nous ne devrions pas utiliser buyProductWithoutFinishTransaction?

@ maxs15 Je ne voulais pas dire ça. Désolé pour la confusion.
Le flux StoreKit peut se produire à n'importe quel achat. C'est un problème iOS, pas ce module.
Pour l'instant, je n'en ai aucune idée non plus. Cette chose se produit dans toutes les applications iOS natives. Droit?
Nous creuserons davantage pendant notre temps libre.

Aujourd'hui, j'ai essayé de déboguer ce problème car j'ai généré ce problème. Le paiement finishTransanction est terminé mais n'obtient pas le callback lorsque payMethod a changé. J'ai essayé de déboguer l'écriture de console.log mais je n'ai pas pu tester la facturation réelle dans l'environnement dev . Quelqu'un pourrait-il me suggérer comment déboguer ce processus afin que je puisse debug ceci pour real purchase ? Dois-je utiliser ceci en mode sandbox ? Cela fonctionne parfaitement dans le bac à sable, donc je n'ai aucune idée de comment le déboguer. C'est très réticent.

Rassemblons quelques idées car je pense qu'il est très important de le corriger.

J'obtiens toujours cette erreur lorsque je tente d'acheter avec un utilisateur non sandbox.

@hyochan Si vous connectez le vrai serveur (le vôtre), que vous soyez en mode débogage ou en mode version n'a pas d'importance. Je suppose que vous avez 2 options.

  1. Vous exécutez sur l'appareil réel en mode débogage dans Xcode. Utilisez le journal de la console JS.
  2. Vous exécutez sur l'appareil réel en mode version dans Xcode. Utilisez NSLog dans le code objective-c.
    Les deux méthodes devraient fonctionner.

@JJMoon Ouais, je l'ai déjà compris mais je ne pouvais toujours pas faire de test d'achat en direct sur iOS. Ce stackoverflow est-il vrai? Alors, comment puis-je résoudre ce problème? Nous devons tester l'achat en direct.

Quiconque est confronté à ce problème, je suis sûr que tous le font, veuillez nous donner une idée de la façon de le résoudre. in-app purchase fail when payment method added live comme décrit dans le titre du numéro. Comment pourrais-je déboguer cela? @anandwahed Avez-vous déjà contacté apple à propos de ce problème?

@hyochan Non, nous n'avons pas contacté l'assistance Apple.

Je pense que nous devons contacter apple pour cela et cela semble vraiment terrible qu'il y ait un cas de test différent qui n'est pas reproductible dans l'environnement sandbox . Pour ceux qui veulent comprendre le problème, j'ai enregistré l'écran et vous pouvez voir le clip ici . @anandwahed Pourriez-vous également contacter Apple à ce sujet? Parce que je sais qu'ils ne les soutiennent pas, il est donc préférable que plus de gens aient des contacts avec eux. Rassemblons-nous pour résoudre ce problème.

Aujourd'hui, nous avons reçu une réponse d'Apple. Un rappel peut être renvoyé après failure . Nous travaillons sur une solution de contournement dans # 348, mais je crains que cela puisse être très désagréable.

J'ai publié dans 2.4.0-beta1 , en essayant de résoudre ce problème. Le PR # 348 a été ajouté à cette version et vous pouvez également voir le readme de cette fonctionnalité . Notez que ceci est en cours de test.

J'ai testé cela dans un achat en direct et semble fonctionner. Cependant, gardez à l'esprit que vous ne devez ajouter un écouteur qu'en cas d'échec ou il peut être dupliqué avec un résultat réussi.

Les gars, merci d'avoir investi du temps dans ce problème! 🙏

Je pense que la documentation actuelle doit être plus claire sur l'utilisation de addAdditionalSuccessPurchaseListenerIOS . C'est quelque chose que j'ai essayé d'aborder au # 414.

Mais je pense aussi que cela laisse place à l'amélioration de l'API. Seriez-vous disposé à discuter d'un changement dans l'API qui peut mieux s'adapter au soi-disant «flux de kit de magasin»? Quelque chose utilise peut-être RxJS, par exemple:

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 quelque chose de différent qui cache mieux l'abonnement supplémentaire pour iOS?

@Edgpaez C'est super mais ça va changer le comportement de l'achat en android . J'espère pouvoir étudier version 3 de ce module dans 2019 .

salut @hyochan , AFAICT, nous n'avons pas besoin de changer le comportement interne du paquet, seulement l'interface publique. Nous pouvons conserver la méthode buyItemByType pour résoudre les promesses et simplement ajouter un peu de Rx en plus de cela sur index.js .

par exemple, nous aurions ceci dans 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
})();

Mon objectif n'est pas cette implémentation spécifique mais de cacher des détails spécifiques au «flux du kit de magasin».
Pensez-vous que ce soit une bonne idée?
Seriez-vous intéressé par un PR?

@Edgpaez Ok. Je comprends le détail maintenant. Cependant, je pense que l'ajout de RxJS est un peu trop pour implémenter le feature car je pense que cela peut être couvert sans cela.

De plus, je pense que ce qui suit serait quelque chose de différent.

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

Si vous appelez buyProduct pour deux articles comme ci-dessous,

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

Nous ne pouvons pas garantir lequel finirait en premier, donc je pense que nous devrions gérer cela en natif de sendEvent à JS .

J'ai l'impression que la mise en œuvre devrait ressembler à

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

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

Dites-moi s'il y a quelque chose que j'ai manqué.

Permet de gérer plus en détail dans # 423

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