В этой статье я расскажу, как интегрировать In-App Purchase в приложение для iOS.
Обзор
In-app purchase (или IAP) позволяет разработчикам взимать плату с пользователей за определенную функциональность или контент во время использования приложения. Внедрение IAP особенно привлекательно по нескольким причинам:
- Это дополнительный способ заработать деньги в дополнение к простой продаже приложения за предоплату. Некоторые пользователи готовы тратить гораздо больше денег на дополнительный контент или функции.
- Приложение может быть предложено бесплатно, что делает его загрузку для большинства людей необременительной. Бесплатные приложения, как правило, получают гораздо больше загрузок, чем платные. Если приложение понравится пользователям, то они смогут позже приобрести дополнительный контент или функциональность.
- Вы можете показывать пользователю рекламу в бесплатном приложении с возможностью удалить ее, купив IAP.
Вы можете предлагать In-App Purchases только для цифровых товаров (например, Byju’s, Netflix), но не для физических товаров или услуг (например, Amazon, Flipkart).
Что такое IAP?
Согласно Apple, In-App Purchase позволяет предлагать пользователям возможность приобретать контент и функции внутри приложения. Покупки могут быть сделаны внутри вашего приложения или непосредственно в App Store. Фреймворк StoreKit подключается к App Store от имени вашего приложения для запроса и безопасной обработки платежей. Затем фреймворк уведомляет ваше приложение, которое доставляет купленные продукты. Для подтверждения покупок вы можете проверить квитанции на своем сервере в App Store или на устройстве.
Типы IAP
Потребительский: Это когда вы покупаете что-то для прогресса в вашем приложении, например, жизнь в игре. По сути, вы покупаете это один раз, используете, а затем можете купить это снова.
Непотребляемые: Пользователи покупают эти предметы один раз, в дальнейшем их можно использовать бесплатно. При переустановке, смене устройства эти продукты не теряются. Если пользователь потеряет, возможно, сможет загрузить его снова бесплатно, восстановив покупки в приложении. Например: обновление приложения до версии pro, удаление рекламы и т.д.
Непродление подписки: Пользователь может использовать эти предметы в течение определенного периода времени, эти предметы можно приобрести снова после окончания подписки. Например: абонемент на спортивные занятия на один, три или шесть месяцев.
Автовозобновляемые подписки: Пользователь может купить эти элементы на определенный период времени, они будут автоматически продлеваться по истечении этого периода. Например: Продолжающиеся услуги (Netflix, Hulu Plus и т.д.), подписка на журналы и т.д.
В этом блоге мы будем реализовывать непродлеваемые подписки.
Создание продуктов для покупки в приложении
При предложении IAP вы должны сначала добавить запись для каждой отдельной покупки в App Store Connect. Если вы когда-либо выставляли приложение на продажу в магазине, это похожий процесс и включает в себя такие вещи, как выбор ценового уровня для покупки. Когда пользователь совершает покупку, App Store обрабатывает сложный процесс взимания платы с пользователя и отвечает данными о такой операции.
Теперь, просматривая запись вашего приложения в App Store Connect, перейдите на вкладку Features и затем выберите In-App Purchases. Чтобы добавить новый IAP-продукт, нажмите на + справа от In-App Purchases.
Появится следующее диалоговое окно:
Далее заполните данные для IAP следующим образом
- Справочное имя: Псевдоним, идентифицирующий IAP в iTunes Connect. Это имя не отображается нигде в приложении. Название RazeFace, которое вы разблокируете с помощью этой покупки, — Swift Shopping, поэтому введите его здесь.
- Идентификатор продукта: Это уникальная строка, идентифицирующая IAP. Обычно лучше всего начать с идентификатора пакета, а затем добавить уникальное название, характерное для данного приобретаемого товара. Например, вы можете использовать: com.iq.colearn.swiftshopping.
- Очистить для продажи: Включает или выключает продажу IAP. Вы хотите включить его!
Уровень цены: Стоимость IAP.
App Store Connect может пожаловаться на отсутствие метаданных для вашего IAP. Прежде чем отправить приложение на рассмотрение, вы должны добавить скриншот IAP в нижней части этой страницы. Снимок экрана используется только для проверки Apple и не отображается в вашем листинге App Store.
Создание пользователя песочницы
Теперь нам нужно создать пользователя песочницы, для этого перейдите в раздел Пользователи и роли в учетной записи iTunes connect и выберите раздел Sandbox Testers. Очень важно добавить пользователя песочницы для тестирования услуг IAP, с помощью пользователя песочницы вы сможете совершать транзакции бесплатно.
Всегда помните, что нужно использовать электронную почту, которая не связана с каким-либо Apple ID, а также это должен быть действительный email id.
Конфигурация проекта
Перейдите на вкладку Capabilities, прокрутите вниз до In-App Purchase и установите переключатель в положение ON.
Код на Swift
Мы будем использовать фреймворк StoreKit, предоставленный Apple, который на удивление легко реализовать. По сути, есть PaymentQueue, куда будут попадать все транзакции. Вам нужно будет слушать эту очередь и действовать соответствующим образом. Поток должен выглядеть следующим образом.
Сначала мы создадим отдельный класс для обработки функций StoreKit. Создайте файл InAppPurchaseHandler.swift для написания кода, связанного с покупкой в приложении. Это будет класс-одноклассник.
import StoreKit
class MyStoreKitDelegate: NSObject {
let monthlySubID = "MyApp.sub.allaccess.monthly"
let yearlySubID = "MyApp.sub.allaccess.yearly"
var products: [String: SKProduct] = [:]
func fetchProducts() {
let productIDs = Set([monthlySubID, yearlySubID])
let request = SKProductsRequest(productIdentifiers: productIDs)
request.delegate = self
request.start()
}
func purchase(productID: String) {
if let product = products[productID] {
let payment = SKPayment(product: product)
SKPaymentQueue.default().add(payment)
}
}
func restorePurchases() {
SKPaymentQueue.default().restoreCompletedTransactions()
}
}
extension MyStoreKitDelegate: SKProductsRequestDelegate {
func productsRequest(_ request: SKProductsRequest, didReceive response: SKProductsResponse) {
response.invalidProductIdentifiers.forEach { product in
print("Invalid: (product)")
}
response.products.forEach { product in
print("Valid: (product)")
products[product.productIdentifier] = product
}
}
func request(_ request: SKRequest, didFailWithError error: Error) {
print("Error for request: (error.localizedDescription)")
}
}
Давайте разберемся в этом. Некоторые ключевые понятия таковы:
SKPaymentQueue: Очередь, в которой все транзакции хранятся для дальнейшей обработки
SKProduct: Продукт, заявленный в Itunes Connect со всей доступной информацией.
SKPayment: Намерение покупки продукта
SKPaymentTransaction: Событие, касающееся SKProduct
SKProductRequest: Запрос на получение информации о SKProducts с указанием ID продукта
SKProductResponse: Ответ, содержащий требуемые продукты. Он состоит из двух списков: продукты и недействительные идентификаторы продуктов. Первый будет содержать успешно найденные SKProducts, второй — все идентификаторы, которые не соотнесены с SKProduct. Если вы получаете недействительные идентификаторы, вот список мер по устранению неполадок, который может вам помочь.
Теперь, во-первых, с помощью следующих строк вы можете легко сделать SKProductRequest.
let request = SKProductsRequest(productIdentifiers: productIDs)
request.delegate = self
request.start()
Убедитесь, что вы реализовали SKProductsRequestDelegate, поскольку следующий метод является обратным вызовом.
func productsRequest(_ request: SKProductsRequest, didReceive response: SKProductsResponse
Здесь вы получите все SKProducts, доступные для вашего приложения. Чтобы совершить покупку, создайте SKPayment.
let payment = SKPayment(product: product)
SKPaymentQueue.default().add(payment)
> Убедитесь, что вы всегда получаете продукты еще до того, как позволите пользователю совершить покупку. Эти SKProducts будут иметь цену, локализованную внутри них, поэтому вы должны отображать ее. Это позволит вам всегда иметь актуальные данные в вашем приложении, так как наличие неправильной цены в приложении может привести к тому, что Apple удалит его из App Store.
Раздел внешних событий
Вы также можете заметить в потоке раздел Внешнее событие. Транзакции могут происходить за пределами вашего приложения. Скажем, ваш пользователь меняет тип подписки в настройках системы, или ваша отложенная транзакция была одобрена родителями пользователя, вы не сможете об этом узнать, если только не ожидаете этого. По этой причине всегда, в AppDelegate, в начале работы вашего приложения, подписывайте приложение на PaymentQueue. Так вы будете уверены, что не пропустите ни одного события.
class AppDelegate: UIResponder, UIApplicationDelegate, SKPaymentTransactionObserver {
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
SKPaymentQueue.default().add(self)
//...
return true
}
func paymentQueue(_ queue: SKPaymentQueue, updatedTransactions transactions: [SKPaymentTransaction]) {
for transaction in transactions {
switch transaction.transactionState {
case .failed:
queue.finishTransaction(transaction)
print("Transaction Failed (transaction)")
case .purchased, .restored:
queue.finishTransaction(transaction)
print("Transaction purchased or restored: (transaction)")
case .deferred, .purchasing:
print("Transaction in progress: (transaction)")
}
}
}
}
Проверка квитанций в App Store
Платное программное обеспечение всегда представляло собой проблему, когда некоторые пользователи пытаются использовать программу, не покупая ее, или мошеннически получают доступ к покупкам внутри приложения. Квитанции предоставляют инструмент для подтверждения таких покупок. Для этого они обеспечивают запись продаж. App Store генерирует квитанцию в пакете приложений каждый раз, когда пользователь приобретает ваше приложение, совершает покупку внутри приложения или обновляет приложение.
Получение данных квитанции
Чтобы получить данные квитанции из приложения на устройстве, используйте метод appStoreReceiptURL в NSBundle, чтобы найти квитанцию приложения и закодировать данные в Base64. Отправьте эти закодированные в Base64 данные на ваш сервер.
// Get the receipt if it's available
if let appStoreReceiptURL = Bundle.main.appStoreReceiptURL,
FileManager.default.fileExists(atPath: appStoreReceiptURL.path) {
do {
let receiptData = try Data(contentsOf: appStoreReceiptURL, options: .alwaysMapped)
print(receiptData)
let receiptString = receiptData.base64EncodedString(options: [])
// Read receiptData
}
catch { print("Couldn't read receipt data with error: " + error.localizedDescription) }
}
Проверка квитанции
Существует два verifyReceiptURL для сбора информации из iTunes как в песочнице, так и в реальном времени. API verifyReceiptURL имеет 2 параметра. Первый — receipt-data, который содержит локальные данные, сохраненные в квитанции, а второй — пароль, в котором вы должны передать Shared Secret.
Песочница: https://sandbox.itunes.apple.com/verifyReceipt
Live: https://buy.itunes.apple.com/verifyReceipt
Обработка уведомлений о возврате
App Store Server отправляет уведомления почти в реальном времени, когда клиенты получают возврат средств за покупки в приложениях. Если вы предлагаете контент на нескольких платформах, например, драгоценные камни или монеты для игр, и обновляете балансы аккаунтов игроков на своем сервере, получение уведомлений о возврате средств очень важно. Реагируйте на уведомления о возврате, интерпретируя и обрабатывая информацию о возврате, и информируйте клиентов в приложении о любых действиях, которые вы предпринимаете в результате возврата.
Когда App Store обрабатывает возврат средств, сервер App Store отправляет уведомление о возврате средств на ваш сервер по адресу URL, который вы настроили. Ваш сервер должен ответить на сообщение с кодом ответа 200.
Уведомление о возврате средств применяется только к расходуемым, нерасходуемым и невозобновляемым подпискам.
Тестирование
Сначала войдите на свое устройство iOS, используя созданную учетную запись пользователя Sandbox, затем запустите свое приложение на реальном устройстве и инициируйте транзакцию. Не беспокойтесь о цене, которая отображается в окне оповещения, просто идите дальше. С вас ничего не будет снято, так как вы являетесь пользователем «песочницы» для приложения.
Покупки в приложении не могут быть протестированы на симуляторе iOS. Поэтому, пожалуйста, используйте реальное устройство.