Покупки в мобильном приложении в Kotlin Multiplatform SDK

Отображение пейволов в мобильном приложении — это ключевой шаг для предоставления пользователям доступа к премиум-контенту или сервисам. Однако само по себе отображение пейвола поддерживает покупки только тогда, когда экран отрисовывает Adapty — то есть флоу или пейвол из старого Paywall Builder.

Если экран отрисовывается в вашем собственном коде, для завершения покупки и открытия нужного контента необходимо использовать отдельный метод .makePurchase(). Именно через него пользователи совершают транзакции с пейволов.

Если в вашем пейволе есть активный promotional offer для продукта, который пользователь пытается купить, Adapty автоматически применит его в момент покупки.

Warning

Учтите, что introductory offer будет применён автоматически только в том случае, если экран отрисовывает Adapty.

В остальных случаях вам нужно проверить право пользователя на получение introductory offer на iOS. Пропуск этого шага может привести к отклонению приложения при релизе. Кроме того, пользователи, имеющие право на introductory offer, могут быть списаны по полной цене.

Убедитесь, что вы выполнили начальную настройку, не пропустив ни одного шага. Без неё мы не сможем валидировать покупки.

Совершение покупки

Note

Adapty отображает ваш экран? Для флоу или пейвола, созданного в Paywall Builder, покупки обрабатываются автоматически — этот шаг можно пропустить.

Нужна пошаговая инструкция? Ознакомьтесь с гайдом по быстрому старту — там есть полное описание реализации с контекстом.


Adapty.makePurchase(product = product).onSuccess { purchaseResult ->
    when (purchaseResult) {
        is AdaptyPurchaseResult.Success -> {
            val profile = purchaseResult.profile
            if (profile.accessLevels["YOUR_ACCESS_LEVEL"]?.isActive == true) {
                // Grant access to the paid features
            }
        }
        is AdaptyPurchaseResult.UserCanceled -> {
            // Handle the case where the user canceled the purchase
        }
        is AdaptyPurchaseResult.Pending -> {
            // Handle deferred purchases (e.g., the user will pay offline with cash)
        }
    }
}.onError { error ->
    // Handle the error
}
ПараметрНаличиеОписание
ProductrequiredОбъект AdaptyPaywallProduct, полученный из пейвола.

Параметры ответа:

ПараметрОписание
Profile

Если запрос выполнен успешно, ответ содержит этот объект. Объект AdaptyProfile предоставляет исчерпывающую информацию об уровнях доступа пользователя, подписках и разовых покупках внутри приложения.

Проверьте статус уровня доступа, чтобы определить, есть ли у пользователя необходимый доступ к приложению.

Warning

Примечание: если вы используете Apple StoreKit версии ниже v2.0 и Adapty SDK версии ниже v2.9.0, вам необходимо указать общий секрет Apple App Store вместо этого. Этот метод в настоящее время устарел по решению Apple.

Изменение подписки при совершении покупки

Когда пользователь выбирает новую подписку вместо продления текущей, поведение зависит от стора. В Google Play подписка не обновляется автоматически. Вам нужно управлять переключением в коде мобильного приложения, как описано ниже.

Чтобы заменить подписку другой в Android, вызовите метод .makePurchase() с дополнительным параметром:


val subscriptionUpdateParams = AdaptyAndroidSubscriptionUpdateParameters(
    oldSubVendorProductId = "old_subscription_product_id",
    replacementMode = AdaptyAndroidSubscriptionUpdateReplacementMode.CHARGE_FULL_PRICE
)

val purchaseParams = AdaptyPurchaseParameters.Builder()
    .setSubscriptionUpdateParams(subscriptionUpdateParams)
    .build()

Adapty.makePurchase(
    product = product,
    parameters = purchaseParams
).onSuccess { purchaseResult ->
    when (purchaseResult) {
        is AdaptyPurchaseResult.Success -> {
            val profile = purchaseResult.profile
            // successful cross-grade
        }
        is AdaptyPurchaseResult.UserCanceled -> {
            // user canceled the purchase flow
        }
        is AdaptyPurchaseResult.Pending -> {
            // the purchase has not been finished yet, e.g. user will pay offline by cash
        }
    }
}.onError { error ->
    // Handle the error
}

Дополнительный параметр запроса:

ПараметрНаличиеОписание
parametersопциональнообъект AdaptyAndroidSubscriptionUpdateParameters, передаваемый через AdaptyPurchaseParameters.

Подробнее о подписках и режимах замены можно прочитать в документации Google для разработчиков:

Погашение промокодов в iOS

Об offer-кодах

Offer-коды позволяют предоставлять скидки или бесплатные пробные периоды конкретным пользователям. В отличие от обычных предложений, которые применяются автоматически, offer-коды распространяются за пределами приложения — через email-рассылки, соцсети или печатные материалы. Пользователи активируют их, вводя код в App Store, переходя по ссылке для активации или через диалог внутри приложения.

Чтобы настроить offer-коды, откройте подписку в App Store Connect и перейдите в раздел Offer Codes. Вы можете создать три вида offer-кодов:

  • Free — подписка бесплатна в течение заданного срока, следующее продление — по полной цене.
  • Pay as you go — пользователь платит сниженную цену за каждый расчётный период в течение заданного срока, затем подписка продлевается по полной цене.
  • Pay up front — пользователь единовременно платит сниженную цену за весь срок предложения, затем подписка продлевается по полной цене.

