MCPInstall

checkout-integration

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.

Installs
997
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 checkout-integration
  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 checkout-integration 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.

Dodo Payments Checkout Integration

Use client.checkoutSessions.create(...) to build hosted checkout pages or overlay checkout modals. This is the recommended path for all new payment integrations.

When to use this skill

  • Build a one-time payment checkout flow
  • Create a subscription checkout with optional trial
  • Embed checkout in an overlay or inline modal
  • Collect customer billing info and custom fields at checkout
  • Apply discount codes or handle currency selection
  • Redirect customers after payment completes

Checkout Methods

Dodo Payments offers three ways to collect payment:

MethodBest ForSetup
Checkout Sessions (recommended)Most integrations; full controlServer-side SDK call
Static Payment LinksNo-code sharing; reusable URLsDashboard or direct URL
Overlay/Inline CheckoutCheckout stays on your siteClient-side SDK

Legacy: Dynamic Payment Links created via POST /payments or POST /subscriptions are deprecated. Use Checkout Sessions instead.


Core Concepts

Amounts in smallest currency unit: All prices are in cents (or equivalent). A $10 USD charge is 1000.

Checkout Session: A single-use session that generates a hosted checkout URL. Expires after 24 hours (or 15 minutes if confirm=true).

Return URL: Where the customer lands after payment. Dodo appends payment_id (one-time) or subscription_id (subscription), status, and when available license_key and email. It is a UI hint only, never proof of payment.

Entitlement on webhook: The browser redirect is not the source of truth. Always verify payment via webhook before granting access. See webhook-integration skill for verification.


Create a Checkout Session

Basic One-Time Payment

import DodoPayments from 'dodopayments';

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

const session = await client.checkoutSessions.create({
  product_cart: [
    { product_id: 'pdt_example', quantity: 1 }
  ],
  customer: {
    email: 'customer@example.com',
    name: 'Jane Doe'
  },
  return_url: 'https://yoursite.com/checkout/success'
});

console.log('Redirect to:', session.checkout_url);

Response fields:

  • session_id: Unique checkout session ID
  • checkout_url: Hosted checkout URL (redirect customer here). Nullable when confirm=true
  • client_secret: Present only if confirm=true
  • publishable_key: Present only if confirm=true. Pair it with client_secret for the inline SDK flow
  • payment_id: Present only if confirm=true

publishable_key is a per-session value returned for confirm-mode inline checkout. It is not a Stripe-style publishable API key — Dodo issues no such credential, and every API key is secret and server-side only.

With Multiple Products

const session = await client.checkoutSessions.create({
  product_cart: [
    { product_id: 'pdt_item_1', quantity: 2 },
    { product_id: 'pdt_item_2', quantity: 1 }
  ],
  customer: { email: 'customer@example.com' },
  return_url: 'https://yoursite.com/success'
});

With Existing Customer

const session = await client.checkoutSessions.create({
  product_cart: [
    { product_id: 'pdt_example', quantity: 1 }
  ],
  customer: { customer_id: 'cus_existing_id' },
  return_url: 'https://yoursite.com/success'
});

With Billing Address and Tax ID

const session = await client.checkoutSessions.create({
  product_cart: [
    { product_id: 'pdt_example', quantity: 1 }
  ],
  customer: {
    email: 'customer@example.com',
    name: 'Jane Doe'
  },
  billing_address: {
    country: 'US',
    street: '123 Main St',
    city: 'San Francisco',
    state: 'CA',
    zipcode: '94105'
  },
  tax_id: 'VAT123456789',
  customer_business_name: 'Acme Corp',
  return_url: 'https://yoursite.com/success'
});

With Discount Codes

const session = await client.checkoutSessions.create({
  product_cart: [
    { product_id: 'pdt_example', quantity: 1 }
  ],
  customer: { email: 'customer@example.com' },
  discount_codes: ['PROMO10', 'WELCOME5'], // max 20, ordered
  feature_flags: {
    allow_discount_code: true
  },
  return_url: 'https://yoursite.com/success'
});

With Subscription and Trial

const session = await client.checkoutSessions.create({
  product_cart: [
    { product_id: 'pdt_monthly_subscription', quantity: 1 }
  ],
  subscription_data: {
    trial_period_days: 14
  },
  customer: { email: 'subscriber@example.com' },
  return_url: 'https://yoursite.com/success'
});

With Custom Fields

const session = await client.checkoutSessions.create({
  product_cart: [
    { product_id: 'pdt_example', quantity: 1 }
  ],
  customer: { email: 'customer@example.com' },
  custom_fields: [
    {
      key: 'company_size',
      label: 'Company Size',
      field_type: 'dropdown',
      options: ['1-10', '11-50', '51-200', '200+'],
      required: true
    },
    {
      key: 'use_case',
      label: 'Primary Use Case',
      field_type: 'text',
      placeholder: 'e.g., analytics, reporting',
      required: false
    }
  ],
  return_url: 'https://yoursite.com/success'
});

With Metadata

