MCPInstall

mobile-checkout

Dodo Payments in-app checkout for React Native, Flutter, native iOS, and Android using SFSafariViewController or Chrome Custom Tabs. Use when a mobile app must open a backend-created checkout session, handle deep link or custom URL scheme returns, recover abandoned checkouts, and verify payment server-side before unlocking access.

Installs
182
Last 24h
—
Trending rank
—
Security
Not audited

Install

Works with every agent the skills CLI supports.

npx skills add https://github.com/dodopayments/skills --skill mobile-checkout
  1. 01

    Install

    Run the command in your project. The CLI asks which agents to install for.

  2. 02

    Describe the task

    Ask your agent for something this skill covers. It loads mobile-checkout automatically when relevant.

  3. 03

    Review the result

    The agent follows the SKILL.md steps, scripts and references bundled with the skill.

SKILL.md

The instructions your agent reads when the skill is triggered.

Mobile In-App Checkout

This skill covers integrating Dodo Payments hosted checkout into native and cross-platform mobile apps using secure system browser contexts.

When to use this skill

  • Building a React Native app with Turbo Module checkout integration
  • Adding checkout to a Flutter app via native bridge
  • Implementing native iOS or Android checkout with secure browser contexts
  • Registering custom URL schemes and deep links for payment return
  • Handling abandoned checkout sessions and recovery flows
  • Confirming payment authority server-side before granting access

Core principle: Backend creates, mobile opens

Your backend creates the checkout session and returns a URL. The mobile app opens that URL in a secure browser context. Your API key must never be embedded in the app binary. The mobile SDK result is informational only; always verify the payment server-side via webhook or API before unlocking features or granting access.

Architecture overview

  1. Backend: Create a checkout session via client.checkoutSessions.create(...) and return the checkout_url to your mobile app.
  2. Mobile app: Call the platform-specific SDK with the checkout URL and a registered return URL scheme.
  3. Browser context: The SDK opens the URL in a secure system browser: SFSafariViewController on iOS and Chrome Custom Tabs on Android.
  4. Return: After payment, the browser navigates to your return URL. The SDK captures the result and passes it to your app.
  5. Verification: Query the checkout session or listen for a webhook to confirm the payment before granting access.

React Native (Turbo Module)

Full guide: references/react-native.md.

Covers:

  • Installation
  • Setup
  • Starting checkout
  • Abandoned session recovery
  • Android minSdk requirement

Flutter

Full guide: references/flutter.md.

Covers:

  • Installation
  • Setup
  • Starting checkout
  • Return URL registration

iOS (native)

Full guide: references/ios.md.

Covers:

  • Installation
  • Starting checkout
  • Forwarding the return URL
  • Abandoned sessions

Android (native)

Full guide: references/android.md.

Covers:

  • Installation
  • Starting checkout
  • Abandoned sessions

Backend: Creating checkout sessions

Always create checkout sessions on your backend. Never embed your API key in the mobile app.

import DodoPayments from 'dodopayments';

const client = new DodoPayments({
  bearerToken: process.env.DODO_PAYMENTS_API_KEY,
  environment: 'test_mode',
});

const MOBILE_PRODUCTS = new Map([
  ['starter', 'pdt_starter123'],
  ['pro', 'pdt_pro456'],
]);

app.post('/api/mobile-checkout', requireAuth, async (req, res) => {
  const productId = MOBILE_PRODUCTS.get(req.body.plan);

  if (!productId) {
    return res.status(400).json({ error: 'Invalid plan' });
  }

  // requireAuth derives this mapping from the authenticated server-side session.
  const customerId = req.auth.dodoCustomerId;
  
  const session = await client.checkoutSessions.create({
    product_cart: [{ product_id: productId, quantity: 1 }],
    customer: { customer_id: customerId },
    return_url: 'myapp://checkout/return',
  });
  
  res.json({ checkout_url: session.checkout_url });
});

Verifying payment server-side

Never grant access based on the mobile SDK result alone. Always verify via webhook or API.

Via webhook

Listen for payment.succeeded webhooks. Webhook signature verification is covered in the webhook-integration skill.

// Mount with express.raw({ type: 'application/json' }) so req.body is the raw Buffer.
app.post('/webhook', async (req, res) => {
  let event;
  try {
    event = client.webhooks.unwrap(req.body.toString(), {
      headers: {
        'webhook-id': req.headers['webhook-id'] as string,
        'webhook-signature': req.headers['webhook-signature'] as string,
        'webhook-timestamp': req.headers['webhook-timestamp'] as string,
      },
    });
  } catch {
    return res.status(401).json({ error: 'Invalid signature' });
  }

  try {
    // Dodo retries and may redeliver: claim webhook-id with a UNIQUE insert
    // and grant in the same transaction so a duplicate is a no-op.
    await db.$transaction(async (tx) => {
      const claim = await tx.webhookLog.createMany({
        data: [{ webhookId: req.headers['webhook-id'] as string, eventType: event.type }],
        skipDuplicates: true,
      });
      if (claim.count === 0) return; // already processed

      if (event.type === 'payment.succeeded') {
        await grantAccess(event.data.customer.customer_id, tx);
      }
    });
  } catch (error) {
    // Non-2xx makes Dodo retry; the transaction rolled back the claim.
    return res.status(500).json({ error: 'Processing failed' });
  }

  res.json({ received: true });
});

