mcpField
B
billing-sdk

billing-sdk

github.com/dodopayments设计与 UI13821 次安装3 周前更新
代码仓库

关于 billing-sdk

BillingSDK 是面向 React 和 Next.js 前端的开源计费界面组件库,适合构建 Dodo Payments 定价、订阅和用量页面。组件以源码形式安装到项目中,API 调用则应放在服务端。

主要功能

  1. 01提供定价与订阅管理界面
  2. 02展示用量、额度和账单
  3. 03通过 CLI 或 shadcn 注册表安装
  4. 04可编辑项目内组件源码

适用场景

  1. 01构建连接结账流程的定价页
  2. 02添加订阅管理与客户门户入口
  3. 03展示用量、额度或发票历史

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/dodopayments/skills --skill billing-sdk

安装后在 Claude Code 中输入/billing-sdk确认已加载。

附带文件references/pricing-page.md

详细介绍

BillingSDK

BillingSDK 是一个用于构建账单界面的开源 React 组件库,由 Dodo Payments 维护,位于 github.com/dodopayments/billingsdk。组件会作为源文件复制到你的代码库中(采用 shadcn/ui 模式),而不是从运行时 npm 包导入。

构建前端时使用此技能。所有 Dodo Payments API 调用都应放在服务端路由处理程序中;组件仅负责展示。

何时使用此技能

  • 构建可启动 Dodo 结账流程的定价页面。
  • 构建包含订阅详情和“管理账单”按钮的账户/账单页面。
  • 显示用量计量器、额度余额、发票历史记录或即将产生的费用。
  • 为现有 React 或 Next.js 应用添加取消/升级/降级界面。
  • 决定使用 @billingsdk/cli init 搭建项目,还是添加单个组件。

核心概念

安装源文件,而非添加依赖。 根目录中的 billingsdk 仓库包是私有的。目前没有经过验证的通用运行时 npm 组件包。你可以通过 CLI 或 shadcn 注册表安装组件,.tsx 文件会放入项目的 components/billingsdk/ 目录中。

已安装文件定义属性契约。 由于组件会以 vendored 文件的形式放入你的代码库,components/billingsdk/<component>.tsx 中导出的 props 类型就是你所用版本的权威依据。接入回调之前请先阅读。不要假设属性名称与其他项目相同。

注册表是判断组件是否可安装的唯一依据。 请使用 registry.json。文档导航与注册表有所出入——例如,导航中有 payment-success-dialog,但当前注册表中没有。如果 add 失败,说明该组件不在注册表中。

组件不会调用 Dodo。 组件接收数据并触发回调。每次 Dodo Payments 调用都在你的服务端使用 DODO_PAYMENTS_API_KEY 执行。该密钥的格式为 dodo_test_... 或 dodo_live_...,绝不能暴露给浏览器。

安装

搭建新的集成(框架配置、API 路由、useBilling hooks、lib/dodopayments.ts、依赖项和环境变量 DODO_PAYMENTS_API_KEY、DODO_PAYMENTS_ENVIRONMENT、DODO_PAYMENTS_WEBHOOK_KEY):

npx @billingsdk/cli init

向现有项目添加单个组件:

npx @billingsdk/cli add pricing-table-one

或者使用 @billingsdk/ 命名空间,通过 shadcn 注册表安装:

npx shadcn@latest add @billingsdk/pricing-table-one

已发布的 CLI 包为 @billingsdk/cli,最近一次看到的版本为 0.9.0。

组件清单

根据当前官方注册表,按用途分组如下:

分组组件块
定价pricing-table-one 至 pricing-table-eight
订阅subscription-management、usage-based-pricing、invoice-history、update-plan-card、update-plan-dialog、proration-preview、cancel-subscription-card、cancel-subscription-dialog
用量和账单usage-meter-linear、usage-meter-circle、usage-table、detailed-usage-table、billing-screen、billing-settings、billing-settings-2、upcoming-charges
付款payment-details、payment-details-two、payment-method-selector、payment-card、payment-failure
促销和试用banner、limited-offer-dialog、trial-expiry-card

大多数组件块的页面上都有已发布的用法示例和 props 表,详见 billingsdk.com/docs/components(例如 subscription-management 和 billing-settings);pricing-table-one 的示例收录于 references/pricing-page.md。生成文件中的 props 类型仍是你所安装版本的权威依据,因此运行 add 后请先阅读。

服务端客户端

