Advertisement
  1. Code
  2. Mobile Development
  3. iOS Development

Покупки в приложениях iOS со Swift 3

Scroll to top
Read Time: 11 min

() translation by (you can also view the original English article)

Final product imageFinal product imageFinal product image
What You'll Be Creating

Вступление

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

В этом уроке я опишу все шаги по созданию Consumable и Non-Consumable IAP в iTunes Connect и покажу код, который вам нужен для покупки обоих продуктов. Я сделал sample Xcode project with a label and two buttons, загрузите его и следуйте по указаниям, чтобы понять, как он работает.

Создание Sandbox Tester в iTunes Connect

Я предполагаю, что вы уже создали приложение iOS в разделе My Apps в iTunes Connect. Теперь сделаем Sandbox Tester для проверки IAP на вашем реальном устройстве (не Simulator - он не поддерживает In-App покупки).

Войдите в Users and Roles, далее на вкладку Sandbox Tester, нажмите (+) рядом с Tester.

Users and roles in iTunes ConnectUsers and roles in iTunes ConnectUsers and roles in iTunes Connect

Заполните форму, чтобы добавить новый sandbox tester. После сохранения вернитесь в раздел My App и щёлкните значок своего приложения, чтобы ввести его данные и создать продукты IAP.

Создание продуктов IAP в iTunes Connect

Consumable Products

Во вкладке Features нажмите (+) рядом с In-App Purchases. Можно создавать только один продукт за раз, поэтому начнём с Consumable.

Select Consumable In-App PurchaseSelect Consumable In-App PurchaseSelect Consumable In-App Purchase

Consumable IAP, как следует из названия, является продуктом, который можно купить многократно. Мы будем использовать его для сбора дополнительных «монет» в нашем демонстрационном приложении.

Нажмите Create, чтобы инициализировать элемент IAP. На следующем экране вы настроите всю информацию о своем продукте:

  • Reference Name: это имя будет использоваться в отчётах iTunes Connect и в Sales and Trends. Оно может быть любым, но не длиннее 64 символов, в App Store оно отражаться не будет.
  • Product ID: уникальный алфавитно-цифровой идентификатор для распознавания вашего продукта. Разработчики часто используют для него синтаксис web-reverse. В нашем примере это com.iaptutorial.coins. Позже мы вставим этот ID в строку нашего кода.
  • Price: выберите ценовой разряд из выпадающего меню. Помните: чтобы продать ваш продукт через приложение в App Store, вы должны подать заявку на получение Paid Application Agreement в Agreements, Tax & Banking.
  • Localizations: для этого урока мы выбрали только английский язык, но вы можете добавить, нажав кнопку (+). Введите Display Name и Description. Оба они будут видны в App Store.
  • Screenshot: загрузите скриншот для обзора. Он не отобразится в App Store, и должен быть допустимого для платформы приложений размера, поэтому, если ваше приложение Universal, можете загрузить скриншот iPad.
  • Review Notes: любая дополнительная информация о вашем IAP, которая может быть полезна для рецензента.
Create a new IAP productCreate a new IAP productCreate a new IAP product

Закончив, нажмите Save и получите предупреждение:

Ваша первая покупка In-App должна быть представлена в новой версии приложения. Выберите его из раздела In-App Purchases и нажмите Submit.

Non-Consumable Products

Теперь нажмите кнопку In-App Purchases в левом списке, прямо над кнопкой Game Center и добавьте новый продукт IAP. На этот раз выберите опцию Non-Consumable:

Create a Non-Consumable productCreate a Non-Consumable productCreate a Non-Consumable product

Нажмите Create и повторите шаги, описанные выше. Поскольку этот продукт Non-Consumable и пользователи смогут купить его только раз, Apple требует подтверждения возможности оплаты. Это на случай, если вы удалите приложение и установите его повторно или зайдёте с другого устройства с тем же Apple ID и вам нужно будет вернуть свои покупки, не заплатив за них дважды. Поэтому позже мы добавим функцию Restore Purchase в нашем коде.

