Реализация Promotional Offers для существующих подписчиков в мобильном приложении

TRUETECH занимается разработкой, поддержкой и обслуживанием мобильных приложений iOS, Android, PWA. Имеем большой опыт и экспертизу для публикации мобильных приложений в популярные маркеты Google Play, App Store, Amazon, AppGallery и другие.
Разработка и поддержка любых видов мобильных приложений:
Информационные и развлекательные мобильные приложения
Новостные приложения, игры, справочники, онлайн-каталоги, погодные, фитнес и здоровье, туристические, образовательные, социальные сети и мессенджеры, квиз, блоги и подкасты, форумы, агрегаторы
Мобильные приложения электронной коммерции
Интернет-магазины, B2B-приложения, маркетплейсы, онлайн-обменники, кэшбэк-сервисы, биржи, дропшиппинг-платформы, программы лояльности, доставка еды и товаров, платежные системы
Мобильные приложения для управления бизнес-процессами
CRM-системы, ERP-системы, управление проектами, инструменты для команды продаж, учет финансов, управление производством, логистика и доставка, управление персоналом, системы мониторинга данных
Мобильные приложения электронных услуг
Доски объявлений, онлайн-школы, онлайн-кинотеатры, платформы предоставления электронных услуг, платформы кешбека, видеохостинги, тематические порталы, платформы онлайн-бронирования и записи, платформы онлайн-торговли

Это лишь некоторые из типы мобильных приложений, с которыми мы работаем, и каждый из них может иметь свои специфические особенности и функциональность, а также быть адаптированным под конкретные потребности и цели клиента.

Предлагаемые услуги
Показано 1 из 1 услугВсе 1735 услуг
Реализация Promotional Offers для существующих подписчиков в мобильном приложении
Средняя
~3-5 рабочих дней
Часто задаваемые вопросы
Наши компетенции:
Этапы разработки
Последние работы
  • image_mobile-applications_feedme_467_0.webp
    Разработка мобильного приложения для компании FEEDME
    756
  • image_mobile-applications_xoomer_471_0.webp
    Разработка мобильного приложения для компании XOOMER
    624
  • image_mobile-applications_rhl_428_0.webp
    Разработка мобильного приложения для компании RHL
    1054
  • image_mobile-applications_zippy_411_0.webp
    Разработка мобильного приложения для компании ZIPPY
    947
  • image_mobile-applications_affhome_429_0.webp
    Разработка мобильного приложения для компании Affhome
    864
  • image_mobile-applications_flavors_409_0.webp
    Разработка мобильного приложения для компании FLAVORS
    445

Реализация Promotional Offers для существующих подписчиков в мобильном приложении

Promotional Offers — это персонализированные скидки для пользователей, которые уже были подписчиками. Introductory Offer можно предложить только один раз и только новому подписчику. Promotional Offer — повторно и только тем, кто уже имел или имеет активную подписку. Типичные сценарии: вернуть пользователя, который отменил подписку; предотвратить отмену через win-back оффер; перевести на более высокий тариф со скидкой.

Отличие от Introductory Offers

Introductory Offer Promotional Offer
Для кого Новые подписчики Существующие/бывшие
Сколько раз Один раз Многократно
Требует подписи сервера Нет Да — обязательно
Настройка App Store Connect App Store Connect + сервер

Серверная подпись — ключевое отличие. Apple требует, чтобы предложение было подписано приватным ключом, сгенерированным в App Store Connect. Без этого оффер не применится — StoreKit вернёт ошибку invalidSignature.

Настройка в App Store Connect

  1. Subscriptions → [Subscription] → Promotional Offers → +
  2. Задаём Reference Name, Offer ID, тип (freeTrial / payAsYouGo / payUpFront), длительность и цену
  3. Сохраняем Offer ID — он потребуется при генерации подписи

Параллельно: Keys → Subscription Key → создаём ключ, скачиваем .p8 файл и запоминаем Key ID.

Серверная подпись

Сервер генерирует подпись по алгоритму ECDSA с ключом .p8. Параметры:

  • appBundleId — bundle ID приложения
  • keyIdentifier — Key ID из App Store Connect
  • productIdentifier — ID продукта
  • offerIdentifier — Offer ID
  • applicationUsername — ID пользователя в вашей системе (опционально, но рекомендуется)
  • nonce — UUID, генерируется сервером
  • timestamp — текущее время в миллисекундах

Подпись создаётся из конкатенации этих значений через \n, подписывается SHA-256 ECDSA:

# Python-пример для сервера (упрощённо)
from cryptography.hazmat.primitives import hashes, serialization
from cryptography.hazmat.primitives.asymmetric import ec
import base64, uuid, time

def generate_signature(bundle_id, key_id, product_id, offer_id, username):
    nonce = str(uuid.uuid4()).lower()
    timestamp = str(int(time.time() * 1000))

    message = "\n".join([bundle_id, key_id, product_id, offer_id, username, nonce, timestamp])

    private_key = serialization.load_pem_private_key(PRIVATE_KEY_PEM, password=None)
    signature = private_key.sign(message.encode(), ec.ECDSA(hashes.SHA256()))
    encoded = base64.b64encode(signature).decode()

    return {"nonce": nonce, "timestamp": timestamp, "signature": encoded, "keyIdentifier": key_id}

Сервер возвращает эти данные клиенту; клиент использует их при оформлении покупки.

Применение на клиенте (StoreKit 2)

import StoreKit

// Получаем параметры подписи с сервера
let signatureData = try await apiClient.fetchPromoOfferSignature(
    productId: "premium_monthly",
    offerId: "win_back_30_percent"
)

// Получаем продукт
guard let product = try? await Product.products(for: ["premium_monthly"]).first else { return }

// Находим оффер по ID
guard let offer = product.subscription?.promotionalOffers.first(where: {
    $0.id == "win_back_30_percent"
}) else { return }

// Создаём объект подписанного оффера
let signedOffer = try await offer.purchase(
    confirmIn: self, // WindowScene или UIViewController
    options: [
        .promotionalOffer(
            offerIdentifier: signatureData.offerId,
            keyIdentifier: signatureData.keyIdentifier,
            nonce: UUID(uuidString: signatureData.nonce)!,
            signature: Data(base64Encoded: signatureData.signature)!,
            timestamp: signatureData.timestamp
        )
    ]
)

Типичные ошибки

Истёкший timestamp. Подпись действительна 24 часа. Если кэшировать её дольше — Apple вернёт ошибку. Генерировать подпись нужно непосредственно перед показом paywall, а не при старте приложения.

Неверный nonce. Nonce должен быть в нижнем регистре (UUID.uuidString.lowercased()). Регистр влияет на валидность подписи.

Предложение показывается всем. Проверка права на promotional offer — ответственность разработчика. Apple не блокирует покупку, если eligibility не проверялась. Нужна серверная проверка истории транзакций: был ли пользователь подписчиком хотя бы один раз.

Что входит в работу

  • Настройка Promotional Offer в App Store Connect
  • Серверный endpoint генерации подписи (ECDSA)
  • Клиентская интеграция StoreKit 2 с promotionalOffer options
  • Проверка eligibility (серверная история транзакций)
  • Тестирование в Sandbox через StoreKit Configuration File

Сроки

3–5 дней — с учётом серверной части (генерация подписи). Если серверная инфраструктура уже готова — 2–3 дня. Стоимость рассчитывается индивидуально.