处理流程与付费墙事件 - Unity

本指南涵盖购买、恢复、产品选择和流程渲染的事件处理。你还需要实现按钮处理(关闭流程、打开链接等)。详情请参阅处理流程操作指南

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

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

处理事件

如需控制或监听移动应用中流程屏幕上发生的过程,请实现 IAdaptyFlowsEventsListener 接口,并通过 Adapty.SetFlowsEventsListener() 进行注册:

using UnityEngine;
using AdaptySDK;

public class FlowEventsHandler : MonoBehaviour, IAdaptyFlowsEventsListener
{
    void Start()
    {
        Adapty.SetFlowsEventsListener(this);
    }

    // Implement all interface methods below
}

这些方法用于添加响应流程事件的自定义逻辑。SDK 不会对其应用任何默认行为:购买成功或发生错误时,视图不会自动关闭——请在适当时机自行调用 view.Dismiss(...)

用户生成的事件

流程出现

当流程视图在屏幕上展示时触发。

在 iOS 上,当用户点击流程内的网页付费墙按钮、并在应用内浏览器中打开网页付费墙时,也会触发此事件。

public void FlowViewDidAppear(AdaptyUIFlowView view) { }

流程消失

当流程视图从屏幕上关闭时触发。

在 iOS 上,当从流程内嵌浏览器打开的 web 付费墙 从屏幕上消失时也会触发。

public void FlowViewDidDisappear(AdaptyUIFlowView view) { }

产品选择

当用户或系统选择某个产品进行购买时触发。

public void FlowViewDidSelectProduct(
    AdaptyUIFlowView view,
    string productId
) { }
事件示例(点击展开)
{
  "productId": "premium_monthly"
}

开始购买

当用户发起购买流程时触发。

public void FlowViewDidStartPurchase(
    AdaptyUIFlowView view,
    AdaptyPaywallProduct product
) { }

观察者模式下,从流程中发起的购买会被传递到您的 IAdaptyUIObserverModeResolver,而非此回调。

事件示例(点击展开)
{
  "product": {
    "vendorProductId": "premium_monthly",
    "localizedTitle": "Premium Monthly",
    "localizedDescription": "Premium subscription for 1 month",
    "localizedPrice": "$9.99",
    "price": 9.99,
    "currencyCode": "USD"
  }
}

购买成功、取消或待处理

当购买成功、用户取消购买或购买处于待处理状态时,此方法将被调用。用户取消以及待处理支付(例如需要家长批准)会触发此方法,而不是 FlowViewDidFailPurchase

流程在购买完成后会保持打开状态,直到你主动关闭它,因此请在用户获得访问权限后自行调用 view.Dismiss(...)

public void FlowViewDidFinishPurchase(
    AdaptyUIFlowView view,
    AdaptyPaywallProduct product,
    AdaptyPurchaseResult purchasedResult
) {
    switch (purchasedResult.Type) {
        case AdaptyPurchaseResultType.Success:
            // Check if user has access to premium features
            if (purchasedResult.Profile != null
                && purchasedResult.Profile.AccessLevels.TryGetValue("premium", out var premium)
                && premium.IsActive) {
                view.Dismiss(null);
            }
            break;
        case AdaptyPurchaseResultType.Pending:
            // Handle pending purchase (e.g., user will pay offline with cash)
            break;
        case AdaptyPurchaseResultType.UserCancelled:
            // Handle user cancellation
            break;
        default:
            break;
    }
}
事件示例(点击展开)
// 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"
        }
      }
    }
  }
}

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

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

我们建议在购买成功后关闭流程页面。

购买失败

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

public void FlowViewDidFailPurchase(
    AdaptyUIFlowView view,
    AdaptyPaywallProduct product,
    AdaptyError error
) { }
事件示例(点击展开)
{
  "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"
    }
  }
}

开始恢复

当用户发起恢复流程时触发:

public void FlowViewDidStartRestore(AdaptyUIFlowView view) { }

恢复成功

当购买恢复成功时触发。恢复完成后,流程界面仍会保持打开状态,直到你手动关闭它:

