Compartilhar via


Comprando produtos consumíveis no Xamarin.iOS

Os produtos consumíveis são os mais simples de implementar, uma vez que não há necessidade de "restauração". Eles são úteis para produtos como moeda do jogo ou uma funcionalidade de uso único. Os usuários podem recomprar produtos consumíveis repetidamente.

Entrega de produtos integrada

O código de exemplo que acompanha este documento demonstra produtos internos – as IDs do produto são codificadas no aplicativo porque estão firmemente acopladas ao código que "desbloqueia" o recurso após o pagamento. O processo de compra pode ser visualizado assim:

A visualização do processo de compra

O fluxo de trabalho básico é:

  1. O aplicativo adiciona um SKPayment à fila. Se necessário, o usuário será solicitado a fornecer seu ID Apple e solicitado a confirmar o pagamento.

  2. O StoreKit envia a solicitação ao servidor para processamento.

  3. Quando a transação é concluída, o servidor responde com um recibo de transação.

  4. A SKPaymentTransactionObserver subclasse recebe o recibo e o processa.

  5. O aplicativo habilita o produto (por atualização NSUserDefaults ou algum outro mecanismo) e, em seguida, chama o StoreKit's FinishTransaction.

Há outro tipo de fluxo de trabalho – Produtos entregues pelo servidor – que é discutido posteriormente no documento (consulte a seção Verificação de recebimento e Produtos entregues pelo servidor).

Exemplo de produtos consumíveis

O exemplo contém um projeto chamado Consumables que implementa uma "moeda do jogo" básica (chamada "monkey credits"). O exemplo mostra como implementar dois produtos de compra no aplicativo para permitir que o usuário compre quantos "créditos de macaco" quiser – em um aplicativo real também haveria alguma maneira de gastá-los!

O aplicativo é mostrado nestas capturas de tela – cada compra adiciona mais "créditos de macaco" ao saldo do usuário:

Cada compra adiciona mais créditos de macaco ao saldo dos usuários

As interações entre classes personalizadas, StoreKit e App Store têm esta aparência:

As interações entre classes personalizadas, StoreKit e App Store

Métodos ViewController

Além das propriedades e métodos necessários para recuperar informações do produto, o controlador de exibição requer observadores de notificação adicionais para ouvir notificações relacionadas à compra. Estes são apenas NSObjects que serão registrados e removidos em ViewWillAppear e ViewWillDisappear respectivamente.

NSObject succeededObserver, failedObserver;

O construtor também criará a subclasse ( InAppPurchaseManager) que, SKProductsRequestDelegate por sua vez, cria e registra o SKPaymentTransactionObserver ( CustomPaymentObserver).

A primeira parte do processamento de uma transação de compra no aplicativo é manipular o botão pressionado quando o usuário deseja comprar algo, conforme mostrado no código a seguir do aplicativo de exemplo:

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

A segunda parte da interface do usuário está manipulando a notificação de que a transação foi bem-sucedida, neste caso, atualizando o saldo exibido:

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

A parte final da interface do usuário é exibir uma mensagem se uma transação for cancelada por algum motivo. No código de exemplo, uma mensagem é simplesmente gravada na janela de saída:

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

Além desses métodos no controlador de exibição, uma transação de compra de produto consumível também requer código no SKProductsRequestDelegate e no SKPaymentTransactionObserver.

Métodos InAppPurchaseManager

O código de exemplo implementa vários métodos relacionados à compra na classe InAppPurchaseManager, incluindo o PurchaseProduct método que cria uma SKPayment instância e a adiciona à fila para processamento:

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

Adicionar o pagamento à fila é uma operação assíncrona. O aplicativo recupera o controle enquanto o StoreKit processa a transação e a envia para os servidores da Apple. É neste ponto que o iOS verificará se o usuário está conectado à App Store e solicitará um ID Apple e senha, se necessário.

Supondo que o usuário se autentique com êxito na App Store e concorde com a transação, o SKPaymentTransactionObserver receberá a resposta do StoreKit e chamará o seguinte método para concluir a transação e finalizá-la.

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

A última etapa é garantir que você notifique o StoreKit de que concluiu com êxito a transação, chamando 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);
       }
   }
}

Uma vez que o produto é entregue, SKPaymentQueue.DefaultQueue.FinishTransaction deve ser chamado para remover a transação da fila de pagamento.

Métodos SKPaymentTransactionObserver (CustomPaymentObserver)

O StoreKit chama o UpdatedTransactions método quando recebe uma resposta dos servidores da Apple e passa uma matriz de SKPaymentTransaction objetos para seu código inspecionar. O método percorre cada transação e executa uma função diferente com base no estado da transação (conforme mostrado aqui):

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

