> ## Documentation Index
> Fetch the complete documentation index at: https://docs.dodopayments.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Android

> Mở checkout được lưu trữ bởi Dodo Payments từ ứng dụng Android trong Custom Tab bằng Android SDK chính thức và nhận kết quả checkout có kiểu dữ liệu từ một lần gọi.

> [!NOTE]
>
> Trang này hướng dẫn về Android checkout SDK, `com.dodopayments.api:checkout-android`, mở checkout được lưu trữ bởi Dodo Payments bên trong ứng dụng của bạn. Để gọi Dodo Payments API từ máy chủ, hãy sử dụng [backend Kotlin SDK](/developer-resources/sdks/kotlin) thay thế.

> **[Checkout Sessions API](/developer-resources/checkout-session)**
>
> Tạo `checkout_url` mà SDK này sẽ mở.

> **[Mobile Integration Guide](/developer-resources/mobile-integration)**
>
> Các phương pháp hay nhất cho quy trình checkout trên thiết bị di động.

Android SDK mở checkout được lưu trữ bởi Dodo Payments trong Custom Tab (`androidx.browser.customtabs`) và trả về `CheckoutResult` có kiểu dữ liệu khi khách hàng hoàn tất hoặc rời khỏi checkout. Backend của bạn tạo checkout session và gửi `checkout_url` của session đó đến ứng dụng. SDK không chứa mã networking và không lưu API key, vì vậy SDK không bao giờ gọi Dodo Payments API.

**Yêu cầu:** `minSdk` 23, Kotlin và Java 17. SDK chỉ phụ thuộc vào `androidx.activity`, `androidx.browser` và `kotlinx-coroutines-android`.

## Cài đặt

**1. Add the Dependency**

Thêm SDK từ Maven Central vào `build.gradle.kts` của app module:

```kotlin build.gradle.kts theme={null}
dependencies {
    implementation("com.dodopayments.api:checkout-android:1.1.0")
}
```

