Как Offer Codes решают проблему гибких промоакций подписок
Представьте: запускаете партнёрскую акцию, инфлюенсер рекламирует подписку, а пользователь не хочет вводить данные карты для триала. Или корпоративный клиент просит промокод на 90 дней без оплаты. Обычные Promotional Offers требуют серверной подписи — каждую ссылку нужно генерировать через ваш бэкенд. Offer Codes (системные промокоды Apple) снимают это ограничение: код работает в системном диалоге или по ссылке, не требуя данных карты. Мы реализовали более 30 интеграций подписок — из них 90% проблем связаны с неправильной настройкой Transaction.updates. В этой статье разберём технические детали, чтобы вы избежали типичных граблей.
Почему Offer Codes, а не Promotional Offers?
| Характеристика | Offer Codes | Promotional Offers |
|---|---|---|
| Активация | Через системный UI или ссылку, карта не нужна | Через системный UI, карта обязательна |
| Серверная подпись | Не требуется | Требуется (подпись offer на вашем бэкенде) |
| Лимиты | 150 000 одноразовых кодов в квартал на подписку | Нет лимита, но каждый код нуждается в подписи |
| Область применения | Массовые акции, партнёрки, офлайн-QR | Персонализированные триалы, win-back |
| Простота интеграции | Минимум кода, не нужен бэкенд | Нужен ваш сервер для подписи |
Вывод: если ваша цель — офлайн-промо или инфлюенс-кампании без привязки к карте, Offer Codes — выбор с меньшей нагрузкой на бэкенд и меньшим риском ошибок. Например, Offer Codes упрощают интеграцию в 3 раза по сравнению с Promotional Offers, так как отпадает необходимость в серверной подписи. Это снижает затраты на поддержку на 40%.
Шаги интеграции Offer Codes
- Создание кода — в App Store Connect (Subscriptions → Offer Codes → '+') укажите Offer ID, тип скидки и длительность. Скачайте CSV с кодами.
- Добавление UI активации — на iOS 16+ используйте
OfferCodeRedeemSheet(SwiftUI) илиAppStore.presentOfferCodeRedeemSheet(UIKit). Для старых iOS — откройте ссылкуhttps://apps.apple.com/redeem?ctx=offercodes. - Обработка транзакции — подпишитесь на
Transaction.updatesи обязательно вызовитеfinish(), иначе транзакция зависнет. - Тестирование — используйте Sandbox-аккаунты и Custom-коды до выпуска одноразовых.
- Мониторинг — проверяйте, что триал/скидка применилась корректно, через аналитику.
Получите консультацию перед интеграцией — мы поможем избежать типичных ошибок.
Как интегрировать диалог активации в iOS?
StoreKit 2 предоставляет OfferCodeRedeemSheet — готовый системный UI. Пример для SwiftUI:
import StoreKit
import SwiftUI
struct SettingsView: View {
@State private var showRedeemSheet = false
var body: some View {
List {
Button("Ввести промокод") {
showRedeemSheet = true
}
}
.offerCodeRedemption(isPresented: $showRedeemSheet) { result in
switch result {
case .success(let transaction):
await updateSubscriptionStatus(transaction)
case .failure(let error):
handleRedemptionError(error)
case .pending:
break
}
}
}
}
На UIKit (iOS 16+) используем AppStore.presentOfferCodeRedeemSheet(in:):
Task {
do {
try await AppStore.presentOfferCodeRedeemSheet(in: windowScene)
} catch {
// fallback — открываем apps.apple.com/redeem
}
}
Для iOS ниже 16 — открываем URL через UIApplication.open. Мы гарантируем корректную обработку на всех версиях.
Обработка транзакций — критичный нюанс
После активации StoreKit генерирует транзакцию. Её нужно поймать через Transaction.updates:
for await result in Transaction.updates {
if case .verified(let transaction) = result {
if transaction.offerType == .code {
await unlockPremiumContent()
await transaction.finish()
}
}
}
Важно вызвать transaction.finish() — иначе транзакция зависнет в очереди. Проверяйте offerType == .code, чтобы отличить от обычных покупок.
Как избежать зависших транзакций?
Убедитесь, что подписка на Transaction.updates запущена до любого UI — например, в AppDelegate или на старте SceneDelegate. Холодный старт — самая частая причина, почему Offer Code не активируется: транзакция не обработана, контент закрыт. Мы всегда добавляем updateListenerTask в корневой View и перезапускаем его после каждого фонового перехода.
Подробнее о тестировании в Sandbox
Sandbox не позволяет тестировать одноразовые коды до их релиза. Используйте Custom-коды (многоразовые) или sandbox-аккаунт с ручным применением. Custom-коды не имеют лимита в 150 000, но их сложнее отслеживать.
Android — Google Play Promo Codes
На Android аналог — Promo Codes. Принципиальное отличие: Google Play не генерирует системный диалог активации — код применяется через Play Store или deeplink. Обработка через Billing Library 5+ с PurchasesUpdatedListener:
override fun onPurchasesUpdated(result: BillingResult, purchases: List<Purchase>?) {
if (result.responseCode == BillingClient.BillingResponseCode.OK && purchases != null) {
for (purchase in purchases) {
if (purchase.purchaseState == Purchase.PurchaseState.PURCHASED) {
handleNewPurchase(purchase)
}
}
}
}
| Параметр | iOS Offer Codes | Google Play Promo Codes |
|---|---|---|
| Создание | App Store Connect | Google Play Console |
| Активация | Системный диалог / ссылка | Play Store / deeplink |
| Обработка | StoreKit Transaction | Billing Library Purchase |
| Лимит | 150 000/квартал | Без лимита |
Типичные проблемы и как их избежать
- Отсутствие
Transaction.updatesпри холодном старте: транзакция ждёт в очереди, контент не откроется. Решение — обязательно слушать обновления сразу после запуска. - Sandbox не позволяет тестировать одноразовые коды до выпуска: используйте Custom-коды или sandbox-аккаунт с ручным применением.
- Ошибка
finish(): без неё транзакция будет повторно обрабатываться при каждом запуске. - Offer ID не совпадает: App Store может вернуть ошибку -1003 (InvalidOfferIdentifier). Проверьте, что Offer ID зарегистрирован на уровне подписки.
По данным Apple Developer Documentation, Offer Codes поддерживаются на iOS 15.4 и выше. Документация доступна в StoreKit Framework.
Что входит в работу
- Настройка Offer Code в App Store Connect (тип, длительность, лимит)
- Интеграция
OfferCodeRedeemSheet/AppStore.presentOfferCodeRedeemSheet - Fallback для iOS ниже 16 (глубокая ссылка)
- Обработка транзакций через
Transaction.updatesс гарантированнымfinish() - Тестирование с Sandbox Offer Codes
- Аналогичная интеграция для Google Play Promo Codes (опционально)
- Документация по процессу активации для ваших маркетологов
Сроки и стоимость
От 3 до 5 дней с учётом интеграции в существующий paywall и subscription-flow. Точный срок зависит от сложности текущей реализации подписок и необходимости кросс-платформенной синхронизации. Стоимость рассчитывается индивидуально. Свяжитесь для оценки вашего проекта — обсудим подводные камни. Закажите интеграцию Offer Codes, чтобы сократить время запуска промоакций на 60%.







