Flutter - 处理 flow 与付费墙事件

本指南涵盖购买、恢复、产品选择和渲染的事件处理。关闭视图和打开链接由默认的 flowViewDidPerformAction 实现处理——如需覆盖这些行为或处理自定义按钮动作,请参阅按钮动作处理指南

通过编辑工具配置的流程和付费墙无需额外代码即可完成购买和恢复购买操作。但它们会产生一些事件供你的应用响应,包括按钮点击(关闭按钮、URL、产品选择等)以及流程或付费墙上与购买相关的操作通知。请参阅以下内容了解如何响应这些事件。

如需在移动应用中控制或监控流程或付费墙屏幕上发生的过程,请实现 AdaptyUIFlowsEventsObserver 方法,并在展示任何屏幕之前设置观察者:

AdaptyUI().setFlowsEventsObserver(this);

有三个观察者方法是必须实现的——缺少它们类将无法编译:flowViewDidFinishPurchaseflowViewDidFinishRestoreflowViewDidReceiveError。其他方法均为可选。若要移除已设置的观察者,请向 setFlowsEventsObserver 传入 null

想看看 Adapty SDK 在移动应用中的实际集成示例吗?欢迎查看我们的示例应用,其中演示了完整的集成流程,包括展示付费墙、完成购买以及其他基本功能。

以下事件示例展示了每个对象上可用的属性,注释中提供了示意性的参考值。

用户生成的事件

视图已出现

当流程或付费墙视图显示在屏幕上时,将调用此方法。

在 iOS 上,当用户点击付费墙内的网页付费墙按钮,且网页付费墙在应用内浏览器中打开时,也会调用此方法。

void flowViewDidAppear(AdaptyUIFlowView view) {
}

视图已消失

当流程或付费墙视图从屏幕上关闭时,将调用此方法。

在 iOS 上,当从付费墙中在应用内浏览器打开的 web 付费墙 从屏幕消失时,也会触发此方法。

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()。有关关闭屏幕的详细信息,请参阅响应按钮操作

完成 Web 支付导航

此方法在尝试为特定产品打开 Web 付费墙后调用,包括导航成功和失败的情况:

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

参数:

参数描述
product打开 Web 付费墙时对应的 AdaptyPaywallProduct。可以为 null
error如果 Web 付费墙导航失败,则为 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) {
}

视图错误

此方法为必填项。它替代了 v3 的 paywallViewDidFailRendering 方法:界面渲染过程中发生的错误以及其他视图错误,都会通过调用此方法来上报。实现此方法后,关闭视图的逻辑由你来决定——我们建议在发生此类错误时关闭视图,这也是未设置观察者时 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(...) 注册)用于处理来自流程的系统请求:操作系统权限提示(如推送通知或相机访问)以及 App Store 评价请求。目前流程尚未触发此类请求,因此无需注册处理程序。 如果你注册了处理器,请注意:handlePermission 是该类的必需方法——用你自己的代码请求权限,然后返回 AdaptyUIPermissionResult.granted()AdaptyUIPermissionResult.denied()handleAppReviewRequest 是可选的。

本指南介绍购买、恢复、产品选择和付费墙渲染的事件处理。你还必须实现按钮处理(关闭付费墙、打开链接等)。详情请参阅按钮操作处理指南

使用付费墙编辑工具配置的付费墙无需额外代码即可完成购买和恢复购买操作。但它们会产生一些事件,供你的应用响应。这些事件包括按钮点击(关闭按钮、URL、产品选择等)以及付费墙上与购买相关操作的通知。请参阅以下内容了解如何响应这些事件。

本指南仅适用于新版付费墙编辑工具付费墙,需要 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;
  }
}

我们建议在这种情况下关闭该页面。有关关闭付费墙页面的详细信息,请参阅响应按钮操作

完成 Web 支付跳转

此方法在尝试为特定产品打开 Web 付费墙后触发,无论跳转成功还是失败均会调用:

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

参数:

参数描述
product打开网页付费墙时对应的 AdaptyPaywallProduct。可以为 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()
}

正常情况下不应出现此类错误,如果遇到,请告知我们。