Обработка событий пейвола в iOS SDK

Этот гайд охватывает обработку событий для покупок, восстановлений, выбора продуктов и отображения пейвола. Вам также необходимо реализовать обработку кнопок (закрытие пейвола, открытие ссылок и т. д.). Подробности смотрите в нашем гайде по обработке действий кнопок.

Пейволы, настроенные с помощью Paywall Builder, не требуют дополнительного кода для совершения и восстановления покупок. Тем не менее они генерируют события, на которые ваше приложение может реагировать. К таким событиям относятся нажатия кнопок (кнопки закрытия, ссылки, выбор продуктов и т. д.), а также уведомления о действиях, связанных с покупками на пейволе. Ниже описано, как обрабатывать эти события.

Это руководство предназначено только для новых пейволов Paywall Builder, которые требуют Adapty SDK версии 3.0 или выше.

Хотите увидеть реальный пример интеграции Adapty SDK в мобильное приложение? Посмотрите наши примеры приложений — они демонстрируют полную настройку: отображение пейволов, совершение покупок и другие базовые функции.

Обработка событий в SwiftUI

Чтобы управлять процессами на экране пейвола или отслеживать их, используйте модификатор .paywall в SwiftUI:

@State var paywallPresented = false

var body: some View {
	Text("Hello, AdaptyUI!")
			.paywall(
          isPresented: $paywallPresented,
          paywall: paywall,
          viewConfiguration: viewConfig,
          didPerformAction: { action in
              switch action {
                  case .close:
                      paywallPresented = false
                  case let .openURL(url):
                      // handle opening the URL (incl. for terms and privacy)
                  default:
                      // handle other actions
              }
          },
          didSelectProduct: { /* Handle the event */  },
          didStartPurchase: { /* Handle the event */ },
          didFinishPurchase: { product, info in /* Handle the event */ },
          didFailPurchase: { product, error in /* Handle the event */ },
          didStartRestore: { /* Handle the event */ },
          didFinishRestore: { /* Handle the event */ },
          didFailRestore: { /* Handle the event */ },
          didFailRendering: { error in
              paywallPresented = false
          },
          didFailLoadingProducts: { error in
              return false
          }
      )
}

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

ПараметрОбязательныйОписание
isPresentedобязательныйПривязка, управляющая отображением экрана пейвола.
paywallConfigurationобязательныйОбъект AdaptyUI.PaywallConfiguration, содержащий визуальные параметры пейвола. Используйте метод AdaptyUI.paywallConfiguration(for:products:viewConfiguration:observerModeResolver:tagResolver:timerResolver:). Подробнее см. в разделе Получение пейволов Paywall Builder и их конфигурации.
didFailPurchaseобязательныйВызывается при ошибке покупки (например, платёж не разрешён, проблемы с сетью, недействительный продукт). Не вызывается при отмене пользователем или ожидающих платежах.
didFinishRestoreобязательныйВызывается при успешном восстановлении покупки.
didFailRestoreобязательныйВызывается при ошибке восстановления покупки.
didFailRenderingобязательныйВызывается при ошибке рендеринга интерфейса. В этом случае обратитесь в поддержку Adapty.
fullScreenнеобязательныйОпределяет, отображается ли пейвол в полноэкранном режиме или как модальное окно. По умолчанию true.
didAppearнеобязательныйВызывается, когда экран пейвола появляется на экране. Также вызывается, когда пользователь нажимает кнопку web-пейвола внутри пейвола и web-пейвол открывается во встроенном браузере.
didDisappearнеобязательныйВызывается, когда экран пейвола закрывается. Также вызывается, когда web-пейвол, открытый из пейвола во встроенном браузере, исчезает с экрана.
didPerformActionнеобязательныйВызывается при нажатии пользователем кнопки. У разных кнопок разные идентификаторы действий. Два идентификатора предопределены: close и openURL, остальные задаются в конструкторе.
didSelectProductнеобязательныйВызывается, если продукт был выбран для покупки — пользователем или системой.
didStartPurchaseнеобязательныйВызывается, когда пользователь начинает процесс покупки.
didFinishPurchaseнеобязательныйВызывается при успешном завершении покупки.
didFinishWebPaymentNavigationнеобязательныйВызывается после попытки открыть web-пейвол для покупки — успешной или нет.
didStartRestoreнеобязательныйВызывается, когда пользователь начинает процесс восстановления покупок.
didFailLoadingProductsнеобязательныйВызывается при ошибках загрузки продуктов. Верните true, чтобы повторить загрузку.
didPartiallyLoadProductsнеобязательныйВызывается, когда продукты загружены частично.
showAlertItemнеобязательныйПривязка, управляющая отображением элементов оповещения поверх пейвола.
showAlertBuilderнеобязательныйФункция для рендеринга представления оповещения.
placeholderBuilderнеобязательныйФункция для рендеринга представления-заглушки, пока пейвол загружается.

