跳转到主要内容

概述

内嵌结账让您创建与您的网站或应用程序无缝集成的结账体验。与 覆盖结账 不同,后者在您的页面上以模态窗口的形式打开,内嵌结账将支付表单直接嵌入到您的页面布局中。 使用内嵌结账,您可以:
  • 创建与您的应用或网站完全集成的结账体验
  • 让 Dodo Payments 安全地捕获客户和支付信息,提供优化的结账框架
  • 在您的页面上显示来自 Dodo Payments 的商品、总额和其他信息
  • 使用 SDK 方法和事件构建高级结账体验
内嵌结账封面图

工作原理

内嵌结账通过将安全的 Dodo Payments 框架嵌入到您的网站或应用中来工作。 结账框架处理收集客户信息和捕获支付细节。您的页面显示商品列表、总额和更改结账内容的选项。SDK 允许您的页面与结账框架相互交互。 Dodo Payments 在结账完成时自动创建订阅,准备供您配置。
内嵌结账框架安全地处理所有敏感支付信息,确保 PCI 合规,而无需您额外认证。

什么是好的内嵌结账?

客户需要知道他们从谁那里购买、购买了什么以及支付了多少。 要构建一个合规且优化转化的内嵌结账,您的实现必须包括:
带有标记的所需元素的内嵌结账示例

示例内嵌结账布局,显示所需元素

  1. 定期信息:如果是定期的,说明其频率和续订时的总额。如果是试用,说明试用期的时长。
  2. 商品描述:对所购买商品的描述。
  3. 交易总额:交易总额,包括小计、总税和总计。确保也包括货币。
  4. Dodo Payments 页脚:完整的内嵌结账框架,包括有关 Dodo Payments 的信息、我们的销售条款和隐私政策的结账页脚。
  5. 退款政策:如果您的退款政策与 Dodo Payments 的标准退款政策不同,请提供退款政策的链接。
始终显示完整的内嵌结账框架,包括页脚。删除或隐藏法律信息会违反合规要求。

客户旅程

结账流程由您的结账会话配置决定。根据您如何配置结账会话,客户将体验到可能在单个页面或多个步骤中呈现所有信息的结账。
1

客户打开结账

您可以通过传递商品或现有交易来打开内联结账。使用 SDK 显示和更新页面信息,并使用 SDK 方法根据客户交互更新商品。初始结账页面,包含商品列表和支付表单
2

客户输入他们的详细信息

内嵌结账首先要求客户输入他们的电子邮件地址、选择他们的国家,并(如有必要)输入他们的邮政编码。此步骤收集所有必要的信息以确定税费和可用的支付选项。您可以预填客户详细信息并提供已保存的地址,以简化体验。
3

客户选择支付方式

在输入详细信息后,客户将看到可用的支付方式和支付表单。选项可能包括信用卡或借记卡、PayPal、Apple Pay、Google Pay,以及根据他们的位置提供的其他本地支付方式。如果有可用的已保存支付方式,请显示以加快结账速度。可用的支付方式和卡片详细信息表单
4

结账完成

Dodo Payments 将每笔支付路由到最佳收单方,以获得最佳的成功机会。客户进入您可以构建的成功工作流。成功屏幕,带有确认勾选标记
5

Dodo Payments 创建订阅

Dodo Payments 自动为客户创建订阅,准备供您配置。客户使用的支付方式将被保留以供续订或订阅更改。创建的订阅,带有 webhook 通知

快速开始

只需几行代码即可开始使用 Dodo Payments 内联结账:
import { DodoPayments } from "dodopayments-checkout";

// Initialize the SDK for inline mode
DodoPayments.Initialize({
  mode: "test",
  displayType: "inline",
  onEvent: (event) => {
    console.log("Checkout event:", event);
  },
});

// Open checkout in a specific container
DodoPayments.Checkout.open({
  checkoutUrl: "https://test.dodopayments.com/session/cks_123",
  elementId: "dodo-inline-checkout" // ID of the container element
});
确保您的页面上有一个包含相应 id 的容器元素: <div id="dodo-inline-checkout"></div>

分步集成指南

1

安装 SDK

