Skip to main content

Quick Start

The four steps from your backend to the checkout and back.

Platform Examples

Code for Android, iOS, React Native, and Flutter.

Checkout Customization

The 14 checkout session parameters that matter most on mobile.

Mobile Recipes

Complete request bodies for 5 common mobile scenarios.
Your mobile app opens Dodo Payments hosted checkout in the platform’s system browser surface and gets the customer back into the app when checkout ends. Your backend creates the checkout session and grants access from webhooks.
Dodo Payments ships an official checkout SDK for Android, iOS, React Native, and Flutter. Each SDK opens the checkout URL, captures the return, and parses the result behind one typed start(...) call, and each one includes abandoned-session recovery. Build the flow by hand only if none of the SDKs fits your stack.

Prerequisites

Before you start, you need:
  • A Dodo Payments account.
  • An API key from Developer → API Keys and a webhook signing secret from Developer → Webhooks.
  • An Android, iOS, React Native, or Flutter app.
  • A backend server that creates checkout sessions. The API key stays on this server.

Integration Workflow

Your backend makes every Dodo Payments API call. Your app only asks your backend for a checkout URL, opens it, and shows the result.
The status in the deep link tells your app what to show the customer. It is not proof of payment. Grant access from the payment.succeeded or subscription.active webhook on your backend, not from the mobile result.
1

Backend: Create Checkout Session

Your backend creates a checkout session with your API key and returns its checkout_url to the app. Set the session’s return_url to the deep link your app registers, for example myapp://checkout/return.

Checkout Session API Docs

Create a checkout session from Node.js, Python, and other languages, with the full parameter reference.
Security: Create checkout sessions on your backend server, never in the mobile app. Anyone can extract an API key from an app binary.
2

Mobile: Get Checkout URL

Your app calls your backend to get the checkout URL. Authenticate this request with the signed-in user’s own session token. In each example, userSessionToken is that token and CheckoutResponse is your own response type.
Security: The app talks only to your backend, never directly to the Dodo Payments API.
3

Mobile: Open Checkout in Browser

Open the checkout URL in the platform’s system browser surface. The official checkout SDK for your platform does this for you and returns a typed result.

Pick your mobile SDK

Install steps and setup instructions for Android, iOS, React Native, and Flutter.
4

Backend: Handle Payment Completion

Grant access when your backend receives the payment.succeeded or subscription.active webhook. Use the result from the return URL only to update the app’s screen.

Choose Your SDK

Every mobile SDK has the same contract. One start(...) call opens Dodo Payments hosted checkout in the platform’s system browser surface and returns a typed CheckoutResult whose status is succeeded, failed, cancelled, pending, or expired. No SDK holds an API key or calls the Dodo Payments API, and all four support abandoned-session recovery.

Android

com.dodopayments.api:checkout-android opens a Custom Tab. Requires minSdk 23.

iOS

dodopayments-mobile-sdk-ios opens SFSafariViewController. Requires iOS 16+.

React Native

@dodopayments/react-native-checkout, a Turbo Module over both native cores. Requires React Native 0.77+ with the New Architecture.

Flutter

dodopayments_checkout, a Pigeon channel over both native cores. Requires Flutter 3.44+.
The returned status is a UI hint, not proof of payment. Confirm every payment on your backend from the payment.succeeded or subscription.active webhook, or by retrieving the payment with your API key. A cancelled status means the customer closed the browser before the return URL arrived, so the payment may still have succeeded. Don’t show it as a failure.

Registering a Callback URL Scheme

All four SDKs return control to your app through a custom URL scheme that you choose, for example myapp://checkout/return. Register it once per platform:
android/app/build.gradle
The SDK’s own manifest already declares the redirect activity, so you add no manifest XML. The scheme must match the scheme of returnUrl.
To build the flow yourself, open the checkout_url in the platform’s system browser surface (a Custom Tab on Android, SFSafariViewController on iOS), intercept the navigation to your return_url, and read the status and payment_id query parameters. The SDKs do this for you.
Không mở checkout bên trong embedded WebView (WKWebView hoặc Android WebView). Embedded WebView có thể làm gián đoạn các thử thách 3-D Secure và chức năng tự động điền thẻ đã lưu, khiến khách hàng gặp nhiều lần thanh toán thất bại hơn. Hãy sử dụng SDK hoặc mở checkout_url trong giao diện trình duyệt hệ thống. Trên iOS, hãy mở trong SFSafariViewController hoặc ASWebAuthenticationSession, hoặc trong trình duyệt hệ thống để Apple Pay khả dụng. Trên Android, hãy mở trong Custom Tab, chạy trong trình duyệt của khách hàng, để Google Pay tiếp tục hoạt động.

