Поставка продуктов

В заключительной части процесса закупки Ваше приложение ожидает App Store для обработки платежного требования, хранит информацию о закупке для будущих запусков, загружает купленное содержание, и затем отмечает транзакцию, как закончено, как показано на рисунке 4-1.

  Этапы рисунка 4-1 процесса закупки — поставка продуктов

Ожидание App Store для обработки транзакций

Очередь транзакции играет центральную роль в разрешении Вашему приложению связаться с App Store через платформу Стора Кита. Вы добавляете работу к очереди, что App Store должен действовать на, такие как платежное требование, которое должно быть обработано. Когда изменения состояния транзакции — например, наблюдателя очереди транзакции приложения, когда платежное требование успешно выполняется — Стор Кит вызывают именно Ваше решение класс действует как наблюдатель. В очень небольших приложениях Вы могли обработать всю логику Стора Кита в делегате приложения, включая наблюдение очереди транзакции. В большинстве приложений Вы создаете отдельный класс, обрабатывающий эту логику наблюдателя вместе с остальной частью логики хранилища Вашего приложения. Наблюдатель должен соответствовать SKPaymentTransactionObserver протокол.

Используя средние значения наблюдателя Ваше приложение постоянно не опрашивает состояние своих активных транзакций. В дополнение к использованию очереди транзакции для платежных требований Ваше приложение также использует очередь транзакции, чтобы загрузить размещенное Apple содержание и узнать, что были возобновлены подписки.

Зарегистрируйте наблюдателя очереди транзакции, когда Ваше приложение будет запущено, как показано в Перечислении 4-1. Удостоверьтесь, что наблюдатель готов обработать транзакцию в любое время, не сразу после того, как Вы добавляете транзакцию к очереди. Например, рассмотрите случай пользователя, покупающего что-то в Вашем приложении прямо перед входом в туннель. Ваше приложение не в состоянии поставить купленное содержание, потому что нет никакого сетевого соединения. В следующий раз, когда Ваше приложение запускается, Стор Кит вызывает Вашего наблюдателя очереди транзакции снова и поставляет купленное содержание в то время. Точно так же, если Вашему приложению не удается отметить транзакцию, как закончено, Стор Кит вызывает наблюдателя каждый раз, когда Ваше приложение запускается, пока транзакция должным образом не закончена.

Перечисление 4-1  , Регистрирующее наблюдателя очереди транзакции

- (BOOL)application:(UIApplication *)application
 didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    /* ... */
 
    [[SKPaymentQueue defaultQueue] addTransactionObserver:observer];
}

Реализуйте paymentQueue:updatedTransactions: метод на Вашем наблюдателе очереди транзакции. Набор хранилища вызывает этот метод, когда состояние транзакции изменяется — например, когда было обработано платежное требование. Состояние транзакции говорит Вам, какое действие Ваше приложение должно выполнить, как показано в Таблице 4-1 и Перечислении 4-2. Транзакции в очереди могут изменить состояние в любом порядке. Ваше приложение должно быть готово работать над любой активной транзакцией в любое время.

  Состояния Table 4-1 Transaction и соответствующие действия

Состояние

Действие для взятия в приложении

SKPaymentTransactionStatePurchasing

Обновите свой UI, чтобы отразить происходящее состояние и ожидать, чтобы быть вызванными снова.

SKPaymentTransactionStateDeferred

Обновите свой UI, чтобы отразить задержанное состояние и ожидать, чтобы быть вызванными снова.

SKPaymentTransactionStateFailed

Используйте значение error свойство для представления сообщения пользователю. Для списка ошибочных констант посмотрите SKErrorDomain в ссылке констант StoreKit.

SKPaymentTransactionStatePurchased

Обеспечьте купленную функциональность.

SKPaymentTransactionStateRestored

Восстановите ранее купленную функциональность.

Перечисление 4-2  , Отвечающее на состояния транзакции

