> ## 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.

# Flutter

> Mở hosted checkout của Dodo Payments từ Flutter trong tab trình duyệt hệ thống và nhận kết quả có kiểu trong một lần gọi.

<Info>
  Đây là package Flutter chính thức của Dodo Payments (`dodopayments_checkout`
  trên pub.dev). Ngoài ra còn có một package do cộng đồng xây dựng riêng, xem
  [Dự án cộng đồng](/community/projects).
</Info>

<CardGroup cols={2}>
  <Card title="Checkout Sessions API" icon="cart-shopping" href="/developer-resources/checkout-session">
    Tạo checkout\_url mà SDK này sẽ mở từ backend của bạn.
  </Card>

  <Card title="Mobile Integration Guide" icon="mobile" href="/developer-resources/mobile-integration">
    Xem cách tích hợp bước này vào toàn bộ luồng thanh toán trên mobile.
  </Card>
</CardGroup>

`dodopayments_checkout` mở hosted checkout của Dodo trong
`SFSafariViewController` trên iOS và Chrome Custom Tab trên Android — cùng
các native core được sử dụng bởi các SDK độc lập [iOS](/developer-resources/sdks/ios) và
[Android](/developer-resources/sdks/android). Toàn bộ logic checkout nằm trong
các native core đó; lớp Dart chuyển tiếp lệnh gọi qua một kênh
[Pigeon](https://pub.dev/packages/pigeon) có kiểu. Lớp này không chứa API key
và không bao giờ gọi Dodo Payments API.

Yêu cầu Flutter 3.44+ / Dart 3.12+, iOS 16+ và Android `minSdk` 23.

## Cài đặt

<Steps>
  <Step title="Add the Dependency">
    ```yaml pubspec.yaml theme={null}
    dependencies:
      dodopayments_checkout: ^1.0.0
    ```
  </Step>

  <Step title="Register a Callback URL Scheme">
    <Tabs>
      <Tab title="iOS">
        Thêm một URL type cho scheme của bạn trong `ios/Runner/Info.plist`:

        ```xml ios/Runner/Info.plist theme={null}
        <key>CFBundleURLTypes</key>
        <array>
          <dict>
            <key>CFBundleURLName</key>
            <string>myapp</string>
            <key>CFBundleURLSchemes</key>
            <array>
              <string>myapp</string>
            </array>
          </dict>
        </array>
        ```

        Sau đó chuyển tiếp các URL đến (ví dụ: thông qua
        [`app_links`](https://pub.dev/packages/app_links)) vào SDK, vì
        `SFSafariViewController` không thể tự bắt return URL của chính nó:

        ```dart theme={null}
        import 'package:dodopayments_checkout/dodopayments_checkout.dart';

        DodoCheckout.instance.handleOpenURL(url);
        ```

        <Note>
          Bạn có thể an toàn chuyển tiếp mọi URL tại đây. `handleOpenURL` chỉ xử lý các URL
          khớp với `returnUrl` đã đăng ký của bạn và resolve `false` cho mọi URL
          khác.
        </Note>
      </Tab>

      <Tab title="Android">
        Đặt callback scheme làm Gradle manifest placeholder:

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

        <Warning>
          Nếu `MainActivity` đặt `android:taskAffinity=""` (giá trị mặc định của `flutter
                    create`), hãy xóa thiết lập này hoặc cung cấp cho các activity của SDK cùng một
          affinity. Nếu không, một số bản dựng Android của OEM có thể làm mất
          checkout đang thực hiện và trả về `PLATFORM_ERROR`.
        </Warning>
      </Tab>
    </Tabs>
  </Step>
</Steps>

## Cách sử dụng

```dart theme={null}
import 'package:dodopayments_checkout/dodopayments_checkout.dart';

final result = await DodoCheckout.instance.start(
  CheckoutParams(
    checkoutUrl: Uri.parse(checkoutUrl), // from your backend's checkout session
    returnUrl: Uri.parse('myapp://checkout/return'), // scheme must be registered (see Setup)
    onEvent: (event) => print(event.type), // logging only
  ),
);

switch (result.status) {
  case CheckoutStatus.succeeded: showSuccess(result.paymentId);
  case CheckoutStatus.failed:    showFailure();
  case CheckoutStatus.cancelled: dismiss();
  case CheckoutStatus.pending:   showPending();
  case CheckoutStatus.expired:   showExpired();
}
```

## Ý nghĩa của kết quả

<Warning>
  `result.status` là gợi ý UI, không phải bằng chứng thanh toán. Xác nhận mọi khoản thanh toán
  trên backend của bạn, thông qua webhook `payment.succeeded` / `subscription.active`.
</Warning>

<ParamField body="status" type="CheckoutStatus" required>
  Một trong các giá trị `succeeded`, `failed`, `cancelled`, `pending`, `expired`.
</ParamField>

<ParamField body="paymentId" type="String?">
  Được đặt khi return URL chứa giá trị này. Hiển thị giá trị trong UI, không dùng
  nó để cấp quyền truy cập. Xem phần Xác minh thanh toán bên dưới.
</ParamField>

<ParamField body="subscriptionId" type="String?">
  Được đặt cho các checkout đăng ký.
</ParamField>

<ParamField body="licenseKeys" type="List<String>?">
  Được đặt khi checkout bao gồm các sản phẩm có license key.
</ParamField>

<ParamField body="customerEmail" type="String?">
  Được đặt khi checkout thu thập email.
</ParamField>

<ParamField body="raw" type="Map<String, String>">
  Mọi query parameter từ return URL, giữ nguyên từng ký tự.
</ParamField>

## Xác minh thanh toán

<CardGroup cols={2}>
  <Card title="Webhooks" icon="webhook" href="/developer-resources/webhooks">
    Dodo Payments gọi backend của bạn khi một khoản thanh toán thành công hoặc một subscription được kích hoạt.
  </Card>

  <Card title="Get Payment Detail" icon="magnifying-glass" href="/api-reference/payments/get-payments-1">
    Tra cứu `paymentId` bằng secret key của bạn để kiểm tra trực tiếp trạng thái.
  </Card>
</CardGroup>

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, không bao giờ chỉ dựa vào
`result.status`.

## Lỗi

`start` chỉ throw `CheckoutException` khi sử dụng sai hoặc nền tảng gặp lỗi.
Thanh toán bị hủy hoặc bị từ chối luôn được trả về dưới dạng kết quả, không phải exception.

* `invalidCheckoutUrl` (`INVALID_CHECKOUT_URL`): không phải session URL của `checkout.dodopayments.com`.
* `invalidReturnUrl` (`INVALID_RETURN_URL`): không phải absolute URL hợp lệ.
* `alreadyInProgress` (`ALREADY_IN_PROGRESS`): một checkout đang chạy.
* `platformError` (`PLATFORM_ERROR`): lỗi nền tảng không mong muốn.

## Session bị bỏ dở

<Info>
  Nếu ứng dụng bị đóng giữa checkout, hãy khôi phục session trong lần khởi chạy tiếp theo và
  đối soát session đó với backend của bạn.
</Info>

```dart theme={null}
import 'package:dodopayments_checkout/dodopayments_checkout.dart';

final abandoned = await DodoCheckout.instance.getAbandonedSession();
if (abandoned != null) {
  // reconcile abandoned.sessionId with your backend, then:
  await DodoCheckout.instance.clearAbandonedSession();
}
```

## Liên quan

<CardGroup cols={2}>
  <Card title="Mobile Integration Guide" icon="mobile" href="/developer-resources/mobile-integration">
    Cùng một contract cho Android, iOS và React Native.
  </Card>

  <Card title="Community Projects" icon="users" href="/community/projects">
    Ngoài ra còn có một package Flutter riêng do cộng đồng xây dựng.
  </Card>
</CardGroup>