Appearance Customization

Every SDK accepts an optional customization parameter on start(...) or CheckoutParams. It controls the system browser surface: the toolbar, the buttons, and how the browser is presented. The checkout page’s own theme is separate. You set it on your server with customization.theme_config when you create the checkout session. The options are grouped by platform, because a Custom Tab on Android and SFSafariViewController on iOS expose different native controls. Every field is optional. An unset field leaves the platform’s own default in place.
Color
Toolbar background color.
Color
Navigation bar color.
Color
Divider color above the navigation bar.
'default' | 'back'
default shows the system “X” icon; back draws a back arrow instead.
'start' | 'end'
Which side of the toolbar the close button appears on.
boolean
Shows the toolbar’s share icon.
boolean
Shows the page title under the URL in the toolbar.
boolean
Lets the toolbar auto-hide as the page scrolls.
boolean
Shows “Bookmark this page” in the overflow menu.
boolean
Shows “Download page” in the overflow menu.
'system' | 'light' | 'dark'
Forces light or dark appearance regardless of the device’s system setting.
'done' | 'close' | 'cancel'
Label or icon for the dismiss button.
'pageSheet' | 'fullScreen'
mặc định:"pageSheet"
pageSheet presents checkout as a card that the customer can swipe to dismiss. fullScreen covers the whole screen.
boolean
Lets the toolbar collapse on scroll. It has a visible effect only when presentationStyle is fullScreen. With pageSheet, the bars stay pinned.
'system' | 'light' | 'dark'
Forces light or dark appearance regardless of the device’s system setting.
The examples below set a toolbar color and close button on Android, and a full-screen dark presentation on iOS. React Native and Flutter take separate android and ios option groups. The native SDKs take only their own platform’s options.

Checkout Page Customization

The checkout page itself (which fields appear, the theme, and which payment methods show) is set on your server when you create the checkout session. Appearance Customization covers only the browser surface around it. The parameters below have the most effect on mobile conversion. Các tham số này nằm ở ba vị trí trong yêu cầu checkout session: cấp cao nhất, trong customization hoặc trong feature_flags. Cột Vị trí cho biết object tương ứng với từng tham số. Đặt mỗi tham số vào object được chỉ định, vì tham số nằm sai object sẽ không có tác dụng.
Pass billing_currency and billing_address.country together. If you omit either one, Adaptive Currency can pick the billing currency from the customer’s IP address. For example, a US customer who travels to Europe can be billed in EUR when the billing country isn’t set.
Largest mobile conversion gain: set show_order_details: false and minimal_address: true. Together they move payment methods above the fold and remove most address fields.
Side-by-side checkout: order details expanded (fields below the fold) vs collapsed (fields at the top)

show_order_details: false moves the contact and payment fields above the fold, instead of behind the order summary.

Set minimal_address: true to collect only a postcode instead of the full street, city, and state fields:
Side-by-side checkout: full billing address form vs postcode-only

minimal_address: true reduces the billing address to a single postcode field.

Set theme: "system" so the checkout follows the device’s light or dark mode preference:
Side-by-side checkout: same page rendered in light mode and dark mode

With theme: system, the checkout follows the device's light or dark appearance automatically.

Payment methods depend on the product type. Apple Pay and Cash App Pay support non-zero recurring subscriptions. One-time payments can use every payment method enabled for your business. See Payment Methods.

Full checkout session parameter reference

Every parameter, type, and default value, in the Checkout Sessions guide.

Mobile-Optimized Recipes