- (void)paymentQueue:(SKPaymentQueue *)queue
 updatedTransactions:(NSArray *)transactions
{
    for (SKPaymentTransaction *transaction in transactions) {
        switch (transaction.transactionState) {
            // Call the appropriate custom method for the transaction state.
            case SKPaymentTransactionStatePurchasing:
                [self showTransactionAsInProgress:transaction deferred:NO];
                break;
            case SKPaymentTransactionStateDeferred:
                [self showTransactionAsInProgress:transaction deferred:YES];
                break;
            case SKPaymentTransactionStateFailed:
                [self failedTransaction:transaction];
                break;
            case SKPaymentTransactionStatePurchased:
                [self completeTransaction:transaction];
                break;
            case SKPaymentTransactionStateRestored:
                [self restoreTransaction:transaction];
                break;
            default:
                // For debugging
                NSLog(@"Unexpected transaction state %@", @(transaction.transactionState));
                break;
        }
    }
}

Чтобы усовершенствовать Ваш пользовательский интерфейс при ожидании, наблюдатель очереди транзакции может реализовать дополнительные методы от SKPaymentTransactionObserver протокол следующим образом. paymentQueue:removedTransactions: когда транзакции удалены из очереди — в Вашей реализации этого метода, удаляют соответствующие элементы из UI Вашего приложения, метод вызывают. paymentQueueRestoreCompletedTransactionsFinished: или paymentQueue:restoreCompletedTransactionsFailedWithError: метод вызывают, когда Стор Кит заканчивает восстанавливать транзакции в зависимости от того, была ли ошибка. В Вашей реализации этих методов обновите UI своего приложения для отражения успеха или ошибки.

Сохранение закупки

После предоставления доступа к продукту Ваше приложение должно сделать персистентную запись из закупки. Ваше использование приложения, что персистентная запись на запуске, чтобы продолжать делать продукт доступным. Это также использует ту запись для восстановления покупок, как описано в Восстановлении Купленных продуктов. Стратегия персистентности Вашего приложения зависит тип продуктов, которые Вы продаете и версии iOS.

При использовании Пользовательской системы Значений по умолчанию или iCloud, Ваше приложение может сохранить значение, такое как число или булевская переменная или копия получения транзакции. В OS X пользователь может отредактировать Пользовательскую систему Значений по умолчанию с помощью defaults команда. Хранение получения требует большей прикладной логики, но препятствует тому, чтобы вмешалась персистентная запись.

При сохранении через iCloud обратите внимание на то, что персистентная запись приложения синхронизируется через устройства, но приложение ответственно за загрузку любого связанного содержания на других устройствах.

Сохранение Используя получение приложения

Получение приложения содержит запись покупок пользователя, криптографически подписанных Apple. Для получения дополнительной информации см. Руководство по программированию Проверки Получения.

Информация о потребляемых продуктах и подписках невозобновления добавляется к получению, когда за них платят, и остается в получении, пока Вы не заканчиваете транзакцию. После окончания транзакции эта информация удалена в следующий раз, когда получение обновляется — например, в следующий раз, когда пользователь совершает покупку.

Информация обо всех других видах покупок добавляется к получению, когда за них платят, и остается в получении неопределенно.

Сохранение Значения в Пользовательских Значениях по умолчанию или iCloud

Чтобы хранить информацию в Пользовательских Значениях по умолчанию или iCloud, установите значение для ключа.

#if USE_ICLOUD_STORAGE
NSUbiquitousKeyValueStore *storage = [NSUbiquitousKeyValueStore defaultStore];
#else
NSUserDefaults *storage = [NSUserDefaults standardUserDefaults];
#endif
 
[storage setBool:YES forKey:@"enable_rocket_car"];
[storage setObject:@15 forKey:@"highest_unlocked_level"];
 
[storage synchronize];

Сохранение Получения в Пользовательских Значениях по умолчанию или iCloud

Для хранения получения транзакции в Пользовательских Значениях по умолчанию или iCloud установите значение для ключа к данным того получения.

#if USE_ICLOUD_STORAGE
NSUbiquitousKeyValueStore *storage = [NSUbiquitousKeyValueStore defaultStore];
#else
NSUserDefaults *storage = [NSUserDefaults standardUserDefaults];
#endif
 
NSData *newReceipt = transaction.transactionReceipt;
NSArray *savedReceipts = [storage arrayForKey:@"receipts"];
if (!receipts) {
    // Storing the first receipt
    [storage setObject:@[newReceipt] forKey:@"receipts"];
} else {
    // Adding another receipt
    NSArray *updatedReceipts = [savedReceipts arrayByAddingObject:newReceipt];
    [storage setObject:updatedReceipts forKey:@"receipts"];
}
 
