---
title: "Migrate Adapty Unity SDK sang v. 4.0"
description: "Migrate sang Adapty Unity SDK v4.0 (beta) bằng cách thay thế các API paywall bằng API flow, tương thích với cả Flow Builder và Paywall Builder."
---

Adapty Unity SDK 4.0 (beta) giới thiệu flows và đổi tên các API paywall tương ứng. Các API mới hoạt động với cả Flow Builder mới và Paywall Builder hiện có — không cần thay đổi cấu hình phía Adapty Dashboard.

## Tham khảo nhanh \{#quick-reference\}

| v3 | v4 |
|---|---|
| `Adapty.GetPaywall(placementId, locale, ...)` | `Adapty.GetFlow(placementId, ...)` |
| `Adapty.GetPaywallForDefaultAudience(placementId, locale, ...)` | `Adapty.GetFlowForDefaultAudience(placementId, ...)` |
| `Adapty.GetPaywallProducts(paywall, ...)` | `Adapty.GetPaywallProducts(flow, ...)` |
| `Adapty.LogShowPaywall(paywall, ...)` | `Adapty.LogShowFlow(flow, ...)` |
| `AdaptyPaywall` | `AdaptyFlow` |
| `AdaptyUI.CreatePaywallView(paywall, ...)` | `AdaptyUI.CreateFlowView(flow, ...)` |
| `AdaptyUICreatePaywallViewParameters` | `AdaptyUICreateFlowViewParameters` |
| `AdaptyUIPaywallView` | `AdaptyUIFlowView` |
| `AdaptyUI.PresentPaywallView(view, ...)` / `DismissPaywallView(view, ...)` | `AdaptyUI.PresentFlowView(view, ...)` / `DismissFlowView(view, ...)` |
| `Adapty.SetPaywallsEventsListener(listener)` | `Adapty.SetFlowsEventsListener(listener)` |
| `AdaptyPaywallsEventsListener` | `IAdaptyFlowsEventsListener` |
| `AdaptyEventListener` | `IAdaptyEventListener` |
| `AdaptyOnboardingsEventsListener` | `IAdaptyOnboardingsEventsListener` |
| `PaywallViewDidPerformAction`, `PaywallViewDidAppear`, và các callback `PaywallView...` khác | `FlowViewDidPerformAction`, `FlowViewDidAppear`, và các callback `FlowView...` khác |
| `PaywallViewDidFailRendering` | `FlowViewDidReceiveError` |
| `Adapty.SetFallbackPaywalls(...)` (deprecated trong v3) | đã xóa — dùng `Adapty.SetFallback(fileName, ...)` |
| `Builder.SetIDFACollectionDisabled(...)` (deprecated trong v3) | đã xóa — dùng `Builder.SetAppleIDFACollectionDisabled(...)` |
| `paywall.Products` (danh sách `AdaptyProductReference`) | đã xóa — dùng `ProductIdentifiers` hoặc `VendorProductIds`, hoặc gọi `GetPaywallProducts(flow)` để lấy đầy đủ sản phẩm |
| `AdaptyProductReference` | đã xóa khỏi kiểu public — xem [Mô hình dữ liệu](#data-model) |
| `paywall.RemoteConfigString` | đã xóa — dùng `flow.RemoteConfig?.Data` |

`AdaptyPaywallProduct` giữ nguyên tên — sản phẩm vẫn thuộc về một flow, và `GetPaywallProducts` cũng giữ nguyên tên, hiện nhận một `AdaptyFlow`. Các phương thức `GetFlow` và `GetFlowForDefaultAudience` không còn nhận tham số `locale` nữa. Các API mua hàng và hồ sơ người dùng (`MakePurchase`, `RestorePurchases`, `GetProfile`, `Identify`, `UpdateProfile`) và fallback qua `SetFallback` không thay đổi. Các phương thức onboarding vẫn hoạt động nhưng đã bị deprecated — xem [Deprecated Onboarding API](#onboarding-api-deprecation). Một số hành vi mặc định đã thay đổi — xem [Thay đổi hành vi mặc định](#default-behavior-changes).

## Cài đặt \{#installation\}

v4.0 là phiên bản pre-release, vì vậy hãy chỉ định chính xác thẻ beta. Để cài đặt qua Unity Package Manager, thêm thẻ vào Git URL:

```
https://github.com/adaptyteam/AdaptySDK-Unity.git?path=/Packages/com.adapty.unity-sdk#4.0.0-beta.1
```

Nếu bạn cài đặt qua Unity package, hãy tải `adapty-unity-plugin-4.0.0-beta.1.unitypackage` từ [bản phát hành 4.0.0-beta.1](https://github.com/adaptyteam/AdaptySDK-Unity/releases/tag/4.0.0-beta.1). Xem [Cài đặt Adapty SDK](sdk-installation-unity#install-adapty-sdk) để biết hướng dẫn thiết lập đầy đủ.

v4 đi kèm với hai thay đổi trong cấu hình build:

- **Các dependency iOS chuyển sang Swift Package Manager.** SDK iOS gốc của Adapty 4.0 được khai báo là remote Swift package thay vì CocoaPods pod. Hãy cập nhật [External Dependency Manager](https://github.com/googlesamples/unity-jar-resolver#getting-started) lên **1.2.188 trở lên** — các phiên bản cũ hơn không hỗ trợ dependency của Swift Package Manager. Các bước CocoaPods (`iOS Resolver -> Install Cocoapods`, mở `Unity-iPhone.xcworkspace`) không còn áp dụng nữa.
- **iOS deployment target phải là 15.0 trở lên.** Một build validator mới trong Unity Editor sẽ dừng iOS build nếu target thấp hơn mức này.

Các SDK gốc bên dưới được nâng lên phiên bản 4.x trên cả hai nền tảng và được phân giải tự động — không cần thay đổi build nào khác.

## Lấy flows \{#fetching-flows\}

### GetPaywall → GetFlow

Kiểu trả về thay đổi từ `AdaptyPaywall` sang `AdaptyFlow`, và tham số `locale` bị loại bỏ — khi bạn render một flow, locale sẽ được tự động xác định; đối với các paywall tùy chỉnh, tất cả các locale được trả về trong `flow.RemoteConfigs`:

```diff showLineNumbers
- Adapty.GetPaywall("YOUR_PLACEMENT_ID", "en", (paywall, error) => {
+ Adapty.GetFlow("YOUR_PLACEMENT_ID", (flow, error) => {
      if (error != null) {
          // handle the error
          return;
      }
-     // use the paywall
+     // use the flow
  });
```

`GetPaywallForDefaultAudience` được đổi tên theo cách tương tự:

```diff showLineNumbers
- Adapty.GetPaywallForDefaultAudience("YOUR_PLACEMENT_ID", "en", (paywall, error) => { /* ... */ });
+ Adapty.GetFlowForDefaultAudience("YOUR_PLACEMENT_ID", (flow, error) => { /* ... */ });
```

### GetPaywallProducts(paywall) → GetPaywallProducts(flow)

`GetPaywallProducts` giữ nguyên tên nhưng giờ nhận vào một `AdaptyFlow`:

```diff showLineNumbers
- Adapty.GetPaywallProducts(paywall, (products, error) => {
+ Adapty.GetPaywallProducts(flow, (products, error) => {
      if (error != null) {
          // handle the error
          return;
      }
      // use the products
  });
```

## Mô hình dữ liệu \{#data-model\}

`GetFlow` trả về một `AdaptyFlow` thay vì `AdaptyPaywall`, và cấu trúc của đối tượng đã thay đổi:

| Thuộc tính v3 `AdaptyPaywall` | Thuộc tính v4 `AdaptyFlow` | Hành động |
|---|---|---|
| `RemoteConfig` (đơn lẻ, nullable) | `RemoteConfigs` (danh sách) | Một flow chứa một remote config cho mỗi ngôn ngữ đã cấu hình. Đọc mục phù hợp với người dùng từ `flow.RemoteConfigs`. Shortcut `flow.RemoteConfig` trả về mục đầu tiên. |
| _(mới)_ | `Paywalls` (danh sách `AdaptyFlowPaywall`) | Mỗi mục là một biến thể paywall trong flow, với `Name`, `VariationId` và `ProductIdentifiers` riêng. Các phương thức web paywall nhận vào `AdaptyFlowPaywall` — xem [Phương thức web paywall](#web-paywall-methods). |
| `ProductIdentifiers`, `VendorProductIds` | giữ nguyên | Trên `AdaptyFlow`, các thuộc tính này tổng hợp sản phẩm từ tất cả các biến thể paywall. Mỗi biến thể cũng có `ProductIdentifiers` và `VendorProductIds` riêng. Để lấy sản phẩm, tiếp tục gọi `GetPaywallProducts(flow)`. |
| `HasViewConfiguration` | đã xóa | Xóa mọi kiểm tra `HasViewConfiguration` trong code của bạn — `CreateFlowView` sẽ trả về lỗi thay thế (xem [Hiển thị flow](#displaying-flows)). |
| `Products` (danh sách `AdaptyProductReference`) | đã xóa | `AdaptyProductReference` không còn là public, cùng với đó là các giá trị `PromotionalOfferId`, `WinBackOfferId` và `AndroidOfferId` mà nó mang theo. Sử dụng `ProductIdentifiers` — danh sách `AdaptyProductIdentifier` với `VendorProductId` và `BasePlanId` chỉ dành cho Android (tương đương `AndroidBasePlanId` của v3) — hoặc gọi `GetPaywallProducts(flow)` khi bạn cần các đối tượng `AdaptyPaywallProduct` đầy đủ với giá và ưu đãi. |
| `RemoteConfigString` | đã xóa | Đọc chuỗi trực tiếp từ remote config: `flow.RemoteConfig?.Data`, hoặc mục tương ứng trong `flow.RemoteConfigs`. |
| _(mới)_ | `FlowVersionId` (nullable) | Định danh phiên bản của flow, hoặc `null` khi không có. |

`AdaptyPaywallProduct` có thêm một trường: `FlowProductId`, định danh sản phẩm trong flow, có giá trị `null` đối với các sản phẩm không thuộc flow nào.

## Các phương thức web paywall \{#web-paywall-methods\}

`OpenWebPaywall` và `CreateWebPaywallUrl` giữ nguyên tên, nhưng tham số `paywall` giờ nhận một `AdaptyFlowPaywall` — một trong các biến thể trong `flow.Paywalls`. Bạn vẫn có thể truyền một `AdaptyPaywallProduct` thay thế:

```diff showLineNumbers
- Adapty.OpenWebPaywall(paywall, AdaptyWebPresentation.ExternalBrowser, (error) => { /* ... */ });
+ var flowPaywall = flow.Paywalls.FirstOrDefault();
+ if (flowPaywall != null) {
+     Adapty.OpenWebPaywall(flowPaywall, AdaptyWebPresentation.ExternalBrowser, (error) => { /* ... */ });
+ }
```

## Theo dõi lượt xem flow \{#tracking-flow-views\}

### LogShowPaywall → LogShowFlow

`LogShowPaywall` được đổi tên thành `LogShowFlow` và hiện nhận vào một `AdaptyFlow`. Sự kiện vẫn được ghi nhận theo cùng một biến thể, vì vậy các chỉ số funnel và A/B test hiện tại vẫn hoạt động bình thường mà không cần thay đổi trên dashboard.

```diff showLineNumbers
- Adapty.LogShowPaywall(paywall, (error) => { /* ... */ });
+ Adapty.LogShowFlow(flow, (error) => { /* ... */ });
```

Tương tự như v3, bạn không cần gọi phương thức này khi hiển thị các flow hoặc paywall được render bởi [Flow Builder](adapty-flow-builder) hoặc [Paywall Builder](adapty-paywall-builder) — Adapty tự động theo dõi những lượt xem đó.

## Hiển thị flow \{#displaying-flows\}

### CreatePaywallView → CreateFlowView

Đổi tên factory method và truyền vào `AdaptyFlow`. Kiểu view trả về được đổi tên từ `AdaptyUIPaywallView` thành `AdaptyUIFlowView`, nhưng các phương thức của nó (`Present`, `Dismiss`) không thay đổi, và đối tượng tham số tùy chọn vẫn giữ nguyên các trường (`LoadTimeout`, `PreloadProducts`, `CustomTags`, `CustomTimers`, `CustomAssets`, `ProductPurchaseParameters`) dưới tên mới `AdaptyUICreateFlowViewParameters`, cùng với hai trường mới — `Locale` và `EnableSafeAreaPaddings`:

```diff showLineNumbers
- AdaptyUI.CreatePaywallView(paywall, parameters, (view, error) => {
+ AdaptyUI.CreateFlowView(flow, parameters, (view, error) => {
      if (error != null) {
          // handle the error
          return;
      }
      view.Present((error) => { /* handle the error */ });
  });
```

`CreateFlowView` trả về lỗi nếu flow không có view được cấu hình — điều này thay thế kiểm tra `HasViewConfiguration` của v3:

```diff showLineNumbers
- if (paywall.HasViewConfiguration) {
-     AdaptyUI.CreatePaywallView(paywall, null, (view, error) => { /* ... */ });
- }
+ AdaptyUI.CreateFlowView(flow, (view, error) => {
+     if (error != null) {
+         // the flow has no view configured, or view creation failed
+         return;
+     }
+     view.Present((error) => { /* handle the error */ });
+ });
```

:::note
Flow view chỉ dùng một lần: sau khi gọi `Dismiss`, view sẽ bị hủy, vì vậy hãy gọi lại `CreateFlowView` nếu muốn hiển thị flow thêm một lần nữa.
:::

### Padding vùng an toàn trên Android \{#android-safe-area-paddings\}

`AdaptyUICreateFlowViewParameters` có thêm `EnableSafeAreaPaddings`, dùng để kiểm soát padding vùng an toàn trên Android tại runtime. Tùy chọn này bị bỏ qua trên iOS và mặc định là `true`:

```csharp showLineNumbers
var parameters = new AdaptyUICreateFlowViewParameters()
    .SetEnableSafeAreaPaddings(false);
```

## Xử lý sự kiện \{#handling-events\}

Các interface listener hiện tuân theo quy ước tiền tố `I` của C#, và không còn giữ các alias cũ nữa — hãy đổi tên `AdaptyEventListener` thành `IAdaptyEventListener` và `AdaptyOnboardingsEventsListener` thành `IAdaptyOnboardingsEventsListener` ở những nơi bạn triển khai chúng.

Trình lắng nghe sự kiện flow được đổi tên từ `AdaptyPaywallsEventsListener` thành `IAdaptyFlowsEventsListener`, phương thức đăng ký từ `SetPaywallsEventsListener` thành `SetFlowsEventsListener`, và các callback thay đổi tiền tố `PaywallView` thành `FlowView`. Phần thân của các handler hiện có không cần thay đổi code — chỉ cần đổi tên interface và các phương thức:

```diff showLineNumbers
- public class MyListener : MonoBehaviour, AdaptyPaywallsEventsListener {
-     public void PaywallViewDidFinishPurchase(
-         AdaptyUIPaywallView view,
+ public class MyListener : MonoBehaviour, IAdaptyFlowsEventsListener {
+     public void FlowViewDidFinishPurchase(
+         AdaptyUIFlowView view,
          AdaptyPaywallProduct product,
          AdaptyPurchaseResult purchasedResult
      ) {
          // custom logic after purchase
      }
      // ...
  }

- Adapty.SetPaywallsEventsListener(myListener);
+ Adapty.SetFlowsEventsListener(myListener);
```

Một callback được đổi tên: `PaywallViewDidFailRendering` thành `FlowViewDidReceiveError`. Callback này được kích hoạt cho các lỗi render như trước, cộng thêm các lỗi runtime không liên quan đến mua hàng:

```diff showLineNumbers
- public void PaywallViewDidFailRendering(AdaptyUIPaywallView view, AdaptyError error) { }
+ public void FlowViewDidReceiveError(AdaptyUIFlowView view, AdaptyError error) { }
```

Xem [Xử lý sự kiện flow & paywall](unity-handling-events) để biết danh sách đầy đủ các callback.

### API mới \{#new-apis\}

- `Adapty.SetObserverModeResolver(...)` với `IAdaptyUIObserverModeResolver` — xử lý các giao dịch mua và khôi phục được khởi tạo từ flow khi SDK chạy ở chế độ [Observer mode](implement-observer-mode-unity). Trước đây tính năng này chỉ có trên SDK iOS và Android gốc. Xem [Hiển thị flow trong Observer mode](unity-present-flows-in-observer-mode).
- `Adapty.SetSystemRequestsHandler(...)` với `IAdaptyUISystemRequestsHandler` — dành cho các yêu cầu hệ thống từ flow: lời nhắc cấp quyền OS (`FlowViewDidAskPermission`) và yêu cầu đánh giá ứng dụng (`FlowViewDidRequestAppReview`). Flow chưa kích hoạt các yêu cầu này, nên bạn chưa cần đăng ký handler.
- `AdaptyUICreateFlowViewParameters.Locale` (đặt bằng `SetLocale`) — hiển thị flow hoặc paywall với một [localization cụ thể trong Builder](add-paywall-locale-in-adapty-paywall-builder) thay vì localization mà Adapty tự phân giải từ thiết bị. Flow được bản địa hóa khi view được tạo, vì vậy đây là nơi duy nhất để chọn localization, và view được tạo ra sẽ báo cáo localization mà nó được dựng với trong `view.Locale`. Xem [Sử dụng localization và mã locale](unity-localizations-and-locale-codes).
- Callback mới `FlowViewDidReceiveAnalyticEvent` trên `IAdaptyFlowsEventsListener` dành riêng cho các sự kiện phân tích tùy chỉnh từ flow. Flow chưa phát ra những sự kiện này tới code của bạn, vì vậy hãy implement với phần thân rỗng.
- `AdaptyUI.OpenUrl(url, openIn, ...)` và `AdaptyUI.RequestAppReview(...)` — xử lý native đằng sau các action `open_url` và yêu cầu đánh giá ứng dụng. Gọi `OpenUrl` từ `FlowViewDidPerformAction` để giữ nguyên hành vi URL mặc định; `RequestAppReview` hỗ trợ lời nhắc đánh giá ứng dụng mặc định, mà flow chưa kích hoạt.

## Thay đổi hành vi mặc định \{#default-behavior-changes\}

Những thay đổi này không gây ra lỗi biên dịch, vì vậy hãy kiểm tra chúng tại runtime:

- **Hoàn tất mua hàng**: Ở v3, view tự động đóng sau khi mua thành công. Ở v4, **flow vẫn mở sau khi mua hàng hoặc xảy ra lỗi cho đến khi bạn đóng nó** — SDK không áp dụng hành vi mặc định nào. Hãy tự gọi `view.Dismiss(...)` trong `FlowViewDidFinishPurchase` khi người dùng có quyền truy cập.
- **Nút back trên Android**: Nút back hệ thống (hoặc thao tác vuốt back) được chuyển đến `FlowViewDidPerformAction` dưới dạng action `SystemBack` và không còn tự đóng flow nữa — giống với iOS, nơi flow không thể bị đóng bằng thao tác hệ thống. Hãy cung cấp cho người dùng cách thoát rõ ràng (nút **Close** hoặc action `on_device_back`), hoặc tự đóng view khi xử lý action đó.
- **View chỉ dùng một lần**: Sau khi gọi `Dismiss`, view sẽ bị hủy. Gọi lại `CreateFlowView` nếu muốn hiển thị flow thêm lần nữa.
- **Giao dịch ở Observer mode**: `ReportTransaction` không còn trả về lỗi giải mã khi thành công nữa — ở v3, response thành công bị phân tích sai, khiến mọi báo cáo thành công đều kết thúc với lỗi.

## Onboarding API không còn được hỗ trợ \{#onboarding-api-deprecation\}

API onboarding cũ đã bị deprecated trong v4.0, thay thế bằng [Flow Builder](adapty-flow-builder). API này vẫn hoạt động, nhưng sẽ bị xóa trong các phiên bản tương lai, vì vậy hãy lên kế hoạch migrate các onboarding của bạn sang Flow Builder.

Các ký hiệu đã bị deprecated: `GetOnboarding`, `GetOnboardingForDefaultAudience`, `AdaptyUI.CreateOnboardingView`, `AdaptyUI.PresentOnboardingView`, `AdaptyUI.DismissOnboardingView`, và `Adapty.SetOnboardingsEventsListener`.