---
title: "Bật tính năng mua hàng với Flow Builder trong Kotlin Multiplatform SDK"
description: "Hướng dẫn nhanh để bật in-app purchase với Adapty Flow Builder."
---

Hướng dẫn này sử dụng API của Adapty Kotlin Multiplatform SDK v4 (beta). Nếu bạn đang dùng v3, hãy xem [hướng dẫn migration](migration-to-kmp-sdk-v4) để biết tên các phương thức tương ứng.

Để kích hoạt in-app purchase, bạn cần nắm rõ ba khái niệm quan trọng:

- [**Sản phẩm**](product) – mọi thứ người dùng có thể mua (gói đăng ký, consumable, quyền truy cập trọn đời)
- [**Flow**](adapty-flow-builder) – chuỗi màn hình trình bày sản phẩm đến người dùng, được xây dựng trong Flow Builder không cần code. SDK lấy chúng qua `getFlow`. Nếu bạn muốn tự xây dựng giao diện trong code của mình, hãy dùng paywall thay thế — xem [Triển khai paywall thủ công](kmp-quickstart-manual).
- [**Placement**](placements) – nơi và thời điểm bạn hiển thị flow trong ứng dụng (ví dụ: `main`, `onboarding`, `settings`). Bạn gắn flow vào placement trong dashboard, sau đó yêu cầu chúng theo placement ID trong code. Điều này giúp bạn dễ dàng chạy A/B test và hiển thị các flow khác nhau cho các nhóm người dùng khác nhau.

Adapty cung cấp cho bạn ba cách để kích hoạt tính năng mua hàng trong ứng dụng. Hãy chọn một trong số đó tùy theo yêu cầu của ứng dụng:

| Triển khai | Độ phức tạp | Khi nào nên dùng |
|---|---|---|
| Adapty Flow Builder | ✅ Dễ | Bạn [tạo một flow hoàn chỉnh, sẵn sàng để mua hàng trong trình tạo no-code](quickstart-paywalls). Adapty tự động hiển thị nó và xử lý toàn bộ flow mua hàng phức tạp, xác thực biên lai và quản lý gói đăng ký ở phía sau. |
| Paywall tự tạo thủ công | 🟡 Trung bình | Bạn tự triển khai giao diện paywall trong code ứng dụng, nhưng vẫn lấy đối tượng flow từ Adapty để duy trì sự linh hoạt trong việc cung cấp sản phẩm. Xem [hướng dẫn](kmp-quickstart-manual). |
| Chế độ Observer | 🔴 Khó | Bạn đã có cơ sở hạ tầng xử lý mua hàng riêng và muốn tiếp tục sử dụng nó. Lưu ý rằng chế độ observer có những hạn chế nhất định trong Adapty. Xem [bài viết](observer-vs-full-mode). |

:::important
**Các bước dưới đây hướng dẫn cách triển khai một flow được tạo trong Adapty Flow Builder.**

Nếu bạn muốn tự xây dựng giao diện paywall, hãy xem [Triển khai paywall thủ công](kmp-quickstart-manual).
:::

Để hiển thị một flow được tạo trong Adapty Flow Builder, trong code của ứng dụng, bạn chỉ cần:

1. **Lấy flow**: Lấy từ Adapty.
2. **Hiển thị flow và Adapty sẽ xử lý thanh toán cho bạn**: Hiển thị view trong ứng dụng.
3. **Xử lý các hành động nút**: Liên kết các tương tác của người dùng với phản hồi của ứng dụng. Ví dụ: mở liên kết hoặc đóng flow khi người dùng nhấn nút.

## Trước khi bắt đầu \{#before-you-start\}

Trước khi bắt đầu, hãy hoàn thành các bước sau:

1. Kết nối ứng dụng của bạn với [App Store](initial_ios) và/hoặc [Google Play](initial-android) trong Adapty Dashboard.
2. [Tạo sản phẩm](create-product) trong Adapty.
3. [Tạo một flow và thêm sản phẩm vào đó](create-paywall).
4. [Tạo một placement và thêm flow vào đó](create-placement).
5. [Cài đặt và kích hoạt SDK](sdk-installation-kotlin-multiplatform) trong code ứng dụng của bạn.

:::tip
Cách nhanh nhất để hoàn thành các bước này là làm theo [hướng dẫn quickstart](quickstart) hoặc tạo flow và placement bằng [Developer CLI](developer-cli-quickstart).
:::

## 1. Lấy flow \{#1-get-the-flow\}

Các flow của bạn được liên kết với các placement được cấu hình trên dashboard. Placement cho phép bạn chạy các flow khác nhau cho các đối tượng khác nhau hoặc chạy [A/B test](ab-tests).

Để lấy một flow được tạo trong Adapty Flow Builder, bạn cần:

1. Lấy đối tượng `flow` theo ID [placement](placements) bằng phương thức `getFlow`.

2. Tạo flow view bằng phương thức `createFlowView`. View chứa các thành phần UI và kiểu dáng cần thiết để hiển thị flow. Nếu flow chưa được cấu hình view, `createFlowView` sẽ trả về lỗi — hãy xử lý lỗi đó trong `onError`.

