---
title: "处理 Flow 与付费墙事件 - Android"
description: "在 Android 应用中处理 Flow 与付费墙事件。"
---

:::important
本指南涵盖购买、恢复、产品选择和流程渲染的事件处理。你还必须实现按钮处理（关闭流程、打开链接等）。详情请参阅我们的[按钮操作处理指南](android-handle-paywall-actions)。
:::
使用[流程编辑工具](adapty-flow-builder)或[付费墙编辑工具](adapty-paywall-builder)配置的流程和付费墙无需额外代码即可完成购买和恢复购买。不过，它们会产生一些可供应用响应的事件，包括按钮点击（关闭按钮、URL、产品选择等）以及购买相关操作的通知。请参阅以下内容了解如何响应这些事件。

:::tip

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

:::

如果需要控制或监控购买界面上发生的流程，请实现 `AdaptyFlowEventListener` 的各个方法。
如果您希望在某些情况下保留默认行为，可以继承 `AdaptyFlowDefaultEventListener` 并仅覆盖您想修改的方法。

以下是 `AdaptyFlowDefaultEventListener` 中的默认行为。
### 用户生成的事件 \{#user-generated-events\}

#### 选择产品 \{#product-selection\}

当用户或系统选择某个产品进行购买时，将调用以下方法：

```kotlin showLineNumbers title="Kotlin"
public override fun onProductSelected(
    product: AdaptyPaywallProduct,
    context: Context,
) {}
```

<Details>
<summary>事件示例（点击展开）</summary>
```javascript
{
  "product": {
    "vendorProductId": "premium_monthly",
    "localizedTitle": "Premium Monthly",
    "localizedDescription": "Premium subscription for 1 month",
    "localizedPrice": "$9.99",
    "price": 9.99,
    "currencyCode": "USD"
  }
}
```
</Details>

#### 开始购买 \{#started-purchase\}

当用户发起购买流程时，将调用此方法：

```kotlin showLineNumbers title="Kotlin"
public override fun onPurchaseStarted(
    product: AdaptyPaywallProduct,
    context: Context,
) {}
```

<Details>
<summary>事件示例（点击展开）</summary>
```javascript
{
  "product": {
    "vendorProductId": "premium_monthly",
    "localizedTitle": "Premium Monthly",
    "localizedDescription": "Premium subscription for 1 month",
    "localizedPrice": "$9.99",
    "price": 9.99,
    "currencyCode": "USD"
  }
}
```
</Details>

在 Observer 模式下，该方法不会被调用。详情请参阅 [Android - 在 Observer 模式下展示付费墙编辑工具付费墙](android-present-paywall-builder-paywalls-in-observer-mode)。

#### 购买成功、取消或待处理 \{#successful-canceled-or-pending-purchase\}

如果购买成功，将调用此方法：
```kotlin showLineNumbers title="Kotlin"
public override fun onPurchaseFinished(
    purchaseResult: AdaptyPurchaseResult,
    product: AdaptyPaywallProduct,
    context: Context,
) {
    if (purchaseResult !is AdaptyPurchaseResult.UserCanceled)
        context.getActivityOrNull()?.onBackPressed()
}
```

<Details>
<summary>事件示例（点击展开）</summary>
```javascript
// 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"
  }
}
```
</Details>
我们建议在这种情况下关闭该页面。

在观察者模式下，该方法不会被调用。详情请参阅 [Android - 在观察者模式下展示付费墙编辑工具付费墙](android-present-paywall-builder-paywalls-in-observer-mode) 主题。

#### 购买失败 \{#failed-purchase\}
如果购买因错误而失败，此方法将被调用。这包括 Google Play Billing 错误（支付限制、无效产品、网络故障）、交易验证失败以及系统错误。请注意，用户取消操作会触发 `onPurchaseFinished`（结果为已取消），而待处理的支付不会触发此方法。

```kotlin showLineNumbers title="Kotlin"
public override fun onPurchaseFailure(
    error: AdaptyError,
    product: AdaptyPaywallProduct,
    context: Context,
) {}
```