Via API

Query the checkout session to confirm payment:

const session = await client.checkoutSessions.retrieve(sessionId);

if (session.payment_status === 'succeeded' && session.payment_id) {
  const payment = await client.payments.retrieve(session.payment_id);
  await grantAccess(payment.customer.customer_id);
}

Selling digital goods on iOS

Dodo Payments hosted checkout can sell digital goods (subscriptions, courses, downloads, SaaS plans) in an iOS app only on App Store storefronts where Apple allows external purchases:

  • United States: Guideline 3.1.1(a) allows buttons and links to other purchase methods without an entitlement (subject to the Epic v. Apple proceedings).
  • European Union: requires Apple's EU external purchase entitlement (the StoreKit External Purchases or Offers Entitlement from October 1, 2026) and DMA compliance.
  • Japan: allowed under the Mobile Software Competition Act, following Apple's Japan-specific entitlement requirements.
  • South Korea is not supported (Apple requires a native, non-web-view flow through an approved Korean PSP).

On other storefronts, digital goods sold inside the iOS app must use Apple in-app purchase (StoreKit). Review Apple's region-specific entitlements before enabling Dodo checkout for a storefront; unsupported flows can get the app rejected.

Common mistakes

Embedding the API key in the app

Never include your API key in the app binary or client-side code. Always create checkout sessions on your backend.

// WRONG
const client = new DodoPayments({
  bearerToken: 'dodo_live_abc123...',  // Never hardcode
});

// CORRECT
const client = new DodoPayments({
  bearerToken: process.env.DODO_PAYMENTS_API_KEY,  // Backend only
});

Trusting the mobile SDK result

The SDK result is informational. Always verify server-side before granting access.

// WRONG
if (result.status === 'succeeded') {
  grantAccess();  // No verification
}

// CORRECT
if (result.status === 'succeeded') {
  const verified = await verifyPaymentOnBackend(result.paymentId);
  if (verified) {
    grantAccess();
  }
}

Forgetting URL scheme registration

If you don't register the custom URL scheme, the app won't receive the return callback and checkout will appear to hang.

  • React Native: Use the Expo plugin or manually register in Info.plist and AndroidManifest.xml.
  • Flutter: Register in both Info.plist and AndroidManifest.xml.
  • iOS: Add CFBundleURLTypes to Info.plist and forward URLs to DodoCheckout.handleOpenURL.
  • Android: Set manifestPlaceholders["dodoCallbackScheme"]; the SDK supplies the intent filter.

Not handling all result statuses

Always handle all five statuses: succeeded, failed, cancelled, pending, and expired. Each requires different UX.

// WRONG
if (result.status === 'succeeded') {
  showSuccess();
}

// CORRECT
switch (result.status) {
  case 'succeeded':
    showSuccess();
    break;
  case 'failed':
    showFailure();
    break;
  case 'cancelled':
  case 'pending':
    // Unknown outcome - reconcile, never show a failure
    reconcileAbandonedSession();
    break;
  case 'expired':
    showExpired();
    break;
}

Ignoring abandoned sessions

If the app crashes or is backgrounded during checkout, the session is abandoned. Always check for and recover abandoned sessions on app startup.

// WRONG
// No recovery logic

// CORRECT: reconcile on launch and after every cancelled/pending result,
// and clear the record only once the backend reports a final outcome.
await reconcileAbandonedSession(); // defined in references/react-native.md

Package names

Use @dodopayments/react-native-checkout for React Native and dodopayments_checkout for Flutter. The similarly named @dodopayments/react-native and dodo_payments_flutter packages do not exist.

Resources

FAQ

What does the mobile-checkout skill do?

Dodo Payments in-app checkout for React Native, Flutter, native iOS, and Android using SFSafariViewController or Chrome Custom Tabs. Use when a mobile app must open a backend-created checkout session, handle deep link or custom URL scheme returns, recover abandoned checkouts, and verify payment server-side before unlocking access.

How do I install mobile-checkout?

Run npx skills add https://github.com/dodopayments/skills --skill mobile-checkout in your project. The skills CLI detects the agents you use and copies the skill into the right folder. For Claude Code you can also place the skill folder at ~/.claude/skills/mobile-checkout/ (personal) or .claude/skills/mobile-checkout/ (project).

