Partager via


Achat de produits consommables dans Xamarin.iOS

Les produits consommables sont les plus simples à implémenter, car il n’existe aucune exigence de « restauration ». Ils sont utiles pour les produits tels que la devise dans le jeu ou une partie de fonctionnalité à usage unique. Les utilisateurs peuvent réinscrire des produits consommables à nouveau.

Livraison de produit intégrée

L’exemple de code qui accompagne ce document illustre les produits intégrés : les ID de produit sont codés en dur dans l’application, car ils sont étroitement couplés au code qui « déverrouille » la fonctionnalité après paiement. Le processus d’achat peut être visualisées comme suit :

Visualisation du processus d’achat

Le flux de travail de base est le suivant :

  1. L’application ajoute une SKPayment à la file d’attente. Si nécessaire, l’utilisateur sera invité à entrer son ID Apple et à confirmer le paiement.

  2. StoreKit envoie la requête au serveur pour traitement.

  3. Une fois la transaction terminée, le serveur répond avec un reçu de transaction.

  4. La SKPaymentTransactionObserver sous-classe reçoit le reçu et le traite.

  5. L’application active le produit (en mettant à jour NSUserDefaults ou un autre mécanisme), puis appelle StoreKit.FinishTransaction

Il existe un autre type de flux de travail – Produits livrés par le serveur – qui est abordé plus loin dans le document (voir la section Vérification des reçus et produits livrés par le serveur).

Exemple de produits consommables

L’exemple contient un projet appelé Consommables qui implémente une devise « in-game » de base (appelée « crédits singe »). L’exemple montre comment implémenter deux produits d’achat dans l’application pour permettre à l’utilisateur d’acheter autant de « crédits singes » que vous le souhaitez – dans une application réelle, il y aurait également une certaine façon de les dépenser !

L’application est affichée dans ces captures d’écran : chaque achat ajoute plus de « crédits singe » à l’équilibre de l’utilisateur :

Chaque achat ajoute plus de crédits singes au solde des utilisateurs

Les interactions entre les classes personnalisées, StoreKit et l’App Store ressemblent à ceci :

Interactions entre les classes personnalisées, StoreKit et l’App Store

ViewController, méthodes

Outre les propriétés et méthodes requises pour récupérer des informations sur le produit, le contrôleur de vue nécessite des observateurs de notification supplémentaires pour écouter les notifications liées à l’achat. Il s’agit simplement NSObjects de ceux qui seront inscrits et supprimés dans et ViewWillDisappear respectivementViewWillAppear.

NSObject succeededObserver, failedObserver;

Le constructeur crée également la SKProductsRequestDelegate sous-classe ( InAppPurchaseManager) qui à son tour crée et inscrit le SKPaymentTransactionObserver ( CustomPaymentObserver).

La première partie du traitement d’une transaction d’achat dans l’application consiste à gérer la pression du bouton lorsque l’utilisateur souhaite acheter quelque chose, comme illustré dans le code suivant de l’exemple d’application :

buy5Button.TouchUpInside += (sender, e) => {
   iap.PurchaseProduct (Buy5ProductId);
};​
buy10Button.TouchUpInside += (sender, e) => {
   iap.PurchaseProduct (Buy10ProductId);
};

La deuxième partie de l’interface utilisateur gère la notification indiquant que la transaction a réussi, dans ce cas en mettant à jour le solde affiché :

succeededObserver = NSNotificationCenter.DefaultCenter.AddObserver (InAppPurchaseManager.InAppPurchaseManagerTransactionSucceededNotification,
(notification) => {
   balanceLabel.Text = CreditManager.Balance() + " monkey credits";
});

La dernière partie de l’interface utilisateur affiche un message si une transaction est annulée pour une raison quelconque. Dans l’exemple de code, un message est simplement écrit dans la fenêtre de sortie :

failedObserver = NSNotificationCenter.DefaultCenter.AddObserver (InAppPurchaseManager.InAppPurchaseManagerTransactionFailedNotification,
(notification) => {
   Console.WriteLine ("Transaction Failed");
});

