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

Important

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

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

Tip

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

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

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

using UnityEngine;
using AdaptySDK;

public class FlowEventsHandler : MonoBehaviour, IAdaptyFlowsEventsListener
{
    void Start()
    {
        Adapty.SetFlowsEventsListener(this);
    }

    // Implement all interface methods below
}
Note

Здесь вы добавляете свою логику реагирования на события флоу. SDK не применяет к ним никакого поведения по умолчанию: успешная покупка или ошибка не скрывают экран автоматически — вызовите view.Dismiss(...) самостоятельно в нужный момент.

Пользовательские события

Флоу появился

Вызывается, когда представление флоу отображается на экране.

Note

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

public void FlowViewDidAppear(AdaptyUIFlowView view) { }

Флоу исчез

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

Note

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

public void FlowViewDidDisappear(AdaptyUIFlowView view) { }

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

Вызывается при выборе продукта для покупки (пользователем или системой).

public void FlowViewDidSelectProduct(
    AdaptyUIFlowView view,
    string productId
) { }
Пример события (нажмите, чтобы раскрыть)
{
  "productId": "premium_monthly"
}

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

Вызывается, когда пользователь инициирует процесс покупки.

public void FlowViewDidStartPurchase(
    AdaptyUIFlowView view,
    AdaptyPaywallProduct product
) { }
Note

В режиме Observer, покупки, начатые из флоу, передаются в ваш IAdaptyUIObserverModeResolver.

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

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

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

Флоу остаётся открытым после покупки, пока вы его не закроете — вызовите view.Dismiss(...) самостоятельно, как только пользователь получит доступ:

public void FlowViewDidFinishPurchase(
    AdaptyUIFlowView view,
    AdaptyPaywallProduct product,
    AdaptyPurchaseResult purchasedResult
) {
    switch (purchasedResult.Type) {
        case AdaptyPurchaseResultType.Success:
            // Check if user has access to premium features
            if (purchasedResult.Profile != null
                && purchasedResult.Profile.AccessLevels.TryGetValue("premium", out var premium)
                && premium.IsActive) {
                view.Dismiss(null);
            }
            break;
        case AdaptyPurchaseResultType.Pending:
            // Handle pending purchase (e.g., user will pay offline with cash)
            break;
        case AdaptyPurchaseResultType.UserCancelled:
            // Handle user cancellation
            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": "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": "UserCancelled"
  }
}

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

Мы рекомендуем закрывать экран флоу при успешной покупке.

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

Этот метод вызывается, если покупка завершилась с ошибкой. Сюда входят ошибки StoreKit/Google Play Billing (ограничения платежей, недействительные продукты, сбои сети), ошибки проверки транзакций и системные ошибки. Обратите внимание, что отмена покупки пользователем вызывает FlowViewDidFinishPurchase с результатом отмены, а ожидающие платежи этот метод не вызывают.

public void FlowViewDidFailPurchase(
    AdaptyUIFlowView 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"
    }
  }
}

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

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

public void FlowViewDidStartRestore(AdaptyUIFlowView view) { }

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

Вызывается при успешном восстановлении покупок. Флоу остаётся открытым после восстановления, пока вы его не закроете:

public void FlowViewDidFinishRestore(
    AdaptyUIFlowView view,
    AdaptyProfile profile
) {
    // Check if user has access to premium features
    if (profile.AccessLevels.TryGetValue("premium", out var premium) && premium.IsActive) {
        view.Dismiss(null);
    }
}
Пример события (нажмите, чтобы развернуть)
{
  "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 void FlowViewDidFailRestore(
    AdaptyUIFlowView view,
    AdaptyError error
) { }
Пример события (нажмите, чтобы развернуть)
{
  "error": {
    "code": "restore_failed",
    "message": "Purchase restoration failed",
    "details": {
      "underlyingError": "No previous purchases found"
    }
  }
}

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

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

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

Параметры:

  • product: Продукт, для которого был открыт (или предпринята попытка открытия) веб-пейвол, либо null
  • error: null, если веб-пейвол успешно открылся, или AdaptyError в случае ошибки
Примеры событий (нажмите, чтобы развернуть)
// 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": null,
  "error": {
    "code": "wrong_param",
    "message": "Current method is not available for this product",
    "details": {
      "underlyingError": "Product not configured for web purchases"
    }
  }
}

Загрузка данных и отрисовка

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

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

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

Ошибки рендеринга и выполнения

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

public void FlowViewDidReceiveError(
    AdaptyUIFlowView view,
    AdaptyError error
) { }
Пример события (нажмите, чтобы раскрыть)
{
  "error": {
    "code": "rendering_failed",
    "message": "Failed to render flow interface",
    "details": {
      "underlyingError": "Invalid flow configuration"
    }
  }
}

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

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

public void FlowViewDidReceiveAnalyticEvent(
    AdaptyUIFlowView view,
    string name,
    IDictionary<string, object> @params
) { }

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

