checkout-integration — Claude Code skill: Dodo Payments checkout
A skill that walks your coding agent through Dodo Payments checkout: hosted sessions, payment links, overlay and inline modes, subscriptions with trials.
npx skills add dodopayments/skills --skill checkout-integration -a claude-codeWhat checkout-integration does
Three ways to take money through Dodo Payments are covered. The main one is Checkout Sessions: a server-side client.checkoutSessions.create(...) call hands back a checkout_url to send the buyer to. Static payment links such as https://checkout.dodopayments.com/buy/{productid} need no server code at all. The third is an overlay or inline checkout that stays on your own page.
Recipes inside cover a single charge, a cart with several products, an existing customer, a billing address with tax_id, discount codes (up to 20), a subscription with trial_period_days, custom fields and metadata. There are worked examples for Next.js App Router, Express.js and Python (FastAPI), plus a separate overlay guide.
Amounts go in the smallest currency unit, so 10 USD is 1000. A session lasts 24 hours, or 15 minutes when confirm=true. Treat return_url as a UI hint, not proof of payment — grant access only after the webhook fires, because a spoofed redirect otherwise hands over the product for free. You need a DODO_PAYMENTS_API_KEY and an environment set to test_mode or live_mode.
What it helps with
- build a checkout page for a one-time charge
- add a subscription with a trial period
- let buyers apply discount codes at checkout
- collect custom fields like company size
- send the buyer back to a success page
Example prompts for the agent
- Build a pay button with Checkout Sessions for product
pdt_example - Add a subscription with
trial_period_days: 14and a success redirect - Show me how to embed the overlay checkout on my site
How to install checkout-integration
1Skills CLI (Vercel)
npx skills add dodopayments/skills --skill checkout-integration -a claude-code2Manually: put the files in place
~/.claude/skills/checkout-integration/SKILL.mdCopy the whole skill folder; for one project use .claude/skills/checkout-integration/
1Skills CLI (Vercel)
npx skills add dodopayments/skills --skill checkout-integration -a codex2Manually: put the files in place
.agents/skills/checkout-integration/SKILL.mdFor all projects use ~/.agents/skills/
1Skills CLI (Vercel)
npx skills add dodopayments/skills --skill checkout-integration -a cursor2Manually: put the files in place
.cursor/skills/checkout-integration/SKILL.mdCursor also reads .agents/skills/ and .claude/skills/
1GitHub CLI
gh skill install dodopayments/skills checkout-integration2Manually: put the files in place
.github/skills/checkout-integration/SKILL.md1Skills CLI (Vercel)
npx skills add dodopayments/skills --skill checkout-integration -a gemini-cli2Platform CLI
gemini skills install https://github.com/dodopayments/skills --path dodo-payments/checkout-integrationManually: put the files in place
.devin/skills/checkout-integration/SKILL.mdThe legacy .windsurf/skills/ path still works
Skills CLI (Vercel)
npx skills add dodopayments/skills --skill checkout-integration -a opencodeClaude.ai
Zip the folder and upload it in Settings → Capabilities → Skills
Step by step, with commands for each platform: How to install skills in Claude Code: five ways
Questions about checkout-integration
How do I install checkout-integration in Claude Code?
Run npx skills add dodopayments/skills --skill checkout-integration -a claude-code. The skill file lands at ~/.claude/skills/checkout-integration/SKILL.md.
What does checkout-integration do?
It gives your agent the patterns for Dodo Payments checkout: hosted sessions, static links, overlay and inline modes, trial subscriptions, discounts and custom fields.
When is a payment actually confirmed?
Not by the redirect to return_url. That browser hop is a UI hint only; access should be granted after the webhook is verified.
Source text
SKILL.md · MIT
---
name: checkout-integration
description: 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 return_url redirects; use subscription-integration for post-checkout lifecycle.
---
# 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:
| Method | Best For | Setup |
|--------|----------|-------|
| **Checkout Sessions** (recommended) | Most integrations; full control | Server-side SDK call |
| **Static Payment Links** | No-code sharing; reusable URLs | Dashboard or direct URL |
| **Overlay/Inline Checkout** | Checkout stays on your site | Client-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
```typescript
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: '[email protected]',
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
```typescript
const session = await client.checkoutSessions.create({
product_cart: [
{ product_id: 'pdt_item_1', quantity: 2 },
{ product_id: 'pdt_item_2', quantity: 1 }
],
customer: { email: '[email protected]' },
return_url: 'https://yoursite.com/success'
});
```
### With Existing Customer
```typescript
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
```typescript
const session = await client.checkoutSessions.create({
product_cart: [
{ product_id: 'pdt_example', quantity: 1 }
],
customer: {
email: '[email protected]',
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
```typescript
const session = await client.checkoutSessions.create({
product_cart: [
{ product_id: 'pdt_example', quantity: 1 }
],
customer: { email: '[email protected]' },
discount_codes: ['PROMO10', 'WELCOME5'], // max 20, ordered
feature_flags: {
allow_discount_code: true
},
return_url: 'https://yoursite.com/success'
});
```
### With Subscription and Trial
```typescript
const session = await client.checkoutSessions.create({
product_cart: [
{ product_id: 'pdt_monthly_subscription', quantity: 1 }
],
subscription_data: {
trial_period_days: 14
},
customer: { email: '[email protected]' },
return_url: 'https://yoursite.com/success'
});
```
### With Custom Fields
```typescript
const session = await client.checkoutSessions.create({
product_cart: [
{ product_id: 'pdt_example', quantity: 1 }
],
customer: { email: '[email protected]' },
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
```typescript
const session = await client.checkoutSessions.create({
product_cart: [
{ product_id: 'pdt_example', quantity: 1 }
],
customer: { email: '[email protected]' },
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](references/server-examples.md).
Covers:
- API Route
- Client Component
- Success Page
## Express.js
Full guide: [references/server-examples.md](references/server-examples.md).
## Python (FastAPI)
Full guide: [references/server-examples.md](references/server-examples.md).
## Overlay Checkout
Full guide: [references/overlay-checkout.md](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
```text
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:
```text
https://checkout.dodopayments.com/buy/pdt_example
```
### With Query Parameters
```text
https://checkout.dodopayments.com/buy/pdt_example?quantity=2&[email protected]&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
```typescript
const status = await client.checkoutSessions.retrieve('cks_session_id');
console.log(status.id);
console.log(status.payment_status);
```
### Preview Session (without creating)
```typescript
const preview = await client.checkoutSessions.preview({
product_cart: [
{ product_id: 'pdt_example', quantity: 1 }
],
customer: { email: '[email protected]' },
billing_currency: 'EUR'
});
console.log('Preview total:', preview.current_breakup.total_amount);
```
---
## Customization
### Theme and Appearance
```typescript
const session = await client.checkoutSessions.create({
product_cart: [{ product_id: 'pdt_example', quantity: 1 }],
customer: { email: '[email protected]' },
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
```typescript
const session = await client.checkoutSessions.create({
product_cart: [{ product_id: 'pdt_example', quantity: 1 }],
customer: { email: '[email protected]' },
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:
```text
# 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](references/server-examples.md#expressjs): 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
- [Checkout Sessions Integration Guide](https://docs.dodopayments.com/developer-resources/integration-guide)
- [Checkout Sessions API Reference](https://docs.dodopayments.com/api-reference/checkout-sessions/create)
- [Overlay Checkout](https://docs.dodopayments.com/developer-resources/overlay-checkout)
- [Inline Checkout](https://docs.dodopayments.com/developer-resources/inline-checkout)
- [Static Payment Links](https://docs.dodopayments.com/developer-resources/integration-guide)
- [Webhook Integration](https://docs.dodopayments.com/developer-resources/webhooks)
© dodopayments · MIT · dodopayments/skills