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

> Buka checkout hosted Dodo Payments dari Flutter di tab browser sistem dan dapatkan hasil bertipe dalam satu pemanggilan.

<Info>
  Ini adalah paket Flutter resmi Dodo Payments (`dodopayments_checkout`
  di pub.dev). Paket terpisah yang dibuat oleh komunitas juga tersedia, lihat
  [Proyek Komunitas](/community/projects).
</Info>

<CardGroup cols={2}>
  <Card title="Checkout Sessions API" icon="cart-shopping" href="/developer-resources/checkout-session">
    Buat checkout\_url yang akan dibuka SDK ini dari backend Anda.
  </Card>

  <Card title="Mobile Integration Guide" icon="mobile" href="/developer-resources/mobile-integration">
    Lihat bagaimana ini terintegrasi ke dalam alur pembayaran seluler lengkap.
  </Card>
</CardGroup>

`dodopayments_checkout` membuka checkout hosted Dodo di
`SFSafariViewController` di iOS dan Chrome Custom Tab di Android — core
native yang sama seperti yang digunakan oleh SDK mandiri [iOS](/developer-resources/sdks/ios) dan
[Android](/developer-resources/sdks/android). Semua logika checkout berada di
core native tersebut; layer Dart meneruskan pemanggilan melalui channel bertipe
[Pigeon](https://pub.dev/packages/pigeon). Layer ini tidak menyimpan API key dan
tidak pernah memanggil Dodo Payments API.

Memerlukan Flutter 3.44+ / Dart 3.12+, iOS 16+, dan Android `minSdk` 23.

## Instalasi

<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">
        Tambahkan tipe URL untuk skema Anda di `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>
        ```

        Kemudian teruskan URL yang masuk (misalnya melalui
        [`app_links`](https://pub.dev/packages/app_links)) ke SDK, karena
        `SFSafariViewController` tidak dapat menangkap URL pengembaliannya sendiri:

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

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

        <Note>
          Aman untuk meneruskan setiap URL di sini. `handleOpenURL` hanya bertindak pada URL
          yang cocok dengan `returnUrl` terdaftar Anda dan menyelesaikan `false` untuk URL
          lainnya.
        </Note>
      </Tab>

      <Tab title="Android">
        Tetapkan skema callback Anda sebagai placeholder manifest Gradle:

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

        <Warning>
          Jika `MainActivity` menetapkan `android:taskAffinity=""` (default `flutter
                    create` bawaan), hapus atau berikan affinity yang sama kepada activity SDK. Jika tidak, beberapa build Android OEM dapat kehilangan checkout yang sedang berlangsung dan mengembalikan `PLATFORM_ERROR`.
        </Warning>
      </Tab>
    </Tabs>
  </Step>
</Steps>

## Penggunaan

```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();
}
```

## Arti Hasil

<Warning>
  `result.status` adalah petunjuk UI, bukan bukti pembayaran. Konfirmasikan setiap pembayaran
  dari backend Anda melalui webhook `payment.succeeded` / `subscription.active`.
</Warning>

<ParamField body="status" type="CheckoutStatus" required>
  Salah satu dari `succeeded`, `failed`, `cancelled`, `pending`, `expired`.
</ParamField>

<ParamField body="paymentId" type="String?">
  Ditetapkan ketika URL pengembalian menyertakan salah satunya. Tampilkan di UI, jangan gunakan untuk memberikan akses. Lihat Verifikasi Pembayaran di bawah.
</ParamField>

<ParamField body="subscriptionId" type="String?">
  Ditetapkan untuk checkout langganan.
</ParamField>

<ParamField body="licenseKeys" type="List<String>?">
  Ditetapkan ketika checkout menyertakan produk dengan license key.
</ParamField>

<ParamField body="customerEmail" type="String?">
  Ditetapkan ketika checkout menangkap email.
</ParamField>

<ParamField body="raw" type="Map<String, String>">
  Setiap parameter query dari URL pengembalian, apa adanya.
</ParamField>

## Verifikasi Pembayaran

<CardGroup cols={2}>
  <Card title="Webhooks" icon="webhook" href="/developer-resources/webhooks">
    Dodo Payments memanggil backend Anda ketika pembayaran berhasil atau langganan aktif.
  </Card>

  <Card title="Get Payment Detail" icon="magnifying-glass" href="/api-reference/payments/get-payments-1">
    Cari `paymentId` menggunakan secret key Anda untuk memeriksa statusnya secara langsung.
  </Card>
</CardGroup>

Berikan akses setelah salah satu hal ini mengonfirmasi pembayaran, jangan pernah hanya berdasarkan `result.status`.

## Error

`start` hanya melempar `CheckoutException` untuk penggunaan yang salah atau kegagalan platform.
Pembayaran yang dibatalkan atau ditolak selalu menjadi hasil, bukan exception.

* `invalidCheckoutUrl` (`INVALID_CHECKOUT_URL`): bukan URL sesi `checkout.dodopayments.com`.
* `invalidReturnUrl` (`INVALID_RETURN_URL`): bukan URL absolut yang valid.
* `alreadyInProgress` (`ALREADY_IN_PROGRESS`): checkout sedang berjalan.
* `platformError` (`PLATFORM_ERROR`): kegagalan platform yang tidak terduga.

## Sesi yang Ditinggalkan

<Info>
  Jika aplikasi dihentikan di tengah checkout, pulihkan sesi saat peluncuran berikutnya dan
  rekonsiliasikan dengan backend Anda.
</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();
}
```

## Terkait

<CardGroup cols={2}>
  <Card title="Mobile Integration Guide" icon="mobile" href="/developer-resources/mobile-integration">
    Kontrak yang sama untuk Android, iOS, dan React Native.
  </Card>

  <Card title="Community Projects" icon="users" href="/community/projects">
    Paket Flutter terpisah yang dibuat oleh komunitas juga tersedia.
  </Card>
</CardGroup>