Обработка событий в UIKit

Чтобы управлять процессами на экране пейвола или отслеживать их, реализуйте методы AdaptyPaywallControllerDelegate.

События, инициированные пользователем

Выбор продукта

Если пользователь выбирает продукт для покупки, вызывается этот метод:

    func paywallController(
        _ controller: AdaptyPaywallController,
        didSelectProduct product: AdaptyPaywallProductWithoutDeterminingOffer
    ) { }
Пример события (нажмите, чтобы развернуть)
{
  "product": {
    "vendorProductId": "premium_monthly",
    "localizedTitle": "Premium Monthly",
    "localizedDescription": "Premium subscription for 1 month",
    "localizedPrice": "$9.99",
    "price": 9.99,
    "currencyCode": "USD"
  }
}

Начало покупки

Если пользователь инициирует процесс покупки, вызывается этот метод:

func paywallController(_ controller: AdaptyPaywallController,
                       didStartPurchase product: AdaptyPaywallProduct) {
}
Пример события (нажмите, чтобы развернуть)
{
  "product": {
    "vendorProductId": "premium_monthly",
    "localizedTitle": "Premium Monthly",
    "localizedDescription": "Premium subscription for 1 month",
    "localizedPrice": "$9.99",
    "price": 9.99,
    "currencyCode": "USD"
  }
}

Метод не вызывается в режиме Observer mode. Подробнее см. в разделе iOS — Отображение пейволов Paywall Builder в режиме Observer mode.

Начало покупки через web-пейвол

Если пользователь инициирует покупку через web-пейвол, вызывается этот метод:

func paywallController(
        _ controller: AdaptyPaywallController,
        shouldContinueWebPaymentNavigation product: AdaptyPaywallProduct
    ) {
    }
Пример события (нажмите, чтобы развернуть)
{
  "product": {
    "vendorProductId": "premium_monthly",
    "localizedTitle": "Premium Monthly",
    "localizedDescription": "Premium subscription for 1 month",
    "localizedPrice": "$9.99",
    "price": 9.99,
    "currencyCode": "USD"
  }
}

Успешная или отменённая покупка

Если покупка выполнена успешно, вызывается этот метод:

func paywallController(
    _ controller: AdaptyPaywallController,
    didFinishPurchase product: AdaptyPaywallProductWithoutDeterminingOffer,
    purchaseResult: AdaptyPurchaseResult
) { }
}
Примеры событий (нажмите, чтобы развернуть)
// Successful purchase
{
  "product": {
    "vendorProductId": "premium_monthly",
    "localizedTitle": "Premium Monthly",
    "localizedDescription": "Premium subscription for 1 month",
    "localizedPrice": "$9.99",
    "price": 9.99,
    "currencyCode": "USD"
  },
  "purchaseResult": {
    "type": "success",
    "profile": {
      "accessLevels": {
        "premium": {
          "id": "premium",
          "isActive": true,
          "expiresAt": "2024-02-15T10:30:00Z"
        }
      }
    }
  }
}

// Cancelled purchase
{
  "product": {
    "vendorProductId": "premium_monthly",
    "localizedTitle": "Premium Monthly",
    "localizedDescription": "Premium subscription for 1 month",
    "localizedPrice": "$9.99",
    "price": 9.99,
    "currencyCode": "USD"
  },
  "purchaseResult": {
    "type": "cancelled"
  }
}

В этом случае рекомендуем закрыть экран пейвола.

Метод не вызывается в режиме Observer mode. Подробнее см. в разделе iOS — Отображение пейволов Paywall Builder в режиме Observer mode.

Ошибка покупки

Если покупка завершается с ошибкой, вызывается этот метод. Это включает ошибки StoreKit (ограничения платежей, недействительные продукты, сбои сети), ошибки верификации транзакций и системные ошибки. Обратите внимание: отмена пользователем вызывает didFinishPurchase с результатом cancelled, а ожидающие платежи этот метод не вызывают.