<Details>
<summary>事件示例（点击展开）</summary>
```javascript
{
  "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"
  }
}
```
</Details>
该方法在 Observer 模式下不会被调用。详情请参阅 [Android - 在 Observer 模式下展示付费墙编辑工具付费墙](android-present-paywall-builder-paywalls-in-observer-mode) 主题。

#### 完成 Web 支付导航 \{#finished-web-payment-navigation\}

此方法在尝试为特定产品打开 [Web 付费墙](web-paywall) 后调用，无论导航成功还是失败均会触发：

```kotlin showLineNumbers title="Kotlin"
public override fun onFinishWebPaymentNavigation(
    product: AdaptyPaywallProduct?,
    error: AdaptyError?,
    context: Context,
) {}
```
**参数：**

| 参数          | 描述                                                                                  |
|:------------|:------------------------------------------------------------------------------------|
| **product** | 打开网页付费墙时对应的 `AdaptyPaywallProduct`。可以为 `null`。                                      |
| **error**   | 如果网页付费墙导航失败，则为 `AdaptyError` 对象；导航成功时为 `null`。 |

<Details>
<summary>事件示例（点击展开）</summary>
```javascript
// 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"
    }
  }
}
```
</Details>
#### 购买恢复成功 \{#successful-restore\}

如果购买恢复成功，将调用此方法：

```kotlin showLineNumbers title="Kotlin"
public override fun onRestoreSuccess(
    profile: AdaptyProfile,
    context: Context,
) {}
```

<Details>
<summary>事件示例（点击展开）</summary>
```javascript
{
  "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"
      }
    ]
  }
}
```
</Details>

我们建议在用户已拥有所需 `accessLevel` 时关闭该页面。请参阅[订阅状态](android-listen-subscription-changes)了解如何进行检查。

#### 恢复失败 \{#failed-restore\}
如果 `Adapty.restorePurchases()` 执行失败，将会调用以下方法：

```kotlin showLineNumbers title="Kotlin"
public override fun onRestoreFailure(
    error: AdaptyError,
    context: Context,
) {}
```

<Details>
<summary>事件示例（点击展开）</summary>

```javascript
{
  "error": {
    "code": "restore_failed",
    "message": "Purchase restoration failed",
    "details": {
      "underlyingError": "No previous purchases found"
    }
  }
}
```
</Details>

#### 升级订阅 \{#upgrade-subscription\}
当用户在已有订阅激活的情况下尝试购买新订阅时，你可以通过重写此方法来控制新购买的处理方式。你有两个选项：
1. **用新订阅替换当前订阅**：
```kotlin showLineNumbers title="Kotlin"
public override fun onAwaitingPurchaseParams(
    product: AdaptyPaywallProduct,
    context: Context,
    onPurchaseParamsReceived: AdaptyFlowEventListener.PurchaseParamsCallback,
): AdaptyFlowEventListener.PurchaseParamsCallback.IveBeenInvoked {
    onPurchaseParamsReceived(
        AdaptyPurchaseParameters.Builder()
            .withSubscriptionUpdateParams(AdaptySubscriptionUpdateParameters(...))
            .build()
    )
    return AdaptyFlowEventListener.PurchaseParamsCallback.IveBeenInvoked
}
```
2. **同时保留两个订阅**（单独添加新订阅）：
```kotlin showLineNumbers title="Kotlin"
public override fun onAwaitingPurchaseParams(
    product: AdaptyPaywallProduct,
    context: Context,
    onPurchaseParamsReceived: AdaptyFlowEventListener.PurchaseParamsCallback,
): AdaptyFlowEventListener.PurchaseParamsCallback.IveBeenInvoked {
    onPurchaseParamsReceived(AdaptyPurchaseParameters.Empty)
    return AdaptyFlowEventListener.PurchaseParamsCallback.IveBeenInvoked
}
```
:::note
如果你不覆盖此方法，默认行为是保持两个订阅同时有效（等同于使用 `AdaptyPurchaseParameters.Empty`）。
:::