ПараметрОписание
instanceIdИдентификатор экрана, который открыл пользователь.
screen_orderПорядковый номер экрана во флоу.
is_last_screentrue, если у экрана нет следующего шага. Флоу с ветвлением может завершаться на нескольких разных экранах — каждый из них возвращает true.

Оба поля isBackendEvent и isCustomerEvent равны true для этого события: Adapty продолжает его учитывать, и ваше приложение тоже его получает.

Подробнее о том, что делать с этими событиями, — в разделе Отслеживание просмотров экранов флоу.

Флоу также сообщает о значениях, которые пользователи вводят и выбирают. Подробнее — в разделе Обработка данных из флоу.

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

IAdaptyUISystemRequestsHandler (регистрируется через Adapty.SetSystemRequestsHandler(...)) предназначен для обработки системных запросов из флоу: запросов разрешений ОС (например, на push-уведомления или доступ к камере) и запросов на оценку приложения. Флоу пока не инициируют такие запросы, поэтому регистрировать обработчик не нужно.

Системная кнопка «Назад» в Android

Системная кнопка «Назад» (или жест «Назад») в Android передаётся в FlowViewDidPerformAction как действие SystemBack и сама по себе не закрывает флоу — пользователь покидает флоу через путь, который вы определяете, например через кнопку Close или действие on_device_back в билдере. Если вы хотите, чтобы системная кнопка «Назад» закрывала флоу, обрабатывайте это действие самостоятельно:

public void FlowViewDidPerformAction(
    AdaptyUIFlowView view,
    AdaptyUIUserAction action
) {
    switch (action.Type) {
        case AdaptyUIUserActionType.Close:
        case AdaptyUIUserActionType.SystemBack:
            view.Dismiss(null);
            break;
        default:
            // handle other events
            break;
    }
}

Смотрите гайд по обработке действий флоу для полного списка действий.

Important

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

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

Warning

Этот гайд охватывает только пейволы, созданные в старом билдере, для которых требуется Adapty SDK версии 3.3.0 или выше.

Tip

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

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

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

using UnityEngine;
using AdaptySDK;

public class PaywallEventsHandler : MonoBehaviour, AdaptyPaywallsEventsListener
{
    void Start()
    {
        Adapty.SetPaywallsEventsListener(this);
    }

    // Implement all required interface methods below
}

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

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

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

Note

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

public void PaywallViewDidAppear(AdaptyUIPaywallView view) { }

Пейвол исчез

Вызывается, когда экран пейвола закрывается.

Note

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

public void PaywallViewDidDisappear(AdaptyUIPaywallView view) { }

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

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

public void PaywallViewDidSelectProduct(
    AdaptyUIPaywallView view, 
    string productId
) { }
Пример события (нажмите, чтобы развернуть)
{
  "productId": "premium_monthly"
}

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

Вызывается, когда пользователь инициирует процесс покупки.

public 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"
  }
}

Успешная, отменённая или ожидающая покупка

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

public void PaywallViewDidFinishPurchase(
    AdaptyUIPaywallView view, 
    AdaptyPaywallProduct product, 
    AdaptyPurchaseResult purchasedResult
) { }
Примеры событий (нажмите, чтобы развернуть)
// 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": "UserCancelled"
  }
}

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

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

Покупка завершилась с ошибкой

Если покупка завершается с ошибкой, вызывается этот метод. Сюда входят ошибки StoreKit/Google Play Billing (ограничения платежей, недействительные продукты, сбои сети), ошибки проверки транзакций и системные ошибки. Обратите внимание: отмены пользователем вызывают PaywallViewDidFinishPurchase с результатом отмены, а ожидающие платежи этот метод не вызывают.

public 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"
    }
  }
}

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

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

public void PaywallViewDidStartRestore(AdaptyUIPaywallView view) { }

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

Вызывается при успешном восстановлении покупок:

public 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. Подробнее о том, как это проверить, см. в разделе Статус подписки.

Восстановление завершилось с ошибкой

Вызывается при ошибке восстановления покупок:

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

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

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

public void PaywallViewDidFinishWebPaymentNavigation(
    AdaptyUIPaywallView view, 
    AdaptyPaywallProduct product, 
    AdaptyError error
) { }

Параметры:

  • product: продукт, для которого был открыт (или предпринята попытка открыть) веб-пейвол
  • error: null, если веб-пейвол успешно открылся, или AdaptyError при ошибке
Примеры событий (нажмите, чтобы развернуть)
// 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": "wrong_param",
    "message": "Current method is not available for this product",
    "details": {
      "underlyingError": "Product not configured for web purchases"
    }
  }
}

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

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

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

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

Ошибки отрисовки

Вызывается при возникновении ошибки в процессе отрисовки интерфейса и предоставляет AdaptyError:

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

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