[storage synchronize];

Сохранение Используя собственный сервер

Отправьте копию получения к Вашему серверу вместе с некоторыми учетными данными или идентификатором, таким образом, можно отслеживать, которых получения принадлежат определенному пользователю. Например, позвольте пользователям идентифицировать себя для Вашего сервера с именем электронной почты или именем пользователя плюс пароль. Не используйте identifierForVendor свойство UIDevice— Вы не можете использовать его, чтобы идентифицировать и восстановить покупки, совершенные тем же пользователем на различном устройстве, потому что различные устройства имеют различные значения для этого свойства.

Разблокирование функциональности приложения

Если продукт добавляет функциональность приложения, установите булево значение, чтобы включить путь выполнения кода и обновить Ваш пользовательский интерфейс по мере необходимости. Для определения что функциональность разблокировать консультируйтесь с персистентной записью, которую сделало приложение, когда произошла транзакция. Ваше приложение должно обновить это булево значение каждый раз, когда закупка завершается и в запуске приложения.

Например, с помощью получения приложения, код мог бы быть похожим на следующее:

NSURL *receiptURL = [[NSBundle mainBundle] appStoreReceiptURL];
NSData *receiptData = [NSData dataWithContentsOfURL:receiptURL];
 
// Custom method to work with receipts
BOOL rocketCarEnabled = [self receipt:receiptData
        includesProductID:@"com.example.rocketCar"];

Или, с помощью Пользовательской системы Значений по умолчанию:

NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
BOOL rocketCarEnabled = [defaults boolForKey:@"enable_rocket_car"];

Тогда используйте ту информацию для включения надлежащих путей выполнения кода в приложении.

if (rocketCarEnabled) {
    // Use the rocket car.
} else {
    // Use the regular car.
}

Поставка связанного содержания

Если продукт связал содержание, Ваше приложение должно поставить то содержание пользователю. Например, закупки уровня в игре требуют поставки файлов, определяющих тот уровень, и закупки дополнительных инструментов в музыкальном приложении требуют, чтобы поставка звуковых файлов должна была позволить пользователю играть те инструменты.

Можно встроить то содержание в пакет приложения, или можно загрузить его по мере необходимости — каждый подход имеет свои преимущества и недостатки. Если Вы включаете слишком мало содержания в свой комплект приложений, пользователь должен ожидать даже маленьких покупок, которые будут загружены. При включении слишком много в комплект приложений начальная загрузка приложения берет слишком долго, и пространство потрачено впустую для пользователей, не покупающих соответствующие продукты. Кроме того, если Ваше приложение будет слишком большим, то пользователи не будут в состоянии загрузить его по сотовым сетям.

Встройте меньшие файлы (до нескольких мегабайтов) в Вашем приложении, особенно если Вы ожидаете, что большинство пользователей купит тот продукт. Содержание в Вашем комплекте приложений может быть сделано доступным сразу, когда пользователь покупает его. Однако, чтобы добавить или обновить содержание в Вашем комплекте приложений, необходимо представить обновленную версию приложения.

Загрузите большие файлы при необходимости. Разделение содержания от Вашего комплекта приложений сохраняет начальную загрузку Вашего приложения маленькой. Например, игра может включать первый уровень в свой комплект приложений и позволить пользователям загрузить остальную часть уровней, когда они куплены. Принятие Вашего приложения выбирает свой список идентификаторов продукта от Вашего сервера, и не трудно кодированное в комплекте приложений, Вы не должны повторно представлять свое приложение, чтобы добавить или обновить содержание, загружающееся Вашим приложением.

В iOS 6 и позже, большинство приложений должно использовать размещенное Apple содержание для загруженных файлов. Вы создаете размещенный Apple пакет содержания с помощью цели Содержания Закупки В приложении в XCode и представляете его Подключению iTunes. При хостинге содержания на серверах Apple, Вы не должны обеспечивать серверы — содержание Вашего приложения сохранено Apple с помощью той же инфраструктуры, поддерживающей другие крупномасштабные операции, такие как App Store. Даже если Ваше приложение не работает, Кроме того, размещенное Apple содержание автоматически загружает в фоновом режиме.