仅由路由处理程序和服务端组件导入的单个模块。

// lib/dodopayments.ts
import DodoPayments from 'dodopayments';

if (!process.env.DODO_PAYMENTS_API_KEY) {
  throw new Error('DODO_PAYMENTS_API_KEY is not set');
}

export const dodo = new DodoPayments({
  bearerToken: process.env.DODO_PAYMENTS_API_KEY,
  environment: process.env.DODO_PAYMENTS_ENVIRONMENT === 'live_mode' ? 'live_mode' : 'test_mode',
});

如果省略 environment,其默认值为 'live_mode',因此开发期间请显式设置。基础 URL 为 https://test.dodopayments.com 和 https://live.dodopayments.com。

定价页面

完整指南:references/pricing-page.md。

涵盖:

  • 套餐目录
  • 产品 ID 对照表 — 仅限服务端
  • 结账路由
  • 客户端组件

订阅管理和客户门户

安装这个区块,然后从生成的文件中读取其 props:

npx @billingsdk/cli add subscription-management

门户路由。调用方法为 customers.customerPortal.create(customerID, { ...params }),会话的 URL 字段为 link。

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

export async function POST() {
  const user = await getCurrentUser();
  if (!user?.dodoCustomerId) {
    return NextResponse.json({ error: 'No billing account' }, { status: 400 });
  }

  try {
    const session = await dodo.customers.customerPortal.create(user.dodoCustomerId, {
      return_url: `${process.env.NEXT_PUBLIC_APP_URL}/account`,
    });

    return NextResponse.json({ url: session.link });
  } catch (error) {
    console.error('Customer portal session failed', error);
    return NextResponse.json({ error: 'Could not open billing portal' }, { status: 500 });
  }
}

门户链接会在 24 小时后过期,因此每次点击时都应创建一个,而不是缓存链接。在门户中,客户可以查看和取消订阅、在已启用的产品系列中更改套餐、更新付款方式、下载发票以及获取许可证密钥。

客户端:

// components/manage-billing-button.tsx
'use client';

import { useState } from 'react';

export function ManageBillingButton() {
  const [pending, setPending] = useState(false);

  async function openPortal() {
    setPending(true);
    try {
      const response = await fetch('/api/portal', { method: 'POST' });
      if (!response.ok) {
        setPending(false);
        return;
      }
      const { url } = (await response.json()) as { url: string };
      window.location.href = url;
    } catch {
      setPending(false);
    }
  }

  return (
    <button type="button" onClick={openPortal} disabled={pending}>
      {pending ? 'Opening…' : 'Manage billing'}
    </button>
  );
}

将 openPortal 绑定到已安装的 subscription-management.tsx 所提供的回调 prop,或在旁边渲染独立按钮。在承载该页面的服务端组件中加载订阅本身 — await dodo.subscriptions.retrieve(user.dodoSubscriptionId) — 并向下传递纯数据。

用量和额度显示

安装一个计量表区块:

npx @billingsdk/cli add usage-meter-linear

在服务端获取数值。额度余额通过 creditEntitlements.balances.retrieve 获取:

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

const CREDIT_ENTITLEMENT_ID = process.env.DODO_CREDIT_ENTITLEMENT_ID;

export async function GET() {
  const user = await getCurrentUser();
  if (!user?.dodoCustomerId || !CREDIT_ENTITLEMENT_ID) {
    return NextResponse.json({ error: 'No billing account' }, { status: 400 });
  }

  try {
    const balance = await dodo.creditEntitlements.balances.retrieve(user.dodoCustomerId, {
      credit_entitlement_id: CREDIT_ENTITLEMENT_ID,
    });

    return NextResponse.json({ balance: balance.balance, overage: balance.overage });
  } catch (error) {
    console.error('Credit balance lookup failed', error);
    return NextResponse.json({ error: 'Could not read balance' }, { status: 500 });
  }
}

使用已安装的 components/billingsdk/usage-meter-linear.tsx 中的 prop 名称,将结果传入计量表区块。适用于同一场景的相关区块:usage-meter-circle、usage-table、detailed-usage-table、upcoming-charges、invoice-history。

额度余额最终会保持一致 — 计量数据到额度的扣减由后台工作进程大约每分钟运行一次 — 因此显示的余额仅供参考。不要据此限制请求。用量计量、额度权益和账本条目详见 usage-based-billing 和 credit-based-billing 技能。

