Condividi tramite


Acquisto di prodotti di consumo in Xamarin.iOS

I prodotti di consumo sono i più semplici da implementare, poiché non esiste alcun requisito di "ripristino". Sono utili per prodotti come valuta in gioco o una parte di funzionalità a uso singolo. Gli utenti possono acquistare nuovamente prodotti di consumo over-and-over.

Distribuzione di prodotti predefinita

Il codice di esempio associato a questo documento illustra i prodotti predefiniti: gli ID prodotto sono hardcoded nell'applicazione perché sono strettamente associati al codice che "sblocca" la funzionalità dopo il pagamento. Il processo di acquisto può essere visualizzato come segue:

Visualizzazione del processo di acquisto

Il flusso di lavoro di base è:

  1. L'app aggiunge un oggetto SKPayment alla coda. Se richiesto, all'utente verrà richiesto l'ID Apple e verrà chiesto di confermare il pagamento.

  2. StoreKit invia la richiesta al server per l'elaborazione.

  3. Al termine della transazione, il server risponde con una ricevuta di transazione.

  4. La SKPaymentTransactionObserver sottoclasse riceve la ricevuta e la elabora.

  5. L'applicazione abilita il prodotto (aggiornando NSUserDefaults o altri meccanismi) e quindi chiama StoreKit.FinishTransaction

C'è un altro tipo di flusso di lavoro , prodotti recapitati dal server, descritti più avanti nel documento (vedere la sezione Verifica della ricevuta e prodotti recapitati dal server).

Esempio di Prodotti di consumo

L'esempio contiene un progetto denominato Consumabili che implementa una "valuta in gioco" di base (denominata "crediti scimmia"). L'esempio mostra come implementare due prodotti di acquisto in-app per consentire all'utente di acquistare il maggior numero di "crediti scimmia" desiderati, in un'applicazione reale ci sarebbe anche un modo per spenderli!

L'applicazione viene visualizzata in questi screenshot: ogni acquisto aggiunge più "crediti scimmia" al saldo dell'utente:

Ogni acquisto aggiunge più crediti scimmia al saldo degli utenti

Le interazioni tra classi personalizzate, StoreKit e App Store hanno un aspetto simile al seguente:

Interazioni tra classi personalizzate, StoreKit e App Store

Metodi ViewController

Oltre alle proprietà e ai metodi necessari per recuperare le informazioni sul prodotto, il controller di visualizzazione richiede che altri osservatori di notifica siano in ascolto delle notifiche relative all'acquisto. Questi sono solo NSObjects che verranno registrati e rimossi rispettivamente in ViewWillAppear e ViewWillDisappear .

NSObject succeededObserver, failedObserver;

Il costruttore creerà anche la SKProductsRequestDelegate sottoclasse ( InAppPurchaseManager) che a sua volta crea e registra l'oggetto SKPaymentTransactionObserver ( CustomPaymentObserver).

La prima parte dell'elaborazione di una transazione di acquisto in-app consiste nel gestire la pressione del pulsante quando l'utente vuole acquistare qualcosa, come illustrato nel codice seguente dall'applicazione di esempio:

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

La seconda parte dell'interfaccia utente gestisce la notifica che la transazione ha avuto esito positivo, in questo caso aggiornando il saldo visualizzato:

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

La parte finale dell'interfaccia utente visualizza un messaggio se una transazione viene annullata per qualche motivo. Nel codice di esempio un messaggio viene semplicemente scritto nella finestra di output:

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

Oltre a questi metodi nel controller di visualizzazione, una transazione di acquisto di prodotti di consumo richiede anche il codice su SKProductsRequestDelegate e .SKPaymentTransactionObserver

Metodi inAppPurchaseManager

Il codice di esempio implementa diversi metodi correlati all'acquisto nella classe InAppPurchaseManager, incluso il PurchaseProduct metodo che crea un'istanza SKPayment e lo aggiunge alla coda per l'elaborazione:

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

L'aggiunta del pagamento alla coda è un'operazione asincrona. L'applicazione riprende il controllo mentre StoreKit elabora la transazione e la invia ai server Apple. A questo punto, iOS verificherà che l'utente sia connesso all'App Store e le richieda un ID Apple e una password, se necessario.

Supponendo che l'utente esegua correttamente l'autenticazione con l'App Store e accetti la transazione, riceverà la SKPaymentTransactionObserver risposta di StoreKit e chiamerà il metodo seguente per soddisfare la transazione e finalizzarla.

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);
}

L'ultimo passaggio consiste nel assicurarsi di notificare a StoreKit che la transazione è stata completata correttamente chiamando 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);
       }
   }
}

Una volta recapitato il prodotto, SKPaymentQueue.DefaultQueue.FinishTransaction deve essere chiamato per rimuovere la transazione dalla coda di pagamento.

Metodi SKPaymentTransactionObserver (CustomPaymentObserver)

StoreKit chiama il UpdatedTransactions metodo quando riceve una risposta dai server Apple e passa una matrice di SKPaymentTransaction oggetti da controllare per il codice. Il metodo esegue un ciclo di ogni transazione ed esegue una funzione diversa in base allo stato della transazione (come illustrato di seguito):

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;
       }
   }
}