[Tùy chỉnh giao diện](#appearance-customization) yêu cầu version 1.1.0 trở lên.

**2. Register a Callback URL Scheme**

Đặt callback scheme làm Gradle manifest placeholder. Manifest riêng của SDK khai báo intent filter của redirect activity với placeholder `${dodoCallbackScheme}`, vì vậy thuộc tính này là bước thiết lập duy nhất. Bạn không cần thêm manifest XML nào:

```kotlin build.gradle.kts theme={null}
android {
    defaultConfig {
        manifestPlaceholders["dodoCallbackScheme"] = "myapp"
    }
}
```

Sử dụng cùng scheme trong `CheckoutParams.returnUrl`, chẳng hạn `myapp://checkout/return`, và đặt cùng URL đó làm `return_url` của checkout session khi backend tạo session. SDK đối chiếu return URL theo scheme, host và path, đồng thời bỏ qua query string. URL không cần tải một trang thực tế.

> [!NOTE]
>
> Nếu bỏ qua placeholder, quá trình build sẽ thất bại với lỗi unresolved-placeholder. Nếu placeholder không khớp với scheme của `returnUrl`, SDK sẽ throw `PLATFORM_ERROR` trước khi mở checkout.

## Cách sử dụng

SDK có hai cách để bắt đầu checkout: activity result launcher và suspend function. Cả hai đều trả về cùng `CheckoutResult`.

**Launcher (Recommended)**

Đăng ký contract bằng `registerForActivityResult`, sau đó khởi chạy contract:

```kotlin theme={null}
import com.dodopayments.checkout.CheckoutParams
import com.dodopayments.checkout.CheckoutStatus
import com.dodopayments.checkout.DodoCheckout

// showSuccess(), showFailure(), showExpired(), and reconcileAbandonedSession()
// are your own functions. See Abandoned Sessions for what to reconcile.
private val checkoutLauncher =
    registerForActivityResult(DodoCheckout.contract()) { result ->
        when (result.status) {
            CheckoutStatus.SUCCEEDED -> showSuccess(result.paymentId)
            CheckoutStatus.FAILED -> showFailure()
            CheckoutStatus.CANCELLED -> reconcileAbandonedSession() // outcome unknown, not a failure
            CheckoutStatus.PENDING -> reconcileAbandonedSession()
            CheckoutStatus.EXPIRED -> showExpired()
        }
    }

checkoutLauncher.launch(
    CheckoutParams(
        checkoutUrl = checkoutUrl, // from your backend's checkout session
        returnUrl = "myapp://checkout/return"
    )
)
```

> [!TIP]
>
> Hãy sử dụng kiểu này khi có thể. Android phân phối result thông qua `ActivityResultRegistry`, vì vậy result vẫn tồn tại sau khi process bị kết thúc.

**Suspend Function**

Gọi `DodoCheckout.start` từ một coroutine scope:

```kotlin theme={null}
import androidx.lifecycle.lifecycleScope
import com.dodopayments.checkout.CheckoutParams
import com.dodopayments.checkout.CheckoutStatus
import com.dodopayments.checkout.DodoCheckout
import kotlinx.coroutines.launch

// showSuccess(), showFailure(), showExpired(), and reconcileAbandonedSession()
// are your own functions. See Abandoned Sessions for what to reconcile.
lifecycleScope.launch {
    val result = DodoCheckout.start(
        activity = this@MyActivity,
        params = CheckoutParams(
            checkoutUrl = checkoutUrl, // from your backend's checkout session
            returnUrl = "myapp://checkout/return"
        ),
        onEvent = { event -> println(event.name) } // logging only
    )

    when (result.status) {
        CheckoutStatus.SUCCEEDED -> showSuccess(result.paymentId)
        CheckoutStatus.FAILED -> showFailure()
        CheckoutStatus.CANCELLED -> reconcileAbandonedSession() // outcome unknown, not a failure
        CheckoutStatus.PENDING -> reconcileAbandonedSession()
        CheckoutStatus.EXPIRED -> showExpired()
    }
}
```

`onEvent` nhận các event `checkout.opened`, `checkout.return_received` và `checkout.closed`. Chỉ sử dụng chúng để logging, không bao giờ dùng để quyết định outcome.

> [!WARNING]
>
> Kiểu này chờ một `CompletableDeferred` trong bộ nhớ, vì vậy result **không** tồn tại sau khi process bị kết thúc. `onEvent` chỉ khả dụng với kiểu này, không khả dụng với launcher.

## Ý nghĩa của Result

SDK xây dựng `CheckoutResult` từ các query parameter trên return URL.

> [!WARNING]
>
> Trường `status` là gợi ý UI, không phải bằng chứng thanh toán. Trước khi cấp quyền truy cập, hãy xác nhận thanh toán trên backend bằng webhook hoặc endpoint Get Payment Detail.

**status** `CheckoutStatus` *required*

Một trong năm giá trị:

* `SUCCEEDED`: return URL có `status=succeeded` (thanh toán một lần) hoặc `status=active` (subscription).
* `FAILED`: thanh toán bị từ chối (`status=failed`).
* `CANCELLED`: khách hàng đã đóng Custom Tab trước khi return URL đến. SDK không biết outcome và thanh toán có thể đã thành công, vì vậy không hiển thị màn hình thất bại. Thay vào đó, hãy đối soát [abandoned session](#abandoned-sessions).
* `PENDING`: thanh toán được hoàn tất sau đó (`status=processing` hoặc bất kỳ giá trị `requires_*` nào), hoặc parameter `status` bị thiếu hoặc không được nhận diện. Hãy đối soát như với `CANCELLED`.
* `EXPIRED`: checkout session đã hết hạn (`status=expired`).

**paymentId** `String?`

Query parameter `payment_id`, nếu return URL có parameter này. Hiển thị parameter trong UI nhưng không dùng nó để cấp quyền truy cập. Xem [Xác minh thanh toán](#verify-the-payment).

**subscriptionId** `String?`

Query parameter `subscription_id`. Được đặt cho các subscription checkout.

**licenseKeys** `List\<String\>?`

Query parameter `license_key`. Được đặt khi checkout bao gồm các sản phẩm có license key.

**customerEmail** `String?`

Query parameter `email`. Được đặt khi checkout thu thập địa chỉ email.

**raw** `Map\<String, String\>`

Mọi query parameter từ return URL, được giữ nguyên văn.

## Xác minh thanh toán

> **[Webhooks](/developer-resources/webhooks)**
>
> Theo dõi các sự kiện thanh toán theo thời gian thực.

> **[Get Payment Detail](/api-reference/payments/get-payments-1)**
>
> Truy vấn trạng thái thanh toán theo yêu cầu.

Chỉ cấp quyền truy cập sau khi một trong các cách này xác nhận thanh toán, chẳng hạn bằng webhook `payment.succeeded` hoặc `subscription.active`. Không chỉ dựa vào `CheckoutResult.status`.

## Tùy chỉnh giao diện

Để thay đổi thanh công cụ, các nút và bảng màu của Custom Tab, hãy truyền một `BrowserCustomization` làm `customization` trên `CheckoutParams`. Mọi trường đều là tùy chọn và mặc định là `null`. Với trường `null`, SDK không thiết lập tùy chọn đó, nên browser lưu trữ Custom Tab sẽ áp dụng giá trị mặc định riêng.

**toolbarColor** `Int?`

Màu nền thanh công cụ, dưới dạng int ARGB `Color`.

**navigationBarColor** `Int?`

Màu thanh điều hướng, dưới dạng int ARGB `Color`.

**navigationBarDividerColor** `Int?`

Màu của đường phân cách phía trên thanh điều hướng, dưới dạng int ARGB `Color`.

**closeButtonStyle** `CloseButtonStyle?`

`DEFAULT` hiển thị biểu tượng "X" của hệ thống. `BACK` hiển thị mũi tên quay lại do SDK vẽ.

**closeButtonPosition** `CloseButtonPosition?`

Phía của thanh công cụ nơi nút đóng xuất hiện: `START` hoặc `END`.

**shareButtonEnabled** `Boolean?`

Hiển thị biểu tượng chia sẻ của thanh công cụ. `false` ẩn biểu tượng này.

**showTitleEnabled** `Boolean?`

Hiển thị tiêu đề trang bên dưới URL trong thanh công cụ.

**urlBarHidingEnabled** `Boolean?`

Tự động ẩn thanh công cụ khi người dùng cuộn trang.

**bookmarksButtonEnabled** `Boolean?`

Hiển thị "Bookmark this page" trong menu mở rộng.

**downloadsButtonEnabled** `Boolean?`

Hiển thị "Download page" trong menu mở rộng.

**colorScheme** `ColorScheme?`

`LIGHT` hoặc `DARK` buộc giao diện đó bất kể cài đặt hệ thống của thiết bị. `SYSTEM` tuân theo cài đặt hệ thống.

Ví dụ này sử dụng lại `checkoutLauncher` từ [Cách sử dụng](#usage):

```kotlin theme={null}
import android.graphics.Color
import com.dodopayments.checkout.BrowserCustomization
import com.dodopayments.checkout.CheckoutParams

checkoutLauncher.launch(
    CheckoutParams(
        checkoutUrl = checkoutUrl,
        returnUrl = "myapp://checkout/return",
        customization = BrowserCustomization(
            toolbarColor = Color.parseColor("#6366F1"),
            closeButtonStyle = BrowserCustomization.CloseButtonStyle.BACK,
            colorScheme = BrowserCustomization.ColorScheme.DARK,
        )
    )
)
```

## Lỗi

`DodoCheckout.start` chỉ throw `CheckoutError` khi sử dụng sai hoặc xảy ra lỗi platform. Đọc lý do từ `CheckoutError.code`:

* `INVALID_CHECKOUT_URL`: `checkoutUrl` không phải là URL checkout session `https` (path bắt đầu bằng `/session/`) trên `checkout.dodopayments.com` hoặc `test.checkout.dodopayments.com`.
* `INVALID_RETURN_URL`: `returnUrl` không phải là absolute URL có scheme và host.
* `ALREADY_IN_PROGRESS`: một checkout khác đang chạy. Mỗi lần chỉ có thể chạy một checkout.
* `PLATFORM_ERROR`: lỗi platform không mong muốn, bao gồm scheme `returnUrl` không khớp với placeholder `dodoCallbackScheme` của bạn.

Khách hàng hủy hoặc thanh toán bị từ chối luôn là một result (`CANCELLED` hoặc `FAILED`), không bao giờ là thrown error. Với launcher, lỗi validation được throw từ `launcher.launch(...)`. Lỗi platform xảy ra sau khi launch không thể được throw qua activity result callback, vì vậy launcher trả về `CANCELLED` với error code trong `raw["error"]`.

## Abandoned Sessions

SDK ghi lại checkout session khi checkout bắt đầu và chỉ xóa bản ghi khi checkout kết thúc với `SUCCEEDED`, `FAILED` hoặc `EXPIRED`. Bản ghi vẫn tồn tại khi ứng dụng bị tắt trong lúc checkout, cũng như sau result `CANCELLED` hoặc `PENDING`, vì trong những trường hợp đó SDK không biết outcome. Hãy kiểm tra bản ghi khi ứng dụng khởi chạy lần tiếp theo và sau mỗi result `CANCELLED` hoặc `PENDING`:

```kotlin theme={null}
// context is any Context, such as your Activity.
DodoCheckout.getAbandonedSession(context)?.let { abandoned ->
    // Ask your backend for the outcome of abandoned.sessionId, and show it.
    // Clear the record only after the outcome is final:
    DodoCheckout.clearAbandonedSession(context)
}
```

`abandoned.sessionId` là ID của checkout session, bắt đầu bằng `cks_`. `abandoned.createdAt` là thời điểm checkout bắt đầu, dưới dạng epoch timestamp tính bằng mili giây. Backend của bạn có thể tra cứu session bằng [Get Checkout Session](/api-reference/checkout-sessions/get-checkouts), endpoint này trả về `payment_id` và `payment_status` của session. Cho đến khi thanh toán đạt trạng thái cuối cùng, hãy xem thanh toán là đang chờ xử lý, không phải thất bại.

## Liên quan

> **[Mobile Integration Guide](/developer-resources/mobile-integration)**
>
> Các phương pháp hay nhất cho quy trình checkout trên thiết bị di động.

> **[Kotlin SDK](/developer-resources/sdks/kotlin)**
>
> Backend SDK cho các thao tác phía máy chủ.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.