En plus de ces méthodes sur le contrôleur de vue, une transaction d’achat de produit consommable nécessite également du code sur le SKProductsRequestDelegate contrôleur de vue et le SKPaymentTransactionObserver.

InAppPurchaseManager, méthodes

L’exemple de code implémente un certain nombre de méthodes associées à l’achat sur la classe InAppPurchaseManager, y compris la PurchaseProduct méthode qui crée une SKPayment instance et l’ajoute à la file d’attente pour le traitement :

public void PurchaseProduct(string appStoreProductId)
{
   SKPayment payment = SKPayment.PaymentWithProduct (appStoreProductId);​
   SKPaymentQueue.DefaultQueue.AddPayment (payment);
}

L’ajout du paiement à la file d’attente est une opération asynchrone. L’application récupère le contrôle pendant que StoreKit traite la transaction et l’envoie aux serveurs d’Apple. À ce stade, iOS vérifie que l’utilisateur est connecté à l’App Store et l’invite à entrer un ID et un mot de passe Apple si nécessaire.

En supposant que l’utilisateur s’authentifie avec l’App Store et accepte la transaction, la SKPaymentTransactionObserver réponse de StoreKit reçoit la réponse de StoreKit et appelle la méthode suivante pour remplir la transaction et la finaliser.

public void CompleteTransaction (SKPaymentTransaction transaction)
{
   var productId = transaction.Payment.ProductIdentifier;
   // Register the purchase, so it is remembered for next time
   PhotoFilterManager.Purchase(productId);
   FinishTransaction(transaction, true);
}

La dernière étape consiste à vous assurer que vous informez StoreKit que vous avez correctement rempli la transaction, en appelant FinishTransaction:

public void FinishTransaction(SKPaymentTransaction transaction, bool wasSuccessful)
{
   // remove the transaction from the payment queue.
   SKPaymentQueue.DefaultQueue.FinishTransaction(transaction);  // THIS IS IMPORTANT - LET'S APPLE KNOW WE'RE DONE !!!!
   using (var pool = new NSAutoreleasePool()) {
       NSDictionary userInfo = NSDictionary.FromObjectsAndKeys(new NSObject[] {transaction},new NSObject[] {new NSString("transaction")});
       if (wasSuccessful) {
           // send out a notification that we've finished the transaction
           NSNotificationCenter.DefaultCenter.PostNotificationName (InAppPurchaseManagerTransactionSucceededNotification, this, userInfo);
       } else {
           // send out a notification for the failed transaction
           NSNotificationCenter.DefaultCenter.PostNotificationName (InAppPurchaseManagerTransactionFailedNotification, this, userInfo);
       }
   }
}

Une fois le produit livré, SKPaymentQueue.DefaultQueue.FinishTransaction doit être appelé pour supprimer la transaction de la file d’attente de paiement.

Méthodes SKPaymentTransactionObserver (CustomPaymentObserver)

StoreKit appelle la UpdatedTransactions méthode lorsqu’elle reçoit une réponse des serveurs d’Apple et transmet un tableau d’objets SKPaymentTransaction pour votre code à inspecter. La méthode effectue une boucle dans chaque transaction et effectue une fonction différente en fonction de l’état de la transaction (comme illustré ici) :

public override void UpdatedTransactions (SKPaymentQueue queue, SKPaymentTransaction[] transactions)
{
   foreach (SKPaymentTransaction transaction in transactions)
   {
       switch (transaction.TransactionState)
       {
           case SKPaymentTransactionState.Purchased:
              theManager.CompleteTransaction(transaction);
               break;
           case SKPaymentTransactionState.Failed:
              theManager.FailedTransaction(transaction);
               break;
           default:
               break;
       }
   }
}

La CompleteTransaction méthode a été abordée plus haut dans cette section : elle enregistre les détails de l’achat dans NSUserDefaults, finalise la transaction avec StoreKit et informe enfin l’interface utilisateur de mettre à jour.

Achat de plusieurs produits

