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

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

Пейволы, настроенные с помощью 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) {
}
Пример события (нажмите, чтобы развернуть)
{
  "productId": "premium_monthly"
}

Покупка начата

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

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

Покупка завершена

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

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;
    }
}
Примеры событий (нажмите, чтобы развернуть)
// 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": "AdaptyPurchaseResultSuccess",
    "profile": {
      "accessLevels": {
        "premium": {
          "id": "premium",
          "isActive": true,
          "expiresAt": "2024-02-15T10:30:00Z"
        }
      }
    }
  }
}

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

// User 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": "AdaptyPurchaseResultUserCancelled"
  }
}

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

Навигация по веб-оплате завершена

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

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

Параметры:

ПараметрОписание
productAdaptyPaywallProduct, для которого был открыт веб-пейвол. Может быть null.
errorОбъект AdaptyError, если навигация по веб-пейволу не удалась; null, если навигация прошла успешно.
Примеры событий (нажмите, чтобы развернуть)
// Successful navigation
{
  "product": {
    "vendorProductId": "premium_monthly",
    "localizedTitle": "Premium Monthly",
    "localizedDescription": "Premium subscription for 1 month",
    "localizedPrice": "$9.99",
    "price": 9.99,
    "currencyCode": "USD"
  },
  "error": null
}

// Failed navigation
{
  "product": {
    "vendorProductId": "premium_monthly",
    "localizedTitle": "Premium Monthly",
    "localizedDescription": "Premium subscription for 1 month",
    "localizedPrice": "$9.99",
    "price": 9.99,
    "currencyCode": "USD"
  },
  "error": {
    "code": "web_navigation_failed",
    "message": "Failed to open web paywall",
    "details": {
      "underlyingError": "Browser unavailable"
    }
  }
}

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

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

void paywallViewDidFailPurchase(AdaptyUIPaywallView view, 
                                AdaptyPaywallProduct product, 
                                AdaptyError error) {
}
Пример события (нажмите, чтобы развернуть)
{
  "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"
    }
  }
}

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

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

void paywallViewDidStartRestore(AdaptyUIPaywallView view) {
}

Восстановление выполнено успешно

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

void paywallViewDidFinishRestore(AdaptyUIPaywallView view, AdaptyProfile profile) {
}
Пример события (нажмите, чтобы развернуть)
{
  "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. Как его проверить — см. в разделе Статус подписки, как закрыть экран пейвола — в разделе Реакция на действия с кнопками.

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

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

void paywallViewDidFailRestore(AdaptyUIPaywallView view, AdaptyError error) {
}
Пример события (нажмите, чтобы развернуть)
{
  "error": {
    "code": "restore_failed",
    "message": "Purchase restoration failed",
    "details": {
      "underlyingError": "No previous purchases found"
    }
  }
}

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

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

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

void paywallViewDidFailLoadingProducts(AdaptyUIPaywallView view, AdaptyError error) {
}
Пример события (нажмите, чтобы развернуть)
{
  "error": {
    "code": "products_loading_failed",
    "message": "Failed to load products from the server",
    "details": {
      "underlyingError": "Network timeout"
    }
  }
}

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

Если во время рендеринга интерфейса возникает ошибка, она будет сообщена через этот метод. По умолчанию (начиная с 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
}
Пример события (нажмите, чтобы развернуть)
{
  "error": {
    "code": "rendering_failed",
    "message": "Failed to render paywall interface",
    "details": {
      "underlyingError": "Invalid paywall configuration"
    }
  }
}

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