安装 Dodo Payments 结账 SDK:
npm install dodopayments-checkout
2

初始化 SDK 以进行内联显示

初始化 SDK 并指定 displayType: 'inline'。您还应该监听 checkout.breakdown 事件,以实时更新您的 UI 以进行税费和总额计算。
import { DodoPayments } from "dodopayments-checkout";

DodoPayments.Initialize({
  mode: "test",
  displayType: "inline",
  onEvent: (event) => {
    if (event.event_type === "checkout.breakdown") {
      const breakdown = event.data?.message;
      // Update your UI with breakdown.subTotal, breakdown.tax, breakdown.total, etc.
    }
  },
});
3

创建容器元素

在您的 HTML 中添加一个元素,以便结账框架将被注入:
<div id="dodo-inline-checkout"></div>
4

打开结账

使用 DodoPayments.Checkout.open() 调用 checkoutUrl 和您的容器的 elementId
DodoPayments.Checkout.open({
  checkoutUrl: "https://test.dodopayments.com/session/cks_123",
  elementId: "dodo-inline-checkout"
});
5

测试您的集成

  1. 启动您的开发服务器:
npm run dev
  1. 测试结账流程:
    • 在内联框架中输入您的电子邮件和地址详细信息。
    • 验证您的自定义订单摘要是否实时更新。
    • 使用测试凭据测试支付流程。
    • 确认重定向是否正常工作。
如果您在 onEvent 回调中添加了控制台日志,您应该在浏览器控制台中看到 checkout.breakdown 事件的记录。
6

上线

当您准备好进行生产时:
  1. 将模式更改为 'live'
DodoPayments.Initialize({
  mode: "live",
  displayType: "inline",
  onEvent: (event) => {
    // Handle events
  }
});
  1. 更新您的结账 URL,以使用来自后端的实时结账会话。
  2. 在生产环境中测试完整流程。

完整的 React 示例

此示例演示如何在内联结账旁边实现自定义订单摘要,并使用 checkout.breakdown 事件保持它们同步。
"use client";

import { useEffect, useState } from 'react';
import { DodoPayments, CheckoutBreakdownData } from 'dodopayments-checkout';

export default function CheckoutPage() {
  const [breakdown, setBreakdown] = useState<Partial<CheckoutBreakdownData>>({});

  useEffect(() => {
    // 1. Initialize the SDK
    DodoPayments.Initialize({
      mode: 'test',
      displayType: 'inline',
      onEvent: (event) => {
        // 2. Listen for the 'checkout.breakdown' event
        if (event.event_type === "checkout.breakdown") {
          const message = event.data?.message as CheckoutBreakdownData;
          if (message) setBreakdown(message);
        }
      }
    });

    // 3. Open the checkout in the specified container
    DodoPayments.Checkout.open({
      checkoutUrl: 'https://test.dodopayments.com/session/cks_123',
      elementId: 'dodo-inline-checkout'
    });

    return () => DodoPayments.Checkout.close();
  }, []);

  const format = (amt: number | null | undefined, curr: string | null | undefined) => 
    amt != null && curr ? `${curr} ${(amt/100).toFixed(2)}` : '0.00';

  const currency = breakdown.currency ?? breakdown.finalTotalCurrency ?? '';

  return (
    <div className="flex flex-col md:flex-row min-h-screen">
      {/* Left Side - Checkout Form */}
      <div className="w-full md:w-1/2 flex items-center">
        <div id="dodo-inline-checkout" className='w-full' />
      </div>

      {/* Right Side - Custom Order Summary */}
      <div className="w-full md:w-1/2 p-8 bg-gray-50">
        <h2 className="text-2xl font-bold mb-4">Order Summary</h2>
        <div className="space-y-2">
          {breakdown.subTotal && (
            <div className="flex justify-between">
              <span>Subtotal</span>
              <span>{format(breakdown.subTotal, currency)}</span>
            </div>
          )}
          {breakdown.discount && (
            <div className="flex justify-between">
              <span>Discount</span>
              <span>{format(breakdown.discount, currency)}</span>
            </div>
          )}
          {breakdown.tax != null && (
            <div className="flex justify-between">
              <span>Tax</span>
              <span>{format(breakdown.tax, currency)}</span>
            </div>
          )}
          <hr />
          {(breakdown.finalTotal ?? breakdown.total) && (
            <div className="flex justify-between font-bold text-xl">
              <span>Total</span>
              <span>{format(breakdown.finalTotal ?? breakdown.total, breakdown.finalTotalCurrency ?? currency)}</span>
            </div>
          )}
        </div>
      </div>
    </div>
  );
}

