Реалізація промокодів і скидок у мобільних додатках
Промокоди в мобільних додатках — це три різних механізми з різною складністю реалізації: нативні промокоди App Store/Google Play, кастомна серверна логіка з власними кодами, та promotional offers через RevenueCat/Adapty. Змішувати їх без розуміння обмежень — типова помилка.
Нативні промокоди App Store
Apple дозволяє генерувати до 1000 промокодів на версію додатку (для paid apps) або на IAP. Користувач вводить код в App Store → отримує продукт. У додаток це приходить як звичайна транзакція — обробляється стандартним StoreKit flow.
Проблема: промокоди App Store не можна застосувати всередині додатку. Користувача потрібно перенаправити в App Store. Для більшості монетизаційних сценаріїв (скидка для конкретного користувача, реферальна програма) це не працює.
Власні промокоди — серверна реалізація
Кастомні промокоди живуть цілком на бекенді:
Схема даних:
promo_codes: id, code, discount_type (percent|fixed|trial_days), value,
max_uses, used_count, expires_at, product_ids[], user_id (опціонально)
promo_redemptions: id, code_id, user_id, created_at, purchase_id
Флоу на клієнті:
- Користувач вводить код → клієнт викликає
POST /api/promo/validateз кодом і product_id - Сервер повертає тип скидки й застосовану ціну
- Клієнт показує підсумкову ціну
- Покупка через нативний IAP по повній ціні → на сервері після верифікації транзакції застосовуємо скидку (нараховуємо різницю валютою, продовжуємо trial, тощо)
Пряме зміна ціни IAP на клієнті неможливе — Apple й Google не дозволяють динамічно змінювати вартість продукту. Вся логіка скидок реалізується post-purchase на сервері або через окремі продукти з уже заниженою ціною.
Promotional offers як механізм скидки
Для підписок на iOS — SKPaymentDiscount / SubscriptionOffer у StoreKit 2. Створюємо offer в App Store Connect з потрібною ціною, сервер генерує підпис для конкретного користувача. Це законний спосіб запропонувати скидку без обходу нативного біллінгу.
// Отримуємо offer з сервера
let offerSignature = await serverAPI.getPromoSignature(userID: user.id, offerID: "discount_50")
let product = try await Product.products(for: ["premium_monthly"]).first!
let purchaseOption = product.subscriptionOffer(
offerID: "discount_50",
keyID: offerSignature.keyID,
nonce: offerSignature.nonce,
signature: offerSignature.signature,
timestamp: offerSignature.timestamp
)
let result = try await product.purchase(options: [purchaseOption!])
Введення промокода в UI
Поле введення промокода — окремий екран або bottom sheet з debounce-валідацією (запит до сервера через 300мс після останнього символу). Важливо: показувати завантаження під час валідації й обробляти помилки — «код закінчився», «код уже використаний», «не застосовується до обраного продукту».
Терміни — 2–3 дні: серверна модель промокодів, API валідації й застосування, UI компонент, інтеграція з IAP flow.







