mcpField
B
billing-sdk

billing-sdk

github.com/dodopaymentsデザイン・UI13821 回インストール3 週間前に更新
リポジトリ

billing-sdk について

BillingSDK は、Dodo Payments を使う React と Next.js のフロントエンド向けオープンソース課金 UI コンポーネントライブラリです。コンポーネントはソースファイルとしてプロジェクトに導入し、API 呼び出しはサーバー側で行います。

主な機能

  1. 01料金とサブスクリプション管理 UI
  2. 02使用量、クレジット、請求情報の表示
  3. 03CLI または 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 は、Dodo Payments がメンテナンスする、請求インターフェース向けのオープンソース React コンポーネントライブラリです。github.com/dodopayments/billingsdk にあります。コンポーネントは実行時の npm パッケージからインポートするのではなく、ソースファイルとしてリポジトリにコピーします(shadcn/ui モデル)。

フロントエンドを構築する際に、このスキルを使用してください。Dodo Payments API の呼び出しはすべてサーバーサイドのルートハンドラーに配置します。コンポーネントは表示専用です。

このスキルを使用する場面

  • Dodo のチェックアウトを開始する料金ページを構築する場合。
  • サブスクリプションの詳細と「請求を管理」ボタンを備えたアカウント/請求ページを構築する場合。
  • 使用量メーター、クレジット残高、請求書履歴、または今後の請求を表示する場合。
  • 既存の React または Next.js アプリに解約/アップグレード/ダウングレード UI を追加する場合。
  • @billingsdk/cli init でスキャフォールドするか、個々のコンポーネントを追加するかを判断する場合。

主な概念

依存関係ではなく、ソースとしてインストールします。 リポジトリルートのパッケージ billingsdk はプライベートです。汎用的な実行時コンポーネント用の npm パッケージとして検証済みのものはありません。コンポーネントは CLI または shadcn レジストリからインストールし、.tsx ファイルをプロジェクト内の components/billingsdk/ に配置します。

インストールしたファイルが props の契約です。 コンポーネントはリポジトリにベンダー化されるため、components/billingsdk/<component>.tsx にエクスポートされている props 型が、使用しているバージョンの信頼できる情報源です。コールバックを設定する前に確認してください。別のプロジェクトで使われている prop 名をそのまま想定しないでください。

レジストリがインストール可否の信頼できる情報源です。 registry.json を使用してください。ドキュメントのナビゲーションにはずれがあります。たとえば payment-success-dialog はナビゲーションに表示されますが、現在のレジストリにはありません。add に失敗した場合、そのブロックはレジストリに登録されていません。

コンポーネントが Dodo を呼び出すことはありません。 コンポーネントはデータを受け取り、コールバックを実行します。Dodo Payments の呼び出しはすべて、DODO_PAYMENTS_API_KEY を使ってサーバー上で実行します。このキーは dodo_test_... または dodo_live_... であり、ブラウザーに渡してはいけません。

インストール

新しい連携をスキャフォールドします(フレームワーク設定、API ルート、useBilling フック、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

ほとんどのブロックには、billingsdk.com/docs/components の各ページに利用例と props の表が掲載されています(たとえば 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 です。

クレジット残高は最終的に整合されます。メーターからクレジットへの差し引きは、およそ 1 分ごとにバックグラウンドワーカーで実行されるため、表示される残高は参考情報です。これを基準にリクエストを制限しないでください。メータリング、クレジットのエンタイトルメント、台帳エントリーについては、usage-based-billing と credit-based-billing のスキルで説明しています。

テーマ設定とカスタマイズ

コンポーネントは Tailwind CSS と shadcn/ui の規約に準拠しているため、次の 3 つのレイヤーがあります。

  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 }) を使用し、.url ではなく .link を読み取ってください。

クライアントで描画されたプランの状態を信頼すること。 React の状態で選択されたプランや、ユーザーが return_url に戻ってきたという事実は、サブスクリプションが有効であることを意味しません。データベースに保存された、ウェブフックで確認済みのサブスクリプションイベントに基づいてのみ利用権限を付与し、その情報を使って UI を描画してください。ウェブフック署名の検証については、webhook-integration スキルで説明しています。

リクエスト本文から生の 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 を使用してください。

リソース

コメント