Вы могли бы принять решение разместить свое собственное содержание, если у Вас уже есть серверная инфраструктура, если необходимо поддерживать более старые версии iOS, или если Вы совместно используете свою серверную инфраструктуру через многократные платформы.

Загрузка локального содержания

Загрузите локальное содержание с помощью NSBundle класс, так же, как Вы загружаете другие ресурсы из своего комплекта приложений.

NSURL *url = [[NSBundle mainBundle] URLForResource:@"rocketCar"
                                     withExtension:@"plist"];
[self loadVehicleAtURL:url];

Загрузка размещенного содержания от сервера Apple

Когда пользователь покупает продукт, связавший размещенное Apple содержание, транзакция передала Вашему наблюдателю очереди транзакции, также включает экземпляр SKDownload это позволяет Вам загрузить связанное содержание.

Для загрузки содержания добавьте объекты загрузки от транзакции downloads свойство очереди транзакции путем вызова startDownloads: метод SKPaymentQueue. Если значение downloads свойство nil, нет никакого размещенного Apple содержания для той транзакции. В отличие от загрузки приложений, загружая содержание автоматически не требует соединения Wi-Fi для содержания, больше, чем определенный размер. Избегайте использования сотовых сетей для загрузки больших файлов без явного действия от пользователя.

Реализуйте paymentQueue:updatedDownloads: метод на наблюдателе очереди транзакции для реакции на изменения в состоянии загрузки — например, путем обновления прогресса UI. Если загрузка перестала работать, используйте информацию в error свойство для представления ошибки пользователю.

Гарантируйте, что Ваше приложение обрабатывает ошибки корректно. Например, если устройство исчерпывает дисковое пространство во время загрузки, дайте пользователю опцию отбросить частичную загрузку или возобновить загрузку позже, когда пространство становится доступным.

Обновите свой пользовательский интерфейс, в то время как содержание загружает использование значений progress и timeRemaining свойства. Можно использовать pauseDownloads:, resumeDownloads:, и cancelDownloads: методы SKPaymentQueue от Вашего UI для разрешения происходящему контролю за работой пользователей загружает. Используйте downloadState свойство, чтобы определить, завершилась ли загрузка. Не используйте progress или timeRemaining свойство объекта загрузки проверить его состояние — эти свойства для обновления Вашего UI.

В iOS Ваше приложение может управлять загруженными файлами. Файлы сохранены для Вас платформой Стора Кита в Caches каталог с резервным флагом сброшен. После того, как загрузка завершается, Ваше приложение ответственно за перемещение ее к надлежащему расположению. Для содержания, которое может быть удалено, если устройство исчерпывает дисковое пространство (и позже повторно загруженный Вашим приложением), оставьте файлы внутри Caches каталог. Иначе, переместите файлы в Documents папка и набор флаг для исключения их из пользовательских резервных копий.

Перечисление 4-3  , Исключая загруженное содержание от резервных копий

NSError *error;
BOOL success = [URL setResourceValue:[NSNumber numberWithBool:YES]
                              forKey:NSURLIsExcludedFromBackupKey
                               error:&error];
if (!success) { /* Handle error... */ }

В OS X загруженными файлами управляет система; Ваше приложение не может переместить или удалить их непосредственно. Для определения местоположения содержания после загрузки его используйте contentURL свойство объекта загрузки. Для определения местоположения файла на последующих запусках используйте contentURLForProductID: метод класса SKDownload. Для удаления файла используйте deleteContentForProductID: метод класса. Для получения информации о чтении идентификаторов продукта после получения Вашего приложения см. Руководство по программированию Проверки Получения.

Загрузка содержания от собственного сервера

Как со всеми другими взаимодействиями между Вашим приложением и Вашим сервером, подробными данными и механикой процесса загрузки содержания от Вашего собственного сервера Ваша ответственность. Коммуникация состоит из, как минимум, следующие шаги:

  1. Ваше приложение отправляет получение в Ваш сервер и запрашивает содержание.

  2. Ваш сервер проверяет получение, чтобы установить, что содержание было куплено, как описано в Руководстве по программированию Проверки Получения.

  3. Принятие получения допустимо, Ваш сервер реагирует на Ваше приложение с содержанием.

