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 :
Le flux de travail de base est le suivant :
L’application ajoute une
SKPayment
à la file d’attente. Si nécessaire, l’utilisateur sera invité à entrer son ID Apple et à confirmer le paiement.StoreKit envoie la requête au serveur pour traitement.
Une fois la transaction terminée, le serveur répond avec un reçu de transaction.
La
SKPaymentTransactionObserver
sous-classe reçoit le reçu et le traite.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 :
Les interactions entre les classes personnalisées, StoreKit et l’App Store ressemblent à ceci :
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 :
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.cs
CanMakePayments
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 :
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.
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 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.