This page covers the official Dodo Payments Flutter package,
dodopayments_checkout on pub.dev. A separate, community-built package also exists. See
Community Projects.Checkout Sessions API
Create the
checkout_url that this SDK opens, from your backend.Mobile Integration Guide
See how this SDK fits into the full mobile payment flow.
dodopayments_checkout opens Dodo Payments hosted checkout in SFSafariViewController on iOS and in a Custom Tab on Android, and returns a typed CheckoutResult. It uses the same native code as the standalone iOS and
Android SDKs, and all checkout logic lives in that native code. The Dart layer passes each call through a typed
Pigeon channel. The package holds no API key and never calls the Dodo Payments API.
Requirements: Flutter 3.44 or later with Dart 3.12 or later, iOS 16 or later, and Android minSdk 23.
Installation
1
Add the Dependency
Add the package to Appearance customization requires version 1.1.0 or later.The Android plugin compiles against Android SDK 35 by default. If another plugin needs a higher
pubspec.yaml:pubspec.yaml
compileSdk, set dodoCompileSdk in your app’s gradle.properties.2
Register a Callback URL Scheme
Register a URL scheme so that the operating system routes the checkout’s return URL back to your app. Use this scheme in the
returnUrl you pass to the SDK, and set the same URL as the checkout session’s return_url when your backend creates the session. The URL doesn’t need to load a real page.- iOS
- Android
Add a URL type for your scheme in
ios/Runner/Info.plist:ios/Runner/Info.plist
SFSafariViewController can’t catch its own return URL, so iOS opens the URL in your app instead. Forward every incoming URL to the SDK, for example from
app_links:You can forward every URL.
handleOpenURL acts only on a URL that matches the
returnUrl of the checkout in progress, and resolves true for it. For any
other URL, it resolves false. On Android, it always resolves false.Usage
CallDodoCheckout.instance.start with the checkout_url from your backend:
onEvent receives events whose type is CheckoutEventType.opened, returnReceived, or closed. Use them for logging only, never to decide the outcome.
What the Result Means
The SDK buildsCheckoutResult from the query parameters on the return URL.
CheckoutStatus
required
One of five values:
succeeded: the return URL hasstatus=succeeded(one-time payment) orstatus=active(subscription).failed: the payment was declined (status=failed).cancelled: the customer closed the browser view before the return URL arrived. The SDK doesn’t know the outcome, and the payment may have succeeded, so don’t show a failure screen. Reconcile the abandoned session instead.pending: the payment settles later (status=processingor anyrequires_*value), or thestatusparameter was missing or unrecognized. Reconcile it likecancelled.expired: the checkout session expired (status=expired).
String?
The
payment_id query parameter, when the return URL includes one. Show it in your UI, but don’t use it
to grant access. See Verify the Payment.String?
The
subscription_id query parameter. Set for subscription checkouts.List<String>?
The
license_key query parameter. Set when the checkout includes license key products.String?
The
email query parameter. Set when checkout captures an email address.Map<String, String>
Every query parameter from the return URL, verbatim.
Verify the Payment
Webhooks
Dodo Payments calls your backend when a payment succeeds or a subscription activates.
Get Payment Detail
Look up
paymentId with your secret key to check its status.result.status alone.
Appearance Customization
To change the checkout browser’s toolbar, buttons, and color scheme, pass aBrowserCustomization as customization on CheckoutParams. Android Custom Tabs and iOS SFSafariViewController expose different native controls, so the options are split into AndroidBrowserOptions and IosBrowserOptions. Each platform ignores the other’s options. Every field is optional and defaults to null. For a null field, the SDK doesn’t set that option and the platform applies its own default.
Android — Custom Tab
Android — Custom Tab
Color?
Toolbar background color.
Navigation bar color.
Color of the divider above the navigation bar.
CloseButtonStyle?
standard shows the system “X” icon. back shows a back arrow that the SDK draws.CloseButtonPosition?
The side of the toolbar where the close button appears:
start or end.Shows the toolbar’s share icon.
false hides it.bool?
Shows the page title under the URL in the toolbar.
bool?
Hides the toolbar automatically as the page scrolls.
bool?
Shows “Bookmark this page” in the overflow menu.
bool?
Shows “Download page” in the overflow menu.
BrowserColorScheme?
light or dark forces that appearance regardless of the device’s system setting. system follows the system setting.iOS — SFSafariViewController
iOS — SFSafariViewController
DismissButtonStyle?
Style of the dismiss button:
done, close, or cancel. iOS decides whether it renders as a label or an icon.PresentationStyle?
pageSheet (used when you leave this null) presents a card that the customer can swipe down to dismiss. fullScreen covers the whole screen.bool?
Lets the toolbar collapse as the page scrolls. It has a visible effect only when
presentationStyle is fullScreen. With pageSheet, the bars stay pinned regardless of this setting.BrowserColorScheme?
light or dark forces that appearance regardless of the device’s system setting. system follows the system setting.SFSafariViewController tint properties are deprecated as of iOS 26.
Errors
start throws CheckoutException only for misuse or a platform failure. Read the reason from code, a CheckoutErrorCode. The native code string is in nativeCode.
A customer who cancels, or a declined payment, is always a result, never an exception.
invalidCheckoutUrl(INVALID_CHECKOUT_URL):checkoutUrlisn’t anhttpscheckout session URL (path starting with/session/) oncheckout.dodopayments.comortest.checkout.dodopayments.com.invalidReturnUrl(INVALID_RETURN_URL):returnUrlisn’t an absolute URL with a scheme and a host.alreadyInProgress(ALREADY_IN_PROGRESS): another checkout is running. Only one checkout can run at a time.platformError(PLATFORM_ERROR): an unexpected platform failure. Unknown native errors also map to this code.
Abandoned Sessions
The native SDK records the checkout session when checkout starts, and clears the
record only when checkout ends with
succeeded, failed, or expired. The record
stays when the app is killed during checkout, and after a cancelled or pending
result. Check for it on the next launch and after every cancelled or pending result.abandoned.sessionId is the checkout session ID, which starts with cks_. abandoned.createdAt is the DateTime checkout started. Your backend can look up the session with Get Checkout Session, which returns its payment_id and payment_status. Until the payment reaches a final status, treat it as pending, not failed.
Related
Mobile Integration Guide
The same contract for Android, iOS, and React Native.
Community Projects
A separate, community-built Flutter package also exists.