S’il est judicieux dans votre application d’acheter plusieurs produits, utilisez la SKMutablePayment classe et définissez le champ Quantity :

public void PurchaseProduct(string appStoreProductId)
{
   SKMutablePayment payment = SKMutablePayment.PaymentWithProduct (appStoreProductId);
   payment.Quantity = 4; // hardcoded as an example
   SKPaymentQueue.DefaultQueue.AddPayment (payment);
}

Le code qui gère la transaction terminée doit également interroger la propriété Quantity pour remplir correctement l’achat :

public void CompleteTransaction (SKPaymentTransaction transaction)
{
   var productId = transaction.Payment.ProductIdentifier;
   var qty = transaction.Payment.Quantity;
   if (productId == ConsumableViewController.Buy5ProductId)
       CreditManager.Add(5 * qty);
   else if (productId == ConsumableViewController.Buy10ProductId)
       CreditManager.Add(10 * qty);
   else
       Console.WriteLine ("Shouldn't happen, there are only two products");
   FinishTransaction(transaction, true);
}

Lorsque l’utilisateur achète plusieurs quantités, l’alerte de confirmation StoreKit reflète la quantité, le prix unitaire et le prix total facturé, comme indiqué dans la capture d’écran suivante :

Confirmation d’un achat

Gestion des pannes réseau

Les achats dans l’application nécessitent une connexion réseau opérationnelle pour que StoreKit communique avec les serveurs d’Apple. Si une connexion réseau n’est pas disponible, l’achat dans l’application n’est pas disponible.

Demandes de produit

Si le réseau n’est pas disponible lors de la création d’un SKProductRequest, la RequestFailed méthode de la SKProductsRequestDelegate sous-classe ( InAppPurchaseManager) est appelée, comme indiqué ci-dessous :

public override void RequestFailed (SKRequest request, NSError error)
{
   using (var pool = new NSAutoreleasePool()) {
       NSDictionary userInfo = NSDictionary.FromObjectsAndKeys(new NSObject[] {error},new NSObject[] {new NSString("error")});
       // send out a notification for the failed transaction
       NSNotificationCenter.DefaultCenter.PostNotificationName (InAppPurchaseManagerRequestFailedNotification, this, userInfo);
   }
}

ViewController écoute ensuite la notification et affiche un message dans les boutons d’achat :

requestObserver = NSNotificationCenter.DefaultCenter.AddObserver (InAppPurchaseManager.InAppPurchaseManagerRequestFailedNotification,
(notification) => {
   Console.WriteLine ("Request Failed");
   buy5Button.SetTitle ("Network down?", UIControlState.Disabled);
   buy10Button.SetTitle ("Network down?", UIControlState.Disabled);
});

Étant donné qu’une connexion réseau peut être temporaire sur des appareils mobiles, les applications peuvent souhaiter surveiller l’état du réseau à l’aide de l’infrastructure SystemConfiguration et réessayer lorsqu’une connexion réseau est disponible. Reportez-vous à Apple ou à celui qui l’utilise.

Transactions d’achat

La file d’attente de paiement StoreKit stocke et transfère les demandes d’achat si possible, l’effet d’une panne réseau varie selon le moment où le réseau a échoué pendant le processus d’achat.

Si une erreur se produit pendant une transaction, la SKPaymentTransactionObserver sous-classe ( CustomPaymentObserver) aura la UpdatedTransactions méthode appelée et la SKPaymentTransaction classe sera dans l’état Échec.

public override void UpdatedTransactions (SKPaymentQueue queue, SKPaymentTransaction[] transactions)
{
   foreach (SKPaymentTransaction transaction in transactions)
   {
       switch (transaction.TransactionState)
       {
           case SKPaymentTransactionState.Purchased:
               theManager.CompleteTransaction(transaction);
               break;
           case SKPaymentTransactionState.Failed:
               theManager.FailedTransaction(transaction);
               break;
           default:
               break;
       }
   }
}

La FailedTransaction méthode détecte si l’erreur était due à l’annulation de l’utilisateur, comme indiqué ici :