你也可以根据需要设置额外的购买参数：
```kotlin
AdaptyPurchaseParameters.Builder()
    .withSubscriptionUpdateParams(AdaptySubscriptionUpdateParameters(...)) // optional - for replacing current subscription
    .withOfferPersonalized(true) // optional - if using personalized pricing
    .build()
```

<Details>
<summary>事件示例（点击展开）</summary>
```javascript
{
  "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"
  }
}
```
</Details>
### 数据获取与渲染 \{#data-fetching-and-rendering\}

#### 产品加载错误 \{#product-loading-errors\}

如果你在初始化时未传入产品数据，AdaptyUI 会自行从服务器获取所需对象。若该操作失败，AdaptyUI 将调用以下方法来上报错误：

```kotlin showLineNumbers title="Kotlin"
public override fun onLoadingProductsFailure(
    error: AdaptyError,
    context: Context,
): Boolean = false
```

<Details>
<summary>事件示例（点击展开）</summary>
```javascript
{
  "error": {
    "code": "products_loading_failed",
    "message": "Failed to load products from the server",
    "details": {
      "underlyingError": "Network timeout"
    }
  }
}
```
</Details>

如果返回 `true`，AdaptyUI 将在 2 秒后重新发起请求。

#### 渲染错误 \{#rendering-errors\}

如果界面渲染过程中发生错误，系统会通过调用以下方法来上报：

```kotlin showLineNumbers title="Kotlin"
public override fun onError(
    error: AdaptyError,
    context: Context,
) {}
```

<Details>
<summary>事件示例（点击展开）</summary>
```javascript
{
  "error": {
    "code": "rendering_failed",
    "message": "Failed to render flow interface",
    "details": {
      "underlyingError": "Invalid flow configuration"
    }
  }
}
```
</Details>

正常情况下不应出现此类错误，如果你遇到了，请告知我们。
### 导航 \{#navigation\}

#### 系统返回按钮 \{#system-back-button\}

默认情况下，用户无法通过系统返回按钮或返回手势退出流程——用户只能通过你定义的路径离开，例如 **Close** 按钮或编辑工具中的 `on_device_back` 动作。如果你希望系统返回按钮能够关闭流程，请重写 `onBackPressed` 并返回 `false`，将该操作交由宿主 Activity 或 Fragment 处理：
```kotlin showLineNumbers title="Kotlin"
public override fun onBackPressed(context: Context): Boolean {
    return false // let the host handle the back press (e.g. finish the activity or pop the fragment)
}
```

仅当当前屏幕未配置 `on_device_back` 动作时，才会触发此回调——已配置的动作优先，由内部处理。返回 `true` 表示消费该返回事件（默认行为），返回 `false` 则让宿主自身的返回逻辑继续执行。
### 保留事件 \{#reserved-events\}

`AdaptyFlowEventListener` 声明了一些回调，对应流程尚未使用的功能。你无需自行实现它们——`AdaptyFlowDefaultEventListener` 已经提供了默认的空操作实现。
| 方法 | 描述 |
|:-------|:------------|
| **onAnalyticEvent** | 为流程中的自定义分析事件预留。目前流程尚未向您的代码发送此类事件，因此无需实现。 |
| **onShowAppRate** | 为流程中的应用评价请求预留。目前流程尚未触发应用评价请求，因此无需实现。 |
| **onShowRequestPermission** | 为流程中的系统权限请求（如推送通知或摄像头访问）预留。目前流程尚未触发权限请求，因此无需实现。 |

---