ID продукта, который мы создали - com.iaptutorial.premium с ценовым уровнем USD $2.99. Мы назвали его Unlock Premium Version.

Когда вы заполните поля, сохраните свой продукт и вернитесь на страницу In-App Purchases. У вас будет список из двух продуктов, с их NameTypeID и StatusReady to Submit.

In-app Purchases listIn-app Purchases listIn-app Purchases list

Вернитесь на страницу своего приложения, нажав на кнопки App Store и Prepare for Submission. Прокрутите вниз до раздела In-App Purchases прямо под General App Information, нажмите кнопку (+), чтобы добавить свои продукты IAP.

Add In-App Purchases to your apps info pageAdd In-App Purchases to your apps info pageAdd In-App Purchases to your apps info page

Выберите их и нажмите Done.

Select your IAP productsSelect your IAP productsSelect your IAP products

Наконец, нажмите Save в правом верхнем углу экрана, и вы сможете настроить продукты In-App Purchase на iTunes Connect.

Войдите в Тестер Sandbox на устройстве iOS

Прежде чем перейти к коду, сделайте ещё шаг. Нажмите Settings > iTunes & App Store на устройстве iOS. Если вы уже вошли в систему со своим Apple ID, нажмите на него и выберите Sign Out. Затем войдите в систему с учётными данными для sandbox tester, которые вы создали. После входа в систему может появиться предупреждение:

Alert from the deviceAlert from the deviceAlert from the device

Просто нажмите Cancel. Ваше устройство снова попросит sandbox login, пытаясь совершить покупку и узнает вашу тестовую учётную запись, чтобы вы не платили ни копейки по кредитной карте за любую покупку, которую совершаете.

Выйдите из Settings, подключите устройство к компьютеру Mac через USB-кабель и, наконец, начните кодирование!

Код

Если вы откроете наш демонстрационный проект, то увидите, что весь необходимый код для In-App Purchase написан:

Demo IAP appDemo IAP appDemo IAP app

Если вы хотите протестировать приложение, то должны изменить Bundle Identifier на свой id. В противном случае Xcode не позволит вам запускать приложение на реальном устройстве и приложение не узнает два ваших IAP-продукта.

Chage Bundle ID in the General tab in XCodeChage Bundle ID in the General tab in XCodeChage Bundle ID in the General tab in XCode

Войдите в ViewController.swift и проверьте код. Прежде всего, мы добавили инструкцию import для StoreKit и делегатов, которые нам нужны для отслеживания транзакций и запросов продукта.

