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

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

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

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

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

Если вам нужно контролировать или отслеживать процессы на экране покупки, реализуйте методы AdaptyUiEventListener.

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

Ниже приведены значения по умолчанию из AdaptyUiDefaultEventListener.

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

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

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

public override fun onProductSelected(
    product: AdaptyPaywallProduct,
    context: Context,
) {}
Пример события (нажмите, чтобы раскрыть)
{
  "product": {
    "vendorProductId": "premium_monthly",
    "localizedTitle": "Premium Monthly",
    "localizedDescription": "Premium subscription for 1 month",
    "localizedPrice": "$9.99",
    "price": 9.99,
    "currencyCode": "USD"
  }
}

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

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

public override fun onPurchaseStarted(
    product: AdaptyPaywallProduct,
    context: Context,
) {}
Пример события (нажмите, чтобы раскрыть)
{
  "product": {
    "vendorProductId": "premium_monthly",
    "localizedTitle": "Premium Monthly",
    "localizedDescription": "Premium subscription for 1 month",
    "localizedPrice": "$9.99",
    "price": 9.99,
    "currencyCode": "USD"
  }
}

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

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

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

public override fun onPurchaseFinished(
    purchaseResult: AdaptyPurchaseResult,
    product: AdaptyPaywallProduct,
    context: Context,
) {
    if (purchaseResult !is AdaptyPurchaseResult.UserCanceled)
        context.getActivityOrNull()?.onBackPressed()
}
Примеры событий (нажмите, чтобы раскрыть)
// Successful purchase
{
  "purchaseResult": {
    "type": "Success",
    "profile": {
      "accessLevels": {
        "premium": {
          "id": "premium",
          "isActive": true,
          "expiresAt": "2024-02-15T10:30:00Z"
        }
      }
    }
  },
  "product": {
    "vendorProductId": "premium_monthly",
    "localizedTitle": "Premium Monthly",
    "localizedDescription": "Premium subscription for 1 month",
    "localizedPrice": "$9.99",
    "price": 9.99,
    "currencyCode": "USD"
  }
}

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

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

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

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

Неуспешная покупка

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

public override fun onPurchaseFailure(
    error: AdaptyError,
    product: AdaptyPaywallProduct,
    context: Context,
) {}
Пример события (нажмите, чтобы раскрыть)
{
  "error": {
    "code": "purchase_failed",
    "message": "Purchase failed due to insufficient funds",
    "details": {
      "underlyingError": "Insufficient funds in account"
    }
  },
  "product": {
    "vendorProductId": "premium_monthly",
    "localizedTitle": "Premium Monthly",
    "localizedDescription": "Premium subscription for 1 month",
    "localizedPrice": "$9.99",
    "price": 9.99,
    "currencyCode": "USD"
  }
}

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

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

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

public override fun onFinishWebPaymentNavigation(
    product: AdaptyPaywallProduct?,
    error: AdaptyError?,
    context: Context,
) {}

Параметры:

ПараметрОписание
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"
    }
  }
}

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

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

public override fun onRestoreSuccess(
    profile: AdaptyProfile,
    context: Context,
) {}
Пример события (нажмите, чтобы раскрыть)
{
  "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. Как это проверить, описано в разделе Статус подписки.

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

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

public override fun onRestoreFailure(
    error: AdaptyError,
    context: Context,
) {}
Пример события (нажмите, чтобы раскрыть)
{
  "error": {
    "code": "restore_failed",
    "message": "Purchase restoration failed",
    "details": {
      "underlyingError": "No previous purchases found"
    }
  }
}

Обновление подписки

Пример события (нажмите, чтобы раскрыть)
{
  "product": {
    "vendorProductId": "premium_yearly",
    "localizedTitle": "Premium Yearly",
    "localizedDescription": "Premium subscription for 1 year",
    "localizedPrice": "$99.99",
    "price": 99.99,
    "currencyCode": "USD"
  },
  "subscriptionUpdateParams": {
    "replacementMode": "with_time_proration"
  }
}

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

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

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

public override fun onLoadingProductsFailure(
    error: AdaptyError,
    context: Context,
): Boolean = false
Пример события (нажмите, чтобы раскрыть)
{
  "error": {
    "code": "products_loading_failed",
    "message": "Failed to load products from the server",
    "details": {
      "underlyingError": "Network timeout"
    }
  }
}

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

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

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

public override fun onRenderingError(
    error: AdaptyError,
    context: Context,
) {}
Пример события (нажмите, чтобы раскрыть)
{
  "error": {
    "code": "rendering_failed",
    "message": "Failed to render paywall interface",
    "details": {
      "underlyingError": "Invalid paywall configuration"
    }
  }
}

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