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:
O fluxo de trabalho básico é:
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.O StoreKit envia a solicitação ao servidor para processamento.
Quando a transação é concluída, o servidor responde com um recibo de transação.
A
SKPaymentTransactionObserver
subclasse recebe o recibo e o processa.O aplicativo habilita o produto (por atualização
NSUserDefaults
ou algum outro mecanismo) e, em seguida, chama o StoreKit'sFinishTransaction
.
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:
As interações entre classes personalizadas, StoreKit e App Store têm esta aparência:
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:
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.cs
CanMakePayments
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:
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.
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):
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.