主题和自定义

组件采用 Tailwind CSS 和 shadcn/ui 约定,因此有三个层次:

  1. 主题令牌。 在 globals.css 中覆盖 shadcn CSS 变量;所有区块都会继承这些变量。

    @layer base {
      :root {
        --primary: 220 90% 56%;
        --primary-foreground: 0 0% 100%;
      }
    }
    
  2. 组件 props。 pricing-table-one 接受 theme(官方示例中为 "classic")和 size("medium")。请查看已安装文件中的完整联合类型。

  3. 修改源文件。 这些文件归你所有。修改它们是受支持的结构性变更方式,但重新运行 add 时需要手动协调改动。

托管的 Dodo 结账页面使用单独的主题设置,通过结账会话中的 customization.theme_config 配置 — 与这些组件无关。

框架支持

这些组件并非与框架无关。它们需要 React 或 Next.js,以及 Tailwind CSS 和 shadcn/ui 约定。

CLI 还附带适用于 Next.js、Express、Hono、Fastify 和 React 的服务端集成模板。这仅涵盖路由处理程序一侧。注意:Dodo 的集成页面与 BillingSDK 自身的介绍,对于哪些服务端模板已提供、哪些仍属“即将推出”说法不一 — 在确定使用某个适配器前,请通过当前 CLI 确认。

对于非 React 前端,可通过框架适配器(@dodopayments/nextjs、@dodopayments/express、@dodopayments/sveltekit 等)调用 Dodo,并自行构建 UI。

环境变量

# .env.local — server-side only, never prefixed with NEXT_PUBLIC_
DODO_PAYMENTS_API_KEY=dodo_test_xxxxxxxxxxxxxxxx
DODO_PAYMENTS_ENVIRONMENT=test_mode
DODO_PAYMENTS_WEBHOOK_KEY=xxxxxxxxxxxxxxxx
DODO_CREDIT_ENTITLEMENT_ID=cde_xxxxxxxxxxxxxxxx

# Safe to expose
NEXT_PUBLIC_APP_URL=https://yoursite.com

请同时切换为 dodo_live_... 和 DODO_PAYMENTS_ENVIRONMENT=live_mode。在 test_mode 下使用线上密钥会失败,在 live_mode 下使用测试密钥也会失败。

常见错误

将 API 密钥暴露给浏览器。 NEXT_PUBLIC_DODO_PAYMENTS_API_KEY 会将密钥内联到 JS 包中,任何人都能读取。Dodo Payments 没有可公开的密钥——所有密钥都是机密。只能在路由处理程序和服务端组件中导入 lib/dodopayments.ts。

调用 customers.createPortalSession。 该方法不存在。请使用 client.customers.customerPortal.create(customerID, { ...params }),并读取 .link,而不是 .url。

信任客户端渲染的套餐状态。 React 状态中选中的套餐,或用户返回了 return_url,都不代表订阅已生效。只有在数据库中持久化并经 Webhook 确认的订阅事件才能授予权益,然后据此渲染界面。webhook-integration 技能介绍了 Webhook 签名验证。

接受请求正文中未经处理的 product_id。 调用方随后可以使用你目录中的任意产品进行结账,包括内部产品或折扣产品。在服务端将不透明的套餐标识映射到 pdt_ ID。

在客户端硬编码价格。 plans[].monthlyPrice / yearlyPrice 是展示文本。若你在 Dodo 控制面板中更改价格,结账会收取新金额,但定价页面仍会展示旧价格。任何价格变更都应同时核对 lib/billingsdk-config.ts 和控制面板中的设置。

执行 npm install billingsdk。 根仓库的包是私有的。请通过 @billingsdk/cli 或 shadcn 注册表进行安装。

猜测属性名称。 请查看 billingsdk.com/docs/components 上对应组件的页面,然后打开 components/billingsdk/ 下生成的文件,在进行其他接线操作之前先阅读导出的属性类型。

使用 DODO_PAYMENTS_WEBHOOK_SECRET。 SDK 会为 webhookKey 选项读取 DODO_PAYMENTS_WEBHOOK_KEY。

根据显示的额度余额限制请求。 扣减是异步的,因此显示的余额会有延迟。Dodo 明确警告,不要将其用作严格的逐请求授权依据。

使用 payments.create 或 subscriptions.create 进行购买。 这两种方法都已弃用。请使用 checkoutSessions.create。

资源

评论

支持 Markdown