API 参考

配置

初始化选项

interface InitializeOptions {
  mode: "test" | "live";
  displayType: "inline"; // Required for inline checkout
  onEvent: (event: CheckoutEvent) => void;
}
选项类型必需描述
mode"test" | "live"环境模式。
displayType"inline" | "overlay"必须设置为 "inline" 以嵌入结账。
onEventfunction处理结账事件的回调函数。

结账选项

interface CheckoutOptions {
  checkoutUrl: string;
  elementId: string; // Required for inline checkout
  options?: {
    showTimer?: boolean;
    showSecurityBadge?: boolean;
    manualRedirect?: boolean;
    payButtonText?: string;
  };
}
选项类型必需描述
checkoutUrlstring结账会话 URL。
elementIdstring结账应呈现的 DOM 元素的 id
options.showTimerboolean显示或隐藏结账计时器。默认为 true。禁用时,您将在会话过期时收到 checkout.link_expired 事件。
options.showSecurityBadgeboolean显示或隐藏安全徽章。默认为 true
options.manualRedirectboolean启用时,结账完成后不会自动重定向。相反,您将收到 checkout.statuscheckout.redirect_requested 事件,以便您自行处理重定向。
options.payButtonTextstring在支付按钮上显示的自定义文本。

方法

打开结账

在指定的容器中打开结账框架。
DodoPayments.Checkout.open({
  checkoutUrl: "https://test.dodopayments.com/session/cks_123",
  elementId: "dodo-inline-checkout"
});
您还可以传递其他选项以自定义结账行为:
DodoPayments.Checkout.open({
  checkoutUrl: "https://test.dodopayments.com/session/cks_123",
  elementId: "dodo-inline-checkout",
  options: {
    showTimer: false,
    showSecurityBadge: false,
    manualRedirect: true,
    payButtonText: "Pay Now",
  },
});
使用 manualRedirect 时,在您的 onEvent 回调中处理结账完成:
DodoPayments.Initialize({
  mode: "test",
  displayType: "inline",
  onEvent: (event) => {
    if (event.event_type === "checkout.status") {
      const status = event.data?.message?.status;
      // Handle status: "succeeded", "failed", or "processing"
    }
    if (event.event_type === "checkout.redirect_requested") {
      const redirectUrl = event.data?.message?.redirect_to;
      // Redirect the customer manually
      window.location.href = redirectUrl;
    }
    if (event.event_type === "checkout.link_expired") {
      // Handle expired checkout session
    }
  },
});

关闭结账

以编程方式移除结账框架并清理事件监听器。
DodoPayments.Checkout.close();

检查状态

返回结账框架当前是否已注入。
const isOpen = DodoPayments.Checkout.isOpen();
// Returns: boolean

事件

SDK 通过 onEvent 回调提供实时事件。对于内联结账,checkout.breakdown 对于同步您的 UI 特别有用。
事件类型描述
checkout.opened结账框架已加载。
checkout.breakdown当价格、税费或折扣更新时触发。
checkout.customer_details_submitted客户详细信息已提交。
checkout.redirect结账将执行重定向(例如,转到银行页面)。
checkout.error结账过程中发生错误。
checkout.link_expired当结账会话过期时触发。仅在 showTimer 设置为 false 时接收。
checkout.statusmanualRedirect 启用时触发。包含结账状态(succeededfailedprocessing)。
checkout.redirect_requestedmanualRedirect 启用时触发。包含重定向客户的 URL。

结账明细数据