Il CompleteTransaction metodo è stato descritto in precedenza in questa sezione: salva i dettagli dell'acquisto in NSUserDefaults, finalizza la transazione con StoreKit e infine invia una notifica all'interfaccia utente da aggiornare.

Acquisto di più prodotti

Se nell'applicazione è opportuno acquistare più prodotti, usare la SKMutablePayment classe e impostare il campo Quantity:

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

Il codice che gestisce la transazione completata deve anche eseguire una query sulla proprietà Quantity per soddisfare correttamente l'acquisto:

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);
}

Quando l'utente acquista più quantità, l'avviso di conferma dello StoreKit rifletterà la quantità, il prezzo unitario e il prezzo totale che verranno addebitati, come illustrato nello screenshot seguente:

Conferma di un acquisto

Gestione delle interruzioni di rete

Gli acquisti in-app richiedono una connessione di rete funzionante per StoreKit per comunicare con i server Apple. Se una connessione di rete non è disponibile, l'acquisto in-app non sarà disponibile.

Richieste di prodotti

Se la rete non è disponibile durante la creazione di , SKProductRequestverrà chiamato il RequestFailed metodo della SKProductsRequestDelegate sottoclasse ( InAppPurchaseManager), come illustrato di seguito:

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 rimane quindi in ascolto della notifica e visualizza un messaggio nei pulsanti di acquisto:

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

Poiché una connessione di rete può essere temporanea nei dispositivi mobili, le applicazioni potrebbero voler monitorare lo stato della rete usando il framework SystemConfiguration e riprovare quando è disponibile una connessione di rete. Fare riferimento a Apple o all'oggetto che lo usa.

Transazioni di acquisto

Se possibile, la coda di pagamento storeKit archivierà e inoltra le richieste di acquisto, quindi l'effetto di un'interruzione della rete varia a seconda del momento in cui la rete non è riuscita durante il processo di acquisto.

Se si verifica un errore durante una transazione, la SKPaymentTransactionObserver sottoclasse ( CustomPaymentObserver) avrà il UpdatedTransactions metodo chiamato e la SKPaymentTransaction classe sarà nello stato Failed.

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;
       }
   }
}

Il FailedTransaction metodo rileva se l'errore è dovuto all'annullamento dell'utente, come illustrato di seguito:

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);
}

Anche se una transazione non riesce, il FinishTransaction metodo deve essere chiamato per rimuovere la transazione dalla coda di pagamento:

SKPaymentQueue.DefaultQueue.FinishTransaction(transaction);

Il codice di esempio invia quindi una notifica in modo che ViewController possa visualizzare un messaggio. Le applicazioni non devono visualizzare un messaggio aggiuntivo se l'utente ha annullato la transazione. Altri codici di errore che possono verificarsi includono:

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.

Gestione delle restrizioni

La funzionalità Impostazioni > restrizioni generali > di iOS consente agli utenti di bloccare determinate funzionalità del dispositivo.

È possibile eseguire query su se l'utente può effettuare acquisti in-app tramite il SKPaymentQueue.CanMakePayments metodo . Se restituisce false, l'utente non può accedere all'acquisto in-app. StoreKit visualizzerà automaticamente un messaggio di errore all'utente se si tenta di acquistare. Controllando questo valore, l'applicazione può invece nascondere i pulsanti di acquisto o eseguire un'altra azione per aiutare l'utente.

Nel file il InAppPurchaseManager.csCanMakePayments metodo esegue il wrapping della funzione StoreKit come segue:

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

Per testare questo metodo, usare la funzionalità Restrizioni di iOS per disabilitare gli acquisti in-app:

Usare la funzionalità Restrizioni di iOS per disabilitare gli acquisti in-app

Questo codice di esempio da ConsumableViewController reagisce alla CanMakePayments restituzione di false visualizzando il testo AppStore Disabilitato nei pulsanti disabilitati.

// 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'applicazione è simile a questa quando la funzionalità Acquisti in-app è limitata: i pulsanti di acquisto sono disabilitati.

L'applicazione ha questo aspetto quando la funzionalità Acquisti in-app è limitata, i pulsanti di acquisto sono disabilitati

Le informazioni sul prodotto possono comunque essere richieste quando CanMakePayments è false, quindi l'app può comunque recuperare e visualizzare i prezzi. Ciò significa che se il controllo è stato rimosso CanMakePayments dal codice i pulsanti di acquisto sarebbero ancora attivi, tuttavia quando si tenta di acquistare l'utente visualizzerà un messaggio che indica che gli acquisti in-app non sono consentiti (generati da StoreKit quando si accede alla coda di pagamento):

Gli acquisti in-app non sono consentiti

Le applicazioni reali possono adottare un approccio diverso per gestire la restrizione, ad esempio nascondere completamente i pulsanti e forse offrire un messaggio più dettagliato rispetto all'avviso visualizzato automaticamente da StoreKit.