Each recipe is a complete checkout session request that your backend sends. Pick the one that matches your scenario and replace the product ID with your own. The Node.js and Python clients are set up in the first recipe, and the other recipes reuse them.
Use this recipe for the shortest form: payment methods at the top, only a postcode for the address, no discount field, and a theme that follows the device.
See Checkout Sessions for all available parameters and their defaults.
Use this recipe when the checkout page must look like part of your app. It sets your brand colors, a border radius, and a custom pay button label.
Branded mobile checkout with a custom dark navy palette applied via theme_config
theme_config accepts separate dark and light objects, so the palette follows the device’s appearance. For every color key and the font option, see Checkout Sessions.
Use this recipe for signed-in customers who have paid before. Pass the customer’s customer_id, their saved payment_method_id, and confirm: true to skip the checkout form. With a payment_method_id, the session charges the saved method directly and returns no checkout_url, so your app has nothing to open. Learn the outcome from webhooks.
Grant access when your backend receives the payment.succeeded webhook. Any status your app shows is a UI hint only.
Use this recipe for a subscription product with a free trial before the first charge. trial_period_days sets the trial length for this session.
Grant access when your backend receives the subscription.active webhook, not when the mobile SDK returns. For the full webhook flow, see the Subscription Integration Guide.
Use this recipe to save a customer’s payment method for later charges, such as wallet top-ups, pay-as-you-go, or BNPL, without showing a subscription label. The customer authorizes the payment method once, and you charge variable amounts later.
Apps that charge by usage follow this pattern. For example, an astrology app charges a pre-authorized card for each session instead of on a fixed schedule.
An on-demand charge must be at least 100 in the smallest currency unit ($1.00 for USD). The API rejects a lower product_price with "product_price: value out of range". To authorize without charging, use mandate_only: true as shown above, then charge at least that minimum later.
For the full charge flow, webhook events, and retry policies, see On-Demand Subscriptions.

Subscription Flows from Mobile

A mobile app starts a subscription with the same checkout session flow as a one-time payment. The SDK opens hosted checkout, the customer subscribes, and your app handles the deep-link return. Your backend manages the rest of the subscription lifecycle.

Regular Recurring Subscriptions

For billing at a fixed interval, such as monthly or yearly, create a checkout session with a subscription product and a deep-link return_url. Your backend receives subscription.active when the subscription starts.
Apple Pay and Cash App Pay support non-zero recurring subscriptions.
For the complete backend webhook flow, see the Subscription Integration Guide.

On-Demand Subscriptions

An on-demand subscription authorizes a customer’s payment method once, so you can charge variable amounts later. Use it for wallet top-ups, pay-as-you-go, and any charge whose amount you don’t know in advance. For the full request body, see the On-Demand Mandate recipe. On mobile, keep these points in mind:
  • Set show_on_demand_tag: false so the checkout page doesn’t show subscription or on-demand wording. Customers who save a card for top-ups don’t expect subscription terms.
  • After the customer authorizes the mandate, your backend receives subscription.active. Store the subscription_id, because every later charge uses it.
An on-demand charge must be at least 100 in the smallest currency unit ($1.00 for USD). The API rejects a lower amount with "product_price: value out of range". Charge at least that minimum, or use mandate_only: true to authorize without charging and collect the first amount later.Don’t retry charges in quick succession. While a previous charge on the same subscription is still processing, a new charge fails with "Cannot create new charge as previous payment is not successful yet". This happens most often with Indian payment methods (UPI and Indian debit and credit cards), where the deduction happens 48 hours after the charge starts. Check that the previous charge has finished before you retry.
For the charge endpoint, webhook events, and retry policies, see On-Demand Subscriptions.

Subscription with Free Trial

To offer a trial before the first charge, pass subscription_data.trial_period_days in the checkout session. The customer authorizes a payment method at signup, and Dodo Payments charges it when the trial ends. For the full request body, see the Subscription with Free Trial recipe.

Upgrades and Downgrades

Your backend changes plans through the API, not through a new checkout session. Dodo Payments calculates proration with the proration mode you choose. To let customers change plans themselves, link to the Customer Portal.

Subscription Integration Guide

Backend setup: webhook flow, access provisioning, and cancellation.

On-Demand Subscriptions

Mandate authorization, variable charges, and retry policies.

Upgrade / Downgrade

Proration modes, plan changes, and seat adjustments.

Customer Portal

Self-service subscription management for your customers.

Reducing Checkout Drop-Offs

Trên màn hình nhỏ, việc điền từng trường biểu mẫu sẽ tốn nhiều công sức hơn. Các cài đặt checkout session bên dưới giúp rút gọn biểu mẫu và giảm tỷ lệ người dùng bỏ dở.

Optimize the Form

These settings shorten the checkout form on mobile:

Pre-Fill Customer Data

Each field you pre-fill is one the customer doesn’t have to type:
  • New customers: set customer.email and customer.name from your auth session.
  • Returning customers: set customer.customer_id to use the customer’s stored details.
  • Currency: pass billing_currency and billing_address.country together.

