dodopayments_checkout 1.0.2 copy "dodopayments_checkout: ^1.0.2" to clipboard
dodopayments_checkout: ^1.0.2 copied to clipboard

Open Dodo Payments' hosted checkout in a system browser tab and get a clean result from one call.

Dodo Payments Checkout Flutter SDK #

Open Dodo Payments' hosted checkout in a system browser tab (SFSafariViewController on iOS, a Custom Tab on Android) and get a clean result from one call.

This package is a thin wrapper: all checkout logic lives in the native iOS (DodoCheckout, Swift) and Android (com.dodopayments.api:checkout-android) cores. The Dart layer passes the call through a typed Pigeon channel and maps the result back.

Install #

dependencies:
  dodopayments_checkout: ^1.0.0

Requirements: Flutter 3.44+ / Dart 3.12+, iOS 16+, Android minSdk 23.

Setup #

Register a callback URL scheme so the OS routes the checkout return back to your app:

  • iOS: add a URL type for your scheme in Info.plist, and forward incoming URLs (e.g. via app_links) into DodoCheckout.instance.handleOpenURL(url)SFSafariViewController has no in-process way to catch its own return URL.

  • Android: set your callback scheme as a Gradle manifest placeholder — the underlying Android core's own manifest already declares the redirect activity's intent-filter, so this one property is the entire setup cost:

    android {
        defaultConfig {
            manifestPlaceholders["dodoCallbackScheme"] = "myapp"
        }
    }
    

    If your MainActivity sets android:taskAffinity="" (the stock flutter create template default, meant to stop the launcher icon from resurrecting a stale engine instance), remove it or give the SDK's activities the same explicit affinity. The SDK's own activities inherit the app's default task affinity, and leaving MainActivity on a different one is a real mismatch — some OEM Android skins (observed on Vivo) can then treat a long-lived Custom Tab's return as belonging to a different task and recreate the SDK's host activity from scratch, losing the in-flight checkout and surfacing PLATFORM_ERROR: Checkout was launched without its parameters.

Use #

import 'package:dodopayments_checkout/dodopayments_checkout.dart';

final result = await DodoCheckout.instance.start(
  CheckoutParams(
    checkoutUrl: Uri.parse(checkoutUrl), // from your backend's POST /checkouts
    returnUrl: Uri.parse('myapp://checkout/return'), // scheme must be registered (see Setup)
    onEvent: (event) => print(event.type), // logging only — never decide outcome from events
  ),
);

switch (result.status) {
  case CheckoutStatus.succeeded: showSuccess(result.paymentId); // UI only — confirm server-side
  case CheckoutStatus.failed:    showFailure();
  case CheckoutStatus.cancelled: dismiss();
  case CheckoutStatus.pending:   showPending();  // settles later; webhook is authority
  case CheckoutStatus.expired:   showExpired();
}

Your backend creates the checkout session (with your secret key) and sends the checkout_url to the app. Set the session's return_url to the same URL you pass as returnUrl — it never has to resolve, because the SDK cancels the navigation before it loads.

What the result means #

The result comes from the return_url query string. It is a UI hint, not proof of payment. This SDK never calls the Dodo API and holds no API key. Grant access on your backend from the webhook (payment.succeeded / subscription.active) or by retrieving the payment with your secret key. result.raw carries every query parameter verbatim.

Verify the payment #

Confirm every payment from your backend, not from the mobile result:

  • Webhook: Dodo Payments calls your backend when a payment succeeds or a subscription activates. Check the Webhooks guide.
  • Verification API: look up paymentId with your secret key via Get Payment Detail.

Abandoned sessions #

If the app is killed mid-checkout, recover the interrupted session on next launch and reconcile it server-side:

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

Errors #

start throws CheckoutException only for misuse or platform failure. The code is a CheckoutErrorCode:

Code Native code Meaning
invalidCheckoutUrl INVALID_CHECKOUT_URL Not a checkout.dodopayments.com / test.checkout.dodopayments.com session URL
invalidReturnUrl INVALID_RETURN_URL Not a valid absolute URL
alreadyInProgress ALREADY_IN_PROGRESS A checkout is already running (only one at a time)
platformError PLATFORM_ERROR Unexpected platform failure

A user cancelling or a declined payment is a result (CheckoutStatus.cancelled / CheckoutStatus.failed), never an exception.

Example #

example/lib/main.dart demonstrates a full price-page flow. Generate its platform folders once with flutter create --platforms=android,ios . inside example/ and run it.

Development notes #

  • Platform channels are generated by Pigeon from pigeons/messages.dart. After editing that file: dart run pigeon --input pigeons/messages.dart.
  • iOS bundles the Swift core as source (see ios/dodopayments_checkout.podspec); Android depends on the com.dodopayments.api:checkout-android Maven artifact (see android/build.gradle for local-dev pointers to the sibling cores in this monorepo).
0
likes
150
points
134
downloads

Documentation

API reference

Publisher

verified publisherdodopayments.com

Weekly Downloads

Open Dodo Payments' hosted checkout in a system browser tab and get a clean result from one call.

Homepage
Repository (GitHub)
View/report issues

License

Apache-2.0 (license)

Dependencies

flutter, meta

More

Packages that depend on dodopayments_checkout

Packages that implement dodopayments_checkout