Гарантируйте, что Ваше приложение обрабатывает ошибки корректно. Например, если устройство исчерпывает дисковое пространство во время загрузки, дайте пользователю опцию отбросить частичную загрузку или возобновить загрузку позже, когда пространство становится доступным.

Рассмотрите импликации безопасности того, как Вы размещаете свое содержание и того, как Ваше приложение связывается с Вашим сервером. Для получения дополнительной информации см. Обзор безопасности.

Окончание транзакции

Окончание транзакции говорит Стору Киту о завершении всего необходимого для закупки. Незаконченные транзакции остаются в очереди, пока они не закончены, и наблюдателя очереди транзакции вызывают каждый раз, когда Ваше приложение запускается так, Ваше приложение может закончить транзакции. Ваше приложение должно закончить каждую транзакцию, независимо от того, успешно выполнилась ли транзакция или отказавший.

Завершите все следующие действия перед окончанием транзакции:

Для окончания транзакции вызовите finishTransaction: метод на очереди платежа.

SKPaymentTransaction *transaction = <# The current payment #>;
[[SKPaymentQueue defaultQueue] finishTransaction:transaction];

После окончания транзакции не принимайте меры на той транзакции или выполняйте любую работу для поставки продукта. Если какая-либо работа остается, Ваше приложение не готово закончить транзакцию все же.

Предложенные шаги тестирования

Протестируйте каждую часть своего кода, чтобы проверить реализацию его правильно.

Протестируйте платежное требование

Создайте экземпляр SKPayment использование допустимого идентификатора продукта, который Вы уже протестировали. Установите точку останова и проверьте платежное требование. Добавьте платежное требование к очереди транзакции и установите точку останова, чтобы подтвердить что paymentQueue:updatedTransactions: метод Вашего наблюдателя вызывают.

Во время тестирования нормально заканчивать транзакцию сразу, не обеспечивая содержание. Однако даже во время тестирования, не удаваясь закончить транзакцию может вызвать проблемы: незаконченные транзакции остаются в очереди неопределенно, которая могла вмешаться в более позднее тестирование.

Проверьте свой код наблюдателя

Рассмотрите реализацию наблюдателя транзакции SKPaymentTransactionObserver протокол. Проверьте, что это может обработать транзакции, даже если Вы в настоящее время не выводите на экран хранилище своего приложения UI и даже если Вы недавно не инициировали закупку.

Найдите вызов к addTransactionObserver: метод SKPaymentQueue в Вашем коде. Проверьте, что Ваше приложение вызывает этот метод в запуске приложения.

Протестируйте успешную транзакцию

Регистрируйтесь к App Store с учетной записью проверочного пользователя и совершите покупку в своем приложении. Установите точку останова в своей реализации наблюдателя очереди транзакции paymentQueue:updatedTransactions: метод, и проверяет транзакцию, чтобы проверить, что ее состояние SKPaymentTransactionStatePurchased.

Установите точку останова в точке в Вашем коде, сохраняющем закупку, и подтвердите, что этот код вызывают в ответ на успешную закупку. Проверьте Пользовательские Значения по умолчанию или хранилище значения ключа iCloud, и подтвердите, что была зарегистрирована корректная информация.

Протестируйте прерванную транзакцию

Установите точку останова в своем наблюдателе очереди транзакции paymentQueue:updatedTransactions: метод, таким образом, можно управлять, поставляет ли он продукт. Тогда совершите покупку, как обычно, в тестовой среде и используйте точку останова для временного игнорирования транзакции — например, путем возврата из метода сразу с помощью thread return команда в LLDB.

Завершите и повторно запустите свое приложение. Набор хранилища вызывает paymentQueue:updatedTransactions: метод снова вскоре после запуска; на сей раз позвольте своему приложению обычно отвечать. Проверьте, что Ваше приложение правильно поставляет продукт и завершает транзакцию.

Проверьте, что закончены транзакции

Найдите, где Ваше приложение вызывает finishTransaction: метод. Проверьте, что вся работа, связанная с транзакцией, была завершена, прежде чем метод вызывают и что метод вызывают для каждой транзакции, успешно выполнилось ли это или перестало работать.