dodopayments/skills1 file

Checkout Integration

Guide for starting hosted Checkout Sessions, payment links, and overlay or inline checkout for one-time and recurring products; use subscription-integration for post-checkout lifecycle management.

Specification
Skill ID
dodopayments/skills/checkout-integration
Publisher
dodopayments
Repository
skills
Installs
0
Files
1
Synced
Sep 23, 2026
How to use it

Open any RiverX project, open the Skills panel in the chat, and search for this identifier. The files are fetched from the source repository at install time.

dodopayments/skills/checkout-integrationInstalls these files
  • SKILL.md

What this skill tells the agent

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. Query parameters include status=success and session_id.

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

API Route

// app/api/checkout/route.ts
import { NextResponse } from 'next/server';
import DodoPayments from 'dodopayments';
import { getCurrentUser } from '@/lib/auth';

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

type PlanId = 'starter' | 'pro';

const PRODUCT_IDS: Record<PlanId, string> = {
  starter: 'pdt_starter_monthly',
  pro: 'pdt_pro_monthly'
};

function productIdForPlan(value: unknown): string | null {
  if (typeof value !== 'string') return null;
  return PRODUCT_IDS[value as PlanId] ?? null;
}

export async function POST(request: Request) {
  const user = await getCurrentUser();
  if (!user) {
    return NextResponse.json({ error: 'Not authenticated' }, { status: 401 });
  }

  let body: unknown;
  try {
    body = await request.json();
  } catch {
    return NextResponse.json({ error: 'Invalid request body' }, { status: 400 });
  }

  if (!body || typeof body !== 'object') {
    return NextResponse.json({ error: 'Invalid request body' }, { status: 400 });
  }

  const input = body as { planId?: unknown; quantity?: unknown };
  const productId = productIdForPlan(input.planId);
  const quantity = input.quantity ?? 1;

  if (!productId) {
    return NextResponse.json({ error: 'Unknown plan' }, { status: 400 });
  }

  if (!Number.isInteger(quantity) || (quantity as number) < 1) {
    return NextResponse.json(
      { error: 'Quantity must be a positive integer' },
      { status: 400 }
    );
  }

  try {
    const session = await client.checkoutSessions.create({
      product_cart: [{ product_id: productId, quantity: quantity as number }],
      customer: { email: user.email, name: user.name },
      metadata: { app_user_id: user.id },
      return_url: `${process.env.NEXT_PUBLIC_APP_URL}/checkout/success`
    });

    return NextResponse.json({
      checkoutUrl: session.checkout_url,
      sessionId: session.session_id
    });
  } catch (error) {
    console.error('Checkout error:', error);
    return NextResponse.json(
      { error: 'Failed to create checkout' },
      { status: 500 }
    );
  }
}

Client Component

// components/CheckoutButton.tsx
'use client';

import { useState } from 'react';

interface CheckoutButtonProps {
  planId: 'starter' | 'pro';

Use this skill in a project

Open any RiverX project, search the Skills panel for dodopayments/skills/checkout-integration, and install it in one click.

Use this skill