Flutter - Обработка событий флоу и пейвола

Это руководство охватывает обработку событий покупок, восстановлений, выбора продукта и рендеринга. Закрытие экрана и открытие ссылок обрабатываются реализацией flowViewDidPerformAction по умолчанию — см. наш гайд по обработке действий кнопок, чтобы переопределить их или обработать пользовательские действия кнопок.

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

Чтобы управлять процессами, происходящими на экране флоу или пейвола в вашем мобильном приложении, или отслеживать их, реализуйте методы AdaptyUIFlowsEventsObserver и установите наблюдатель перед отображением любого экрана:

AdaptyUI().setFlowsEventsObserver(this);

Три метода наблюдателя обязательны — без них класс не скомпилируется: flowViewDidFinishPurchase, flowViewDidFinishRestore и flowViewDidReceiveError. Остальные методы опциональны. Чтобы отвязать ранее установленный наблюдатель, передайте null в setFlowsEventsObserver.

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

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

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

Отображение вью

Этот метод вызывается, когда флоу или вью пейвола появляется на экране.

На iOS также вызывается, когда пользователь нажимает кнопку веб-пейвола внутри пейвола и веб-пейвол открывается во встроенном браузере.

void flowViewDidAppear(AdaptyUIFlowView view) {
}

Скрытие вью

Этот метод вызывается, когда флоу или вью пейвола убирается с экрана.

На iOS также вызывается, когда веб-пейвол, открытый из пейвола во встроенном браузере, исчезает с экрана.

void flowViewDidDisappear(AdaptyUIFlowView view) {
}

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

Если продукт выбран для покупки (пользователем или системой), будет вызван следующий метод:

void flowViewDidSelectProduct(AdaptyUIFlowView view, String productId) {
}
Пример события (нажмите, чтобы развернуть)
void flowViewDidSelectProduct(AdaptyUIFlowView view, String productId) {
  // productId is a String:
  productId; // 'premium_monthly'
}

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

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

void flowViewDidStartPurchase(AdaptyUIFlowView view, AdaptyPaywallProduct product) {
}
Пример события (нажмите, чтобы развернуть)
void flowViewDidStartPurchase(AdaptyUIFlowView view, AdaptyPaywallProduct product) {
  // product — AdaptyPaywallProduct:
  product.vendorProductId;        // 'premium_monthly'
  product.localizedTitle;         // 'Premium Monthly'
  product.localizedDescription;   // 'Premium subscription for 1 month'
  product.price.amount;           // 9.99            (double)
  product.price.currencyCode;     // 'USD'
  product.price.localizedString;  // '$9.99'
}

Завершённая покупка

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

void flowViewDidFinishPurchase(AdaptyUIFlowView view, 
                               AdaptyPaywallProduct product, 
                               AdaptyPurchaseResult purchaseResult) {
    switch (purchaseResult) {
      case AdaptyPurchaseResultSuccess(profile: final profile):
        // successful purchase
        break;
      case AdaptyPurchaseResultPending():
        // purchase is pending
        break;
      case AdaptyPurchaseResultUserCancelled():
        // user cancelled the purchase
        break;
      default:
        break;
    }
}
Примеры событий (нажмите, чтобы развернуть)
void flowViewDidFinishPurchase(AdaptyUIFlowView view,
                               AdaptyPaywallProduct product,
                               AdaptyPurchaseResult purchaseResult) {
  // product — AdaptyPaywallProduct:
  product.vendorProductId; // 'premium_monthly'

  switch (purchaseResult) {
    case AdaptyPurchaseResultSuccess(profile: final profile):
      // profile — AdaptyProfile:
      profile.accessLevels['premium']?.isActive;  // true
      profile.accessLevels['premium']?.expiresAt; // DateTime(2027, 2, 15, 10, 30)
      break;
    case AdaptyPurchaseResultPending():
      // no additional data
      break;
    case AdaptyPurchaseResultUserCancelled():
      // no additional data
      break;
  }
}

