处理 flow 与付费墙事件 - Capacitor
本指南介绍购买、恢复、产品选择及流程渲染的事件处理。你也可以设置按钮处理(关闭流程、打开链接、自定义操作等)。详情请参阅按钮操作处理指南。
流程编辑工具构建的流程和付费墙无需额外代码即可完成购买和恢复购买。但它们会生成一些你的应用可以响应的事件,包括按钮点击(关闭按钮、URL、产品选择等)以及与流程中购买相关操作的通知。请参阅下文了解如何响应这些事件。
要在移动应用中控制或监听流程界面上发生的事件,请实现 view.setEventHandlers 方法:
每个事件只能设置一个处理器:多次调用 setEventHandlers 会覆盖你提供的处理器,同时替换掉那些特定事件的默认处理器和之前设置的处理器。未设置的处理器保持默认行为。setEventHandlers 返回一个取消订阅的函数,view.dismiss() 会清除所有处理器。
const view = await createFlowView(flow);
const unsubscribe = await view.setEventHandlers({
onCloseButtonPress() {
return true; // close the flow (default behavior)
},
onAndroidSystemBack() {
return true; // close the flow; by default, it stays open
},
onPurchaseCompleted(purchaseResult, product) {
return purchaseResult.type === 'success'; // close the flow on a successful purchase, keep it open for cancelled or pending purchases
},
onPurchaseStarted(product) { /***/ },
onPurchaseFailed(error, product) { /***/ },
onRestoreCompleted(profile) { /***/ },
onRestoreFailed(error) { /***/ },
onProductSelected(productId) { /***/ },
onError(error) { /***/ },
onLoadingProductsFailed(error) { /***/ },
onUrlPress(url, openIn) {
adapty.openWebUrl({ url, openIn }).catch(console.warn); // same as the SDK default
return false; // keep the flow open
},
onAppeared(appearedView) { /***/ },
onDisappeared() { /***/ },
onWebPaymentNavigationFinished() { /***/ },
});事件示例(点击展开)
以下示例展示了每个处理程序中可用的属性,注释中提供了说明性的值。
// onUrlPress
url; // 'https://example.com/terms'
openIn; // 'browser_in_app' or 'browser_out_app'
// onCustomAction
actionId; // 'login'
// onProductSelected
productId; // 'premium_monthly'
// onAppeared
appearedView.id; // '3f8a1c7e-9b24-4d51-8e30-6c5b2a9f1d47'
appearedView.placementId; // 'onboarding_paywall'
appearedView.variationId; // 'd21c4b6a-57e8-4f39-b0a2-8c7e13f5d94b'
appearedView.locale; // 'es'
// onPurchaseStarted, onPurchaseCompleted, onPurchaseFailed
product.vendorProductId; // 'premium_monthly'
product.localizedTitle; // 'Premium Monthly'
product.localizedDescription; // 'Premium subscription for 1 month'
product.price?.amount; // 9.99
product.price?.currencyCode; // 'USD'
product.price?.localizedString; // '$9.99'
// onPurchaseCompleted
purchaseResult.type; // 'success', 'pending', or 'user_cancelled'
if (purchaseResult.type === 'success') {
purchaseResult.profile.accessLevels['premium']?.isActive; // true
}
// onRestoreCompleted
profile.accessLevels['premium']?.isActive; // true
// onPurchaseFailed, onRestoreFailed, onError, onLoadingProductsFailed
error.message; // 'Purchase failed due to insufficient funds'您可以只注册需要的事件处理程序,忽略不需要的。这样就不会创建多余的事件监听器。所有事件处理程序均为可选。
事件处理程序返回一个布尔值。如果返回 true,则认为展示流程已完成,流程屏幕随即关闭,并移除该视图的所有事件监听器。
某些事件处理器具有默认行为,您可以根据需要进行覆盖:
onCloseButtonPress:当用户按下关闭按钮时,关闭流程。onUrlPress:通过adapty.openWebUrl在原生浏览器中打开被点击的 URL,遵循编辑工具中设置的 Open in 选项,并保持流程开启。onAndroidSystemBack:当用户按下 Back 按钮时,保持流程开启。返回true可关闭流程。onPurchaseCompleted:购买完成后保持流程开启。返回true可关闭流程。onRestoreCompleted:成功恢复购买后保持流程开启。返回true可关闭流程。onError:如果流程渲染失败,则关闭流程。
事件处理程序
| 事件处理器 | 描述 |
|---|---|
| onCustomAction | 当用户执行自定义操作时触发,例如点击自定义按钮。 |
| onUrlPress | 当用户点击流程中的 URL 时触发。 |
| onAndroidSystemBack | 当用户点击 Android 系统的 Back 按钮时触发。默认情况下流程保持打开状态;返回 true 可关闭流程。 |
| onCloseButtonPress | 当关闭按钮可见且用户点击时触发。建议在此处理器中关闭流程页面。 |
| onPurchaseCompleted | 当购买完成时触发,无论是成功、用户取消还是待审批。购买成功时会提供更新后的 AdaptyProfile。用户取消和待处理支付(例如需要家长批准)会触发此事件,而非 onPurchaseFailed。 |
| onPurchaseStarted | 当用户点击”购买”操作按钮开始购买流程时触发。 |
| onPurchaseFailed | 当购买因错误失败时触发(例如支付限制、无效产品、网络故障、交易验证失败)。用户取消或待处理支付不会触发此事件,而是触发 onPurchaseCompleted。 |
| onRestoreStarted | 当用户开始购买恢复流程时触发。 |
| onRestoreCompleted | 当购买恢复成功时触发,并提供更新后的 AdaptyProfile。如果用户已获得所需的 accessLevel,建议关闭页面。请参阅订阅状态主题了解如何检查。 |
| onRestoreFailed | 当恢复流程失败时触发,并提供 AdaptyError。 |
| onProductSelected | 当流程视图中有产品被选中时触发,可用于监控用户在购买前的选择行为。 |
| onError | 当视图渲染过程中发生错误时触发,并提供 AdaptyError。此类错误本不应出现,如果遇到请告知我们。 |
| onLoadingProductsFailed | 当产品加载失败时触发,并提供 AdaptyError。如果在创建视图时未设置 prefetchProducts: true,AdaptyUI 将自动从服务器获取所需对象。 |
| onAppeared | 当流程向用户展示时触发,并提供出现的视图——参见 view 参数。在 iOS 上,当用户点击流程内的 web 付费墙按钮 且 web 付费墙在应用内浏览器中打开时,也会触发此事件。 |
| onDisappeared | 当用户关闭流程时触发。在 iOS 上,当从流程中以应用内浏览器打开的 web 付费墙 从屏幕消失时,也会触发此事件。 |
| onWebPaymentNavigationFinished | 在尝试打开 web 付费墙 进行购买后触发,无论成功还是失败。 |
| onRequestAppReview | 为流程中的应用评价请求预留。流程目前尚未触发应用评价请求,无需实现此处理器。 |
| onAnalytics | 当流程上报分析事件(例如页面浏览)时触发。请参阅下方的分析事件。 |
| onRequestPermission | 为流程中的系统权限请求(如推送通知或相机访问)预留。流程目前尚未触发权限请求,无需实现此处理器。 |
| onObserverPurchaseInitiated | 仅限观察者模式:当用户点击流程中的购买按钮时触发。Adapty 不会执行购买——请使用您自己的购买代码完成购买,然后将交易报告给 Adapty。请参阅下方的在观察者模式下处理购买。 |
| onObserverRestoreInitiated | 仅限观察者模式:当用户点击流程中的恢复按钮时触发。Adapty 不会执行恢复——请自行完成恢复操作,然后报告所有已恢复的交易。请参阅下方的在观察者模式下处理购买。 |
view 参数
view 参数需要 Capacitor SDK 4.0.2-beta.1 或更高版本。在所有流程处理器中,只有 onAppeared 会接收视图本身的描述——一个包含以下字段的 FlowEventView 对象:
| 字段 | 描述 |
|---|---|
| id | 此视图实例的标识符。该标识符仅供 SDK 内部使用,与 Adapty 看板中的任何内容均不对应。 |
| placementId | 获取该流程所对应的版位。 |
| variationId | 该流程解析到的实验变体,用于将您自己的分析数据归因到 A/B 测试。 |
| locale | 构建该视图所使用的流程本地化配置。如果该流程不包含您所请求的本地化语言,则此值可能与您请求的语言不同。由 createFlowView 返回的视图会在其 locale 属性中报告相同的值。请参阅使用本地化与语言代码。 |
分析事件
view.setEventHandlers({
onAnalytics(name, params) {
return false; // keep the flow open
},
});每当用户打开某个流程中的屏幕时,该流程就会上报 flow_screen_showed 事件。Adapty 会在其自身的流程分析中统计这些事件,同时也会将其传递给您的应用,以便您在自己的分析系统中构建相同的漏斗。
| 参数 | 说明 |
|---|---|
instanceId | 用户打开的屏幕 ID。 |
screen_order | 该屏幕在流程中的位置顺序。 |
is_last_screen | 当该屏幕没有后续跳转时为 true。分支流程可能有多个结束屏幕,每个结束屏幕都会上报 true。 |
此事件的 isBackendEvent 和 isCustomerEvent 均为 true:Adapty 会持续统计该事件,同时您的应用也会收到它。
请参阅跟踪流程屏幕浏览,了解如何处理这些事件。
在观察者模式下处理购买
如果你以观察者模式(observerMode: true)激活了 SDK 并展示 Adapty 渲染的流程,SDK 不会为你执行购买操作。当用户点击购买或恢复按钮时,SDK 会调用 onObserverPurchaseInitiated 或 onObserverRestoreInitiated,你可以用自己的代码来处理购买或恢复逻辑。完整设置请参阅在观察者模式下展示流程。
本指南涵盖购买、恢复、产品选择和付费墙渲染的事件处理。你还必须实现按钮处理(关闭付费墙、打开链接等)。详情请参阅我们的按钮操作处理指南。
通过付费墙编辑工具配置的付费墙无需额外代码即可完成购买和恢复购买操作。但它们会生成一些事件,供您的应用响应。这些事件包括按钮点击(关闭按钮、URL、产品选择等),以及付费墙上与购买相关操作的通知。请参阅下文了解如何响应这些事件。
如需在移动应用中控制或监控付费墙界面上发生的流程,请实现 view.setEventHandlers 方法:
const view = await createPaywallView(paywall);
const unsubscribe = view.setEventHandlers({
onCloseButtonPress() {
console.log('User closed paywall');
return true; // Allow the paywall to close
},
onAndroidSystemBack() {
console.log('User pressed back button');
return true; // Allow the paywall to close
},
onAppeared() {
console.log('Paywall appeared');
return false; // Don't close the paywall
},
onDisappeared() {
console.log('Paywall disappeared');
},
onPurchaseCompleted(purchaseResult, product) {
console.log('Purchase completed:', purchaseResult);
return purchaseResult.type !== 'user_cancelled'; // Close if not cancelled
},
onPurchaseStarted(product) {
console.log('Purchase started:', product);
return false; // Don't close the paywall
},
onPurchaseFailed(error, product) {
console.error('Purchase failed:', error);
return false; // Don't close the paywall
},
onRestoreCompleted(profile) {
console.log('Restore completed:', profile);
return true; // Close the paywall after successful restore
},
onRestoreFailed(error) {
console.error('Restore failed:', error);
return false; // Don't close the paywall
},
onProductSelected(productId) {
console.log('Product selected:', productId);
return false; // Don't close the paywall
},
onRenderingFailed(error) {
console.error('Rendering failed:', error);
return false; // Don't close the paywall
},
onLoadingProductsFailed(error) {
console.error('Loading products failed:', error);
return false; // Don't close the paywall
},
onUrlPress(url) {
window.open(url, '_blank');
return false; // Don't close the paywall
},
});事件示例(点击展开)
// onCloseButtonPress
{
"event": "close_button_press"
}
// onAndroidSystemBack
{
"event": "android_system_back"
}
// onAppeared
{
"event": "paywall_shown"
}
// onDisappeared
{
"event": "paywall_closed"
}
// onUrlPress
{
"event": "url_press",
"url": "https://example.com/terms"
}
// onCustomAction
{
"event": "custom_action",
"actionId": "login"
}
// onProductSelected
{
"event": "product_selected",
"productId": "premium_monthly"
}
// onPurchaseStarted
{
"event": "purchase_started",
"product": {
"vendorProductId": "premium_monthly",
"localizedTitle": "Premium Monthly",
"localizedDescription": "Premium subscription for 1 month",
"localizedPrice": "$9.99",
"price": 9.99,
"currencyCode": "USD"
}
}
// onPurchaseCompleted - Success
{
"event": "purchase_completed",
"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"
}
}
// onPurchaseCompleted - Cancelled
{
"event": "purchase_completed",
"purchaseResult": {
"type": "user_cancelled"
},
"product": {
"vendorProductId": "premium_monthly",
"localizedTitle": "Premium Monthly",
"localizedDescription": "Premium subscription for 1 month",
"localizedPrice": "$9.99",
"price": 9.99,
"currencyCode": "USD"
}
}
// onPurchaseFailed
{
"event": "purchase_failed",
"error": {
"code": "purchase_failed",
"message": "Purchase failed due to insufficient funds",
"details": {
"underlyingError": "Insufficient funds in account"
}
}
}
// onRestoreCompleted
{
"event": "restore_completed",
"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"
}
]
}
}
// onRestoreFailed
{
"event": "restore_failed",
"error": {
"code": "restore_failed",
"message": "Purchase restoration failed",
"details": {
"underlyingError": "No previous purchases found"
}
}
}
// onRenderingFailed
{
"event": "rendering_failed",
"error": {
"code": "rendering_failed",
"message": "Failed to render paywall interface",
"details": {
"underlyingError": "Invalid paywall configuration"
}
}
}
// onLoadingProductsFailed
{
"event": "loading_products_failed",
"error": {
"code": "products_loading_failed",
"message": "Failed to load products from the server",
"details": {
"underlyingError": "Network timeout"
}
}
}您可以只注册需要的事件处理器,无需注册的可以忽略。这样就不会创建多余的事件监听器。所有事件处理器均为可选。
事件处理器返回一个布尔值。若返回 true,则视为展示流程已完成,付费墙页面将关闭,该视图的所有事件监听器也会随之移除。
某些事件处理程序具有默认行为,您可以根据需要进行覆盖:
onCloseButtonPress:点击关闭按钮时关闭付费墙。onAndroidSystemBack:按下 Back 按钮时关闭付费墙。onRestoreCompleted:恢复成功后关闭付费墙。onPurchaseCompleted:除非用户取消,否则关闭付费墙。onRenderingFailed:付费墙渲染失败时关闭付费墙。onUrlPress:在系统浏览器中打开 URL,并保持付费墙开启。
事件处理器
| 事件处理程序 | 描述 |
|---|---|
| onCustomAction | 当用户执行自定义操作时触发,例如点击自定义按钮。 |
| onUrlPress | 当用户点击付费墙中的 URL 时触发。 |
| onAndroidSystemBack | 当用户点击 Android 系统 Back 按钮时触发。 |
| onCloseButtonPress | 当关闭按钮可见且用户点击时触发。建议在此处理程序中关闭付费墙屏幕。 |
| onPurchaseCompleted | 购买完成时触发,无论成功、用户取消还是等待审批。购买成功时提供更新后的 AdaptyProfile。用户取消和待处理支付(如需要家长批准)会触发此事件,而非 onPurchaseFailed。 |
| onPurchaseStarted | 当用户点击”购买”操作按钮开始购买流程时触发。 |
| onPurchaseCancelled | 当用户发起购买流程后手动中断(取消支付对话框)时触发。 |
| onPurchaseFailed | 购买因错误失败时触发(如支付限制、无效产品、网络故障、交易验证失败)。用户取消或待处理支付不会触发此事件,这些情况会触发 onPurchaseCompleted。 |
| onRestoreStarted | 当用户开始购买恢复流程时触发。 |
| onRestoreCompleted | 购买恢复成功时触发,并提供更新后的 AdaptyProfile。如果用户拥有所需的 accessLevel,建议关闭屏幕。请参阅订阅状态主题了解如何进行检查。 |
| onRestoreFailed | 恢复流程失败时触发,并提供 AdaptyError。 |
| onProductSelected | 付费墙视图中任意产品被选择时触发,允许您监控用户在购买前的选择。 |
| onAppeared | 付费墙视图出现在屏幕上时触发。在 iOS 上,当用户点击付费墙内的网页付费墙按钮且网页付费墙在应用内浏览器中打开时,也会触发此事件。 |
| onDisappeared | 付费墙视图从屏幕消失时触发。在 iOS 上,当从付费墙在应用内浏览器中打开的网页付费墙从屏幕消失时,也会触发此事件。 |
| onRenderingFailed | 视图渲染期间发生错误时触发,并提供 AdaptyError。此类错误不应出现,如遇到请告知我们。 |
| onLoadingProductsFailed | 产品加载失败时触发,并提供 AdaptyError。如果您在创建视图时未设置 prefetchProducts: true,AdaptyUI 将自行从服务器获取所需对象。 |