checkout.breakdown 事件提供以下数据:
interface CheckoutBreakdownData {
  subTotal?: number;          // Amount in cents
  discount?: number;         // Amount in cents
  tax?: number;              // Amount in cents
  total?: number;            // Amount in cents
  currency?: string;         // e.g., "USD"
  finalTotal?: number;       // Final amount including adjustments
  finalTotalCurrency?: string; // Currency for the final total
}

结账状态事件数据

manualRedirect 启用时,您将收到 checkout.status 事件,包含以下数据:
interface CheckoutStatusEventData {
  message: {
    status?: "succeeded" | "failed" | "processing";
  };
}

结账重定向请求事件数据

manualRedirect 启用时,您将收到 checkout.redirect_requested 事件,包含以下数据:
interface CheckoutRedirectRequestedEventData {
  message: {
    redirect_to?: string;
  };
}

理解明细事件

checkout.breakdown 事件是保持您的应用程序 UI 与 Dodo Payments 结账状态同步的主要方式。 触发时机:
  • 初始化时:结账框架加载并准备好后立即。
  • 地址更改时:每当客户选择一个国家或输入一个导致税费重新计算的邮政编码时。
字段详细信息:
字段描述
subTotal会话中所有行项目的总和,在应用任何折扣或税费之前。
discount所有应用折扣的总值。
tax计算的税额。在 inline 模式下,用户与地址字段交互时动态更新。
totalsubTotal - discount + tax 在会话的基本货币中的数学结果。
currency标准小计、折扣和税值的 ISO 货币代码(例如,"USD")。
finalTotal客户实际支付的金额。这可能包括额外的外汇调整或不属于基本价格明细的本地支付方式费用。
finalTotalCurrency客户实际支付的货币。如果购买力平价或本地货币转换处于活动状态,则可能与 currency 不同。
关键集成提示:
  1. 货币格式化:价格始终以最小货币单位的整数形式返回(例如,美元的分,日元的日元)。要显示它们,请除以 100(或适当的 10 的幂)或使用格式化库,如 Intl.NumberFormat
  2. 处理初始状态:当结账首次加载时,taxdiscount 可能是 0null,直到用户提供其账单信息或应用代码。您的 UI 应该优雅地处理这些状态(例如,显示一个破折号 或隐藏该行)。
  3. “最终总计”与“总计”:虽然 total 为您提供标准价格计算,finalTotal 是交易的真实来源。如果 finalTotal 存在,它准确反映将向客户的卡收取的费用,包括任何动态调整。
  4. 实时反馈:使用 tax 字段向用户显示税费正在实时计算。这为您的结账页面提供了“实时”感觉,并减少了地址输入步骤中的摩擦。

实施选项

包管理器安装

通过 npm、yarn 或 pnpm 安装,如 逐步集成指南 所示。

CDN 实施

为了快速集成而无需构建步骤,您可以使用我们的 CDN:
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Dodo Payments Inline Checkout</title>
    
    <!-- Load DodoPayments -->
    <script src="https://cdn.jsdelivr.net/npm/dodopayments-checkout@latest/dist/index.js"></script>
    <script>
        // Initialize the SDK
        DodoPaymentsCheckout.DodoPayments.Initialize({
            mode: "test",
            displayType: "inline",
            onEvent: (event) => {
                console.log('Checkout event:', event);
            }
        });
    </script>
</head>
<body>
    <div id="dodo-inline-checkout"></div>

    <script>
        // Open the checkout
        DodoPaymentsCheckout.DodoPayments.Checkout.open({
            checkoutUrl: "https://test.dodopayments.com/session/cks_123",
            elementId: "dodo-inline-checkout"
        });
    </script>
</body>
</html>

主题自定义

您可以通过在打开结账时将一个 themeConfig 对象传递给 options 参数来自定义结账外观。主题配置支持浅色和深色模式,允许您自定义颜色、边框、文本、按钮和边框半径。

基本主题配置

DodoPayments.Checkout.open({
  checkoutUrl: "https://checkout.dodopayments.com/session/cks_123",
  options: {
    themeConfig: {
      light: {
        bgPrimary: "#FFFFFF",
        textPrimary: "#344054",
        buttonPrimary: "#A6E500",
      },
      dark: {
        bgPrimary: "#0D0D0D",
        textPrimary: "#FFFFFF",
        buttonPrimary: "#A6E500",
      },
      radius: "8px",
    },
  },
});

