Приобретение потребляемых продуктов в Xamarin.iOS

Потребляемые продукты являются самыми простыми для реализации, так как не требуется "восстановление". Они полезны для продуктов, таких как валюта в игре или однопользовательская функциональность. Пользователи могут повторно приобрести потребляемые продукты снова и снова.

Встроенная доставка продуктов

Пример кода, сопровождающий этот документ, демонстрирует встроенные продукты— идентификаторы продуктов жестко закодированы в приложение, так как они тесно связаны с кодом, который "разблокирует" функцию после оплаты. Процесс приобретения можно визуализировать следующим образом:

Визуализация процесса приобретения

Базовый рабочий процесс:

  1. Приложение добавляет в SKPayment очередь. Если требуется, пользователь получит запрос на получение идентификатора Apple ID и попросите подтвердить оплату.

  2. StoreKit отправляет запрос серверу для обработки.

  3. По завершении транзакции сервер отвечает с получением транзакции.

  4. Подкласс SKPaymentTransactionObserver получает квитанцию и обрабатывает его.

  5. Приложение включает продукт (путем обновления NSUserDefaults или другого механизма), а затем вызывает StoreKit FinishTransaction.

Существует другой тип рабочего процесса — доставленные сервером продукты , которые рассматриваются далее в документе (см. раздел "Проверка квитанций и доставленные сервером продукты").

Пример потребляемых продуктов

В примере содержится проект с именем "Потребляемые ресурсы ", реализующий базовую "встроенную валюту" (называется "кредиты обезьяны"). В примере показано, как реализовать два продукта покупки в приложении, чтобы позволить пользователю покупать столько "обезьяньи кредиты", сколько они хотят - в реальном приложении также будет какой-то способ тратить их!

Приложение отображается на этих снимках экрана: каждая покупка добавляет дополнительные "кредиты обезьяны" на баланс пользователя:

Каждая покупка добавляет больше кредитов обезьяны в баланс пользователей

Взаимодействие между пользовательскими классами, StoreKit и App Store выглядит следующим образом:

Взаимодействие между пользовательскими классами, StoreKit и App Store

Методы ViewController

Помимо свойств и методов, необходимых для получения сведений о продукте, контроллер представления требует дополнительных наблюдателей уведомлений для прослушивания уведомлений, связанных с покупкой. Это только NSObjects то, что будет зарегистрировано и удалено ViewWillAppearViewWillDisappear соответственно.

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, чтобы отключить покупки в приложении:

Использование функции ограничений 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 отображается автоматически.