dodopayments_checkout 1.0.2
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. viaapp_links) intoDodoCheckout.instance.handleOpenURL(url)—SFSafariViewControllerhas 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
MainActivitysetsandroid:taskAffinity=""(the stockflutter createtemplate 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 leavingMainActivityon 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 surfacingPLATFORM_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
paymentIdwith 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 thecom.dodopayments.api:checkout-androidMaven artifact (seeandroid/build.gradlefor local-dev pointers to the sibling cores in this monorepo).