完整主题配置

所有可用的主题属性:
DodoPayments.Checkout.open({
  checkoutUrl: "https://checkout.dodopayments.com/session/cks_123",
  options: {
    themeConfig: {
      light: {
        // Background colors
        bgPrimary: "#FFFFFF",        // Primary background color
        bgSecondary: "#F9FAFB",      // Secondary background color (e.g., tabs)
        
        // Border colors
        borderPrimary: "#D0D5DD",     // Primary border color
        borderSecondary: "#6B7280",  // Secondary border color
        inputFocusBorder: "#D0D5DD", // Input focus border color
        
        // Text colors
        textPrimary: "#344054",       // Primary text color
        textSecondary: "#6B7280",    // Secondary text color
        textPlaceholder: "#667085",  // Placeholder text color
        textError: "#D92D20",        // Error text color
        textSuccess: "#10B981",      // Success text color
        
        // Button colors
        buttonPrimary: "#A6E500",           // Primary button background
        buttonPrimaryHover: "#8CC500",      // Primary button hover state
        buttonTextPrimary: "#0D0D0D",       // Primary button text color
        buttonSecondary: "#F3F4F6",         // Secondary button background
        buttonSecondaryHover: "#E5E7EB",     // Secondary button hover state
        buttonTextSecondary: "#344054",     // Secondary button text color
      },
      dark: {
        // Background colors
        bgPrimary: "#0D0D0D",
        bgSecondary: "#1A1A1A",
        
        // Border colors
        borderPrimary: "#323232",
        borderSecondary: "#D1D5DB",
        inputFocusBorder: "#323232",
        
        // Text colors
        textPrimary: "#FFFFFF",
        textSecondary: "#909090",
        textPlaceholder: "#9CA3AF",
        textError: "#F97066",
        textSuccess: "#34D399",
        
        // Button colors
        buttonPrimary: "#A6E500",
        buttonPrimaryHover: "#8CC500",
        buttonTextPrimary: "#0D0D0D",
        buttonSecondary: "#2A2A2A",
        buttonSecondaryHover: "#3A3A3A",
        buttonTextSecondary: "#FFFFFF",
      },
      radius: "8px", // Border radius for inputs, buttons, and tabs
    },
  },
});

仅限浅色模式

如果您只想自定义浅色主题:
DodoPayments.Checkout.open({
  checkoutUrl: "https://checkout.dodopayments.com/session/cks_123",
  options: {
    themeConfig: {
      light: {
        bgPrimary: "#FFFFFF",
        textPrimary: "#000000",
        buttonPrimary: "#0070F3",
      },
      radius: "12px",
    },
  },
});

仅限深色模式

如果您只想自定义深色主题:
DodoPayments.Checkout.open({
  checkoutUrl: "https://checkout.dodopayments.com/session/cks_123",
  options: {
    themeConfig: {
      dark: {
        bgPrimary: "#000000",
        textPrimary: "#FFFFFF",
        buttonPrimary: "#0070F3",
      },
      radius: "12px",
    },
  },
});

部分主题覆盖

您可以仅覆盖特定属性。结账将对您未指定的属性使用默认值:
DodoPayments.Checkout.open({
  checkoutUrl: "https://checkout.dodopayments.com/session/cks_123",
  options: {
    themeConfig: {
      light: {
        buttonPrimary: "#FF6B6B", // Only override primary button color
      },
      radius: "16px", // Override border radius
    },
  },
});

结合其他选项的主题配置

您可以将主题配置与其他结账选项结合使用:
DodoPayments.Checkout.open({
  checkoutUrl: "https://checkout.dodopayments.com/session/cks_123",
  options: {
    showTimer: true,
    showSecurityBadge: true,
    manualRedirect: false,
    themeConfig: {
      light: {
        bgPrimary: "#FFFFFF",
        buttonPrimary: "#A6E500",
      },
      dark: {
        bgPrimary: "#0D0D0D",
        buttonPrimary: "#A6E500",
      },
      radius: "8px",
    },
  },
});