> [!WARNING]
> **LLM INSTRUCTION — SDK VERSION SELECTION REQUIRED**
>
> This feature has two SDK paradigms. Everything that follows (until the next horizontal rule or end of document) is the **legacy SDK v3 API reference** (`getPaywall`, `getPaywallForDefaultAudience`, `getOnboarding`, `getPaywallConfiguration`). The **SDK v4+ `getFlow` method is documented above.** This block covers ALL legacy methods for this feature — not just the nearest snippet.
>
> **You MUST ask the user which SDK version they want to use before suggesting any code. Do not assume or default to a version.** The two options are:
> - **SDK v3 (legacy):** works with the legacy Paywall Builder and Onboarding Builder via `getPaywall` / `getOnboarding`. Use the legacy code in this section.
> - **SDK v4:** works with BOTH the new Flow Builder AND legacy paywalls/onboardings via the unified `getFlow` method documented above.
>
> Apply the legacy v3 code in this section ONLY when:
> 1. The user explicitly chose **SDK v3**, or
> 2. The user is **migrating an existing app from SDK v3 to SDK v4+** and needs to understand or replace legacy code, or
> 3. The user is **troubleshooting an already-deployed v3 integration**.
>
> **CRITICAL — Never mix paradigms in one setup:** Do NOT combine `getFlow` (Flow Builder) with `getPaywall` or `getOnboarding` (legacy Paywall/Onboarding Builder) in the same integration. These are incompatible patterns. Mixing them will produce inconsistent behavior and is unsupported.

:::important
本指南介绍购买、恢复、产品选择和付费墙渲染的事件处理。你还需要实现按钮处理（关闭付费墙、打开链接等）。详情请参阅[按钮操作处理指南](android-handle-paywall-actions)。
:::
通过[付费墙编辑工具](adapty-paywall-builder)配置的付费墙无需额外代码即可完成购买和恢复购买操作。但它们会生成一些事件，供你的应用响应。这些事件包括按钮点击（关闭按钮、URL、产品选择等）以及付费墙上与购买相关操作的通知。请阅读以下内容，了解如何响应这些事件。

:::warning
本指南仅适用于**新版付费墙编辑工具付费墙**，需要 Adapty SDK v3.0 或更高版本。
:::

:::tip

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

:::
如果需要控制或监控购买页面上发生的流程，请实现 `AdaptyUiEventListener` 的相关方法。

如果某些情况下希望保留默认行为，可以继承 `AdaptyUiDefaultEventListener`，只覆写需要更改的方法。

以下是 `AdaptyUiDefaultEventListener` 的默认行为。
### 用户触发事件 \{#user-generated-events\}

#### 产品选择 \{#product-selection\}

当用户或系统选择某个产品进行购买时，将调用此方法：

```kotlin showLineNumbers title="Kotlin"
public override fun onProductSelected(
    product: AdaptyPaywallProduct,
    context: Context,
) {}
```

<Details>
<summary>事件示例（点击展开）</summary>
```javascript
{
  "product": {
    "vendorProductId": "premium_monthly",
    "localizedTitle": "Premium Monthly",
    "localizedDescription": "Premium subscription for 1 month",
    "localizedPrice": "$9.99",
    "price": 9.99,
    "currencyCode": "USD"
  }
}
```
</Details>

#### 已开始购买 \{#started-purchase\}

当用户发起购买流程时，将调用此方法：

```kotlin showLineNumbers title="Kotlin"
public override fun onPurchaseStarted(
    product: AdaptyPaywallProduct,
    context: Context,
) {}
```

<Details>
<summary>事件示例（点击展开）</summary>
```javascript
{
  "product": {
    "vendorProductId": "premium_monthly",
    "localizedTitle": "Premium Monthly",
    "localizedDescription": "Premium subscription for 1 month",
    "localizedPrice": "$9.99",
    "price": 9.99,
    "currencyCode": "USD"
  }
}
```
</Details>

该方法在 Observer 模式下不会被调用。详情请参阅 [Android - 在 Observer 模式下展示付费墙编辑工具付费墙](android-present-paywall-builder-paywalls-in-observer-mode)。

#### 购买成功、取消或待处理 \{#successful-canceled-or-pending-purchase\}