const session = await client.checkoutSessions.create({
  product_cart: [
    { product_id: 'pdt_example', quantity: 1 }
  ],
  customer: { email: 'customer@example.com' },
  metadata: {
    order_id: 'order_12345',
    referral_code: 'FRIEND20',
    campaign: 'summer_sale'
  },
  return_url: 'https://yoursite.com/success'
});

Next.js App Router

Full guide: references/server-examples.md.

Covers:

  • API Route
  • Client Component
  • Success Page

Express.js

Full guide: references/server-examples.md.

Python (FastAPI)

Full guide: references/server-examples.md.

Overlay Checkout

Full guide: references/overlay-checkout.md.

Covers:

  • Installation
  • Server Route
  • Browser Overlay and Inline Modes

Static Payment Links

No-code shareable links. No server-side API call needed.

Basic Format

https://checkout.dodopayments.com/buy/{productid}

In test mode the host is test.checkout.dodopayments.com (for example https://test.checkout.dodopayments.com/buy/{productid}); a test-mode product does not exist on the live host.

Example:

https://checkout.dodopayments.com/buy/pdt_example

With Query Parameters

https://checkout.dodopayments.com/buy/pdt_example?quantity=2&email=customer@example.com&redirect_url=https%3A%2F%2Fyoursite.com%2Fsuccess

Supported parameters:

  • quantity: Item quantity
  • redirect_url: Success redirect URL
  • email: Prefill customer email
  • fullName, firstName, lastName: Prefill name
  • country, city, state, zipCode, addressLine: Prefill address
  • paymentCurrency: Force currency
  • metadata_*: Custom metadata (e.g., metadata_orderId=123)

Retrieve and Preview Sessions

Retrieve Session Status

const status = await client.checkoutSessions.retrieve('cks_session_id');

console.log(status.id);
console.log(status.payment_status);

Preview Session (without creating)

const preview = await client.checkoutSessions.preview({
  product_cart: [
    { product_id: 'pdt_example', quantity: 1 }
  ],
  customer: { email: 'customer@example.com' },
  billing_currency: 'EUR'
});

console.log('Preview total:', preview.current_breakup.total_amount);

Customization

Theme and Appearance

const session = await client.checkoutSessions.create({
  product_cart: [{ product_id: 'pdt_example', quantity: 1 }],
  customer: { email: 'customer@example.com' },
  customization: {
    theme: 'dark', // 'light', 'dark', or 'system'
    force_language: 'en',
    show_order_details: true,
    theme_config: {
      font_size: 'md',
      font_weight: 'normal',
      radius: '8px',
      pay_button_text: 'Complete Purchase',
      light: {
        bg_primary: '#ffffff',
        text_primary: '#000000',
        button_primary: '#0066ff'
      }
    }
  },
  return_url: 'https://yoursite.com/success'
});

Feature Flags

const session = await client.checkoutSessions.create({
  product_cart: [{ product_id: 'pdt_example', quantity: 1 }],
  customer: { email: 'customer@example.com' },
  feature_flags: {
    allow_discount_code: true,
    allow_currency_selection: true,
    allow_customer_editing_email: true,
    allow_phone_number_collection: true,
    require_phone_number: false,
    allow_tax_id: true
  },
  return_url: 'https://yoursite.com/success'
});

Post-Payment Flow

Return URL Handling

After payment, the customer is redirected to your return_url with query parameters:

# One-time payment
https://yoursite.com/success?payment_id=pay_xxx&status=succeeded&email=customer%40example.com

# Subscription (with license keys)
https://yoursite.com/success?subscription_id=sub_xxx&status=active&license_key=LK-001,LK-002&email=customer%40example.com

Query parameters:

  • payment_id (one-time payments) or subscription_id (subscriptions)
  • status: succeeded (one-time) or active (subscription) on success; failed if declined; processing (or a requires_* value) if it settles later; expired if the session expired
  • license_key: present when the product issues license keys (comma-separated if several)
  • email: present when the customer has an email on record

There is no session_id parameter. Treat processing/missing status as "unknown" and wait for the webhook.

Verify Payment Server-Side

Do not trust the browser redirect. Use the Express webhook route in references/server-examples.md: it receives the raw signed body before express.json(), fulfills one-time purchases on verified payment.succeeded using event.data.customer.customer_id, and grants subscription access only on verified subscription.active.

Webhook signature verification is covered in the webhook-integration skill.


Common Mistakes

1. Granting access from the return URL The return URL redirect is not proof of payment. Never grant access or fulfill an order from its query parameters; wait for a verified webhook event.

2. Using deprecated APIs Do not use client.payments.create() or client.subscriptions.create() for new integrations. Both are deprecated. Use client.checkoutSessions.create().

3. Forgetting the environment flag The default is live_mode. Always set environment: 'test_mode' during development to avoid charging real cards.

4. Using the deprecated discount_code Use discount_codes (an array). The singular discount_code string is deprecated (still accepted for backward compatibility) and cannot be combined with discount_codes in the same request.

5. Amounts in wrong unit All amounts are in the smallest currency unit (cents for USD). $10 is 1000, not 10.

6. Reusing checkout URLs Checkout URLs are single-use. Create a new session for each checkout attempt.

7. Ignoring confirm=true behavior When confirm=true, the session is finalized immediately and the checkout URL expires in 15 minutes instead of 24 hours. Use only when you have all required customer data.

8. Forgetting raw body for webhooks Webhook signature verification requires the raw request body, not a re-serialized JSON object. Mount the webhook route with express.raw({ type: 'application/json' }) before express.json().

9. Trusting a client-supplied product id Do not accept an arbitrary pdt_ id from the browser. Authenticate the user, accept a public plan slug, map it to an allowlisted product id on the server, and reject quantities that are not positive integers.


Resources

FAQ

What does the checkout-integration skill do?

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.

How do I install checkout-integration?

Run npx skills add https://github.com/dodopayments/skills --skill checkout-integration 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/checkout-integration/ (personal) or .claude/skills/checkout-integration/ (project).

Which AI agents can use checkout-integration?

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 checkout-integration 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 checkout-integration?

It is published by dodopayments in dodopayments/skills. It has 997 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.

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

license-keys

dodopayments/skills

Dodo Payments license keys for software products, covering activation, validation, and deactivation via client.licenses, activation limits, expiry, entitlement grants, revocation, and license webhooks. Use when gating a desktop app, CLI, plugin, or SaaS feature behind a license key, enforcing per-seat or per-device limits, or building a license dashboard.

Data & Databases683 installs

Popular backend & apis skills

View category →

find-skills

vercel-labs/skills

Helps users discover and install agent skills when they ask questions like "how do I do X", "find a skill for X", "is there a skill that can...", or express interest in extending capabilities. This skill should be used when the user is looking for functionality that might exist as an installable skill.

OfficialBackend & APIs4.1M installs

entra-app-registration

microsoft/azure-skills

Guides Microsoft Entra ID app registration, OAuth 2.0 authentication, and MSAL integration. USE FOR: create app registration, register Azure AD app, configure OAuth, set up authentication, add API permissions, generate service principal, MSAL example, console app auth, Entra ID setup, Azure AD authentication. DO NOT USE FOR: Key Vault secrets (use azure-keyvault-expiration-audit), general Azure resource security guidance.

OfficialBackend & APIs566.7K installs

This skill should be used when the user wants to "write agent code", "build an agent with ADK", "add a tool", "create a callback", "define an agent", "use state management" — in a project that needs ADK (Agent Development Kit) API patterns and code examples. It provides a quick reference for agent types, tool definitions, orchestration patterns, callbacks, state management, the graph Workflow API, and reference recipes to study. Do NOT use for scaffolding (use google-agents-cli-scaffold) or deployment (use google-agents-cli-deploy).

Backend & APIs514.8K installs

embedded-captions

heygen-com/hyperframes

Add captions or subtitles to an existing single-subject talking-head video without editing the footage. Use for plain verbatim captions, cinematic captions embedded behind the subject, VFX captions, “炸/特效/酷炫字幕,” or a named identity from the 35-style catalog. Route by visual identity, not by backend engine. The quiet anchor rail is the default; embed every word only when the user explicitly wants a fully cinematic treatment. The workflow runs locally end to end, including transcription and subject matting; split multi-shot footage before applying it.

Backend & APIs453.3K installs

entra-agent-id

microsoft/azure-skills

Provision Microsoft Entra Agent Identity Blueprints, BlueprintPrincipals, and per-instance Agent Identities via Microsoft Graph, and configure OAuth 2.0 token exchange (fmipath, OBO, cross-tenant) including the Microsoft Entra SDK for AgentID sidecar. USE FOR: Agent Identity Blueprint, BlueprintPrincipal, agent OAuth, fmipath token exchange, agent OBO, Workload Identity Federation for agents, polyglot agent auth, Microsoft.Identity.Web.AgentIdentities. DO NOT USE FOR: standard Entra app registration (use entra-app-registration), Microsoft Foundry agent authoring (use microsoft-foundry).

OfficialBackend & APIs336.6K installs

neon

neondatabase/agent-skills

Overview of Neon, a complete set of cloud backend primitives around Lakebase Postgres: Auth, Object Storage, Functions, the AI Gateway, and Realtime. Start here to choose Neon for undecided login, files, APIs, and LLM calls, set up the CLI or MCP server, and follow the branch-first workflow. Use when building an app or backend on Neon, or when "Neon" or "Lakebase Postgres" is mentioned. Child skill neon-postgres wins for an existing DATABASEURL, SQL, schema, inspect, or search. Child skill neon-auth wins for login, users, sessions, identity routing, and Managed Better Auth setup. Child skill neon-realtime wins for live queries in reactive apps and agents. Also use for object storage, S3, buckets, serverless functions, function triggers, cron, AI gateway, LLM calls, realtime, logs, Loki, Grafana, observability, postgres, database, backend, Claimable Neon, neon.new, or a no-signup database.

Backend & APIs237.1K installs