public void FailedTransaction (SKPaymentTransaction transaction)
{
   //SKErrorPaymentCancelled == 2
   if (transaction.Error.Code == 2) // user cancelled
       Console.WriteLine("User CANCELLED FailedTransaction Code=" + transaction.Error.Code + " " + transaction.Error.LocalizedDescription);
   else // error!
       Console.WriteLine("FailedTransaction Code=" + transaction.Error.Code + " " + transaction.Error.LocalizedDescription);
   FinishTransaction(transaction,false);
}

Même si une transaction échoue, la FinishTransaction méthode doit être appelée pour supprimer la transaction de la file d’attente de paiement :

SKPaymentQueue.DefaultQueue.FinishTransaction(transaction);

L’exemple de code envoie ensuite une notification afin que ViewController puisse afficher un message. Les applications ne doivent pas afficher de message supplémentaire si l’utilisateur a annulé la transaction. D’autres codes d’erreur qui peuvent se produire sont les suivants :

FailedTransaction Code=0 Cannot connect to iTunes Store
FailedTransaction Code=5002 An unknown error has occurred
FailedTransaction Code=5020 Forget Your Password?
Applications may detect and respond to specific error codes, or handle them in the same way.

Gestion des restrictions

La fonctionnalité Paramètres > Restrictions générales > d’iOS permet aux utilisateurs de verrouiller certaines fonctionnalités de leur appareil.

Vous pouvez interroger si l’utilisateur est autorisé à effectuer des achats dans l’application via la SKPaymentQueue.CanMakePayments méthode. Si cette valeur est false, l’utilisateur ne peut pas accéder à l’achat dans l’application. StoreKit affiche automatiquement un message d’erreur à l’utilisateur si un achat est tenté. En case activée cette valeur, votre application peut masquer les boutons d’achat ou prendre d’autres mesures pour aider l’utilisateur.

Dans le fichier, la InAppPurchaseManager.csCanMakePayments méthode encapsule la fonction StoreKit comme suit :

public bool CanMakePayments()
{
   return SKPaymentQueue.CanMakePayments;​
}

Pour tester cette méthode, utilisez la fonctionnalité Restrictions d’iOS pour désactiver les achats dans l’application :

Utiliser la fonctionnalité Restrictions d’iOS pour désactiver les achats dans l’application

Cet exemple de ConsumableViewController code réagit au CanMakePayments retour de false en affichant le texte Désactivé AppStore sur les boutons désactivés.

// only if we can make payments, request the prices
if (iap.CanMakePayments()) {
   // now go get prices, if we don't have them already
   if (!pricesLoaded)
       iap.RequestProductData(products); // async request via StoreKit -> App Store
} else {
   // can't make payments (purchases turned off in Settings?)
   // the buttons are disabled by default, and only enabled when prices are retrieved
   buy5Button.SetTitle ("AppStore disabled", UIControlState.Disabled);
   buy10Button.SetTitle ("AppStore disabled", UIControlState.Disabled);
}

L’application ressemble à ceci lorsque la fonctionnalité Achats dans l’application est restreinte : les boutons d’achat sont désactivés.

L’application ressemble à ceci lorsque la fonctionnalité Achats dans l’application est limitée, les boutons d’achat sont désactivés

Les informations sur le produit peuvent toujours être demandées lorsque CanMakePayments la valeur est false, afin que l’application puisse toujours récupérer et afficher les prix. Cela signifie que si nous avons supprimé le CanMakePayments case activée du code, les boutons d’achat sont toujours actifs. Toutefois, lorsqu’un achat est tenté, l’utilisateur voit un message indiquant que les achats dans l’application ne sont pas autorisés (générés par StoreKit lorsque la file d’attente de paiement est accessible) :

Les achats dans l’application ne sont pas autorisés

Les applications réelles peuvent adopter une approche différente de la gestion de la restriction, comme le masquage total des boutons et peut-être offrir un message plus détaillé que l’alerte que StoreKit affiche automatiquement.