Recovery Tools

Recovery tools bring back customers whose checkout or renewal didn’t complete:

Abandoned Cart Recovery

Email sequences for abandoned or failed checkouts.

Payment Retries

Automatic retries for failed subscription renewals.

Subscription Dunning

Emails that recover subscriptions with failed payments.

Recovery Overview

Every recovery tool and the revenue it recovers.

Các phương pháp hay nhất

  • Bảo mật: Không bao giờ đưa API key vào ứng dụng của bạn. Tạo checkout session trên backend và chỉ truyền checkout_url cho ứng dụng.
  • Quyền hạn: Xem CheckoutResult.status như một gợi ý về giao diện người dùng. Chỉ cấp quyền sau khi backend xác nhận thanh toán.
  • Trải nghiệm người dùng: Hiển thị trạng thái đang tải trong khi backend tạo session. Đừng coi cancelled là lỗi, vì thanh toán vẫn có thể đã thành công.
  • Kiểm thử: Sử dụng chế độ test và thẻ test, đồng thời kiểm tra toàn bộ quy trình return URL trên thiết bị thật cũng như trình mô phỏng.
  • Tỷ lệ chuyển đổi: Thiết lập show_order_details: false và minimal_address: true. Kết hợp lại, chúng đưa các phương thức thanh toán lên phần đầu màn hình và loại bỏ hầu hết các trường địa chỉ.
  • Tiền tệ: Truyền cả billing_currency và billing_address.country. Nếu thiếu một trong hai, Adaptive Currency có thể chọn tiền tệ thanh toán dựa trên địa chỉ IP của khách hàng.
  • Thanh toán theo nhu cầu: Thiết lập show_on_demand_tag: false khi bạn chỉ sử dụng subscription theo nhu cầu để lưu thẻ. Khách hàng nạp tiền vào ví sẽ không mong đợi nội dung về subscription.
  • Khôi phục: Bật Abandoned Cart Recovery trong dashboard để gửi email cho những khách hàng không hoàn tất checkout.

Khắc phục sự cố

Các vấn đề thường gặp

  • Callback không bao giờ đến: Scheme trong returnUrl phải khớp với scheme bạn đã đăng ký. Trên Android, đó là manifest placeholder dodoCallbackScheme. Trên iOS, đó là URL type Info.plist. Ứng dụng React Native và Flutter cần cả hai, còn trong Expo, config plugin sẽ thiết lập cả hai.
  • Checkout quay lại trình duyệt thay vì ứng dụng của bạn (iOS): Ứng dụng của bạn không chuyển tiếp URL đến. Hãy gọi DodoCheckout.handleOpenURL(url) từ .onOpenURL, scene(_:openURLContexts:) hoặc listener Linking của React Native.
  • PLATFORM_ERROR trên Android: Nguyên nhân phổ biến nhất là scheme không khớp. Lỗi này cũng xuất hiện khi MainActivity của bạn thiết lập android:taskAffinity="" (giá trị mặc định flutter create), khiến một số bản build Android của OEM làm mất checkout đang thực hiện.
  • ALREADY_IN_PROGRESS: Một checkout vẫn đang mở. Hãy chờ checkout trước đó hoàn tất hoặc đóng nó trước khi bắt đầu checkout khác.
  • Build thất bại với placeholder chưa được phân giải: Bạn đã thêm Android SDK nhưng chưa thiết lập manifestPlaceholders["dodoCallbackScheme"].
  • Thanh toán thành công nhưng quyền truy cập chưa được cấp: Ứng dụng của bạn cấp quyền truy cập dựa trên kết quả từ mobile. Thay vào đó, hãy cấp quyền truy cập từ webhook payment.succeeded hoặc subscription.active.
  • Apple Pay hoặc Google Pay không xuất hiện trên mobile: Kiểm tra xem checkout có đang tải bên trong embedded WebView hay không (WKWebView hoặc Android WebView). Hãy mở bằng SDK hoặc trong giao diện trình duyệt hệ thống: Custom Tab trên Android, hoặc SFSafariViewController hay ASWebAuthenticationSession trên iOS.

Tài nguyên bổ sung

Contact Support

Nếu có câu hỏi hoặc cần hỗ trợ, hãy gửi email đến support@dodopayments.com.
Lần sửa đổi cuối 26 tháng 9, 2026