Добавлять offer-коды в Adapty не нужно. Apple помечает каждую транзакцию в течение периода предложения категорией offer-кода. Это касается как первоначальной активации, так и всех последующих продлений со скидкой. Adapty обнаруживает метку и записывает каждую транзакцию с категорией предложения offer_code. Когда период предложения заканчивается и подписка продлевается по полной цене, метка больше не проставляется. Вы можете фильтровать аналитику по типу предложения Offer Code в дашборде Adapty.

Устранение расхождений в выручке

Если транзакция по offer-коду отображается в Adapty по полной цене продукта вместо сниженной, проверьте следующее в App Store Connect:

  • Для offer-кода настроены корректные цены для всех регионов, где пользователи могут его активировать.
  • Цена предложения задана для конкретной страны или региона пользователя. Apple передаёт региональную цену в транзакции. Если для предложения не указана региональная цена, Apple может передать полную цену продукта.

Вы можете фильтровать и проверять транзакции по offer-кодам в дашборде Adapty с помощью фильтров по типу предложения Offer Code и Offer Discount Type.

Устаревшие промокоды (deprecated)

Warning

Apple отказалась от промокодов для встроенных покупок в марте 2026 года. Offer-коды заменяют их с расширенными возможностями: настраиваемые условия применения, сроки действия и до 1 миллиона кодов в квартал. Если вы ранее использовали промокоды для встроенных покупок, перейдите на offer-коды в App Store Connect.

Устаревшие промокоды (не более 100 на приложение на версию) предоставляли бесплатный доступ к подписке. В отличие от offer-кодов, Apple не включала информацию о скидке в транзакции по промокодам — в чеке передавалась полная цена продукта. В результате Adapty записывал эти транзакции по полной цене, что приводило к расхождениям в выручке между аналитикой Adapty и App Store Connect.

Если вы видите исторические транзакции по полной цене, которые должны были быть бесплатными, скорее всего, они относятся к устаревшим промокодам. Поскольку такие коды теперь устарели, переходите на offer-коды для точного учёта выручки.

Чтобы показать в приложении экран погашения промокодов:

Adapty.presentCodeRedemptionSheet()
    .onSuccess {
        // code redemption sheet presented successfully
    }
    .onError { error ->
        // handle the error
    }
Danger

По нашим наблюдениям, экран погашения промокодов в некоторых приложениях может работать нестабильно. Рекомендуем перенаправлять пользователя напрямую в App Store.

Чтобы сделать это, нужно открыть URL следующего формата: https://apps.apple.com/redeem?ctx=offercodes&id={apple_app_id}&code={code}

Управление предоплаченными планами (Android)

Если пользователи вашего приложения могут приобретать предоплаченные планы (например, неавтоматически возобновляемую подписку на несколько месяцев), вы можете включить отложенные транзакции для предоплаченных планов.


Adapty.activate(
    AdaptyConfig.Builder("PUBLIC_SDK_KEY")
        .withGoogleEnablePendingPrepaidPlans(true)
        .build()
).onSuccess {
    // successful activation
}.onError { error ->
    // handle the error
}

Продвигаемые встроенные покупки из App Store

Info

Ваше приложение получает продвигаемые встроенные покупки начиная с SDK версии 4.1, на iOS 16.4 и выше. На iOS ниже 16.4 слушатель никогда не срабатывает, а продвигаемые покупки завершаются самостоятельно. Это функция только для iOS: на Android слушатель также никогда не срабатывает, а makePromotedPurchase возвращает AdaptyErrorCode.DEVELOPER_ERROR.

Когда пользователь начинает покупку со страницы вашего продукта в App Store и транзакция переходит в ваше приложение, SDK передаёт продукт через OnPromotedPurchaseListener. Завершение покупки остаётся за вашим приложением: передайте продукт в makePromotedPurchase. Поскольку вы сами контролируете момент запуска, можно показать собственный экран до начала покупки.

Чтобы поддержать продвигаемые покупки, зарегистрируйте слушателя и завершите покупку из него:


Adapty.setOnPromotedPurchaseListener(OnPromotedPurchaseListener { product ->
    scope.launch {
        Adapty.makePromotedPurchase(product)
            .onSuccess { result -> /* process the purchase result */ }
            .onError { error -> /* handle the error */ }
    }
})
Warning

Без зарегистрированного слушателя продвигаемая покупка никогда не завершится: App Store передаёт продукт вашему приложению и ждёт. Передача null в setOnPromotedPurchaseListener также останавливает работу продвигаемых покупок.

Регистрируйте слушатель при запуске приложения, сразу после Adapty.activate. Продвигаемая покупка обычно запускает приложение с нуля, поэтому она нередко достигает SDK раньше, чем выполнится ваша регистрация. SDK удерживает одну такую покупку и доставляет её сразу после регистрации, сохраняя только самую последнюю, и каждый раз выводит предупреждение в консоль, когда удерживает покупку.

Если продвигаемый продукт содержит предложение по подписке, SDK автоматически применяет его при покупке. Предложение считывается из намерения о покупке App Store, которое доступно начиная с iOS 18.0. На iOS 16.4–17.x покупка проходит по базовой цене.

makePromotedPurchase не принимает параметры покупки — продвигаемый продукт поступает из App Store, а не из пейвола, поэтому у него нет контекста пейвола. Метод возвращает тот же AdaptyPurchaseResult, что и makePurchase.

AdaptyPromotedProduct содержит vendorProductId, localizedTitle, localizedDescription, price, regionCode, isFamilyShareable и subscription типа AdaptyProductSubscription.