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

> Abre el checkout alojado de Dodo Payments desde Flutter en una pestaña del navegador del sistema y obtén un resultado tipado en una sola llamada.

<Info>
  Este es el paquete oficial de Dodo Payments para Flutter (`dodopayments_checkout`
  en pub.dev). También existe un paquete independiente desarrollado por la comunidad; consulta
  [Proyectos de la comunidad](/community/projects).
</Info>

<CardGroup cols={2}>
  <Card title="Checkout Sessions API" icon="cart-shopping" href="/developer-resources/checkout-session">
    Crea el checkout\_url que este SDK abre desde tu backend.
  </Card>

  <Card title="Mobile Integration Guide" icon="mobile" href="/developer-resources/mobile-integration">
    Consulta cómo encaja esto en el flujo de pago móvil completo.
  </Card>
</CardGroup>

`dodopayments_checkout` abre el checkout alojado de Dodo en
`SFSafariViewController` en iOS y en una pestaña personalizada de Chrome en Android: los mismos
núcleos nativos que utilizan los SDK independientes de [iOS](/developer-resources/sdks/ios) y
[Android](/developer-resources/sdks/android). Toda la lógica del checkout reside en
esos núcleos nativos; la capa de Dart pasa la llamada a través de un canal
[Pigeon](https://pub.dev/packages/pigeon) tipado. No contiene ninguna API key ni
realiza llamadas a la API de Dodo Payments.

Requiere Flutter 3.44+ / Dart 3.12+, iOS 16+ y Android `minSdk` 23.

## Instalación

<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">
        Añade un tipo URL para tu esquema en `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>
        ```

        Después, reenvía las URL entrantes (por ejemplo, mediante
        [`app_links`](https://pub.dev/packages/app_links)) al SDK, porque
        `SFSafariViewController` no puede detectar su propia URL de retorno:

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

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

        <Note>
          Es seguro reenviar aquí todas las URL. `handleOpenURL` solo actúa sobre las URL
          que coinciden con tu `returnUrl` registrado y resuelve `false` para cualquier
          otra URL.
        </Note>
      </Tab>

      <Tab title="Android">
        Configura tu esquema de callback como un placeholder del manifiesto de Gradle:

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

        <Warning>
          Si `MainActivity` configura `android:taskAffinity=""` (el valor predeterminado de `flutter
                    create`), elimínalo o asigna a las activities del SDK la misma
          affinity. De lo contrario, algunas compilaciones de Android de ciertos OEM pueden perder el
          checkout en curso y devolver `PLATFORM_ERROR`.
        </Warning>
      </Tab>
    </Tabs>
  </Step>
</Steps>

## Uso

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

## Qué significa el resultado

<Warning>
  `result.status` es una indicación de la interfaz, no una prueba de pago. Confirma cada pago
  desde tu backend mediante el webhook `payment.succeeded` / `subscription.active`.
</Warning>

<ParamField body="status" type="CheckoutStatus" required>
  Uno de `succeeded`, `failed`, `cancelled`, `pending`, `expired`.
</ParamField>

<ParamField body="paymentId" type="String?">
  Se establece cuando la URL de retorno incluía uno. Muéstralo en la interfaz; no lo uses
  para conceder acceso. Consulta Verificar el pago más abajo.
</ParamField>

<ParamField body="subscriptionId" type="String?">
  Se establece para los checkouts de suscripciones.
</ParamField>

<ParamField body="licenseKeys" type="List<String>?">
  Se establece cuando el checkout incluye productos con claves de licencia.
</ParamField>

<ParamField body="customerEmail" type="String?">
  Se establece cuando el checkout captura un correo electrónico.
</ParamField>

<ParamField body="raw" type="Map<String, String>">
  Cada parámetro de consulta de la URL de retorno, literalmente.
</ParamField>

## Verificar el pago

<CardGroup cols={2}>
  <Card title="Webhooks" icon="webhook" href="/developer-resources/webhooks">
    Dodo Payments llama a tu backend cuando un pago se realiza correctamente o se activa una suscripción.
  </Card>

  <Card title="Get Payment Detail" icon="magnifying-glass" href="/api-reference/payments/get-payments-1">
    Consulta `paymentId` con tu secret key para comprobar su estado directamente.
  </Card>
</CardGroup>

Concede acceso después de que uno de estos confirme el pago; nunca lo hagas basándote únicamente en
`result.status`.

## Errores

`start` lanza `CheckoutException` únicamente por un uso incorrecto o un fallo de la plataforma.
Un pago cancelado o rechazado siempre es un resultado, nunca una excepción.

* `invalidCheckoutUrl` (`INVALID_CHECKOUT_URL`): no es una URL de sesión `checkout.dodopayments.com`.
* `invalidReturnUrl` (`INVALID_RETURN_URL`): no es una URL absoluta válida.
* `alreadyInProgress` (`ALREADY_IN_PROGRESS`): ya hay un checkout en curso.
* `platformError` (`PLATFORM_ERROR`): fallo inesperado de la plataforma.

## Sesiones abandonadas

<Info>
  Si la aplicación se cierra durante el checkout, recupera la sesión en el siguiente inicio y
  reconcíliala con tu backend.
</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();
}
```

## Relacionado

<CardGroup cols={2}>
  <Card title="Mobile Integration Guide" icon="mobile" href="/developer-resources/mobile-integration">
    El mismo contrato para Android, iOS y React Native.
  </Card>

  <Card title="Community Projects" icon="users" href="/community/projects">
    También existe un paquete de Flutter independiente desarrollado por la comunidad.
  </Card>
</CardGroup>
