Non-consumable IAP — категория, где каждая ошибка в логике восстановления покупок превращается в жалобу в App Store и чарджбек. Пользователь купил «безлимитный режим» или «убрать рекламу», переустановил приложение — и не получил своё. Поддержка Apple не поможет: восстановление non-consumable это ответственность разработчика. Наш опыт пяти лет и более двадцати проектов с IAP показывает, что корректная реализация с первого раза экономит до 40% времени на баг-фиксах. Согласно документации StoreKit, правильное восстановление критически важно.
Что чаще всего идёт не так
Самая распространённая ошибка — вызывать SKPaymentQueue.default().restoreCompletedTransactions() только по нажатию кнопки «Восстановить». Правильно: при каждом запуске проверять originalTransaction через SKReceiptRefreshRequest или серверную валидацию. Без этого пользователь, вернувшийся через полгода с новым iPhone, окажется без оплаченного контента.
Второй кейс — неправильная обработка SKPaymentTransactionObserver. Если updatedTransactions не вызывает finishTransaction(_:) для всех состояний (.purchased, .restored, .failed), транзакция зависает в очереди и при следующем запуске приложения повторно триггерит observer. Видели проекты, где это приводило к двойному открытию paid-контента после рестарта.
Третья проблема — отсутствие серверной валидации. Локальная проверка receipt через ASN.1 парсинг сложна и уязвима: злоумышленник может подменить receipt в файловой системе. Серверная верификация через эндпоинт Apple /verifyReceipt (или альтернативный) даёт гарантию подлинности.
Как устроена правильная реализация
Архитектура non-consumable IAP строится вокруг StoreKit 2 (iOS 15+) или StoreKit 1 с поддержкой iOS 13–14. Сравним два подхода:
| Аспект | StoreKit 2 | StoreKit 1 (Legacy) |
|---|---|---|
| Минимальная версия iOS | 15.0 | 3.0 |
| API стиль | async/await | Delegate + completion handlers |
| Восстановление покупок | Автоматическое через Transaction.currentEntitlements |
Ручное restoreCompletedTransactions() |
| Проверка подписи | Встроенная VerificationResult |
Требуется ReceiptValidator |
| Сложность кода | Низкая | Средняя |
StoreKit 2 радикально упрощает код: он в 2 раза сокращает количество строк по сравнению с StoreKit 1.
// Запрос продуктов let products = try await Product.products(for: ["com.app.premium_unlock"]) // Покупка let result = try await products.first?.purchase() switch result { case .success(let verification): switch verification { case .verified(let transaction): // unlock контент await transaction.finish() case .unverified: // receipt подделан — не разблокируем break } case .pending: // SCA или родительский контроль — ждём break case .userCancelled: break } Transaction.currentEntitlements — async sequence, который при каждом запуске приложения возвращает все активные покупки. Итерируем его в @main или в AppDelegate.applicationDidFinishLaunching и восстанавливаем состояние без кнопки «Восстановить».
Для iOS 13–14 остаётся StoreKit 1 с SKPaymentTransactionObserver. Там нужен отдельный ReceiptValidator — либо локальная верификация через openssl (сложно, но без сетевых запросов), либо серверная через Apple /verifyReceipt endpoint (deprecated, но работает). Рекомендуем серверную: локальная требует embedding Apple root certificate и корректной ASN.1-парсинга.
Как избежать проблем с восстановлением покупок?
Ключевое — делать восстановление автоматическим при каждом запуске, а не только по кнопке. В StoreKit 2 это достигается подпиской на Transaction.updates. В StoreKit 1 — вызовом restoreCompletedTransactions() и сохранением originalTransaction.transactionIdentifier в UserDefaults или Keychain. Без автоматизации пользователи с новыми устройствами останутся без контента.
Почему серверная валидация критична?
Для приложений с бэкендом: при покупке клиент отправляет appStoreReceiptURL на сервер, сервер запрашивает Apple Sandbox/Production и сохраняет original_transaction_id в базе. При восстановлении на новом устройстве — запрос к своему API по apple_id пользователя. Это единственный способ гарантировать «покупка на одном устройстве, доступ на другом» в рамках одного Apple ID. Средняя экономия от правильной реализации — до $10,000 в год на поддержку.
Пошаговый план внедрения StoreKit 2
- Настройте продукты в App Store Connect (локализация, цены).
- Реализуйте запрос продуктов через
Product.products(for:). - Добавьте обработку покупки с проверкой
VerificationResult. - Подпишитесь на
Transaction.updatesдля автоматического восстановления. - Интегрируйте серверную верификацию через
/verifyReceipt. - Протестируйте в Sandbox на сценарий «покупка → удаление → восстановление».
Типичная ошибка восстановления
// НЕПРАВИЛЬНО: восстановление только по кнопке func restorePurchases() { SKPaymentQueue.default().restoreCompletedTransactions() } // ПРАВИЛЬНО: автоматическое восстановление при старте Task { for await result in Transaction.currentEntitlements { if case .verified(let transaction) = result { // разблокировать контент } } } Процесс работы и что входит
Наша реализация IAP под ключ включает:
- Настройку продуктов в App Store Connect (создание, локализация, цены — конкретные суммы рассчитываются индивидуально).
- Интеграцию StoreKit 2 с fallback на StoreKit 1 для максимальной совместимости.
- Серверную верификацию receipt с сохранением
original_transaction_id. - Тестирование в Sandbox и на реальных устройствах с чек-листом сценариев.
- Документацию по поддержке и доступам.
- Гарантию прохождения App Review (соблюдение параграфа 3.1.1 App Store Review Guidelines).
Тестирование
В Xcode Simulator StoreKit работает через локальный .storekit файл — можно тестировать без реальных продуктов. Для device-тестирования нужен Sandbox Account в App Store Connect. Важно проверять сценарий: покупка → удаление → переустановка → восстановление. Этот путь ломается чаще всего. Получите консультацию по вашему проекту — оценим риски и предложим оптимальное решение.
Сроки реализации — от 2 до 3 дней: настройка продуктов в App Store Connect, интеграция StoreKit 2 с fallback на StoreKit 1, покрытие тестами на Sandbox, прохождение ревью (App Review требует кнопку «Restore Purchases» в интерфейсе). Закажите интеграцию IAP под ключ — гарантируем корректную работу на всех устройствах и версиях iOS.