В отличие от v3, у этого метода нет поведения по умолчанию — экран больше не закрывается автоматически после успешной покупки. Решите самостоятельно, что происходит дальше: продолжить флоу или вызвать view.dismiss(). Подробнее об управлении закрытием экрана — в разделе Реагирование на действия кнопок.

Завершение навигации в веб-платёжке

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

void flowViewDidFinishWebPaymentNavigation(AdaptyUIFlowView view, 
                                           AdaptyPaywallProduct? product, 
                                           AdaptyError? error) {
}

Параметры:

ПараметрОписание
productОбъект AdaptyPaywallProduct, для которого был открыт веб-пейвол. Может быть null.
errorОбъект AdaptyError, если навигация по веб-пейволу завершилась ошибкой; null, если навигация прошла успешно.

Неудачная покупка

Этот метод вызывается при неудачной попытке покупки (например, из-за проблем с оплатой или сетевых ошибок). Он не срабатывает при отмене пользователем или ожидающих транзакциях — они обрабатываются через flowViewDidFinishPurchase:

void flowViewDidFailPurchase(AdaptyUIFlowView view, 
                             AdaptyPaywallProduct product, 
                             AdaptyError error) {
}

Восстановление начато

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

void flowViewDidStartRestore(AdaptyUIFlowView view) {
}

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

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

void flowViewDidFinishRestore(AdaptyUIFlowView view, AdaptyProfile profile) {
}
Пример события (нажмите, чтобы развернуть)
void flowViewDidFinishRestore(AdaptyUIFlowView view, AdaptyProfile profile) {
  // profile — AdaptyProfile:
  profile.accessLevels['premium']?.isActive;            // true
  profile.accessLevels['premium']?.expiresAt;           // DateTime(2027, 2, 15, 10, 30)
  profile.subscriptions['premium_monthly']?.isActive;   // true
  profile.subscriptions['premium_monthly']?.expiresAt;  // DateTime(2027, 2, 15, 10, 30)
}

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

Неудачное восстановление

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

void flowViewDidFailRestore(AdaptyUIFlowView view, AdaptyError error) {
}

Загрузка данных и отображение

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

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

void flowViewDidFailLoadingProducts(AdaptyUIFlowView view, AdaptyError error) {
}

Ошибки отображения

Этот метод обязателен. Он заменяет метод paywallViewDidFailRendering из v3: ошибки, возникающие при рендеринге интерфейса, а также другие ошибки представления, передаются через него. После реализации метода решение о закрытии остаётся за вами — мы рекомендуем закрывать представление при таких ошибках; именно так ведёт себя встроенная логика SDK по умолчанию, когда наблюдатель не задан:

void flowViewDidReceiveError(AdaptyUIFlowView view, AdaptyError error) {
  // log the error and dismiss the broken view
  view.dismiss();
}

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

Аналитические события

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

Обработка покупок в режиме наблюдателя

Если вы активировали SDK в режиме наблюдателя и отображаете флоу или пейвол, отрисованный Adapty, SDK не совершает покупки самостоятельно. Когда пользователь нажимает кнопку покупки или восстановления, SDK вызывает ваш AdaptyUIObserverModeResolver. Подробнее о настройке читайте в разделе Отображение флоу в режиме наблюдателя.

Обработка системных запросов

AdaptyUISystemRequestsHandler (регистрируется через AdaptyUI().setSystemRequestsHandler(...)) предназначен для системных запросов из флоу: запросы разрешений ОС (например, push-уведомления или доступ к камере) и запросы на оценку в App Store. Флоу пока не инициируют такие запросы, поэтому регистрировать обработчик не нужно. Если вы регистрируете обработчик, обратите внимание: handlePermission — это обязательный метод класса; запросите разрешение своим кодом, затем верните AdaptyUIPermissionResult.granted() или AdaptyUIPermissionResult.denied(); handleAppReviewRequest — необязательный.

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

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

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

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

AdaptyUI().setPaywallsEventsObserver(this);

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

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

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

Пейвол появился

