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

> Abra o checkout hospedado do Dodo Payments a partir de um app Flutter em uma visualização do navegador do sistema com o pacote oficial do Flutter e obtenha um resultado de checkout tipado em uma única chamada.

> [!NOTE]
>
> Esta página aborda o pacote oficial do Dodo Payments para Flutter, `dodopayments_checkout` no pub.dev. Também existe um pacote separado criado pela comunidade. Consulte
> [Projetos da comunidade](https://docs.dodopayments.com/community/projects.md).

> **[Checkout Sessions API](https://docs.dodopayments.com/developer-resources/checkout-session.md)**
>
> Crie o `checkout_url` que este SDK abre a partir do seu backend.

> **[Mobile Integration Guide](https://docs.dodopayments.com/developer-resources/mobile-integration.md)**
>
> Veja como este SDK se integra ao fluxo completo de pagamentos móveis.

`dodopayments_checkout` abre o checkout hospedado do Dodo Payments em `SFSafariViewController` no iOS e em uma Custom Tab no Android, e retorna um `CheckoutResult` tipado. Ele usa o mesmo código nativo que os SDKs independentes para [iOS](https://docs.dodopayments.com/developer-resources/sdks/ios.md) e
[Android](https://docs.dodopayments.com/developer-resources/sdks/android.md), e toda a lógica do checkout reside nesse código nativo. A camada Dart encaminha cada chamada por meio de um canal tipado do
[Pigeon](https://pub.dev/packages/pigeon). O pacote não armazena nenhuma chave de API e nunca chama a API do Dodo Payments.

**Requisitos:** Flutter 3.44 ou posterior com Dart 3.12 ou posterior, iOS 16 ou posterior e Android `minSdk` 23.

## Instalação

**1. Add the Dependency**

Adicione o pacote a `pubspec.yaml`:

```yaml pubspec.yaml theme={null}
dependencies:
  dodopayments_checkout: ^1.1.0
```

A [personalização da aparência](#appearance-customization) requer a versão 1.1.0 ou posterior.

O plugin do Android compila com o Android SDK 35 por padrão. Se outro plugin precisar de um `compileSdk` superior, defina `dodoCompileSdk` no `gradle.properties` do seu app.

**2. Register a Callback URL Scheme**

Registre um esquema de URL para que o sistema operacional encaminhe a URL de retorno do checkout de volta ao seu app. Use esse esquema no `returnUrl` que você transmite ao SDK e defina a mesma URL como `return_url` da sessão de checkout quando o backend criar a sessão. A URL não precisa carregar uma página real.

**iOS**

Adicione um tipo de URL para o seu esquema em `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>
```

`SFSafariViewController` não consegue capturar a própria URL de retorno, então o iOS abre a URL no seu app. Encaminhe todas as URLs recebidas ao SDK, por exemplo a partir de
[`app_links`](https://pub.dev/packages/app_links):

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

// url is the incoming link as a String. For a Uri from app_links, pass uri.toString().
DodoCheckout.instance.handleOpenURL(url);
```

> [!NOTE]
>
> Você pode encaminhar todas as URLs. `handleOpenURL` atua somente sobre uma URL que corresponda ao
> `returnUrl` do checkout em andamento e resolve `true` para ela. Para qualquer
> outra URL, resolve `false`. No Android, sempre resolve `false`.

**Android**

Defina o esquema de callback como um placeholder de manifesto do Gradle em `android/app/build.gradle.kts`. A mesma linha funciona em um arquivo `build.gradle` do Groovy:

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

> [!WARNING]
>
> Se `MainActivity` definir `android:taskAffinity=""` (o padrão no modelo
> `flutter create`), remova essa definição ou atribua às activities do SDK a mesma
> affinity. Caso contrário, algumas compilações OEM do Android podem perder o checkout em andamento
> e falhar com `PLATFORM_ERROR` e a mensagem "Checkout was launched without its parameters."

## Uso

Chame `DodoCheckout.instance.start` com o `checkout_url` do seu backend:

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

// showSuccess(), showFailure(), showExpired(), and reconcileAbandonedSession()
// are your own functions. See Abandoned Sessions for what to reconcile.
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 Installation)
    onEvent: (event) => print(event.type), // logging only
  ),
);

switch (result.status) {
  case CheckoutStatus.succeeded: showSuccess(result.paymentId);
  case CheckoutStatus.failed:    showFailure();
  case CheckoutStatus.cancelled: await reconcileAbandonedSession(); // outcome unknown, not a failure
  case CheckoutStatus.pending:   await reconcileAbandonedSession();
  case CheckoutStatus.expired:   showExpired();
}
```

`onEvent` recebe eventos cujo `type` é `CheckoutEventType.opened`, `returnReceived` ou `closed`. Use-os somente para logging, nunca para decidir o resultado.

## O que o resultado significa

O SDK cria `CheckoutResult` a partir dos parâmetros de consulta na URL de retorno.

> [!WARNING]
>
> `result.status` é uma indicação da UI, não uma prova de pagamento. Confirme todos os pagamentos
> no seu backend, usando o webhook `payment.succeeded` ou `subscription.active`.

**status** `CheckoutStatus` *required*

Um de cinco valores:

* `succeeded`: a URL de retorno tem `status=succeeded` (pagamento único) ou `status=active` (assinatura).
* `failed`: o pagamento foi recusado (`status=failed`).
* `cancelled`: o cliente fechou a visualização do navegador antes de a URL de retorno chegar. O SDK não sabe o resultado, e o pagamento pode ter sido concluído; portanto, não mostre uma tela de falha. Em vez disso, faça a conciliação da [sessão abandonada](#abandoned-sessions).
* `pending`: o pagamento é liquidado posteriormente (`status=processing` ou qualquer valor de `requires_*`), ou o parâmetro `status` estava ausente ou não era reconhecido. Faça a conciliação como em `cancelled`.
* `expired`: a sessão de checkout expirou (`status=expired`).

**paymentId** `String?`

O parâmetro de consulta `payment_id`, quando a URL de retorno inclui um. Mostre-o na sua UI, mas não o use
para conceder acesso. Consulte [Verificar o pagamento](#verify-the-payment).

**subscriptionId** `String?`

O parâmetro de consulta `subscription_id`. Definido para checkouts de assinatura.

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

O parâmetro de consulta `license_key`. Definido quando o checkout inclui produtos com chaves de licença.

**customerEmail** `String?`

O parâmetro de consulta `email`. Definido quando o checkout captura um endereço de e-mail.

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

Todos os parâmetros de consulta da URL de retorno, literalmente.

## Verificar o pagamento

> **[Webhooks](https://docs.dodopayments.com/developer-resources/webhooks.md)**
>
> O Dodo Payments chama seu backend quando um pagamento é concluído ou uma assinatura é ativada.

> **[Get Payment Detail](https://docs.dodopayments.com/api-reference/payments/get-payments-1.md)**
>
> Consulte `paymentId` com sua chave secreta para verificar o status.

Conceda acesso somente depois que uma dessas opções confirmar o pagamento. Não dependa apenas de
`result.status`.

## Personalização da aparência

Para alterar a barra de ferramentas, os botões e o esquema de cores do navegador do checkout, passe um `BrowserCustomization` como `customization` em `CheckoutParams`. As Custom Tabs do Android e `SFSafariViewController` do iOS expõem controles nativos diferentes, portanto as opções são divididas em `AndroidBrowserOptions` e `IosBrowserOptions`. Cada plataforma ignora as opções da outra. Todos os campos são opcionais e assumem `null` por padrão. Para um campo `null`, o SDK não define essa opção e a plataforma aplica seu próprio padrão.

> **Android — Custom Tab**
>
> **toolbarColor** `Color?`
>
> Cor de fundo da barra de ferramentas.
>
> **navigationBarColor** `Color?`
>
> Cor da barra de navegação.
>
> **navigationBarDividerColor** `Color?`
>
> Cor do divisor acima da barra de navegação.
>
> **closeButtonStyle** `CloseButtonStyle?`
>
> `standard` exibe o ícone de sistema "X". `back` exibe uma seta de voltar desenhada pelo SDK.
>
> **closeButtonPosition** `CloseButtonPosition?`
>
> O lado da barra de ferramentas em que o botão de fechar aparece: `start` ou `end`.
>
> **shareButtonEnabled** `bool?`
>
> Exibe o ícone de compartilhamento da barra de ferramentas. `false` o oculta.
>
> **showTitleEnabled** `bool?`
>
> Exibe o título da página abaixo da URL na barra de ferramentas.
>
> **urlBarHidingEnabled** `bool?`
>
> Oculta automaticamente a barra de ferramentas conforme a página rola.
>
> **bookmarksButtonEnabled** `bool?`
>
> Exibe "Adicionar esta página aos favoritos" no menu de opções.
>
> **downloadsButtonEnabled** `bool?`
>
> Exibe "Baixar página" no menu de opções.
>
> **colorScheme** `BrowserColorScheme?`
>
> `light` ou `dark` força essa aparência independentemente da configuração do sistema do dispositivo. `system` segue a configuração do sistema.

> **iOS — SFSafariViewController**
>
> **dismissButtonStyle** `DismissButtonStyle?`
>
> Estilo do botão de fechar: `done`, `close` ou `cancel`. O iOS decide se ele será renderizado como um rótulo ou um ícone.
>
> **presentationStyle** `PresentationStyle?`
>
> `pageSheet` (usado quando você deixa este `null`) exibe um cartão que o cliente pode deslizar para baixo para fechar. `fullScreen` ocupa a tela inteira.
>
> **barCollapsingEnabled** `bool?`
>
> Permite que a barra de ferramentas seja recolhida conforme a página rola. Isso só produz efeito quando `presentationStyle` é `fullScreen`. Com `pageSheet`, as barras permanecem fixas independentemente desta configuração.
>
> **colorScheme** `BrowserColorScheme?`
>
> `light` ou `dark` força essa aparência independentemente da configuração do sistema do dispositivo. `system` segue a configuração do sistema.

O iOS não tem uma opção de cor da barra de ferramentas, pois as propriedades de tonalidade subjacentes de `SFSafariViewController` foram descontinuadas a partir do iOS 26.

```dart theme={null}
import 'package:dodopayments_checkout/dodopayments_checkout.dart';
import 'package:flutter/material.dart' show Color;

final result = await DodoCheckout.instance.start(
  CheckoutParams(
    checkoutUrl: Uri.parse(checkoutUrl),
    returnUrl: Uri.parse('myapp://checkout/return'),
    customization: BrowserCustomization(
      android: AndroidBrowserOptions(
        toolbarColor: Color(0xFF6366F1),
        closeButtonStyle: CloseButtonStyle.back,
      ),
      ios: IosBrowserOptions(
        presentationStyle: PresentationStyle.fullScreen,
        colorScheme: BrowserColorScheme.dark,
      ),
    ),
  ),
);
```

## Erros

`start` lança `CheckoutException` somente em caso de uso incorreto ou falha da plataforma. Leia o motivo em `code`, um `CheckoutErrorCode`. A string do código nativo está em `nativeCode`.
Um cliente que cancela ou um pagamento recusado é sempre um resultado, nunca uma exceção.

* `invalidCheckoutUrl` (`INVALID_CHECKOUT_URL`): `checkoutUrl` não é uma URL de sessão de checkout `https` (caminho iniciado por `/session/`) em `checkout.dodopayments.com` ou `test.checkout.dodopayments.com`.
* `invalidReturnUrl` (`INVALID_RETURN_URL`): `returnUrl` não é uma URL absoluta com esquema e host.
* `alreadyInProgress` (`ALREADY_IN_PROGRESS`): outro checkout está em execução. Somente um checkout pode ser executado por vez.
* `platformError` (`PLATFORM_ERROR`): falha inesperada da plataforma. Erros nativos desconhecidos também são mapeados para este código.

## Sessões abandonadas

> [!NOTE]
>
> O SDK nativo registra a sessão de checkout quando o checkout começa e limpa o
> registro somente quando o checkout termina com `succeeded`, `failed` ou `expired`. O registro
> permanece quando o app é encerrado durante o checkout e após um resultado `cancelled` ou `pending`.
> Verifique sua existência na próxima inicialização e após cada resultado `cancelled` ou `pending`.

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

final abandoned = await DodoCheckout.instance.getAbandonedSession();
if (abandoned != null) {
  // Ask your backend for the outcome of abandoned.sessionId, and show it.
  // Clear the record only after the outcome is final:
  await DodoCheckout.instance.clearAbandonedSession();
}
```

`abandoned.sessionId` é o ID da sessão de checkout, que começa com `cks_`. `abandoned.createdAt` é o `DateTime` em que o checkout foi iniciado. Seu backend pode consultar a sessão com [Obter sessão de checkout](https://docs.dodopayments.com/api-reference/checkout-sessions/get-checkouts.md), que retorna `payment_id` e `payment_status`. Até que o pagamento alcance um status final, trate-o como pendente, não como falha.

## Relacionado

> **[Mobile Integration Guide](https://docs.dodopayments.com/developer-resources/mobile-integration.md)**
>
> O mesmo contrato para Android, iOS e React Native.

> **[Community Projects](https://docs.dodopayments.com/community/projects.md)**
>
> Também existe um pacote Flutter separado criado pela comunidade.


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