O CompleteTransaction método foi abordado anteriormente nesta seção – ele salva os detalhes da compra no NSUserDefaults, finaliza a transação com o StoreKit e, finalmente, notifica a interface do usuário para atualizar.

Compra de vários produtos

Se fizer sentido em seu aplicativo comprar vários produtos, use a SKMutablePayment classe e defina o campo Quantidade:

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

O código que manipula a transação concluída também deve consultar a propriedade Quantity para cumprir corretamente a compra:

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 o usuário compra várias quantidades, o alerta de confirmação do StoreKit refletirá a quantidade, o preço unitário e o preço total que será cobrado, conforme mostrado na captura de tela a seguir:

Confirmando uma compra

Lidando com interrupções de rede

As compras no aplicativo exigem uma conexão de rede funcional para que o StoreKit se comunique com os servidores da Apple. Se uma conexão de rede não estiver disponível, a compra no aplicativo não estará disponível.

Solicitações de Produtos

Se a rede estiver indisponível durante a SKProductsRequestDelegate criação de um SKProductRequest, o RequestFailed método da subclasse ( InAppPurchaseManager) será chamado, conforme mostrado abaixo:

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

Em seguida, o ViewController escuta a notificação e exibe uma mensagem nos botões de compra:

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

Como uma conexão de rede pode ser transitória em dispositivos móveis, os aplicativos podem desejar monitorar o status da rede usando a estrutura SystemConfiguration e tentar novamente quando uma conexão de rede estiver disponível. Consulte a Apple ou a que a usa.

Transações de Compra

A fila de pagamento do StoreKit armazenará e encaminhará solicitações de compra, se possível, portanto, o efeito de uma interrupção de rede variará dependendo de quando a rede falhou durante o processo de compra.

Se ocorrer um erro durante uma transação, a SKPaymentTransactionObserver subclasse ( CustomPaymentObserver) terá o UpdatedTransactions método chamado e a SKPaymentTransaction classe estará no estado Fail.

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

O FailedTransaction método detecta se o erro foi devido ao cancelamento do usuário, conforme mostrado aqui:

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

Mesmo que uma transação falhe, o FinishTransaction método deve ser chamado para remover a transação da fila de pagamento:

SKPaymentQueue.DefaultQueue.FinishTransaction(transaction);

O código de exemplo, em seguida, envia uma notificação para que o ViewController pode exibir uma mensagem. Os aplicativos não devem mostrar uma mensagem adicional se o usuário cancelou a transação. Outros códigos de erro que podem ocorrer incluem:

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.

Restrições de manuseio

O recurso Restrições gerais > de configurações > do iOS permite que os usuários bloqueiem determinados recursos de seus dispositivos.

Você pode consultar se o usuário tem permissão para fazer compras no aplicativo por meio do SKPaymentQueue.CanMakePayments método. Se isso retornar false o usuário não poderá acessar a compra no aplicativo. O StoreKit exibirá automaticamente uma mensagem de erro para o usuário se uma compra for tentada. Ao verificar esse valor, seu aplicativo pode ocultar os botões de compra ou executar alguma outra ação para ajudar o usuário.

No arquivo, o InAppPurchaseManager.csCanMakePayments método encapsula a função StoreKit da seguinte maneira:

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

Para testar esse método, use o recurso Restrições do iOS para desabilitar as compras no aplicativo:

Use o recurso Restrições do iOS para desativar as compras no aplicativo

Este código de exemplo de reage ao CanMakePayments retorno false exibindo texto Desabilitado ConsumableViewController da AppStore nos botões desabilitados.

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

O aplicativo se parece com isso quando o recurso Compras no aplicativo é restrito – os botões de compra estão desativados.

O aplicativo tem esta aparência: quando o recurso Compras no aplicativo é restrito, os botões de compra são desativados

As informações do produto ainda podem ser solicitadas quando CanMakePayments são falsas, para que o aplicativo ainda possa recuperar e exibir preços. Isso significa que se removermos o CanMakePayments cheque do código, os botões de compra ainda estarão ativos, no entanto, quando uma compra for tentada, o usuário verá uma mensagem de que as compras no aplicativo não são permitidas (gerada pelo StoreKit quando a fila de pagamento é acessada):

Compras no aplicativo não são permitidas

Os aplicativos do mundo real podem adotar uma abordagem diferente para lidar com a restrição, como ocultar completamente os botões e talvez oferecer uma mensagem mais detalhada do que o alerta que o StoreKit mostra automaticamente.