Skip to main content
This page covers the Android checkout SDK, com.dodopayments.api:checkout-android, which opens Dodo Payments hosted checkout inside your app. To call the Dodo Payments API from your server, use the backend Kotlin SDK instead.

Checkout Sessions API

Create the checkout_url that this SDK opens.

Mobile Integration Guide

Best practices for mobile checkout flows.
The Android SDK opens Dodo Payments hosted checkout in a Custom Tab (androidx.browser.customtabs) and returns a typed CheckoutResult when the customer finishes or leaves checkout. Your backend creates the checkout session and sends its checkout_url to the app. The SDK contains no networking code and holds no API key, so it never calls the Dodo Payments API. Requirements: minSdk 23, Kotlin, and Java 17. The SDK depends only on androidx.activity, androidx.browser, and kotlinx-coroutines-android.

Installation

1

Add the Dependency

Add the SDK from Maven Central to your app module’s build.gradle.kts:
build.gradle.kts
Appearance customization requires version 1.1.0 or later.
2

Register a Callback URL Scheme

Set your callback scheme as a Gradle manifest placeholder. The SDK’s own manifest declares the redirect activity’s intent filter with the ${dodoCallbackScheme} placeholder, so this property is the only setup step. You don’t add any manifest XML:
build.gradle.kts
Use the same scheme in CheckoutParams.returnUrl, for example myapp://checkout/return, and set the same URL as the checkout session’s return_url when your backend creates the session. The SDK matches the return URL on scheme, host, and path, and ignores the query string. The URL doesn’t need to load a real page.
If you omit the placeholder, the build fails with an unresolved-placeholder error. If the placeholder doesn’t match the scheme of returnUrl, the SDK throws PLATFORM_ERROR before it opens checkout.

Usage

The SDK has two ways to start checkout: an activity result launcher and a suspend function. Both return the same CheckoutResult.

What the Result Means

The SDK builds CheckoutResult from the query parameters on the return URL.
The status field is a UI hint, not proof of payment. Before you grant access, confirm the payment on your backend with a webhook or the Get Payment Detail endpoint.
CheckoutStatus
required
One of five values:
  • SUCCEEDED: the return URL has status=succeeded (one-time payment) or status=active (subscription).
  • FAILED: the payment was declined (status=failed).
  • CANCELLED: the customer closed the Custom Tab 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=processing or any requires_* value), or the status parameter was missing or unrecognized. Reconcile it like CANCELLED.
  • 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

Listen for payment events in real time.

Get Payment Detail

Query the payment status on demand.
Grant access only after one of these confirms the payment, for example with the payment.succeeded or subscription.active webhook. Don’t rely on CheckoutResult.status alone.

Appearance Customization

To change the Custom Tab’s toolbar, buttons, and color scheme, pass a BrowserCustomization as customization on CheckoutParams. Every field is optional and defaults to null. For a null field, the SDK doesn’t set that option, so the browser that hosts the Custom Tab applies its own default.
Int?
Toolbar background color, as an ARGB Color int.
Int?
Navigation bar color, as an ARGB Color int.
Int?
Color of the divider above the navigation bar, as an ARGB Color int.
CloseButtonStyle?
DEFAULT 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.
Boolean?
Shows the toolbar’s share icon. false hides it.
Boolean?
Shows the page title under the URL in the toolbar.
Boolean?
Hides the toolbar automatically as the page scrolls.
Boolean?
Shows “Bookmark this page” in the overflow menu.
Boolean?
Shows “Download page” in the overflow menu.
ColorScheme?
LIGHT or DARK forces that appearance regardless of the device’s system setting. SYSTEM follows the system setting.
This example reuses checkoutLauncher from Usage:

Errors

DodoCheckout.start throws CheckoutError only for misuse or a platform failure. Read the reason from CheckoutError.code:
  • INVALID_CHECKOUT_URL: checkoutUrl isn’t an https checkout session URL (path starting with /session/) on checkout.dodopayments.com or test.checkout.dodopayments.com.
  • INVALID_RETURN_URL: returnUrl isn’t an absolute URL with a scheme and a host.
  • ALREADY_IN_PROGRESS: another checkout is running. Only one checkout can run at a time.
  • PLATFORM_ERROR: an unexpected platform failure, including a returnUrl scheme that doesn’t match your dodoCallbackScheme placeholder.
A customer who cancels, or a declined payment, is always a result (CANCELLED or FAILED), never a thrown error. With the launcher, validation errors throw from launcher.launch(...). A platform failure after the launch can’t be thrown through the activity result callback, so the launcher returns CANCELLED with the error code in raw["error"].

Abandoned Sessions

The 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, because in those cases the SDK doesn’t know the outcome. Check for it on the next app launch and after every CANCELLED or PENDING result:
abandoned.sessionId is the checkout session ID, which starts with cks_. abandoned.createdAt is the time checkout started, as an epoch timestamp in milliseconds. 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.

Mobile Integration Guide

Best practices for mobile checkout flows.

Kotlin SDK

Backend SDK for server-side operations.
Last modified on September 25, 2026