TypeScript 类型

对于 TypeScript 用户,所有主题配置类型均已导出:
import { ThemeConfig, ThemeModeConfig } from "dodopayments-checkout";

const themeConfig: ThemeConfig = {
  light: {
    bgPrimary: "#FFFFFF",
    // ... other properties
  },
  dark: {
    bgPrimary: "#0D0D0D",
    // ... other properties
  },
  radius: "8px",
};

错误处理

SDK 通过事件系统提供详细的错误信息。始终在您的 onEvent 回调中实现适当的错误处理:
DodoPayments.Initialize({
  mode: "test",
  displayType: "inline",
  onEvent: (event: CheckoutEvent) => {
    if (event.event_type === "checkout.error") {
      console.error("Checkout error:", event.data?.message);
      // Handle error appropriately
    }
  }
});
始终处理 checkout.error 事件,以在出现问题时提供良好的用户体验。

最佳实践

  1. 响应式设计:确保您的容器元素具有足够的宽度和高度。iframe 通常会扩展以填充其容器。
  2. 同步:使用 checkout.breakdown 事件保持您的自定义订单摘要或定价表与用户在结账框中看到的内容同步。
  3. 骨架状态:在 checkout.opened 事件触发之前,在您的容器中显示加载指示器。
  4. 清理:当您的组件卸载时调用 DodoPayments.Checkout.close() 以清理 iframe 和事件监听器。
对于深色模式实现,建议使用 #0d0d0d 作为背景色,以便与内联结账框进行最佳视觉集成。

故障排除

  • 验证 elementId 是否与实际存在于 DOM 中的 iddiv 匹配。
  • 确保 displayType: 'inline' 已传递给 Initialize
  • 检查 checkoutUrl 是否有效。
  • 确保您正在监听 checkout.breakdown 事件。
  • 税费仅在用户在结账框中输入有效的国家和邮政编码后计算。

启用 Apple Pay

Apple Pay 允许客户使用其保存的支付方式快速安全地完成支付。启用后,客户可以直接从支持的设备上的结账覆盖中启动 Apple Pay 模态。
Apple Pay 支持 iOS 17+、iPadOS 17+ 和 macOS 上的 Safari 17+。
要在生产环境中为您的域启用 Apple Pay,请按照以下步骤操作:
1

下载并上传 Apple Pay 域关联文件

下载 Apple Pay 域关联文件将文件上传到您的网络服务器,路径为 /.well-known/apple-developer-merchantid-domain-association。例如,如果您的网站是 example.com,请确保文件在 https://example.com/well-known/apple-developer-merchantid-domain-association 可用。
2

请求启用 Apple Pay

发送电子邮件至 [email protected],提供以下信息:
  • 您的生产域 URL(例如,https://example.com
  • 请求为您的域启用 Apple Pay
您将在 1-2 个工作日内收到确认,一旦为您的域启用 Apple Pay。
3

验证 Apple Pay 可用性

收到确认后,在您的结账中测试 Apple Pay:
  1. 在支持的设备上打开您的结账(iOS 17+、iPadOS 17+ 或 macOS 上的 Safari 17+)
  2. 验证 Apple Pay 按钮是否作为支付选项出现
  3. 测试完整的支付流程
在生产环境中,必须为您的域启用 Apple Pay,才能将其作为支付选项显示。如果您计划提供 Apple Pay,请在上线前联系支持团队。

浏览器支持

Dodo Payments Checkout SDK 支持以下浏览器:
  • Chrome(最新)
  • Firefox(最新)
  • Safari(最新)
  • Edge(最新)
  • IE11+

内联与覆盖结账

为您的用例选择合适的结账类型:
特性内联结账覆盖结账
集成深度完全嵌入页面模态在页面上方
布局控制完全控制有限
品牌无缝与页面分开
实施工作量较高较低
最佳适用自定义结账页面,高转化流程快速集成,现有页面
当您希望对结账体验和品牌进行最大控制时,请使用 内联结账。对于快速集成且对现有页面的更改最小,请使用 覆盖结账

相关资源

如需更多帮助,请访问我们的 Discord 社区 或联系开发者支持团队。