Приобретение потребляемых продуктов в Xamarin.iOS
Потребляемые продукты являются самыми простыми для реализации, так как не требуется "восстановление". Они полезны для продуктов, таких как валюта в игре или однопользовательская функциональность. Пользователи могут повторно приобрести потребляемые продукты снова и снова.
Встроенная доставка продуктов
Пример кода, сопровождающий этот документ, демонстрирует встроенные продукты— идентификаторы продуктов жестко закодированы в приложение, так как они тесно связаны с кодом, который "разблокирует" функцию после оплаты. Процесс приобретения можно визуализировать следующим образом:
Базовый рабочий процесс:
Приложение добавляет в
SKPayment
очередь. Если требуется, пользователь получит запрос на получение идентификатора Apple ID и попросите подтвердить оплату.StoreKit отправляет запрос серверу для обработки.
По завершении транзакции сервер отвечает с получением транзакции.
Подкласс
SKPaymentTransactionObserver
получает квитанцию и обрабатывает его.Приложение включает продукт (путем обновления
NSUserDefaults
или другого механизма), а затем вызывает StoreKitFinishTransaction
.
Существует другой тип рабочего процесса — доставленные сервером продукты , которые рассматриваются далее в документе (см. раздел "Проверка квитанций и доставленные сервером продукты").
Пример потребляемых продуктов
В примере содержится проект с именем "Потребляемые ресурсы ", реализующий базовую "встроенную валюту" (называется "кредиты обезьяны"). В примере показано, как реализовать два продукта покупки в приложении, чтобы позволить пользователю покупать столько "обезьяньи кредиты", сколько они хотят - в реальном приложении также будет какой-то способ тратить их!
Приложение отображается на этих снимках экрана: каждая покупка добавляет дополнительные "кредиты обезьяны" на баланс пользователя:
Взаимодействие между пользовательскими классами, StoreKit и App Store выглядит следующим образом:
Методы ViewController
Помимо свойств и методов, необходимых для получения сведений о продукте, контроллер представления требует дополнительных наблюдателей уведомлений для прослушивания уведомлений, связанных с покупкой. Это только NSObjects
то, что будет зарегистрировано и удалено ViewWillAppear
ViewWillDisappear
соответственно.
NSObject succeededObserver, failedObserver;
Конструктор также создаст подкласс (InAppPurchaseManager
), который, в свою очередь, создает SKProductsRequestDelegate
и регистрирует SKPaymentTransactionObserver
( CustomPaymentObserver
).
Первая часть обработки транзакции покупки в приложении заключается в обработке нажатия кнопки, когда пользователь хочет приобрести что-то, как показано в следующем коде из примера приложения:
buy5Button.TouchUpInside += (sender, e) => {
iap.PurchaseProduct (Buy5ProductId);
};
buy10Button.TouchUpInside += (sender, e) => {
iap.PurchaseProduct (Buy10ProductId);
};
Вторая часть пользовательского интерфейса обрабатывает уведомление об успешном выполнении транзакции, в этом случае обновив отображаемый баланс:
succeededObserver = NSNotificationCenter.DefaultCenter.AddObserver (InAppPurchaseManager.InAppPurchaseManagerTransactionSucceededNotification,
(notification) => {
balanceLabel.Text = CreditManager.Balance() + " monkey credits";
});
Последняя часть пользовательского интерфейса отображает сообщение, если транзакция отменена по какой-то причине. В примере кода сообщение просто записывается в окно вывода:
failedObserver = NSNotificationCenter.DefaultCenter.AddObserver (InAppPurchaseManager.InAppPurchaseManagerTransactionFailedNotification,
(notification) => {
Console.WriteLine ("Transaction Failed");
});
Помимо этих методов на контроллере представления, для транзакции покупки потребляемого продукта также требуется код для SKProductsRequestDelegate
контроллера представления.SKPaymentTransactionObserver
Методы InAppPurchaseManager
Пример кода реализует ряд связанных с покупкой методов класса InAppPurchaseManager, включая PurchaseProduct
метод, который создает SKPayment
экземпляр и добавляет его в очередь для обработки:
public void PurchaseProduct(string appStoreProductId)
{
SKPayment payment = SKPayment.PaymentWithProduct (appStoreProductId);
SKPaymentQueue.DefaultQueue.AddPayment (payment);
}
Добавление платежа в очередь является асинхронной операцией. Приложение восстанавливает контроль, пока StoreKit обрабатывает транзакцию и отправляет его на серверы Apple. На этом этапе iOS убедится, что пользователь вошел в App Store и предложит ей идентификатор Apple ID и пароль, если это необходимо.
Если пользователь успешно проходит проверку подлинности в App Store и соглашается с транзакцией, SKPaymentTransactionObserver
он получит ответ StoreKit и вызовет следующий метод, чтобы выполнить транзакцию и завершить ее.
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);
}
Последний шаг — убедиться, что вы уведомите StoreKit о успешном выполнении транзакции, вызвав: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);
}
}
}
После доставки продукта необходимо вызвать, SKPaymentQueue.DefaultQueue.FinishTransaction
чтобы удалить транзакцию из очереди оплаты.
Методы SKPaymentTransactionObserver (CustomPaymentObserver)
StoreKit вызывает UpdatedTransactions
метод при получении ответа от серверов Apple и передает массив SKPaymentTransaction
объектов для проверки кода. Метод циклит каждую транзакцию и выполняет другую функцию в зависимости от состояния транзакции (как показано здесь):
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;
}
}
}
Метод был описан ранее в этом разделе. Он CompleteTransaction
сохраняет сведения NSUserDefaults
о покупке, чтобы завершить транзакцию с StoreKit и, наконец, уведомляет пользовательский интерфейс об обновлении.
Приобретение нескольких продуктов
Если в приложении имеет смысл приобрести несколько продуктов, используйте SKMutablePayment
класс и задайте поле "Количество":
public void PurchaseProduct(string appStoreProductId)
{
SKMutablePayment payment = SKMutablePayment.PaymentWithProduct (appStoreProductId);
payment.Quantity = 4; // hardcoded as an example
SKPaymentQueue.DefaultQueue.AddPayment (payment);
}
Код, обрабатывая завершенную транзакцию, также должен запрашивать свойство Quantity, чтобы правильно выполнить покупку:
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);
}
Когда пользователь приобретает несколько объемов, оповещение о подтверждении StoreKit будет отражать количество, цену единицы и общую цену, которую они будут взимать, как показано на следующем снимке экрана:
Обработка сетевых сбоев
Для покупок в приложении требуется рабочее сетевое подключение для StoreKit для взаимодействия с серверами Apple. Если сетевое подключение недоступно, приобретение в приложении будет недоступно.
Запросы на продукты
Если сеть недоступна при выполнении SKProductRequest
действия, RequestFailed
вызывается метод подкласса SKProductsRequestDelegate
( InAppPurchaseManager
), как показано ниже:
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 прослушивает уведомление и отображает сообщение в кнопках покупки:
requestObserver = NSNotificationCenter.DefaultCenter.AddObserver (InAppPurchaseManager.InAppPurchaseManagerRequestFailedNotification,
(notification) => {
Console.WriteLine ("Request Failed");
buy5Button.SetTitle ("Network down?", UIControlState.Disabled);
buy10Button.SetTitle ("Network down?", UIControlState.Disabled);
});
Так как сетевое подключение может быть временным на мобильных устройствах, приложениям может потребоваться отслеживать состояние сети с помощью платформы SystemConfiguration и повторно пытаться при наличии сетевого подключения. Обратитесь к Apple или к тому, что использует его.
Транзакции покупки
По возможности очередь оплаты StoreKit будет хранить и пересылать запросы на покупку, поэтому влияние сетевого сбоя зависит от того, когда сеть завершилась сбоем во время процесса покупки.
Если во время транзакции возникает ошибка, подкласс (CustomPaymentObserver
) будет вызывать UpdatedTransactions
метод, SKPaymentTransactionObserver
а SKPaymentTransaction
класс будет находиться в состоянии 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;
}
}
}
Метод FailedTransaction
определяет, была ли ошибка вызвана отменой пользователем, как показано ниже:
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);
}
Даже если транзакция завершается ошибкой, FinishTransaction
метод должен вызываться для удаления транзакции из очереди платежей:
SKPaymentQueue.DefaultQueue.FinishTransaction(transaction);
Затем пример кода отправляет уведомление, чтобы ViewController смог отобразить сообщение. Приложения не должны отображать дополнительное сообщение, если пользователь отменил транзакцию. Другие коды ошибок, которые могут возникнуть, включают:
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.
Обработка ограничений
Функция Параметры > общих > ограничений iOS позволяет пользователям блокировать определенные функции своего устройства.
Вы можете запросить, разрешено ли пользователю совершать покупки в приложении с помощью SKPaymentQueue.CanMakePayments
метода. Если это возвращает значение false, пользователь не может получить доступ к приобретению в приложении. StoreKit автоматически отобразит пользователю сообщение об ошибке, если покупка предпринята. По проверка это значение приложение может вместо этого скрыть кнопки покупки или предпринять другие действия, чтобы помочь пользователю.
InAppPurchaseManager.cs
В файле CanMakePayments
метод упаковывает функцию StoreKit следующим образом:
public bool CanMakePayments()
{
return SKPaymentQueue.CanMakePayments;
}
Чтобы протестировать этот метод, используйте функцию ограничений iOS, чтобы отключить покупки в приложении:
В этом примере кода от ConsumableViewController
реагирования на CanMakePayments
возврат false отображается отключенный текст AppStore на отключенных кнопках.
// 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);
}
Приложение выглядит следующим образом, когда функция "Покупки в приложении" ограничена — кнопки покупки отключаются.
Сведения о продукте по-прежнему могут запрашиваться, если CanMakePayments
значение равно false, поэтому приложение по-прежнему может получать и отображать цены. Это означает, что если мы удалили CanMakePayments
проверка из кода, кнопки покупки по-прежнему будут активными, однако при попытке покупки пользователь увидит сообщение о том, что покупки в приложении не разрешены (создается StoreKit при доступе к очереди оплаты):
В реальных приложениях может потребоваться другой подход к обработке ограничений, например скрытие кнопок в целом и, возможно, предложение более подробного сообщения, чем оповещение о том, что StoreKit отображается автоматически.