:::important
Để hiển thị view, bạn phải bật toggle **Show on device** trong Flow Builder. Nếu không, `createFlowView` sẽ trả về lỗi và flow sẽ không được hiển thị.
:::

```kotlin showLineNumbers
Adapty.getFlow("YOUR_PLACEMENT_ID")
    .onSuccess { flow ->
        AdaptyUI.createFlowView(flow)
            .onSuccess { view ->
                view.present()
            }
            .onError { error ->
                // the flow has no view configured, or view creation failed
            }
    }
    .onError { error ->
        // handle the error
    }
```

## 2. Hiển thị flow \{#2-display-the-flow\}

Bây giờ khi bạn đã có flow, chỉ cần thêm vài dòng code để hiển thị nó.

Để hiển thị flow trực quan trên màn hình thiết bị, trước tiên bạn phải tạo view. Để làm điều đó, hãy gọi phương thức `AdaptyUI.createFlowView()`:

```kotlin showLineNumbers
AdaptyUI.createFlowView(flow)
    .onSuccess { view ->
        view.present()
    }
    .onError { error ->
        // handle the error
    }
```

Sau khi view được tạo thành công, bạn có thể hiển thị nó trên màn hình thiết bị. Mỗi view chỉ được sử dụng một lần: sau khi gọi `dismiss()`, hãy gọi lại `createFlowView` để hiển thị flow thêm một lần nữa.

:::tip
Để biết thêm chi tiết về cách hiển thị một flow, hãy xem [hướng dẫn](kmp-present-paywalls) của chúng tôi.
:::

## 3. Xử lý hành động nút \{#handle-button-actions\}

Khi người dùng nhấn các nút trong flow, Kotlin Multiplatform SDK tự động xử lý việc mua hàng, khôi phục, đóng flow và mở liên kết.

Tuy nhiên, các nút khác có ID tùy chỉnh hoặc được định sẵn và yêu cầu xử lý hành động trong code của bạn. Hoặc, bạn có thể muốn ghi đè hành vi mặc định của chúng.

Ví dụ, đây là hành vi mặc định của nút đóng. Bạn không cần thêm nó vào code, nhưng ở đây bạn có thể thấy cách thực hiện nếu cần.

Lưu ý rằng theo mặc định, flow vẫn mở sau khi mua hàng thành công. Nếu bạn muốn đóng flow sau khi mua xong, hãy dismiss view trong callback `flowViewDidFinishPurchase`.

:::tip
Xem các hướng dẫn của chúng tôi về cách xử lý [hành động](kmp-handle-paywall-actions) và [sự kiện](kmp-handling-events) của nút bấm.
:::

```kotlin showLineNumbers
AdaptyUI.setFlowsEventsObserver(object : AdaptyUIFlowsEventsObserver {
    override fun flowViewDidPerformAction(view: AdaptyUIFlowView, action: AdaptyUIAction) {
        when (action) {
            is AdaptyUIAction.CloseAction -> mainUiScope.launch { view.dismiss() }
            else -> Unit
        }
    }

    override fun flowViewDidFinishPurchase(
        view: AdaptyUIFlowView,
        product: AdaptyPaywallProduct,
        purchaseResult: AdaptyPurchaseResult
    ) {
        if (purchaseResult !is AdaptyPurchaseResult.UserCanceled) {
            mainUiScope.launch { view.dismiss() }
        }
    }
})
```

## Các bước tiếp theo \{#next-steps\}

Paywall của bạn đã sẵn sàng để hiển thị trong ứng dụng. Hãy kiểm tra mua hàng của bạn trong [App Store sandbox](test-purchases-in-sandbox) hoặc trong [Google Play Store](testing-on-android) để đảm bảo bạn có thể hoàn thành một giao dịch mua thử từ paywall.

Tiếp theo, bạn cần [kiểm tra mức độ truy cập của người dùng](kmp-check-subscription-status) để đảm bảo bạn hiển thị paywall hoặc cấp quyền truy cập vào các tính năng trả phí cho đúng người dùng.

## Ví dụ đầy đủ \{#full-example\}

Dưới đây là cách tích hợp tất cả các bước đó vào ứng dụng của bạn.

```kotlin showLineNumbers
// Set up the observer for handling flow events
AdaptyUI.setFlowsEventsObserver(object : AdaptyUIFlowsEventsObserver {
    override fun flowViewDidPerformAction(view: AdaptyUIFlowView, action: AdaptyUIAction) {
        when (action) {
            is AdaptyUIAction.CloseAction -> mainUiScope.launch { view.dismiss() }
            else -> Unit
        }
    }

    override fun flowViewDidFinishPurchase(
        view: AdaptyUIFlowView,
        product: AdaptyPaywallProduct,
        purchaseResult: AdaptyPurchaseResult
    ) {
        if (purchaseResult !is AdaptyPurchaseResult.UserCanceled) {
            mainUiScope.launch { view.dismiss() }
        }
    }
})

// Get and display the flow
Adapty.getFlow("YOUR_PLACEMENT_ID")
    .onSuccess { flow ->
        AdaptyUI.createFlowView(flow)
            .onSuccess { view ->
                view.present()
            }
            .onError { error ->
                // the flow has no view configured — use custom logic
            }
    }
    .onError { error ->
        // handle the error
    }
```