Обзор StoreKit и получение сведений о продукте в Xamarin.iOS
Пользовательский интерфейс для покупки в приложении показан на снимках экрана ниже. Перед выполнением любой транзакции приложение должно получить цену и описание продукта для отображения. Затем, когда пользователь нажимает кнопку "Купить", приложение отправляет запрос в StoreKit, который управляет диалоговым окном подтверждения и именем входа Apple ID. Если транзакция завершается успешно, StoreKit уведомляет код приложения, который должен хранить результат транзакции и предоставить пользователю доступ к своей покупке.
Классы
Для реализации покупок в приложении требуются следующие классы из платформы StoreKit:
SKProductsRequest — запрос на StoreKit для утвержденных продуктов для продажи (App Store). Можно настроить с помощью нескольких идентификаторов продукта.
- SKProductsRequestDelegate — объявляет методы для обработки запросов и ответов на продукты.
- SKProductsResponse — отправлено делегату из StoreKit (App Store). Содержит skProducts, соответствующие идентификаторам продукта, отправленным с запросом.
- SKProduct — продукт, полученный из StoreKit (настроенный в iTunes Подключение). Содержит сведения о продукте, например идентификатор продукта, название, описание и цена.
- SKPayment — создан с идентификатором продукта и добавлен в очередь оплаты для выполнения покупки.
- SKPaymentQueue — запросы на оплату в очереди, отправляемые в Apple. Уведомления активируются в результате обработки каждого платежа.
- SKPaymentTransaction — представляет завершенную транзакцию (запрос на покупку, обработанный App Store и отправленный обратно в приложение через StoreKit). Транзакция может быть приобретена, восстановлена или не выполнена.
- SKPaymentTransactionObserver — настраиваемый подкласс, который отвечает на события, созданные очередью оплаты StoreKit.
- Операции StoreKit являются асинхронными — после запуска SKProductRequest или skPayment добавляется в очередь, элемент управления возвращается в код. StoreKit будет вызывать методы на подклассе SKProductsRequestDelegate или SKPaymentTransactionObserver при получении данных с серверов Apple.
На следующей схеме показаны связи между различными классами StoreKit (абстрактные классы должны быть реализованы в приложении):
Эти классы подробно описаны далее в этом документе.
Тестирование
Большинство операций StoreKit требуют реального устройства для тестирования. Получение сведений о продукте (т. е. цена и описание) будет работать в симуляторе, но операции покупки и восстановления возвращают ошибку (например, FailedTransaction Code=5002 Произошла неизвестная ошибка).
Примечание. StoreKit не работает в симуляторе iOS. При запуске приложения в симуляторе iOS StoreKit записывает предупреждение, если приложение пытается получить очередь оплаты. Тестирование хранилища должно выполняться на фактических устройствах.
Важно. Не войдите с помощью тестовой учетной записи в приложении Параметры. Вы можете использовать приложение Параметры для выхода из любой существующей учетной записи Apple ID, а затем необходимо дождаться запроса в последовательности покупки в приложении для входа с помощью тестового идентификатора Apple ID.
Если вы пытаетесь войти в реальное хранилище с помощью тестовой учетной записи, она будет автоматически преобразована в реальный идентификатор Apple ID. Эта учетная запись больше не будет использоваться для тестирования.
Чтобы проверить код StoreKit, необходимо выйти из обычной тестовой учетной записи iTunes и войти с помощью специальной тестовой учетной записи (созданной в iTunes Подключение), связанной с тест-хранилищем. Чтобы выйти из текущей учетной записи, посетите Параметры > iTunes и App Store, как показано ниже.
затем выполните вход с помощью тестовой учетной записи при запросе StoreKit в приложении:
Чтобы создать тестовых пользователей в iTunes Подключение щелкните "Пользователи и роли" на главной странице.
Выбор тестировщиков песочницы
Отображается список существующих пользователей. Вы можете добавить нового пользователя или удалить существующую запись. Портал не позволяет просматривать или изменять существующих тестовых пользователей, поэтому рекомендуется сохранить хорошую запись каждого тестового пользователя, созданного (особенно пароля). После удаления тестового пользователя адрес электронной почты нельзя повторно использовать для другой тестовой учетной записи.
Новые тестовые пользователи имеют похожие атрибуты на реальный идентификатор Apple ID (например, имя, пароль, секретный вопрос и ответ). Сохраните запись всех сведений, введенных здесь. Поле Select iTunes Store определяет, какая валюта и язык покупки в приложении будут использоваться при входе в систему в качестве этого пользователя.
Получение сведений о продукте
Первым шагом в продаже продукта покупки в приложении является его отображение: получение текущей цены и описания из App Store для отображения.
Независимо от типа продуктов, которые продает приложение (потребляемые, неиспотребляемые или тип подписки), процесс получения сведений о продукте для отображения совпадает. Код InAppPurchaseSample, сопровождающий эту статью, содержит проект с именем Consumables , демонстрирующий получение рабочей информации для отображения. В примере показано:
- Создайте реализацию
SKProductsRequestDelegate
и реализуйте абстрактныйReceivedResponse
метод. Пример кода вызывает этотInAppPurchaseManager
класс. - Проверьте, разрешены ли платежи (с помощью
SKPaymentQueue.CanMakePayments
). - Создайте
SKProductsRequest
экземпляр с идентификаторами продукта, определенными в iTunes Подключение. Это делается в методе примераInAppPurchaseManager.RequestProductData
. - Вызов метода Start в объекте
SKProductsRequest
. Это активирует асинхронный вызов серверов App Store. Делегат (InAppPurchaseManager
) будет вызываться с результатами. - Метод делегата
InAppPurchaseManager
ReceivedResponse
обновляет пользовательский интерфейс с данными, возвращаемыми из App Store (цены и описания продуктов, или сообщения о недопустимых продуктах).
Общее взаимодействие выглядит следующим образом ( StoreKit встроенный в iOS, и App Store представляет серверы Apple):
Пример отображения сведений о продукте
Пример кода для использования показывает, как можно получить сведения о продукте. На главном экране примера отображаются сведения о двух продуктах, полученных из App Store:
Пример кода для получения и отображения сведений о продукте подробно описан ниже.
Методы ViewController
Класс ConsumableViewController
будет управлять отображением цен для двух продуктов, идентификаторы продуктов которых жестко закодированы в классе.
public static string Buy5ProductId = "com.xamarin.storekit.testing.consume5credits",
Buy10ProductId = "com.xamarin.storekit.testing.consume10credits";
List<string> products;
InAppPurchaseManager iap;
public ConsumableViewController () : base()
{
// two products for sale on this page
products = new List<string>() {Buy5ProductId, Buy10ProductId};
iap = new InAppPurchaseManager();
}
На уровне класса также должен быть объявлен NSObject, который будет использоваться для настройки NSNotificationCenter
наблюдателя:
NSObject priceObserver;
В методе ViewWillAppear наблюдатель создается и назначается с помощью центра уведомлений по умолчанию:
priceObserver = NSNotificationCenter.DefaultCenter.AddObserver (
InAppPurchaseManager.InAppPurchaseManagerProductsFetchedNotification,
(notification) => {
// display code goes here, to handle the response from the App Store
}
В конце ViewWillAppear
метода вызовите RequestProductData
метод для запуска запроса StoreKit. После выполнения этого запроса StoreKit асинхронно обращается к серверам Apple, чтобы получить информацию и отправить ее обратно в приложение. Это достигается подклассом SKProductsRequestDelegate
( InAppPurchaseManager
), описанным в следующем разделе.
iap.RequestProductData(products);
Код для отображения цены и описания просто извлекает сведения из SKProduct и назначает его элементам управления UIKit (обратите внимание, что мы отображаем LocalizedTitle
и LocalizedDescription
— StoreKit автоматически разрешает правильный текст и цены на основе параметров учетной записи пользователя). Следующий код относится к созданному выше уведомлению:
priceObserver = NSNotificationCenter.DefaultCenter.AddObserver (
InAppPurchaseManager.InAppPurchaseManagerProductsFetchedNotification,
(notification) => {
// display code goes here, to handle the response from the App Store
var info = notification.UserInfo;
if (info.ContainsKey(NSBuy5ProductId)) {
var product = (SKProduct) info.ObjectForKey(NSBuy5ProductId);
buy5Button.Enabled = true;
buy5Title.Text = product.LocalizedTitle;
buy5Description.Text = product.LocalizedDescription;
buy5Button.SetTitle("Buy " + product.Price, UIControlState.Normal); // price display should be localized
}
}
Наконец, метод должен убедиться, что ViewWillDisappear
наблюдатель удален:
NSNotificationCenter.DefaultCenter.RemoveObserver (priceObserver);
Методы SKProductRequestDelegate (InAppPurchaseManager)
Метод RequestProductData
вызывается, когда приложение хочет получить цены на продукты и другие сведения. Он анализирует коллекцию идентификаторов продукта в правильный тип данных, а затем создает SKProductsRequest
с этой информацией. Вызов метода Start приводит к тому, что сетевой запрос будет выполнен на серверах Apple. Запрос будет выполняться асинхронно и вызывать ReceivedResponse
метод делегата после успешного завершения.
public void RequestProductData (List<string> productIds)
{
var array = new NSString[productIds.Count];
for (var i = 0; i < productIds.Count; i++) {
array[i] = new NSString(productIds[i]);
}
NSSet productIdentifiers = NSSet.MakeNSObjectSet<NSString>(array);
productsRequest = new SKProductsRequest(productIdentifiers);
productsRequest.Delegate = this; // for SKProductsRequestDelegate.ReceivedResponse
productsRequest.Start();
}
iOS автоматически перенаправит запрос на версию песочницы или рабочей версии App Store в зависимости от того, с каким профилем подготовки приложение работает, поэтому при разработке или тестировании приложения запрос будет иметь доступ ко всем продуктам, настроенным в iTunes Подключение (даже тех, которые еще не были отправлены или утверждены Apple). Когда приложение находится в рабочей среде, запросы StoreKit возвращают только сведения для утвержденных продуктов.
Переопределенный ReceivedResponse
метод вызывается после того, как серверы Apple ответили данными. Так как это вызывается в фоновом режиме, код должен проанализировать допустимые данные и использовать уведомление для отправки сведений о продукте в любые представленияController, которые прослушиваются для этого уведомления. Код для сбора допустимых сведений о продукте и отправки уведомления показан ниже:
public override void ReceivedResponse (SKProductsRequest request, SKProductsResponse response)
{
SKProduct[] products = response.Products;
NSDictionary userInfo = null;
if (products.Length > 0) {
NSObject[] productIdsArray = new NSObject[response.Products.Length];
NSObject[] productsArray = new NSObject[response.Products.Length];
for (int i = 0; i < response.Products.Length; i++) {
productIdsArray[i] = new NSString(response.Products[i].ProductIdentifier);
productsArray[i] = response.Products[i];
}
userInfo = NSDictionary.FromObjectsAndKeys (productsArray, productIdsArray);
}
NSNotificationCenter.DefaultCenter.PostNotificationName (InAppPurchaseManagerProductsFetchedNotification, this, userInfo);
}
Хотя на RequestFailed
схеме не отображается, метод также следует переопределить, чтобы предоставить пользователю некоторые отзывы, если серверы App Store недоступны (или другая ошибка). Пример кода просто записывается в консоль, но реальное приложение может выбрать запрос к error.Code
свойству и реализовать пользовательское поведение (например, оповещение пользователю).
public override void RequestFailed (SKRequest request, NSError error)
{
Console.WriteLine (" ** InAppPurchaseManager RequestFailed() " + error.LocalizedDescription);
}
На этом снимке экрана показан пример приложения сразу после загрузки (если сведения о продукте недоступны):
Недопустимые продукты
Также SKProductsRequest
может возвращать список недопустимых идентификаторов продуктов. Недопустимые продукты обычно возвращаются из-за одной из следующих:
Идентификатор продукта неправильно указан. Принимаются только допустимые идентификаторы продукта.
Продукт не был утвержден . Во время тестирования все продукты, которые очищаются для продажи, должны быть возвращены SKProductsRequest
; однако в рабочей среде возвращаются только утвержденные продукты.
Идентификатор приложения не является явным — идентификаторы приложений карта (с звездочкой) не допускают покупку в приложении.
Неправильный профиль подготовки. Если вы вносите изменения в конфигурацию приложения на портале подготовки (например, включение покупок в приложении), не забудьте повторно создать и использовать правильный профиль подготовки при создании приложения.
Контракт с платными приложениями iOS не существует . Функции StoreKit не будут работать вообще, если нет действительного контракта для учетной записи разработчика Apple.
Двоичный файл находится в состоянии "Отклонено" — если в состоянии "Отклонено " (либо командой App Store, либо разработчиком), функции StoreKit не будут работать.
Метод ReceivedResponse
в примере кода выводит недопустимые продукты в консоль:
public override void ReceivedResponse (SKProductsRequest request, SKProductsResponse response)
{
// code removed for clarity
foreach (string invalidProductId in response.InvalidProducts) {
Console.WriteLine("Invalid product id: " + invalidProductId );
}
}
Отображение локализованных цен
Ценовые категории указывают определенную цену для каждого продукта во всех международных магазинах приложений. Чтобы убедиться, что цены отображаются правильно для каждой валюты, используйте следующий метод расширения (определенный в SKProductExtension.cs
) вместо свойства Price каждого SKProduct
:
public static class SKProductExtension {
public static string LocalizedPrice (this SKProduct product)
{
var formatter = new NSNumberFormatter ();
formatter.FormatterBehavior = NSNumberFormatterBehavior.Version_10_4;
formatter.NumberStyle = NSNumberFormatterStyle.Currency;
formatter.Locale = product.PriceLocale;
var formattedString = formatter.StringFromNumber(product.Price);
return formattedString;
}
}
Код, задающий заголовок кнопки, использует метод расширения следующим образом:
string Buy = "Buy {0}"; // or a localizable string
buy5Button.SetTitle(String.Format(Buy, product.LocalizedPrice()), UIControlState.Normal);
Использование двух разных тестовых учетных записей iTunes (один для американского магазина и одного для японского магазина) приводит к следующим снимкам экрана:
Обратите внимание, что магазин влияет на язык, используемый для сведений о продукте и цене валюты, а язык устройства влияет на метки и другое локализованное содержимое.
Помните, что для использования другой тестовой учетной записи магазина необходимо выйти из Параметры > iTunes и App Store и повторно запустить приложение для входа с другой учетной записью. Чтобы изменить язык устройства, перейдите на Параметры общий > международный > язык>.