Реалізація Introductory Offers (знижка на перший період) у мобільних застосунках
Introductory Offers — механізм StoreKit, який дозволяє запропонувати новому абоненту знижену ціну або безплатний пробний період на перший розрахунковий цикл. Apple підтримує три типи: freeTrial (безплатно), payAsYouGo (платити за кожен період за зниженою ціною) та payUpFront (заплатити один раз за кілька періодів за зниженою ціною). Без правильної реалізації на клієнті користувач вводний оффер не побачить.
Налаштування в App Store Connect
Introductory Offer створюється на рівні підписки в App Store Connect → Subscriptions → [Subscription] → Introductory Offers. Потрібно вказати тип, тривалість та ціну. Важливо: оффер застосовується тільки до користувачів, які ніколи не були абонентами цієї subscription group. Apple перевіряє це на сервері.
Читання оффера через StoreKit 2
import StoreKit
// Завантажуємо продукт
guard let product = try? await Product.products(for: ["premium_monthly"]).first else { return }
// Перевіряємо наявність introductory offer
if let intro = product.subscription?.introductoryOffer {
switch intro.paymentMode {
case .freeTrial:
// Показуємо: "Перші 7 днів безплатно"
let days = intro.period.value // наприклад, 7
let unit = intro.period.unit // .day
showFreeTrialBanner(days: days)
case .payAsYouGo:
// Показуємо: "Перший місяць за $99"
showDiscountedPriceBanner(price: intro.displayPrice, period: intro.period)
case .payUpFront:
showUpFrontBanner(price: intro.displayPrice, duration: intro.subscriptionPeriod)
@unknown default: break
}
}
Перевірка eligibility — ключовий момент
Користувач бачить introductory offer тільки якщо він eligible. Однак product.subscription?.introductoryOffer не повідомляє про eligibility безпосередньо — оффер присутній в об'єкті незалежно від того, чи має право конкретний користувач.
Перевірка через StoreKit 2:
// Перевіряємо статус підписки через Transaction
for await result in Transaction.currentEntitlements {
if case .verified(let transaction) = result {
if transaction.productID == "premium_monthly" {
// Користувач вже був абонентом — оффер не показуємо
userHasBeenSubscriber = true
}
}
}
Альтернатива — серверна перевірка через App Store Server API (/inApps/v1/subscriptions/{originalTransactionId}): сервер повертає isInBillingRetryPeriod та повну історію транзакцій, за якою можна точно визначити, використовував ли користувач introductory offer.
Не варто покладатися тільки на клієнтську перевірку для бізнес-логіки. Золоте правило: рішення про показ оффера на клієнті, валідація права на оффер — на сервері через App Store Server API або RevenueCat.
Відображення в paywall UI
Типовий сценарій: одна і та ж paywall-сторінка показує різні варіанти залежно від eligibility:
struct PaywallView: View {
let product: Product
var body: some View {
VStack {
if let intro = product.subscription?.introductoryOffer,
isEligibleForIntro {
IntroOfferBanner(offer: intro)
.transition(.opacity)
}
SubscriptionButton(product: product)
}
}
}
isEligibleForIntro — @State або @Published властивість, яка встановлюється після асинхронної перевірки транзакцій.
Використання RevenueCat
Якщо проект вже використовує RevenueCat, перевірка eligibility значно простіша:
Purchases.shared.getOfferings { offerings, error in
if let intro = offerings?.current?.monthly?.product.introductoryDiscount {
// RevenueCat сам перевіряє eligibility через StoreKit
Purchases.shared.checkTrialOrIntroductoryPriceEligibility(
productIdentifiers: ["premium_monthly"]
) { eligibilityDict in
let eligible = eligibilityDict["premium_monthly"]?.status == .eligible
}
}
}
Що входить у роботу
- Читання та відображення introductory offer з об'єкту
Product(StoreKit 2) - Перевірка eligibility через
Transaction.currentEntitlementsабо серверну валідацію - UI-компонент paywall з умовним відображенням оффера
- Тестування через StoreKit Configuration File в Xcode (sandbox без очікування 24 годин)
- Логування в аналітику: показ оффера, конверсія, тип оффера
Терміни
3–5 днів залежно від складності paywall UI та наявності серверної валідації. Вартість розраховується індивідуально після аналізу вимог.