Этот метод вызывается, когда представление пейвола отображается на экране.

На iOS также вызывается, когда пользователь нажимает на кнопку веб-пейвола внутри пейвола, и веб-пейвол открывается во встроенном браузере.

void paywallViewDidAppear(AdaptyUIPaywallView view) {
}

Пейвол исчез

Этот метод вызывается, когда представление пейвола убирается с экрана.

На iOS также вызывается, когда веб-пейвол, открытый из пейвола во встроенном браузере, исчезает с экрана.

void paywallViewDidDisappear(AdaptyUIPaywallView view) {
}

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

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

void paywallViewDidSelectProduct(AdaptyUIPaywallView view, String productId) {
}
Пример события (нажмите, чтобы развернуть)
void paywallViewDidSelectProduct(AdaptyUIPaywallView view, String productId) {
  // productId is a String:
  productId; // 'premium_monthly'
}

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

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

void paywallViewDidStartPurchase(AdaptyUIPaywallView view, AdaptyPaywallProduct product) {
}
Пример события (нажмите, чтобы развернуть)
void paywallViewDidStartPurchase(AdaptyUIPaywallView view, AdaptyPaywallProduct product) {
  // product — AdaptyPaywallProduct:
  product.vendorProductId;        // 'premium_monthly'
  product.localizedTitle;         // 'Premium Monthly'
  product.localizedDescription;   // 'Premium subscription for 1 month'
  product.price.amount;           // 9.99            (double)
  product.price.currencyCode;     // 'USD'
  product.price.localizedString;  // '$9.99'
}

Завершённая покупка

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

void paywallViewDidFinishPurchase(AdaptyUIPaywallView view, 
                                  AdaptyPaywallProduct product, 
                                  AdaptyPurchaseResult purchaseResult) {
    switch (purchaseResult) {
      case AdaptyPurchaseResultSuccess(profile: final profile):
        // successful purchase
        break;
      case AdaptyPurchaseResultPending():
        // purchase is pending
        break;
      case AdaptyPurchaseResultUserCancelled():
        // user cancelled the purchase
        break;
      default:
        break;
    }
}
Примеры событий (нажмите, чтобы развернуть)
void paywallViewDidFinishPurchase(AdaptyUIPaywallView view,
                                  AdaptyPaywallProduct product,
                                  AdaptyPurchaseResult purchaseResult) {
  // product — AdaptyPaywallProduct:
  product.vendorProductId; // 'premium_monthly'

  switch (purchaseResult) {
    case AdaptyPurchaseResultSuccess(profile: final profile):
      // profile — AdaptyProfile:
      profile.accessLevels['premium']?.isActive;  // true
      profile.accessLevels['premium']?.expiresAt; // DateTime(2027, 2, 15, 10, 30)
      break;
    case AdaptyPurchaseResultPending():
      // no additional data
      break;
    case AdaptyPurchaseResultUserCancelled():
      // no additional data
      break;
  }
}

Мы рекомендуем закрывать экран в этом случае. Подробнее о закрытии экрана пейвола см. в разделе Реакция на действия кнопок.

Завершение навигации веб-платежа

Этот метод вызывается после попытки открыть веб-пейвол для конкретного продукта. Это включает как успешные, так и неудачные попытки навигации:

void paywallViewDidFinishWebPaymentNavigation(AdaptyUIPaywallView view, 
                                               AdaptyPaywallProduct? product, 
                                               AdaptyError? error) {
}

Параметры:

ПараметрОписание
productAdaptyPaywallProduct — продукт, для которого открыт веб-пейвол. Может быть null.
errorОбъект AdaptyError, если навигация по веб-пейволу завершилась с ошибкой; null, если навигация прошла успешно.
Примеры событий (нажмите, чтобы развернуть)
void paywallViewDidFinishWebPaymentNavigation(AdaptyUIPaywallView view,
                                               AdaptyPaywallProduct? product,
                                               AdaptyError? error) {
  // product — AdaptyPaywallProduct?:
  product?.vendorProductId; // 'premium_monthly'

  if (error == null) {
    // navigation succeeded
  } else {
    // error — AdaptyError:
    error.code;    // AdaptyErrorCode.networkFailed (2005)
    error.message; // 'Network request failed'
    error.detail;  // platform-specific underlying error, or null
  }
}