func paywallController(
    _ controller: AdaptyPaywallController,
    didFailPurchase product: AdaptyPaywallProduct,
    error: AdaptyError
) { }
Пример события (нажмите, чтобы развернуть)
{
  "product": {
    "vendorProductId": "premium_monthly",
    "localizedTitle": "Premium Monthly",
    "localizedDescription": "Premium subscription for 1 month",
    "localizedPrice": "$9.99",
    "price": 9.99,
    "currencyCode": "USD"
  },
  "error": {
    "code": "purchase_failed",
    "message": "Purchase failed due to insufficient funds",
    "details": {
      "underlyingError": "Insufficient funds in account"
    }
  }
}

Метод не вызывается в режиме Observer mode. Подробнее см. в разделе iOS — Отображение пейволов Paywall Builder в режиме Observer mode.

Ошибка покупки через web-пейвол

Если Adapty.openWebPaywall() завершается с ошибкой, вызывается этот метод:

func paywallController(
        _ controller: AdaptyPaywallController,
        didFailWebPaymentNavigation product: AdaptyPaywallProduct,
        error: AdaptyError
    ) { }
Пример события (нажмите, чтобы развернуть)
{
  "product": {
    "vendorProductId": "premium_monthly",
    "localizedTitle": "Premium Monthly",
    "localizedDescription": "Premium subscription for 1 month",
    "localizedPrice": "$9.99",
    "price": 9.99,
    "currencyCode": "USD"
  },
  "error": {
    "code": "web_payment_failed",
    "message": "Web payment navigation failed",
    "details": {
      "underlyingError": "Network connection error"
    }
  }
}

Успешное восстановление покупок

Если восстановление покупки выполнено успешно, вызывается этот метод:

func paywallController(
    _ controller: AdaptyPaywallController, 
    didFinishRestoreWith profile: AdaptyProfile
) { }
Пример события (нажмите, чтобы развернуть)
{
  "profile": {
    "accessLevels": {
      "premium": {
        "id": "premium",
        "isActive": true,
        "expiresAt": "2024-02-15T10:30:00Z"
      }
    },
    "subscriptions": [
      {
        "vendorProductId": "premium_monthly",
        "isActive": true,
        "expiresAt": "2024-02-15T10:30:00Z"
      }
    ]
  }
}

Рекомендуем закрыть экран, если у пользователя есть необходимый accessLevel. Как это проверить — читайте в разделе Статус подписки.

Ошибка восстановления покупок

Если восстановление покупки завершается с ошибкой, вызывается этот метод:

public func paywallController(
    _ controller: AdaptyPaywallController, 
    didFailRestoreWith error: AdaptyError
) { }
Пример события (нажмите, чтобы развернуть)
{
  "error": {
    "code": "restore_failed",
    "message": "Purchase restoration failed",
    "details": {
      "underlyingError": "No previous purchases found"
    }
  }
}

Загрузка данных и рендеринг

Ошибки загрузки продуктов

Если массив продуктов не передан при инициализации, AdaptyUI самостоятельно запросит необходимые объекты с сервера. В случае ошибки AdaptyUI сообщит о ней через этот метод:

public func paywallController(
    _ controller: AdaptyPaywallController,
    didFailLoadingProductsWith error: AdaptyError
) -> Bool {
    return true
}
Пример события (нажмите, чтобы развернуть)
{
  "error": {
    "code": "products_loading_failed",
    "message": "Failed to load products from the server",
    "details": {
      "underlyingError": "Network timeout"
    }
  }
}

Если вернуть true, AdaptyUI повторит запрос через 2 секунды.

Ошибки рендеринга

Если при рендеринге интерфейса возникает ошибка, она будет сообщена через этот метод:

public func paywallController(
    _ controller: AdaptyPaywallController,
    didFailRenderingWith error: AdaptyError
) { }
Пример события (нажмите, чтобы развернуть)
{
  "error": {
    "code": "rendering_failed",
    "message": "Failed to render paywall interface",
    "details": {
      "underlyingError": "Invalid paywall configuration"
    }
  }
}

В штатной ситуации такие ошибки возникать не должны — если всё же столкнётесь с одной из них, пожалуйста, сообщите нам.