Kotlin Multiplatform - 处理付费墙事件

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

本指南仅适用于新版付费墙编辑工具付费墙

要控制或监控移动应用中付费墙界面上发生的过程,请实现 AdaptyUIPaywallsEventsObserver 接口方法。某些方法具有默认实现,可自动处理常见场景。

这些方法是您添加自定义逻辑以响应付费墙事件的位置。您可以使用 view.dismiss() 关闭付费墙,或实现任何其他所需的自定义行为。

用户生成的事件

付费墙的显示与隐藏

当付费墙显示或隐藏时,以下方法将被调用:

override fun paywallViewDidAppear(view: AdaptyUIPaywallView) {
    // Handle paywall appearance
    // You can track analytics or update UI here
}

override fun paywallViewDidDisappear(view: AdaptyUIPaywallView) {
    // Handle paywall disappearance
    // You can track analytics or update UI here
}
  • 在 iOS 上,当用户点击付费墙内的 web 付费墙按钮 且 web 付费墙在应用内浏览器中打开时,paywallViewDidAppear 也会被触发。
  • 在 iOS 上,当从付费墙在应用内浏览器中打开的 web 付费墙 从屏幕上消失时,paywallViewDidDisappear 也会被触发。
事件示例(点击展开)
// Paywall appeared
{
  // No additional data
}

// Paywall disappeared
{
  // No additional data
}

产品选择

如果用户选择了要购买的产品,将调用此方法:

override fun paywallViewDidSelectProduct(view: AdaptyUIPaywallView, productId: String) {
    // Handle product selection
    // You can update UI or track analytics here
}
事件示例(点击展开)
{
  "productId": "premium_monthly"
}

已开始购买

如果用户发起购买流程,将会调用此方法:

override fun paywallViewDidStartPurchase(view: AdaptyUIPaywallView, product: AdaptyPaywallProduct) {
    // Handle purchase start
    // You can show loading indicators or track analytics here
}
事件示例(点击展开)
{
  "product": {
    "vendorProductId": "premium_monthly",
    "localizedTitle": "Premium Monthly",
    "localizedDescription": "Premium subscription for 1 month",
    "localizedPrice": "$9.99",
    "price": 9.99,
    "currencyCode": "USD"
  }
}

成功、取消或待处理的购买

如果购买成功,将调用此方法。默认情况下,它会自动关闭付费墙,除非购买被用户取消:

override fun paywallViewDidFinishPurchase(
    view: AdaptyUIPaywallView,
    product: AdaptyPaywallProduct,
    purchaseResult: AdaptyPurchaseResult
) {
    when (purchaseResult) {
        is AdaptyPurchaseResult.Success -> {
            // Check if user has access to premium features
            if (purchaseResult.profile.accessLevels["premium"]?.isActive == true) {
                view.dismiss()
            }
        }
        AdaptyPurchaseResult.Pending -> {
            // Handle pending purchase (e.g., user will pay offline with cash)
        }
        AdaptyPurchaseResult.UserCanceled -> {
            // Handle user cancellation
        }
    }
}
事件示例(点击展开)
// 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"
        }
      }
    }
  }
}

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

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

我们建议在购买成功后关闭付费墙页面。

购买失败

如果购买因错误而失败,此方法将被调用。这包括 StoreKit/Google Play Billing 错误(支付限制、无效产品、网络故障)、交易验证失败以及系统错误。请注意,用户取消操作会触发 paywallViewDidFinishPurchase 并返回已取消的结果,待处理的付款不会触发此方法。

override fun paywallViewDidFailPurchase(
    view: AdaptyUIPaywallView,
    product: AdaptyPaywallProduct,
    error: AdaptyError
) {
    // Add your purchase failure handling logic here
    // For example: show error message, retry option, or custom error handling
}
事件示例(点击展开)
{
  "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"
    }
  }
}

开始恢复购买

如果用户发起恢复购买流程,将调用此方法:

override fun paywallViewDidStartRestore(view: AdaptyUIPaywallView) {
    // Handle restore start
    // You can show loading indicators or track analytics here
}

成功恢复购买

如果恢复购买成功,将调用此方法:

override fun paywallViewDidFinishRestore(view: AdaptyUIPaywallView, profile: AdaptyProfile) {
    // Add your successful restore handling logic here
    // For example: show success message, update UI, or dismiss paywall
    
    // Check if user has access to premium features
    if (profile.accessLevels["premium"]?.isActive == true) {
        view.dismiss()
    }
}
事件示例(点击展开)
{
  "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() 失败,将调用此方法:

override fun paywallViewDidFailRestore(view: AdaptyUIPaywallView, error: AdaptyError) {
    // Add your restore failure handling logic here
    // For example: show error message, retry option, or custom error handling
}
事件示例(点击展开)
{
  "error": {
    "code": "restore_failed",
    "message": "Purchase restoration failed",
    "details": {
      "underlyingError": "No previous purchases found"
    }
  }
}

Web 支付导航完成

如果用户使用 web 付费墙 发起购买流程,将调用此方法:

override fun paywallViewDidFinishWebPaymentNavigation(
    view: AdaptyUIPaywallView,
    product: AdaptyPaywallProduct?,
    error: AdaptyError?
) {
    if (error != null) {
        // Handle web payment navigation error
    } else {
        // Handle successful web payment navigation
    }
}
事件示例(点击展开)
// Successful web payment 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 web payment navigation
{
  "product": null,
  "error": {
    "code": "web_payment_failed",
    "message": "Web payment navigation failed",
    "details": {
      "underlyingError": "Network connection error"
    }
  }
}

数据获取与渲染

产品加载错误

如果您在初始化时未传入产品,AdaptyUI 将自行从服务器检索所需对象。若此操作失败,AdaptyUI 将通过调用以下方法来报告错误:

override fun paywallViewDidFailLoadingProducts(view: AdaptyUIPaywallView, error: AdaptyError) {
    // Add your product loading failure handling logic here
    // For example: show error message, retry option, or custom error handling
}
事件示例(点击展开)
{
  "error": {
    "code": "products_loading_failed",
    "message": "Failed to load products from the server",
    "details": {
      "underlyingError": "Network timeout"
    }
  }
}

渲染错误

如果在界面渲染过程中发生错误,该方法将报告此错误:

override fun paywallViewDidFailRendering(view: AdaptyUIPaywallView, error: AdaptyError) {
    // Handle rendering error
    // In a normal situation, such errors should not occur
    // If you come across one, please let us know
}
事件示例(点击展开)
{
    "error": {
        "code": "rendering_failed",
            "message": "Failed to render paywall interface",
            "details": {
            "underlyingError": "Invalid paywall configuration"
        }
    }
}

在正常情况下,此类错误不应发生,因此如果您遇到此类错误,请告知我们。