iPhone разработка: Интегрируем In-App Purchases

    In-App Purchases — это простой и удобный механизм для организации продаж своих приложений или дополнительных фич непосредственно из своего приложения. In-App Purchases легко встраивается и открывает для Вас новый канал продаж. Взаимодействие с App Store осуществляется с помощью StoreKit.framework, который поставляется вместе с SDK, начиная с версии 3.0.


    Общая информация


    In-App Purchases бывает трех типов:
    • Consumables
    • Non-Consumables
    • Subscriptions

    Consumable — потребляемый тип. Purchase такого типа может покупаться несколько раз. Например, в игре Eliminate игрок покупает себе энергию, которая со временем растрачивается и приходится покупать ее заново, либо ждать три часа, пока энергия востановится.

    Non-Consumable — непотребляемый тип. Purchase покупается только один раз. Его обычно используют для разблокировки новых тем, дополнительных уровней и т.п.

    Subscription — подписка на что-либо. Например, Вы можете написать iPhone-приложение для Web-сервиса, в котором есть Premium аккаунт, открывающий дополнительные возможности. С помощью Subscription Вы сможете аккаунт активировать, скажем, на месяц или на год.

    Реализовать In-App Purchases можно с помощью двух моделей:
    • Встроенная модель
    • Серверная модель

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

    Серверная модель является более гибкой. В модели принимают участие три сущности: iPhone-приложение, сервер Apple и наш сервер. Все новые фичи хранятся на нашем сервере, поэтому нет необходимости обновлять приложение при добавлении новых фич или продуктов. Работает модель следующим образом:
    1. iPhone-приложение запрашивает список продуктов у своего сервера,
    2. iPhone-приложение отображает новые продукты пользователю,
    3. Пользователь покупает (или не покупает :) )
    4. iPhone-приложение запрашивает покупку у сервера Apple через StoreKit,
    5. StoreKit возвращает ответ,
    6. iPhone-приложение отсылает ответ на свой сервер
    7. Еще раз проверяется ответ (обязательно проверяется, что ответ пришел от Apple),
    8. После этого iPhone-приложение скачивает со своего сервера новый продукт.

    В данном посте рассматривается встроенная модель.

    Реализация


    Работу встроенной модели In-App Purchases я продемонстрирую на тестовом примере AppPurchasesExample. Это небольшое iPhone-приложение с 3 окнами. Первое (оно же главное) окно будет доступно пользователю по-умолчанию. Это окно будет содержать информацию о двух других окнах, разблокировать которые можно только за деньги.

    Шаг 1. Создание App ID


    Заходим на iPhone Developer Program Portal и открываем вкладку App IDs:



    В правом верхнем углу экрана нажимаем New App ID. Затем вводим информацию о приложении. Для своего примера я заполнил форму так:



    Для создания Bundle Identifier Apple рекомендует использовать нотацию Reverse DNS, что гарантирует уникальность Вашего Identifier и избавит Вас от дальнейших проблем при публикации приложения. Очень Важно не использовать символ '*' в Bundle Identifier. Если ввести 'com.wordpress.indiedevelop.*', то In-App Purchases работать не будет.

    Далее необходимо включить In App Purchases для App ID. В списке App IDs, напротив нужного Bundle Identifier в графе Action следует нажать Configure. Появится форма Configure App ID, на которой необходимо включить checkBox 'Enable In App Purchase'.



    Шаг 2. Создание Development профиля


    1. В левом столбце нужно нажать на 'Provisioning' и перейти на вкладку 'Development'.
    2. Нажать 'New Profile' и заполнить всю необходимую информацию. Поле Profile Name может быть любое (я записал 'InAppPurchasesExample Dev').
    3. В появившемся списке созданный профиль имеет статус pending. Нужно обновить страницу, либо перейти на другую вкладку и обратно, тогда профиль станет доступен для скачивания.
    4. Профиль можно скачивать и устанавливать в xCode. Для установки достаточно просто нажать на профиль двойным кликом, либо перетащить профайл на иконку xCode.

    Если профиль установился правильно, тогда в xCode откроется Organizer и вы увидите примерно следующее:



    Шаг 3. Создание приложения в iTunes Connect


    Для того, чтобы протестировать наше In-App Purchase приложение, его нужно обязательно создать в iTunes Connect. Чтобы это сделать:
    1. Необходимо перейти в iTunes Connect и нажать 'Manage Your Applications->Add New Application'.
    2. На вопрос 'Does your product contain encryption?' ответить отрицательно.
    3. Заполнить форму, где нужно указать имя приложения, описание, номер версии, категорию и т.д. Все достаточно тривиально. Затруднения может вызвать разве что поле 'SKU Number'. Это поле должно быть уникальным, я ввел в нем 'IAPEX' (сокращение от In-App Purchases Example).
    4. На форме 'Upload' необходимо выставить флаг 'Upload application binary later'. Все остальные параметры и формы для тестого примера никакого значения не имеют.


    Шаг 4. Создание In-App Purchases в iTunes Connect


    1. В iTunes Connect необходимо нажать 'Manage Your In App Purchases->Create New' и выбрать необходимое приложение.
    2. Выбрать Bundle ID и заполнить инфрормацию о Purchase (тип, название, цену и т.д.) Также нужно ввести 'Product ID', который может быть произвольным, но я советую использовать Reverse DNS. Лучше всего Product ID формировать из Bundle ID вашего приложения и имени фичи. Для моего примера это выглядит так:



    Для тестового приложения я создал два In-App продукта с Product Id 'com.wordpress.indiedevelop.InAppPurchasesExample.f1' и 'com.wordpress.indiedevelop.InAppPurchasesExample.f2'. Обе фичи с типом Non-Consumables.

    Шаг 5. Создание тестового пользователя


    Для тестирования In-App Purchases необходимо создать хотя бы одного тестового пользователя. Делается это просто:
    1. В iTunes Connect необходимо перейти на 'Manage Users->In App Purchase Test User'
    2. Нажать 'Add New User'
    3. Ввести информацию о пользователе

    E-mail пользователя не обязательно должен быть реальным. Для своего примера я создал одного тестового пользователя:



    Важный момент. Если у Вас не закончен контракт с Apple, тогда In-App Purchase работать у Вас не будет. Для того, чтобы закончить контракт, необходимо указать Contact Info, Bank Info и Tax Info.

    Шаг 6. Программирование


    Для своего тестового примера я создал каркас проекта и User Interface:



    Для работы с App Store я рекомендую использовать MKStoreKit, разработанный в 2009 году разработчиком Кумаром (Mugunth Kumar). Данный набор классов значительно облегчит работу со StoreKit. Кроме MKStoreKit, в проект необходимо добавить StoreKit.framework.

    В своем примере я использую слегка модернизированную первую весрию MKStoreKit. Для удобства я добавил к классу MKStoreManager делегат следующего вида:

    @protocol MKStoreKitDelegate @optional
    - (void)productAPurchased;
    - (void)productBPurchased;
    - (void)failed;
    @end


    * This source code was highlighted with Source Code Highlighter.


    Делегату посылается сообщение productAPurchased когда куплена фича 1, productBPurchased — когда куплена фича 2 и failed — когда пользователь либо отменил покупку, либо покупка не прошла.

    Класс-Singleton MKStoreManager является основным в MKStoreKit. Так выглядит его объявление:

    @interface MKStoreManager : NSObject<SKProductsRequestDelegate> {
        ...
    }

    // делегат
    @property (nonatomic, retain) id<MKStoreKitDelegate> delegate;
    // продукты, доступные для покупки
    @property (nonatomic, retain) NSMutableArray *purchasableObjects;

    // фабричный метод для Singleton
    + (MKStoreManager*)sharedManager;

    // методы для покупки фич
    - (void) buyFeatureA;
    - (void) buyFeatureB;

    // методы позволяют узнать куплена ли фича
    + (BOOL) featureAPurchased;
    + (BOOL) featureBPurchased;
    ...
    @end


    * This source code was highlighted with Source Code Highlighter.


    Рассмотрим использование класса на моем тестовом примере.

    Сначала в файле MKStoreManager.m я прописал Product ID своих фич:

    static NSString *featureAId = @"com.wordpress.indiedevelop.InAppPurchasesExample.f1";
    static NSString *featureBId = @"com.wordpress.indiedevelop.InAppPurchasesExample.f2";


    * This source code was highlighted with Source Code Highlighter.


    Также нужно проверить были ли куплены фичи программы. Основной класс примера унаследован от UIViewController, поэтому код проверки целесообразно встроить в метод viewDidLoad:

    - (void)viewDidLoad {
      [super viewDidLoad];

      [MKStoreManager sharedManager].delegate = self; // назначаем делагата для объекта MKStoreManager

      if ([MKStoreManager featureAPurchased]) // если куплена фича 1
      {
        feature1Button.hidden = YES; // скрываем кнопку 'Купить фичу 1'
        seeFeature1Button.hidden = NO; // показываем кнопку 'Перейти на фичу 1'
      }

      if ([MKStoreManager featureBPurchased]) // если куплена фича 2
      {
        feature2Button.hidden = YES; // скрываем кнопку 'Купить фичу 2'
        seeFeature2Button.hidden = NO; // показываем кнопку 'Перейти на фичу 2'
      }
    }


    * This source code was highlighted with Source Code Highlighter.


    Внутри MKStoreKit информация о том, куплен ли продукт сохраняется через NSUserDefaults, поэтому при удалении приложения информация сбрасывается. Однако, пользователь не будет покупать фичи два раза, поскольку StoreKit откроет доступ к фиче бесплатно.

    Дальше нужно реализовать методы 'Купить'. Они привязаны к событию TouchUpInside соотвествующих кнопок:

    -(IBAction)feature1ButtonPressed
    {
      [self showLockView]; // показываем пользователю, что происходит загрузка
      [[MKStoreManager sharedManager] buyFeatureA]; // посылаем сообщение магазину 'Купить фичу 1'
    }

    -(IBAction)feature2ButtonPressed
    {
      [self showLockView]; // показываем пользователю, что происходит загрузка
      [[MKStoreManager sharedManager] buyFeatureB]; // посылаем сообщение магазину 'Купить фичу 2'
    }


    * This source code was highlighted with Source Code Highlighter.


    Далее я реализовал методы делегата MKStoreKitDelegate:

    // фича 1 куплена
    - (void)productAPurchased
    {
      [self hideLockView]; // скрываем отображение загрузки
      feature1Button.hidden = YES; // скрываем кнопку 'Купить'
      seeFeature1Button.hidden = NO; // показываем кнопку 'Перейти'
    }

    // фича 2 куплена
    - (void)productBPurchased
    {
      [self hideLockView]; // скрываем отображение загрузки
      feature2Button.hidden = YES; // скрываем кнопку 'Купить'
      seeFeature2Button.hidden = NO; // показываем кнопку 'Перейти'
    }

    // покупка не прошла, либо была отменена
    - (void)failed
    {
      [self hideLockView]; // скрываем отображение загрузки
    }


    * This source code was highlighted with Source Code Highlighter.


    Новые фичи реализованы в виде отдельных UIView под управлением UIViewController. Переход на новые фичи я реализовал с помощь UINavigationController:

    // перейти на фичу 1
    -(IBAction)seeFeature1
    {
      [self.navigationController pushViewController:feature1ViewController animated:YES];
    }

    // перейти на фичу 2
    -(IBAction)seeFeature2
    {
      [self.navigationController pushViewController:feature2ViewController animated:YES];
    }


    * This source code was highlighted with Source Code Highlighter.


    Также при манипуляции с магазином можно добавить проверку на его доступность. Это делается так:

    if ([SKPaymentQueue canMakePayments])
    {
    ... // Отобразить магазин пользователю
    }
    else
    {
    ... // Уведомить пользователя, что Purchases недоступны
    }


    * This source code was highlighted with Source Code Highlighter.


    Как видите все достаточно просто. Остается скопилировать и тестировать :)

    Шаг 7. Тестирование


    Чтобы протестировать In-App Purchases нужно скомпилировать, установить и запустить приложение. При тестировании следует помнить следующее:
    1. Тестировать Purchases можно только на устройстве.
    2. Перед тестированием необходимо выйти из iTunes на своем iPhone. Это делается через 'Settings->Store->Sign Out'.
    3. При тестировании, в сообщениях с предложением купить фичу, будет появляться [Environment: sandbox] — это признак тестового режима.
    4. Тестировать можно только тест-аккаунтами (см. Шаг 5)

    Тестовый пример InAppPurchasesExample


    В результате у меня получилось приложение, которое может разблокировать два дополнительных окна. Это приложение можно использовать в качестве примера для создания своих In-App Purchase проектов.

    Исходный код: InAppPurchasesExample.zip

    Скриншоты:


    Я очень надеюсь, что Вам помог этот пост. Если у Вас есть Вопросы, пожалуйста, задавайте. Я с удовольствием на них отвечу.

    — Яков

    Средняя зарплата в IT

    120 000 ₽/мес.
    Средняя зарплата по всем IT-специализациям на основании 7 122 анкет, за 1-ое пол. 2021 года Узнать свою зарплату
    Реклама
    AdBlock похитил этот баннер, но баннеры не зубы — отрастут

    Подробнее

    Комментарии 22

      +2
      Спасибо, очень полезно, правда с ним пришлось разобраться раньше — но зато теперь занесу статью в избранное — пусть будет под рукой.

      От себя добавлю — если у вас Ad Hoc версия приложения, то также нужно создать Entitlements.plist
      идем в New File -> iPhone OS -> Code Signing -> Entitlements
      называем файл Entitlements.plist
      теперь в нем снимаем галочку «get-task-allow»
      теперь в Build settings вашего таргета в графе «Code Signing Entitlements» укажем «Entitlements.plist»

      если этого не сделать — будете получать при установке приложения сообщение «The executable was signed with invalid entitlements.»
        +2
        спасибо за статью, мне очень скоро это пригодится
          0
          Буду очень рад, если Вам поможет разобраться в теме
          0
          Помог с кармой, переносите в тематический блог
            0
            полезно, сэкономит время если придется столкнутся.
            Автор не ленись, пиши дальше о том, что считаешь интересным :-), а то в хабре доминирует копипаст и так мало авторского.
              0
              Большое спасибо :) Буду иногда писать подобные уроки.
              0
              Подскажите, как при такой схеме платеже обстоят дела с комиссией и выводом денег?
                0
                Коммиссия такая же как и при «прямых» продажах — 30%. Вывод денег осуществляется точно также. Отчеты приходят вместе с отчетом о «прямых» продажах в одном файле.
                  0
                  Спасибо за информацию. У меня предполагаются крупные транзакции, поэтому придется использовать что-нибудь другое. Скажем, Chronopay или PayPal через браузер, открытый в приложении.

                  Или, может быть, вы посоветуете какой-нибудь другой вариант?
                    0
                    Могу ошибаться, но мне кажется, что эппл может не пропустить приложение с покупками фич через браузер.
                    Я тоже планирую делать крупные транзакции, но через In-App Purchase. Большой минус — отдаем 30% эппл, большой плюс — удобно для пользователя.
                    Хотя если делать через внутренний браузер, то может и прокатит, и, возможно, получится даже лучше и удобнее, чем через In-App purchase.

                    Вообщем, тут большое поле для экспериментов. Как правильно сделать сказать очень сложно.
                      0
                      Удобнее — это вряд ли, потому что каждый раз заполнять Хронопеевскую форму с айфона не самое приятное занятие. Но у меня прибыль с транзакции менее 10%, и 30% комиссия — это смерть такому бизнесу.
                        0
                        Видимо, в Вашем случае In-App Purchases не подойдут однозначно :)
                0
                Я думаю более актуальнее будет перенести блог в Разработка под Mac OS X и iPhone
                  0
                  «На вопрос 'Does your product contain encryption?' ответить отрицательно.»

                  Да ну? Всегда? Не задумываясь? Не вникая в суть этого вопроса и смысл его существования?
                  :)
                    0
                    Прошу прощения за двусмысленность :) Здесь рассматривается работа тестового примера. И чтобы не загромождать пост я написал «ответить отрицательно».

                    Естественно, сначала нужно задуматься, затем ответить либо 'да', либо 'нет'.

                    Но для того, чтобы протестировать работу Purchases вполне подойдет 'NO' :)
                    0
                    Спасибо. Полезно.
                      0
                      Далее необходимо включить In App Purchases для App ID. В списке App IDs, напротив нужного Bundle Identifier в графе Action следует нажать Configure. Появится форма Configure App ID, на которой необходимо включить checkBox 'Enable In App Purchase'.

                      У меня этой галочки почему то нет. Что я делаю не так?
                        0
                        Насколько я помню, в случае с сервером, приложение сначала запрашивает у сервера только идентификаторы продуктов, а потом по этим идентификаторам забирает информацию о них с сервера Apple и показывает их пользователю. Таким образом происходит проверка соответствия того, что содержится на вашем сервере тому, что вы внесли через iTunes Connect.
                          0
                          А у вас никогда не было такого, что для отдельных девайсов список айдишников с аппстора возвращался в массиве невалидных?
                            0
                            Важный момент. Если у Вас не закончен контракт с Apple, тогда In-App Purchase работать у Вас не будет. Для того, чтобы закончить контракт, необходимо указать Contact Info, Bank Info и Tax Info.

                            Ммм… непонятно. О чем здесь речь? Что за контракт?
                              0
                              Речь о том, что нужно завершить заключение контракта. Заполнить все необходимые данные: контакты, банковские счета и т.д.
                              Иначе, будут доступны к сабмиту лишь бесплатные приложения.
                              0
                              Отличная статья, спасибо)
                              Возникла проблема, в тестовом приложении тестовым юзером не получается совершить покупку продукта с типом Consumable. Подскажите кто знает почему так происходит?
                              При этом покупка продукта с типом Free Subscription проходит нормально.

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

                              Самое читаемое