购买成功时，将调用以下方法：
```kotlin showLineNumbers title="Kotlin"
public override fun onPurchaseFinished(
    purchaseResult: AdaptyPurchaseResult,
    product: AdaptyPaywallProduct,
    context: Context,
) {
    if (purchaseResult !is AdaptyPurchaseResult.UserCanceled)
        context.getActivityOrNull()?.onBackPressed()
}
```

<Details>
<summary>事件示例（点击展开）</summary>
```javascript
// 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"
  }
}
```
</Details>
我们建议在这种情况下关闭当前页面。

该方法在 Observer 模式下不会被调用。详情请参阅 [Android - 在 Observer 模式下展示付费墙编辑工具付费墙](android-present-paywall-builder-paywalls-in-observer-mode)。

#### 购买失败 \{#failed-purchase\}
如果购买因错误而失败，将调用此方法。这包括 Google Play Billing 错误（支付限制、无效产品、网络故障）、交易验证失败以及系统错误。请注意，用户取消操作会触发 `onPurchaseFinished`（结果为 cancelled），待处理的支付则不会触发此方法。

```kotlin showLineNumbers title="Kotlin"
public override fun onPurchaseFailure(
    error: AdaptyError,
    product: AdaptyPaywallProduct,
    context: Context,
) {}
```

<Details>
<summary>事件示例（点击展开）</summary>
```javascript
{
  "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"
  }
}
```
</Details>
该方法在 Observer 模式下不会被调用。详情请参考 [Android - 在 Observer 模式下展示付费墙编辑工具付费墙](android-present-paywall-builder-paywalls-in-observer-mode)。

#### 完成网页支付导航 \{#finished-web-payment-navigation\}

此方法在尝试为特定产品打开[网页付费墙](web-paywall)后触发，包括导航成功和失败两种情况：

```kotlin showLineNumbers title="Kotlin"
public override fun onFinishWebPaymentNavigation(
    product: AdaptyPaywallProduct?,
    error: AdaptyError?,
    context: Context,
) {}
```
**参数：**

| 参数        | 说明                                                                                     |
|:------------|:-----------------------------------------------------------------------------------------|
| **product** | 打开网页付费墙时对应的 `AdaptyPaywallProduct`。可以为 `null`。                           |
| **error**   | 若网页付费墙跳转失败，则为 `AdaptyError` 对象；跳转成功时为 `null`。 |

<Details>
<summary>事件示例（点击展开）</summary>
```javascript
// 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"
    }
  }
}
```
</Details>
#### 购买恢复成功 \{#successful-restore\}

如果购买恢复成功，将调用此方法：

```kotlin showLineNumbers title="Kotlin"
public override fun onRestoreSuccess(
    profile: AdaptyProfile,
    context: Context,
) {}
```

<Details>
<summary>事件示例（点击展开）</summary>
```javascript
{
  "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"
      }
    ]
  }
}
```
</Details>

我们建议在用户拥有所需 `accessLevel` 时关闭该页面。请参阅[订阅状态](android-listen-subscription-changes)主题，了解如何进行检查。

#### 恢复失败 \{#failed-restore\}
如果 `Adapty.restorePurchases()` 失败，将调用此方法：

```kotlin showLineNumbers title="Kotlin"
public override fun onRestoreFailure(
    error: AdaptyError,
    context: Context,
) {}
```

<Details>
<summary>事件示例（点击展开）</summary>

```javascript
{
  "error": {
    "code": "restore_failed",
    "message": "Purchase restoration failed",
    "details": {
      "underlyingError": "No previous purchases found"
    }
  }
}
```
</Details>

#### 升级订阅 \{#upgrade-subscription\}
<Tabs groupId="current-os" queryString>
<TabItem value="new" label="SDK version 3.10.0 or later" default>