Неудачная покупка

Этот метод вызывается, когда покупка завершается ошибкой (например, из-за проблем с оплатой или сетевых ошибок). Он не срабатывает при отмене пользователем или незавершённых транзакциях — они обрабатываются через paywallViewDidFinishPurchase:

void paywallViewDidFailPurchase(AdaptyUIPaywallView view, 
                                AdaptyPaywallProduct product, 
                                AdaptyError error) {
}
Пример события (нажмите, чтобы развернуть)
void paywallViewDidFailPurchase(AdaptyUIPaywallView view,
                                AdaptyPaywallProduct product,
                                AdaptyError error) {
  // product — AdaptyPaywallProduct:
  product.vendorProductId; // 'premium_monthly'

  // error — AdaptyError:
  error.code;    // AdaptyErrorCode.productPurchaseFailed (1006)
  error.message; // 'Product purchase failed.'
  error.detail;  // platform-specific underlying error, or null
}

Восстановление начато

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

void paywallViewDidStartRestore(AdaptyUIPaywallView view) {
}

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

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

void paywallViewDidFinishRestore(AdaptyUIPaywallView view, AdaptyProfile profile) {
}
Пример события (нажмите, чтобы развернуть)
void paywallViewDidFinishRestore(AdaptyUIPaywallView view, AdaptyProfile profile) {
  // profile — AdaptyProfile:
  profile.accessLevels['premium']?.isActive;            // true
  profile.accessLevels['premium']?.expiresAt;           // DateTime(2027, 2, 15, 10, 30)
  profile.subscriptions['premium_monthly']?.isActive;   // true
  profile.subscriptions['premium_monthly']?.expiresAt;  // DateTime(2027, 2, 15, 10, 30)
}

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

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

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

void paywallViewDidFailRestore(AdaptyUIPaywallView view, AdaptyError error) {
}
Пример события (нажмите, чтобы развернуть)
void paywallViewDidFailRestore(AdaptyUIPaywallView view, AdaptyError error) {
  // error — AdaptyError:
  error.code;    // AdaptyErrorCode.receiveRestoredTransactionsFailed (1011)
  error.message; // 'Error occurred in the process of restoring purchases.'
  error.detail;  // platform-specific underlying error, or null
}

Получение данных и рендеринг

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

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

void paywallViewDidFailLoadingProducts(AdaptyUIPaywallView view, AdaptyError error) {
}
Пример события (нажмите, чтобы развернуть)
void paywallViewDidFailLoadingProducts(AdaptyUIPaywallView view, AdaptyError error) {
  // error — AdaptyError:
  error.code;    // AdaptyErrorCode.productRequestFailed (1002)
  error.message; // 'Unable to fetch available In-App Purchase products at the moment.'
  error.detail;  // platform-specific underlying error, or null
}

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

Если во время отображения интерфейса возникает ошибка, она сообщается путём вызова этого метода. По умолчанию (начиная с v3.15.2) пейвол автоматически закрывается при ошибке рендеринга, но при необходимости это поведение можно переопределить.

void paywallViewDidFailRendering(AdaptyUIPaywallView view, AdaptyError error) {
  // Default behavior: view.dismiss()
  // Override with custom logic if needed, for example:
  // - Log the error
  // - Show an error message to the user
}
Пример события (нажмите, чтобы развернуть)
void paywallViewDidFailRendering(AdaptyUIPaywallView view, AdaptyError error) {
  // error — AdaptyError:
  error.code;    // AdaptyErrorCode.jsException (4105)
  error.message; // 'An exception was thrown from JS during AdaptyUI flow execution.'
  error.detail;  // platform-specific underlying error, or null

  // Default behavior: view.dismiss()
}

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