1
import StoreKit
2
3
class ViewController: UIViewController,
4
SKProductsRequestDelegate,
5
SKPaymentTransactionObserver
6
{

Затем мы объявили несколько полезных просмотров.

1
 /* Views */
2
    @IBOutlet weak var coinsLabel: UILabel!
3
    @IBOutlet weak var premiumLabel: UILabel!
4
    @IBOutlet weak var consumableLabel: UILabel!
5
    @IBOutlet weak var nonConsumableLabel: UILabel!
6
    

CoinsLabel и premiumLabel будут использоваться для показа результатов покупок обоих продуктов. ConsumableLabel и nonConsumableLabel покажут описание и цену каждого продукта IAP, которые мы ранее создали в iTunes Connect.

Теперь добавим некоторые переменные:

1
/* Variables */
2
    let COINS_PRODUCT_ID = "com.iaptutorial.coins"
3
    let PREMIUM_PRODUCT_ID = "com.iaptutorial.premium"
4
    
5
    var productID = ""
6
    var productsRequest = SKProductsRequest()
7
    var iapProducts = [SKProduct]()
8
    var nonConsumablePurchaseMade = UserDefaults.standard.bool(forKey: "nonConsumablePurchaseMade")
9
    var coins = UserDefaults.standard.integer(forKey: "coins")
10
    

Первые две строки - это напоминание об ID продуктов. Важно, чтобы эти строки точно соответствовали тем, которые были зарегистрированы в разделе iTunes Connect In-App Purchase.

  • productID - это строка, которую мы будем использовать для определения продукта для покупки.
  • productsRequest - это экземпляр SKProductsRequest, необходимый для поиска продуктов IAP из вашего приложения в iTC.
  • iapProducts просто набор SKProducts. Обратите внимание, что префикс SK означает StoreKit, структуру iOS для обработки покупок.

Последние две строки загружают две переменные типа Boolean и Integer, необходимые для отслеживания покупок монет и премиальной версии, consumable и non-consumable продуктов.

Следующий код в viewDidLoad() выполняет несколько действий сразу после запуска приложения:

1
    // Check your In-App Purchases

2
    print("NON CONSUMABLE PURCHASE MADE: \(nonConsumablePurchaseMade)")
3
    print("COINS: \(coins)")
4
    
5
    // Set text

6
    coinsLabel.text = "COINS: \(coins)"
7
    
8
    if nonConsumablePurchaseMade { premiumLabel.text = "Premium version PURCHASED!"
9
    } else { premiumLabel.text = "Premium version LOCKED!"}
10
    
11
    // Fetch IAP Products available

12
    fetchAvailableProducts()

Сначала мы просто регистрируем каждую покупку в консоли Xcode. Затем мы показываем общее количество монет, которые мы купили с помощью coinsLabel. Поскольку мы запускаем демонстрационное приложение впервые, оно отображает COINS: 0.

if ставится в текст premiumLabel в зависимости от того, был ли приобретен non-consumable продукт. Для начала он будет показывать Premium version LOCKED! пока мы не сделали премиальную покупку.

Последняя строка кода вызывает метод, его мы увидим позже, он извлекает продукты, которые мы хранили в iTC.

Теперь давайте посмотрим, что делают две кнопки покупки, которые мы установили в нашем demo app:

1
// MARK: -  BUY 10 COINS BUTTON

2
@IBAction func buy10coinsButt(_ sender: Any) {
3
    purchaseMyProduct(product: iapProducts[0])
4
}
5
    
6
    
7
// MARK: - UNLOCK PREMIUM BUTTON

8
@IBAction func unlockPremiumButt(_ sender: Any) {
9
    purchaseMyProduct(product: iapProducts[1])
10
}

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

Как упоминалось ранее, нам нужна третья кнопка для восстановления нашей non-consumable покупки. Вот её код:

1
// MARK: - RESTORE NON-CONSUMABLE PURCHASE BUTTON

2
@IBAction func restorePurchaseButt(_ sender: Any) {
3
    SKPaymentQueue.default().add(self)
4
    SKPaymentQueue.default().restoreCompletedTransactions()
5
}
6
    
7
func paymentQueueRestoreCompletedTransactionsFinished(_ queue: SKPaymentQueue) {
8
    nonConsumablePurchaseMade = true
9
    UserDefaults.standard.set(nonConsumablePurchaseMade, forKey: "nonConsumablePurchaseMade")
10
        
11
    UIAlertView(title: "IAP Tutorial",
12
    message: "You've successfully restored your purchase!",
13
    delegate: nil, cancelButtonTitle: "OK").show()
14
}
15
 

Функция IBAction подключается к кнопке Restore Purchase в Storyboard и подсоединяется к системе Apple's In-App Purchase, чтобы восстановить покупку, если она уже была сделана.

paymentQueueRestoreCompletedTransactionsFinished() - это метод из фреймворка StoreKit, который подтвердит переменную nonConsumablePurchaseMade  после того, как покупка будет успешно восстановлена.

Мы закончили с кнопками, поэтому давайте посмотрим, что делает функция fetchAvailableProducts ():

1
// MARK: - FETCH AVAILABLE IAP PRODUCTS

2
func fetchAvailableProducts()  {
3
        
4
    // Put here your IAP Products ID's

5
    let productIdentifiers = NSSet(objects:
6
        COINS_PRODUCT_ID,
7
        PREMIUM_PRODUCT_ID
8
    )
9
        
10
    productsRequest = SKProductsRequest(productIdentifiers: productIdentifiers as! Set<String>)
11
    productsRequest.delegate = self
12
    productsRequest.start()
13
}
14
    

Сначала мы создаем экземпляр NSSet, который в основном представляет собой массив строк. Мы сохраним два ID продукта, которые мы ранее объявили.

Затем мы запускаем SKProductsRequest на основе этих ID для отображения информации о продуктах (описании и цене) IAP, которые будут обрабатываться этим методом:

1
// MARK: - REQUEST IAP PRODUCTS

2
func productsRequest (_ request:SKProductsRequest, didReceive response:SKProductsResponse) {
3
    if (response.products.count > 0) {
4
        iapProducts = response.products
5
            
6
        // 1st IAP Product (Consumable) ------------------------------------

7
        let firstProduct = response.products[0] as SKProduct
8
        
9
        // Get its price from iTunes Connect

10
        let numberFormatter = NumberFormatter()
11
        numberFormatter.formatterBehavior = .behavior10_4
12
        numberFormatter.numberStyle = .currency
13
        numberFormatter.locale = firstProduct.priceLocale
14
        let price1Str = numberFormatter.string(from: firstProduct.price)
15
        
16
        // Show its description

17
        consumableLabel.text = firstProduct.localizedDescription + "\nfor just \(price1Str!)"
18
        // ------------------------------------------------

19
        
20
        
21
        
22
        // 2nd IAP Product (Non-Consumable) ------------------------------

23
        let secondProd = response.products[1] as SKProduct
24
        
25
        // Get its price from iTunes Connect

26
        numberFormatter.locale = secondProd.priceLocale
27
        let price2Str = numberFormatter.string(from: secondProd.price)
28
        
29
        // Show its description

30
        nonConsumableLabel.text = secondProd.localizedDescription + "\nfor just \(price2Str!)"
31
        // ------------------------------------

32
    }
33
}
34
    

В приведённой выше функции сначала проверим, есть ли продукты, зарегистрированные в iTunes Connect и соответственно настроим наш массив iapProducts. Затем мы можем инициализировать два SKProducts и распечатать их описание и цену на этикетках.

Прежде чем перейти к ядру кода In-App Purchase, добавим ещё пару функций:

1
// MARK: - MAKE PURCHASE OF A PRODUCT
2
func canMakePurchases() -> Bool {  return SKPaymentQueue.canMakePayments()  }
3
func purchaseMyProduct(product: SKProduct) {
4
    if self.canMakePurchases() {
5
        let payment = SKPayment(product: product)
6
        SKPaymentQueue.default().add(self)
7
        SKPaymentQueue.default().add(payment)
8
        
9
        print("PRODUCT TO PURCHASE: \(product.productIdentifier)")
10
        productID = product.productIdentifier
11
        
12
        
13
    // IAP Purchases dsabled on the Device
14
    } else {
15
        UIAlertView(title: "IAP Tutorial",
16
        message: "Purchases are disabled in your device!",
17
        delegate: nil, cancelButtonTitle: "OK").show()
18
    }
19
}

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

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

1
// MARK:- IAP PAYMENT QUEUE

2
func paymentQueue(_ queue: SKPaymentQueue, updatedTransactions transactions: [SKPaymentTransaction]) {
3
    for transaction:AnyObject in transactions {
4
            if let trans = transaction as? SKPaymentTransaction {
5
                switch trans.transactionState {
6
                    
7
                case .purchased:
8
                    SKPaymentQueue.default().finishTransaction(transaction as! SKPaymentTransaction)
9
                    
10
                    // The Consumable product (10 coins) has been purchased -> gain 10 extra coins!

11
                   if productID == COINS_PRODUCT_ID {
12
                        
13
                        // Add 10 coins and save their total amount

14
                        coins += 10
15
                        UserDefaults.standard.set(coins, forKey: "coins")
16
                        coinsLabel.text = "COINS: \(coins)"
17
                        
18
                        UIAlertView(title: "IAP Tutorial",
19
                        message: "You've successfully bought 10 extra coins!",
20
                        delegate: nil,
21
                        cancelButtonTitle: "OK").show()
22
                        
23
                        
24
                        
25
                    // The Non-Consumable product (Premium) has been purchased!

26
                    } else if productID == PREMIUM_PRODUCT_ID {
27
                        
28
                        // Save your purchase locally (needed only for Non-Consumable IAP)

29
                        nonConsumablePurchaseMade = true
30
                        UserDefaults.standard.set(nonConsumablePurchaseMade, forKey: "nonConsumablePurchaseMade")
31
                    
32
                        premiumLabel.text = "Premium version PURCHASED!"
33
                        
34
                        UIAlertView(title: "IAP Tutorial",
35
                        message: "You've successfully unlocked the Premium version!",
36
                        delegate: nil,
37
                        cancelButtonTitle: "OK").show()
38
                    }
39
                    
40
                    break
41
                    
42
                case .failed:
43
                    SKPaymentQueue.default().finishTransaction(transaction as! SKPaymentTransaction)
44
                    break
45
                case .restored:
46
                    SKPaymentQueue.default().finishTransaction(transaction as! SKPaymentTransaction)
47
                    break
48
                    
49
                default: break
50
    }}}
51
}

Эта функция имеет сообщение switch и проверяет каждое состояние платежа. Первый case вызывается, если покупка была успешно выполнена и завершает транзакцию.

Внутри этого блока мы проверим выбор ID продукта и выполним необходимые действия для обновления приложения, поэтому, если мы купим 10 дополнительных монет, мы добавим 10 к переменной coins, сохраним её значение с помощью UserDefaults, отобразим новое количество монет и заявим об этом.

Обратите внимание, что вы можете совершать эту покупку без ограничений, поскольку это consumable IAP.

Точно так же, если мы купили продукт non-consumable, приложение устанавливает переменную nonConsumablePurchaseMade в true, сохраняет её, изменяет текст premiumLabel и запускает предупреждение о том, что покупка прошла успешно.

Другие два cases обрабатывают результаты платежей за отказ и восстановление. Приложение запустит оповещение, если ваша транзакция завершится неудачей или если вы восстановили non-consumable покупку.

Вот так! Теперь войдите в систему со своими учётными данными Sandbox Tester и запустите проверку приложения. Сначала появится предупреждение:

App Store Sign In alertApp Store Sign In alertApp Store Sign In alert

Выберите Use Existing Apple ID и снова введите данные Sandbox Tester для входа в систему. Это потому, что приложение распознаёт реального пользователя из настроек iTunes и App Store, а не из Sandbox.

После входа в систему, вы сможете совершать покупки обоих продуктов.

10 Coins purchase made10 Coins purchase made10 Coins purchase made

Non-Consumable Premium purchase madeNon-Consumable Premium purchase madeNon-Consumable Premium purchase made

Шаблоны CodeCanyon  

Если вы работаете с iOS и хотите глубже узнать язык Swift и разработку приложений, смотрите my iOS app templates on CodeCanyon.

Есть сотни других iOS app templates on the Envato Market, готовых к работе и ускорению вашего приложения. Покопайтесь в них! Возможно, этим вы сэкономите часы работы в своём следующем приложении.

Заключение

В этом уроке мы рассмотрели все шаги по созданию продуктов In-App Purchase в iTunes Connect и написанию кода для вашего приложения. Надеюсь, вы сможете использовать эти знания в своём следующем iOS app!

Спасибо за чтение, и до встречи! Ознакомьтесь с другими нашими курсами и пособиями по разработке iOS app с помощью Swift.

Advertisement
Did you find this post useful?
Want a weekly email summary?
Subscribe below and we’ll send you a weekly email summary of all new Code tutorials. Never miss out on learning about the next big thing.
Advertisement
Looking for something to help kick start your next project?
Envato Market has a range of items for sale to help get you started.