public void FlowViewDidFinishRestore(
    AdaptyUIFlowView view,
    AdaptyProfile profile
) {
    // Check if user has access to premium features
    if (profile.AccessLevels.TryGetValue("premium", out var premium) && premium.IsActive) {
        view.Dismiss(null);
    }
}
事件示例(点击展开)
{
  "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 时关闭该页面。请参阅订阅状态主题,了解如何检查。

恢复失败

当购买恢复失败时触发:

public void FlowViewDidFailRestore(
    AdaptyUIFlowView view,
    AdaptyError error
) { }
事件示例(点击展开)
{
  "error": {
    "code": "restore_failed",
    "message": "Purchase restoration failed",
    "details": {
      "underlyingError": "No previous purchases found"
    }
  }
}

完成网页支付导航

尝试打开网页付费墙进行购买(无论成功或失败)后,将调用此方法:

public void FlowViewDidFinishWebPaymentNavigation(
    AdaptyUIFlowView view,
    AdaptyPaywallProduct product,
    AdaptyError error
) { }

参数:

  • product:打开(或尝试打开)网页付费墙所对应的产品,或为 null
  • error:若网页付费墙成功打开则为 null,若失败则为 AdaptyError
事件示例(点击展开)
// 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": null,
  "error": {
    "code": "wrong_param",
    "message": "Current method is not available for this product",
    "details": {
      "underlyingError": "Product not configured for web purchases"
    }
  }
}

数据获取与渲染

产品加载错误

当产品加载失败时触发,并提供 AdaptyError。如果你在初始化时未传入产品数组,AdaptyUI 会自行从服务器获取所需对象。该操作可能失败,AdaptyUI 将通过调用此方法报告错误:

public void FlowViewDidFailLoadingProducts(
    AdaptyUIFlowView view,
    AdaptyError error
) { }
事件示例(点击展开)
{
  "error": {
    "code": "products_loading_failed",
    "message": "Failed to load products from the server",
    "details": {
      "underlyingError": "Network timeout"
    }
  }
}

渲染和运行时错误

如果在界面渲染过程中发生错误,或出现其他非购买类运行时错误,该方法会上报此错误。视图不会自动关闭——如需关闭,请手动调用 view.Dismiss(...)

public void FlowViewDidReceiveError(
    AdaptyUIFlowView view,
    AdaptyError error
) { }
事件示例(点击展开)
{
  "error": {
    "code": "rendering_failed",
    "message": "Failed to render flow interface",
    "details": {
      "underlyingError": "Invalid flow configuration"
    }
  }
}

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

分析事件

FlowViewDidReceiveAnalyticEvent 预留用于处理来自流程的自定义分析事件。目前流程尚未向您的代码发送此类事件,因此方法体保持为空即可——IAdaptyFlowsEventsListener 是一个 C# 接口,所以该方法仍然必须存在:

public void FlowViewDidReceiveAnalyticEvent(
    AdaptyUIFlowView view,
    string name,
    IDictionary<string, object> @params
) { }

处理系统请求

IAdaptyUISystemRequestsHandler(通过 Adapty.SetSystemRequestsHandler(...) 注册)专门用于处理流程中的系统请求:包括操作系统权限提示(如推送通知或摄像头访问权限)以及应用评价请求。目前流程尚未触发这些请求,因此无需注册处理程序。

Android 系统返回按钮

Android 系统返回按钮(或返回手势)会以 SystemBack 动作的形式传递给 FlowViewDidPerformAction,不会自动关闭流程——用户需要通过你定义的路径离开流程,例如 Close 按钮或编辑工具中的 on_device_back 动作。如果你希望系统返回按钮能关闭流程,请自行处理该动作:

public void FlowViewDidPerformAction(
    AdaptyUIFlowView view,
    AdaptyUIUserAction action
) {
    switch (action.Type) {
        case AdaptyUIUserActionType.Close:
        case AdaptyUIUserActionType.SystemBack:
            view.Dismiss(null);
            break;
        default:
            // handle other events
            break;
    }
}

有关所有操作的完整列表,请参阅处理流程操作的指南

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

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

本指南仅适用于新版付费墙编辑工具付费墙,需要 Adapty SDK v3.3.0 或更高版本。

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

处理事件

要控制或监控应用付费墙界面上发生的流程,请实现 AdaptyPaywallsEventsListener 接口:

using UnityEngine;
using AdaptySDK;

public class PaywallEventsHandler : MonoBehaviour, AdaptyPaywallsEventsListener
{
    void Start()
    {
        Adapty.SetPaywallsEventsListener(this);
    }

    // Implement all required interface methods below
}

用户触发的事件

付费墙已显示

当付费墙视图呈现到屏幕上时触发。

