Checkout Handler
将 Dodo Payments 结账集成到您的 Remix 应用中。
Customer Portal
允许客户管理订阅和详细信息。
Webhooks
接收和处理 Dodo Payments webhook 事件。
安装
1
Install the package
在项目根目录中运行以下命令:
npm install @dodopayments/remix
2
Set up environment variables
在项目根目录下创建一个
.env 文件:DODO_PAYMENTS_API_KEY=your-api-key
DODO_PAYMENTS_WEBHOOK_SECRET=your-webhook-secret
DODO_PAYMENTS_RETURN_URL=https://yourdomain.com/checkout/success
DODO_PAYMENTS_ENVIRONMENT="test_mode" or "live_mode"
绝不要将您的
.env 文件或机密提交到版本控制。路由处理器示例
所有示例都假设您正在使用 Remix App Router。
- Checkout Handler
- Customer Portal Handler
- Webhook Handler
使用此处理器将 Dodo Payments 结账集成到您的 Remix 应用中。支持静态 (GET)、动态 (POST) 和会话 (POST) 支付流。
// app/routes/api.checkout.tsx
import { Checkout } from "@dodopayments/remix";
import type { LoaderFunctionArgs } from "@remix-run/node";
const checkoutGetHandler = Checkout({
bearerToken: process.env.DODO_PAYMENTS_API_KEY,
returnUrl: process.env.DODO_PAYMENTS_RETURN_URL,
environment: process.env.DODO_PAYMENTS_ENVIRONMENT,
type: "static"
});
const checkoutPostHandler = Checkout({
bearerToken: process.env.DODO_PAYMENTS_API_KEY,
returnUrl: process.env.DODO_PAYMENTS_RETURN_URL,
environment: process.env.DODO_PAYMENTS_ENVIRONMENT,
type: "dynamic", // for dynamic checkout
});
const checkoutSessionHandler = Checkout({
bearerToken: process.env.DODO_PAYMENTS_API_KEY,
returnUrl: process.env.DODO_PAYMENTS_RETURN_URL,
environment: process.env.DODO_PAYMENTS_ENVIRONMENT,
type: "session", // for checkout sessions
});
export const loader = ({ request }: LoaderFunctionArgs) => checkoutGetHandler(request);
export const action = ({ request }: LoaderFunctionArgs) => {
// You can conditionally route to different handlers based on your needs
// For checkout sessions, use checkoutSessionHandler(request)
return checkoutPostHandler(request);
};
curl --request GET \
--url 'https://example.com/api/checkout?productId=pdt_fqJhl7pxKWiLhwQR042rh' \
--header 'User-Agent: insomnia/11.2.0' \
--cookie mode=test
curl --request POST \
--url https://example.com/api/checkout \
--header 'Content-Type: application/json' \
--header 'User-Agent: insomnia/11.2.0' \
--cookie mode=test \
--data '{
"billing": {
"city": "Texas",
"country": "US",
"state": "Texas",
"street": "56, hhh",
"zipcode": "560000"
},
"customer": {
"email": "test@example.com",
"name": "test"
},
"metadata": {},
"payment_link": true,
"product_id": "pdt_QMDuvLkbVzCRWRQjLNcs",
"quantity": 1,
"billing_currency": "USD",
"discount_codes": ["IKHZ23M9GQ"],
"return_url": "https://example.com",
"trial_period_days": 10
}'
curl --request POST \
--url https://example.com/api/checkout \
--header 'Content-Type: application/json' \
--header 'User-Agent: insomnia/11.2.0' \
--cookie mode=test \
--data '{
"product_cart": [
{
"product_id": "pdt_QMDuvLkbVzCRWRQjLNcs",
"quantity": 1
}
],
"customer": {
"email": "test@example.com",
"name": "test"
},
"return_url": "https://example.com/success"
}'
使用此处理器允许客户通过 Dodo Payments 客户门户管理其订阅和详细信息。
// app/routes/api.customer-portal.tsx
import { CustomerPortal } from "@dodopayments/remix";
import type { LoaderFunctionArgs } from "@remix-run/node";
const customerPortalHandler = CustomerPortal({
bearerToken: process.env.DODO_PAYMENTS_API_KEY,
environment: process.env.DODO_PAYMENTS_ENVIRONMENT
});
export const loader = ({ request }: LoaderFunctionArgs) => customerPortalHandler(request);
curl --request GET \
--url 'https://example.com/api/customer-portal?customer_id=cus_9VuW4K7O3GHwasENg31m&send_email=true' \
--header 'User-Agent: insomnia/11.2.0' \
--cookie mode=test
使用此处理器在您的 Remix 应用中安全地接收和处理 Dodo Payments webhook 事件。
// app/routes/api.webhook.tsx
import { Webhooks } from "@dodopayments/remix";
import type { LoaderFunctionArgs } from "@remix-run/node";
const webhookHandler = Webhooks({
webhookKey: process.env.DODO_PAYMENTS_WEBHOOK_SECRET,
onPayload: async (payload) => {
//Handle Payload Here
console.log(payload)
}
});
export const action = ({ request }: LoaderFunctionArgs) => webhookHandler(request);
结账路由处理器
Dodo Payments 支持将支付集成到您的网站的三种支付流,这个适配器支持所有类型的支付流。
- 静态支付链接: 可即时共享的 URL,用于快速、无代码的支付收集。
- 动态支付链接: 通过 API 或 SDKs 以编程方式生成具有自定义详细信息的支付链接。
- 结账会话: 创建带有预配置产品购物车和客户详细信息的安全、可自定义的结账体验。
Static Checkout (GET)
Static Checkout (GET)
支持的查询参数
string
必填
产品标识符(例如,
?productId=pdt_nZuwz45WAs64n3l07zpQR)。integer
产品数量。
string
客户的全名。
string
客户的名字。
string
客户的姓氏。
string
客户的邮箱地址。
string
客户的国家。
string
客户的地址行。
string
客户的城市。
string
客户的州/省。
string
客户的邮政编码。
boolean
禁用全名字段。
boolean
禁用名字字段。
boolean
禁用姓氏字段。
boolean
禁用邮箱字段。
boolean
禁用国家字段。
boolean
禁用地址行字段。
boolean
禁用城市字段。
boolean
禁用州字段。
boolean
禁用邮政编码字段。
string
指定支付货币(例如,
USD)。boolean
显示货币选择器。
integer
指定支付金额(例如,
1000 表示 $10.00)。boolean
显示折扣字段。
string
任何以
metadata_ 开头的查询参数将作为元数据传递。如果缺少
productId,处理器会返回 400 响应。无效的查询参数也会导致 400 响应。响应格式
静态结账返回具有结账 URL 的 JSON 响应:{
"checkout_url": "https://checkout.dodopayments.com/..."
}
Dynamic Checkout (POST)
Dynamic Checkout (POST)
- 作为 POST 请求中的 JSON 主体发送参数。
- 支持一次性和定期支付。
- 有关支持的 POST 主体字段的完整列表,请参考:
响应格式
动态结账返回具有结账 URL 的 JSON 响应:{
"checkout_url": "https://checkout.dodopayments.com/..."
}
客户门户路由处理器
客户门户路由处理器使您可以无缝集成 Dodo Payments 客户门户到您的 Remix 应用中。查询参数
string
必填
门户会话的客户 ID(例如,
?customer_id=cus_123)。boolean
如果设置为
true,则会发送带有门户链接的电子邮件给客户。如果缺少
customer_id,则返回 400。Webhook 路由处理器
- 方法: 仅支持 POST 请求。其他方法返回 405。
- 签名验证: 使用
webhookKey验证 webhook 签名。如果验证失败,返回 401。 - 有效负载验证: 使用 Zod 验证。对于无效的负载,返回 400。
- 错误处理:
- 401:无效签名
- 400:无效负载
- 500:验证过程中的内部错误
- 事件路由: 根据有效负载类型调用相应的事件处理器。
支持的 Webhook 事件处理器
onPayload?: (payload: WebhookPayload) => Promise<void>;
onPaymentSucceeded?: (payload: WebhookPayload) => Promise<void>;
onPaymentFailed?: (payload: WebhookPayload) => Promise<void>;
onPaymentProcessing?: (payload: WebhookPayload) => Promise<void>;
onPaymentCancelled?: (payload: WebhookPayload) => Promise<void>;
onRefundSucceeded?: (payload: WebhookPayload) => Promise<void>;
onRefundFailed?: (payload: WebhookPayload) => Promise<void>;
onDisputeOpened?: (payload: WebhookPayload) => Promise<void>;
onDisputeExpired?: (payload: WebhookPayload) => Promise<void>;
onDisputeAccepted?: (payload: WebhookPayload) => Promise<void>;
onDisputeCancelled?: (payload: WebhookPayload) => Promise<void>;
onDisputeChallenged?: (payload: WebhookPayload) => Promise<void>;
onDisputeWon?: (payload: WebhookPayload) => Promise<void>;
onDisputeLost?: (payload: WebhookPayload) => Promise<void>;
onSubscriptionActive?: (payload: WebhookPayload) => Promise<void>;
onSubscriptionOnHold?: (payload: WebhookPayload) => Promise<void>;
onSubscriptionRenewed?: (payload: WebhookPayload) => Promise<void>;
onSubscriptionPlanChanged?: (payload: WebhookPayload) => Promise<void>;
onSubscriptionCancelled?: (payload: WebhookPayload) => Promise<void>;
onSubscriptionFailed?: (payload: WebhookPayload) => Promise<void>;
onSubscriptionExpired?: (payload: WebhookPayload) => Promise<void>;
onSubscriptionUpdated?: (payload: WebhookPayload) => Promise<void>;
onLicenseKeyCreated?: (payload: WebhookPayload) => Promise<void>;
onAbandonedCheckoutDetected?: (payload: WebhookPayload) => Promise<void>;
onAbandonedCheckoutRecovered?: (payload: WebhookPayload) => Promise<void>;
onDunningStarted?: (payload: WebhookPayload) => Promise<void>;
onDunningRecovered?: (payload: WebhookPayload) => Promise<void>;
onCreditAdded?: (payload: WebhookPayload) => Promise<void>;
onCreditDeducted?: (payload: WebhookPayload) => Promise<void>;
onCreditExpired?: (payload: WebhookPayload) => Promise<void>;
onCreditRolledOver?: (payload: WebhookPayload) => Promise<void>;
onCreditRolloverForfeited?: (payload: WebhookPayload) => Promise<void>;
onCreditOverageCharged?: (payload: WebhookPayload) => Promise<void>;
onCreditManualAdjustment?: (payload: WebhookPayload) => Promise<void>;
onCreditBalanceLow?: (payload: WebhookPayload) => Promise<void>;
LLM 提示
You are an expert Remix developer assistant. Your task is to guide a user through integrating the @dodopayments/remix adapter into their existing Remix project.
The @dodopayments/remix adapter provides route handlers for Dodo Payments' Checkout, Customer Portal, and Webhook functionalities, designed for Remix route handlers.
First, install the necessary packages. Use the package manager appropriate for your project (npm, yarn, or bun) based on the presence of lock files (e.g., package-lock.json for npm, yarn.lock for yarn, bun.lockb for bun):
npm install @dodopayments/remix
Here's how you should structure your response:
Ask the user which functionalities they want to integrate.
"Which parts of the @dodopayments/remix adapter would you like to integrate into your project? You can choose one or more of the following:
Checkout Route Handler (for handling product checkouts)
Customer Portal Route Handler (for managing customer subscriptions/details)
Webhook Route Handler (for receiving Dodo Payments webhook events)
All (integrate all three)"
Based on the user's selection, provide detailed integration steps for each chosen functionality.
If Checkout Route Handler is selected:
Purpose: This handler redirects users to the Dodo Payments checkout page.
File Creation: Create a new file at app/routes/api.checkout.tsx in your Remix project.
Code Snippet:
// app/routes/api.checkout.tsx
import { Checkout } from "@dodopayments/remix";
import type { LoaderFunctionArgs } from "@remix-run/node";
const checkoutGetHandler = Checkout({
bearerToken: process.env.DODO_PAYMENTS_API_KEY,
returnUrl: process.env.DODO_PAYMENTS_RETURN_URL,
environment: process.env.DODO_PAYMENTS_ENVIRONMENT,
type: "static",
});
const checkoutPostHandler = Checkout({
bearerToken: process.env.DODO_PAYMENTS_API_KEY,
returnUrl: process.env.DODO_PAYMENTS_RETURN_URL,
environment: process.env.DODO_PAYMENTS_ENVIRONMENT,
type: "dynamic", // for dynamic checkout
});
const checkoutSessionHandler = Checkout({
bearerToken: process.env.DODO_PAYMENTS_API_KEY,
returnUrl: process.env.DODO_PAYMENTS_RETURN_URL,
environment: process.env.DODO_PAYMENTS_ENVIRONMENT,
type: "session", // for checkout sessions
});
export const loader = ({ request }: LoaderFunctionArgs) => checkoutGetHandler(request);
export const action = ({ request }: LoaderFunctionArgs) => {
// You can conditionally route to different handlers based on your needs
// For checkout sessions, use checkoutSessionHandler(request)
return checkoutPostHandler(request);
};
If Customer Portal Route Handler is selected:
Purpose: This handler redirects authenticated users to their Dodo Payments customer portal.
File Creation: Create a new file at app/routes/api.customer-portal.tsx in your Remix project.
Code Snippet:
// app/routes/api.customer-portal.tsx
import { CustomerPortal } from "@dodopayments/remix";
import type { LoaderFunctionArgs } from "@remix-run/node";
const customerPortalHandler = CustomerPortal({
bearerToken: process.env.DODO_PAYMENTS_API_KEY,
environment: process.env.DODO_PAYMENTS_ENVIRONMENT
});
export const loader = ({ request }: LoaderFunctionArgs) => customerPortalHandler(request);
Query Parameters:
customer_id (required): The customer ID for the portal session (e.g., ?customer_id=cus_123)
send_email (optional, boolean): If set to true, sends an email to the customer with the portal link.
Returns 400 if customer_id is missing.
If Webhook Route Handler is selected:
Purpose: This handler processes incoming webhook events from Dodo Payments, allowing your application to react to events like successful payments, refunds, or subscription changes.
File Creation: Create a new file at app/routes/api.webhook.tsx in your Remix project.
Code Snippet:
// app/routes/api.webhook.tsx
import { Webhooks } from "@dodopayments/remix";
import type { LoaderFunctionArgs } from "@remix-run/node";
const webhookHandler = Webhooks({
webhookKey: process.env.DODO_PAYMENTS_WEBHOOK_SECRET,
onPayload: async (payload) => {
//Handle Payload Here
console.log(payload)
}
});
export const action = ({ request }: LoaderFunctionArgs) => webhookHandler(request);
Handler Details:
Method: Only POST requests are supported. Other methods return 405.
Signature Verification: The handler verifies the webhook signature using the webhookKey and returns 401 if verification fails.
Payload Validation: The payload is validated with Zod. Returns 400 for invalid payloads.
Error Handling:
401: Invalid signature
400: Invalid payload
500: Internal error during verification
Event Routing: Calls the appropriate event handler based on the payload type.
Supported Webhook Event Handlers:
onPayload?: (payload: WebhookPayload) => Promise<void>
onPaymentSucceeded?: (payload: WebhookPayload) => Promise<void>
onPaymentFailed?: (payload: WebhookPayload) => Promise<void>
onPaymentProcessing?: (payload: WebhookPayload) => Promise<void>
onPaymentCancelled?: (payload: WebhookPayload) => Promise<void>
onRefundSucceeded?: (payload: WebhookPayload) => Promise<void>
onRefundFailed?: (payload: WebhookPayload) => Promise<void>
onDisputeOpened?: (payload: WebhookPayload) => Promise<void>
onDisputeExpired?: (payload: WebhookPayload) => Promise<void>
onDisputeAccepted?: (payload: WebhookPayload) => Promise<void>
onDisputeCancelled?: (payload: WebhookPayload) => Promise<void>
onDisputeChallenged?: (payload: WebhookPayload) => Promise<void>
onDisputeWon?: (payload: WebhookPayload) => Promise<void>
onDisputeLost?: (payload: WebhookPayload) => Promise<void>
onSubscriptionActive?: (payload: WebhookPayload) => Promise<void>
onSubscriptionOnHold?: (payload: WebhookPayload) => Promise<void>
onSubscriptionRenewed?: (payload: WebhookPayload) => Promise<void>
onSubscriptionPlanChanged?: (payload: WebhookPayload) => Promise<void>
onSubscriptionCancelled?: (payload: WebhookPayload) => Promise<void>
onSubscriptionFailed?: (payload: WebhookPayload) => Promise<void>
onSubscriptionExpired?: (payload: WebhookPayload) => Promise<void>
onSubscriptionUpdated?: (payload: WebhookPayload) => Promise<void>
onLicenseKeyCreated?: (payload: WebhookPayload) => Promise<void>
onAbandonedCheckoutDetected?: (payload: WebhookPayload) => Promise<void>
onAbandonedCheckoutRecovered?: (payload: WebhookPayload) => Promise<void>
onDunningStarted?: (payload: WebhookPayload) => Promise<void>
onDunningRecovered?: (payload: WebhookPayload) => Promise<void>
onCreditAdded?: (payload: WebhookPayload) => Promise<void>
onCreditDeducted?: (payload: WebhookPayload) => Promise<void>
onCreditExpired?: (payload: WebhookPayload) => Promise<void>
onCreditRolledOver?: (payload: WebhookPayload) => Promise<void>
onCreditRolloverForfeited?: (payload: WebhookPayload) => Promise<void>
onCreditOverageCharged?: (payload: WebhookPayload) => Promise<void>
onCreditManualAdjustment?: (payload: WebhookPayload) => Promise<void>
onCreditBalanceLow?: (payload: WebhookPayload) => Promise<void>
Environment Variable Setup:
To ensure the adapter functions correctly, you will need to manually set up the following environment variables in your Remix project's deployment environment (e.g., Vercel, Netlify, AWS, etc.):
DODO_PAYMENTS_API_KEY: Your Dodo Payments API Key (required for Checkout and Customer Portal).
DODO_PAYMENTS_RETURN_URL: (Optional) The URL to redirect to after a successful checkout (for Checkout handler).
DODO_PAYMENTS_ENVIRONMENT: (Optional) The environment for the API (e.g., test or live).
DODO_PAYMENTS_WEBHOOK_SECRET: Your Dodo Payments Webhook Secret (required for Webhook handler).
Example .env file:
DODO_PAYMENTS_API_KEY=your-api-key
DODO_PAYMENTS_RETURN_URL=your-return-url
DODO_PAYMENTS_ENVIRONMENT=test
DODO_PAYMENTS_WEBHOOK_SECRET=your-webhook-secret
Usage in your code:
bearerToken: process.env.DODO_PAYMENTS_API_KEY
webhookKey: process.env.DODO_PAYMENTS_WEBHOOK_SECRET
Important: Never commit sensitive environment variables directly into your version control. Use environment variables for all sensitive information.
If the user needs assistance setting up environment variables for their specific deployment environment, ask them what platform they are using (e.g., Vercel, Netlify, AWS, etc.), and provide guidance. You can also add comments to their PR or chat depending on the context