Which AI agents can use mobile-checkout?

Any agent that supports the open Agent Skills format (a folder with a SKILL.md file), including Claude Code, Codex, Cursor, GitHub Copilot, Gemini CLI, Windsurf, Cline, OpenCode and more.

Is mobile-checkout safe to install?

No partner security audit is available yet. Skills can run commands and scripts, so read the SKILL.md and any bundled files before installing.

Who maintains mobile-checkout?

It is published by dodopayments in dodopayments/skills. It has 182 recorded installs.

More from dodopayments/skills

View all →

webhook-integration

dodopayments/skills

Dodo Payments webhook handling, covering endpoint setup, Standard Webhooks signature verification on the raw body, payment, subscription, and refund event types, idempotency with webhook-id, retries, and local testing. Use when receiving Dodo events, verifying webhook-signature headers, or syncing your database from payment.succeeded or subscription events.

Backend & APIs1.1K installs

subscription-integration

dodopayments/skills

Dodo Payments subscription lifecycle after checkout, covering trials, statuses like active, onhold, and cancelled, plan upgrades and downgrades, proration, cancellation, failed-payment recovery, mandates, and on-demand charges. Use when managing recurring billing, plan changes, dunning, or off-session charging; use checkout-integration to start the purchase.

checkout-integration

dodopayments/skills

Dodo Payments checkout via hosted Checkout Sessions (client.checkoutSessions.create), static payment links, and overlay or inline checkout for one-time and subscription products. Use when building a pay button, checkout page, trial signup, custom fields, discount codes at checkout, or returnurl redirects; use subscription-integration for post-checkout lifecycle.

Backend & APIs997 installs

dodo-best-practices

dodopayments/skills

Starting point for any Dodo Payments integration, covering Merchant of Record basics, SDK installation and client setup, test and live environments, API keys, and the canonical checkout-to-webhook flow. Use when starting a new integration, initializing the dodopayments client, choosing an architecture, or unsure which Dodo Payments skill applies.

billing-sdk

dodopayments/skills

BillingSDK, the open-source React and shadcn component library for Dodo Payments billing UI. Use when building pricing tables, pricing pages, account billing pages, usage meters, credit balances, invoice history, or cancel and upgrade UI in React or Next.js, or running @billingsdk/cli init; keep Dodo API calls server-side.

Frontend & Design817 installs

usage-based-billing

dodopayments/skills

Dodo Payments usage-based billing that charges per measured unit, covering meters, usage event ingestion by eventname, aggregation, free thresholds, priceperunit, and metered subscriptions. Use when billing per API call, AI token, storage, or bandwidth, sending usage events, or adding metered pricing to a plan; use credit-based-billing for prepaid credit pools.

Backend & APIs792 installs

Popular mobile skills

View category →

design-mobile-apps

designed-by-ai/skills

Design mobile app screens with Sleek, edit Sleek projects, and implement their designs in React Native or HTML.

Mobile1.1M installs

ios-design

uizze.sh

Design and review iOS UI/UX for iPhone apps in SwiftUI, UIKit, or React Native. Use for mobile app design, screen redesigns, navigation flows, typography, forms, keyboard and safe-area layout, and accessibility checks. Apply Apple Human Interface Guidelines and consult real product references through the optional Uizze connection.

Mobile416.5K installs

vercel-react-native-skills

vercel-labs/agent-skills

React Native and Expo best practices for building performant mobile apps. Use when building React Native components, optimizing list performance, implementing animations, or working with native modules. Triggers on tasks involving React Native, Expo, mobile performance, or native platform APIs.

OfficialMobile256.7K installs

animate-expo

emilkowalski/skills

Build animations in React Native and Expo, making the decisions in the order that determines whether they feel right — should it animate, which thread it runs on, which properties, spring or timing, how the gesture hands off, how it degrades. Writes the implementation with Reanimated, Gesture Handler, Expo Router and expo-haptics. Use when animating anything in an Expo app, adding gestures, sheets, screen transitions, press feedback or haptics, or fixing motion that stutters on device. For web animation use animate.

Mobile140.1K installs

xcode-project-setup

firebase/agent-skills

Safely modifies Xcode projects (.pbxproj) to add Swift Packages and link files. Use this skill whenever an iOS project needs dependencies installed (e.g. Firebase, Alamofire).

OfficialMobile131.2K installs

firebase-remote-config-basics

firebase/agent-skills

Manages Firebase Remote Config templates, feature flags, loading strategies, and SDKs (Android, iOS). Use when downloading/deploying remoteconfig JSON templates, managing version history/feature flags, setting in-app defaults, fetchAndActivate(), real-time listeners, or SDK setup. Don't use for Firebase Hosting, Auth, Firestore, Data Connect, Crashlytics, or App Hosting.

OfficialMobile121.7K installs