在 iOS 上,当用户点击付费墙内的网页付费墙按钮并在应用内浏览器中打开网页付费墙时,也会触发此事件。

public void PaywallViewDidAppear(AdaptyUIPaywallView view) { }

付费墙已消失

当付费墙视图从屏幕上关闭时触发。

在 iOS 上,当从付费墙打开的网页付费墙在应用内浏览器中消失时,也会触发此事件。

public void PaywallViewDidDisappear(AdaptyUIPaywallView view) { }

产品选择

当用户或系统选择要购买的产品时触发。

public void PaywallViewDidSelectProduct(
    AdaptyUIPaywallView view, 
    string productId
) { }
事件示例(点击展开)
{
  "productId": "premium_monthly"
}

开始购买

当用户发起购买流程时触发。

public void PaywallViewDidStartPurchase(
    AdaptyUIPaywallView view, 
    AdaptyPaywallProduct product
) { }
事件示例(点击展开)
{
  "product": {
    "vendorProductId": "premium_monthly",
    "localizedTitle": "Premium Monthly",
    "localizedDescription": "Premium subscription for 1 month",
    "localizedPrice": "$9.99",
    "price": 9.99,
    "currencyCode": "USD"
  }
}

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

如果购买成功、用户取消购买,或购买处于待处理状态,此方法将被调用。用户取消和待处理付款(例如需要家长批准)会触发此方法,而不是 PaywallViewDidFailPurchase

public void PaywallViewDidFinishPurchase(
    AdaptyUIPaywallView view, 
    AdaptyPaywallProduct product, 
    AdaptyPurchaseResult purchasedResult
) { }
事件示例(点击展开)
// 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"
        }
      }
    }
  }
}

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

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

在这种情况下,我们建议关闭该界面。

购买失败

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

public void PaywallViewDidFailPurchase(
    AdaptyUIPaywallView view, 
    AdaptyPaywallProduct product, 
    AdaptyError error
) { }
事件示例(点击展开)
{
  "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"
    }
  }
}

开始恢复购买

当用户发起恢复购买流程时触发:

public void PaywallViewDidStartRestore(AdaptyUIPaywallView view) { }

恢复成功

当购买恢复成功时触发:

public void PaywallViewDidFinishRestore(
    AdaptyUIPaywallView view, 
    AdaptyProfile profile
) { }
事件示例(点击展开)
{
  "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,我们建议关闭该界面。请参阅订阅状态了解如何检查。

恢复失败

当购买恢复失败时触发:

public void PaywallViewDidFailRestore(
    AdaptyUIPaywallView view, 
    AdaptyError error
) { }
事件示例(点击展开)
{
  "error": {
    "code": "restore_failed",
    "message": "Purchase restoration failed",
    "details": {
      "underlyingError": "No previous purchases found"
    }
  }
}

网页支付导航完成

尝试打开网页付费墙进行购买后(无论成功还是失败),此方法将被调用:

public void PaywallViewDidFinishWebPaymentNavigation(
    AdaptyUIPaywallView view, 
    AdaptyPaywallProduct product, 
    AdaptyError error
) { }

参数:

  • product:已打开(或尝试打开)网页付费墙的产品
  • error:网页付费墙成功打开时为 null,失败时为 AdaptyError
事件示例(点击展开)
// 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": "wrong_param",
    "message": "Current method is not available for this product",
    "details": {
      "underlyingError": "Product not configured for web purchases"
    }
  }
}

数据获取与渲染

产品加载错误

当产品加载失败时触发,并提供 AdaptyError。如果您在初始化时未传入产品数组,AdaptyUI 将自行从服务器获取所需对象。该操作可能失败,AdaptyUI 会通过调用此方法来报告错误:

public void PaywallViewDidFailLoadingProducts(
    AdaptyUIPaywallView view, 
    AdaptyError error
) { }
事件示例(点击展开)
{
  "error": {
    "code": "products_loading_failed",
    "message": "Failed to load products from the server",
    "details": {
      "underlyingError": "Network timeout"
    }
  }
}

渲染错误

当界面渲染过程中发生错误时触发,并提供 AdaptyError

public void PaywallViewDidFailRendering(
    AdaptyUIPaywallView view, 
    AdaptyError error
) { }
事件示例(点击展开)
{
  "error": {
    "code": "rendering_failed",
    "message": "Failed to render paywall interface",
    "details": {
      "underlyingError": "Invalid paywall configuration"
    }
  }
}

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