当用户在已有活跃订阅的情况下尝试购买新订阅时，你可以通过重写此方法来控制新购买的处理方式。你有两个选项：
1. **用新订阅替换当前订阅**：
```kotlin showLineNumbers title="Kotlin"
public override fun onAwaitingPurchaseParams(
    product: AdaptyPaywallProduct,
    context: Context,
    onPurchaseParamsReceived: AdaptyUiEventListener.PurchaseParamsCallback,
): AdaptyUiEventListener.PurchaseParamsCallback.IveBeenInvoked {
    onPurchaseParamsReceived(
        AdaptyPurchaseParameters.Builder()
            .withSubscriptionUpdateParams(AdaptySubscriptionUpdateParameters(...))
            .build()
    )
    return AdaptyUiEventListener.PurchaseParamsCallback.IveBeenInvoked
}
```
2. **保留两个订阅**（单独添加新订阅）：
```kotlin showLineNumbers title="Kotlin"
public override fun onAwaitingPurchaseParams(
    product: AdaptyPaywallProduct,
    context: Context,
    onPurchaseParamsReceived: AdaptyUiEventListener.PurchaseParamsCallback,
): AdaptyUiEventListener.PurchaseParamsCallback.IveBeenInvoked {
    onPurchaseParamsReceived(AdaptyPurchaseParameters.Empty)
    return AdaptyUiEventListener.PurchaseParamsCallback.IveBeenInvoked
}
```
:::note
如果不覆盖此方法，默认行为是保持两个订阅同时有效（等同于使用 `AdaptyPurchaseParameters.Empty`）。
:::

您也可以根据需要设置额外的购买参数：
```kotlin
AdaptyPurchaseParameters.Builder()
    .withSubscriptionUpdateParams(AdaptySubscriptionUpdateParameters(...)) // optional - for replacing current subscription
    .withOfferPersonalized(true) // optional - if using personalized pricing
    .build()
```

</TabItem>
<TabItem value="old" label="SDK 版本低于 3.10.0" default>
如果在某个订阅仍处于活跃状态时购买了新订阅，可覆盖此方法，将当前订阅替换为新订阅。如果希望保留原有的活跃订阅，同时单独添加新订阅，则调用 `onSubscriptionUpdateParamsReceived(null)`：

```kotlin showLineNumbers title="Kotlin"
public override fun onAwaitingSubscriptionUpdateParams(
    product: AdaptyPaywallProduct,
    context: Context,
    onSubscriptionUpdateParamsReceived: SubscriptionUpdateParamsCallback,
) {
    onSubscriptionUpdateParamsReceived(AdaptySubscriptionUpdateParameters(...))
}
```
</TabItem>
</Tabs>

<Details>
<summary>事件示例（点击展开）</summary>

```javascript
{
  "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"
  }
}
```
</Details>
### 数据获取与渲染 \{#data-fetching-and-rendering\}

#### 产品加载错误 \{#product-loading-errors\}

如果你在初始化时没有传入产品，AdaptyUI 会自动从服务器获取所需对象。若该操作失败，AdaptyUI 将调用以下方法来报告错误：

```kotlin showLineNumbers title="Kotlin"
public override fun onLoadingProductsFailure(
    error: AdaptyError,
    context: Context,
): Boolean = false
```

<Details>
<summary>事件示例（点击展开）</summary>
```javascript
{
  "error": {
    "code": "products_loading_failed",
    "message": "Failed to load products from the server",
    "details": {
      "underlyingError": "Network timeout"
    }
  }
}
```
</Details>

如果返回 `true`，AdaptyUI 将在 2 秒后重试请求。

#### 渲染错误 \{#rendering-errors\}

如果在界面渲染过程中发生错误，系统将通过调用以下方法来上报：

```kotlin showLineNumbers title="Kotlin"
public override fun onRenderingError(
    error: AdaptyError,
    context: Context,
) {}
```

<Details>
<summary>事件示例（点击展开）</summary>
```javascript
{
  "error": {
    "code": "rendering_failed",
    "message": "Failed to render paywall interface",
    "details": {
      "underlyingError": "Invalid paywall configuration"
    }
  }
}
```
</Details>

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

---