mcpField
C
clerk-billing

clerk-billing

github.com/clerk開発8917.0k 回インストール今日更新
リポジトリ

clerk-billing について

Clerk Billing を利用する開発者向けに、プラン、機能の利用権限、アクセス制御、課金 Webhook の実装を説明するスキルです。ユーザーと組織の課金パターンに加え、CLI と API による設定管理も扱います。

主な機能

  1. 01ユーザーと組織の課金プラン
  2. 02料金表とサブスクリプション UI
  3. 03プランと機能の権限チェック
  4. 04組織プランの席数制限
  5. 05課金設定と Webhook 処理

活用シーン

  1. 01サブスクリプションプランを表示して選択を促す
  2. 02機能やプランに応じてページと API を制限する
  3. 03組織サブスクリプションの席数上限を設定する

インストール

コマンドをコピーしてターミナルで実行します。

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

インストール後、Claude Code で/clerk-billingを入力して読み込まれたか確認します。

同梱ファイル

SKILL.md と一緒にインストールされる参考資料やスクリプト。

evals/
evals.json
references/
b2b-patterns.md
b2c-patterns.md
billing-components.md
billing-webhooks.md

詳細

翻訳後に原文が更新されています。最新の内容は原文をご確認ください。

請求

停止してください。前提条件です。 <PricingTable />、<CheckoutButton />、has({ plan })、または has({ feature }) を使用するには、事前に請求を有効にする必要があります。方法は2つあります。(1)ダッシュボード → 請求 → 設定、または(2)clerk enable billing(下記の「エージェント優先:プログラムによる請求設定」を参照)。有効にすると、デフォルトの free_user/free_org プランが自動作成されます。開発インスタンスでは Clerk の共有開発ゲートウェイを利用できます(Stripe アカウントは不要です)。本番環境では、決済処理にのみ Stripe アカウントが必要です。

注: 請求 API はまだ実験的です。@clerk/nextjs と clerk-js のパッケージバージョンを固定してください。対応バージョンの一覧は clerk スキルを参照してください。

クイックスタート

  1. ダッシュボード → 請求 → 設定または clerk enable billing(「エージェント優先」セクションを参照)で請求を有効にします。この手順を省略すると、開発環境では cannot_render_billing_disabled がスローされ、本番環境では何も表示されません。

  2. 対応するタブでプランを作成します。ダッシュボード → 請求 → プランを開きます。タブは2つあり、スラッグはタブごとに設定され、作成後に移動できません。

    • ユーザープラン → <PricingTable />(デフォルトは for="user")
    • 組織プラン → <PricingTable for="organization" />

    <PricingTable /> が空になる最大の原因は、プランを間違ったタブで作成することです。プランは Clerk に保存され、Stripe とは同期されません。

  3. プラン内に機能を追加します。 ダッシュボード → 請求 → プランでプランを開き、「機能」セクションを使用します。機能はプランごとに設定され、全体には適用されません。同じスラッグを複数のプランに追加できます。アクティブなプランにそのスラッグが含まれていれば、has({ feature: 'export' }) が一致します。

  4. <PricingTable /> を表示します(B2B の場合は for="organization" を渡します)。

  5. auth() の has({ plan }) または has({ feature }) を使って、アクセスを制御します。

  6. サブスクリプションのライフサイクルに関する Webhook を処理します。

ダッシュボードのショートカット

操作URL
請求を有効にするhttps://dashboard.clerk.com/last-active?path=billing/settings
プランを作成/編集するhttps://dashboard.clerk.com/last-active?path=billing/plans
メンバーシップモード(B2C と B2B の併用)https://dashboard.clerk.com/last-active?path=organizations-settings
機能を編集するプラン → プランをクリック → 「機能」セクション(直接 URL はありません)

エージェント優先:プログラムによる請求設定

請求設定全体(有効化の切り替え、プラン、機能、プランへの機能の追加)は、ダッシュボードを使わずに PLAPI から編集できます。エージェントによるプランの初期設定、インスタンス間での設定の複製、請求構成のバージョン管理に便利です。

前提条件:プロジェクトを Clerk アプリにリンクしておく必要があります(clerk auth login と clerk link を実行。clerk-cli を参照)。請求はアカウント単位の機能です。組織とは異なり、未取得のアプリでは有効にできないため、先にアプリを取得してください。

CLI で請求を有効にする
clerk enable billing                # both targets (default, auto-creates free_user + free_org plans)
clerk enable billing --for org      # org only
clerk enable billing --for user     # user only
現在の請求設定を取得する
clerk config pull --keys billing > billing.json

これにより、リンク済みインスタンスの現在の請求設定(切り替え設定、プラン、機能)が billing.json に書き込まれます。

編集して適用する

billing.json を編集してプランや機能を追加または削除し、差分をプレビューして適用します。

clerk config patch --file billing.json --dry-run
clerk config patch --file billing.json

開発用インスタンスではなく本番用インスタンスを対象にするには、--instance prod を指定します。

生の PATCH(すべてを制御)

設定ファイルを使わずに、プランや機能を一度だけ更新する場合:

clerk api --platform PATCH /v1/platform/applications/<app_id>/instances/<ins_id>/config \
  -d '{"billing":{"plans":[{"slug":"pro","name":"Pro","amount":2000,"currency":"usd","payer_type":"user","is_recurring":true}],"features":[{"slug":"export","name":"Export"}]}}'
注記
  • これは請求設定(切り替え設定とプラン・機能のカタログ)を扱います。サブスクリプションのライフサイクル(ユーザーによるプランの選択、チェックアウト、更新、キャンセル)は、引き続き <PricingTable /> と請求 Webhook を通じて処理されます。ライフサイクルイベントについては clerk-webhooks スキルを参照してください。
  • 最上位の features マップの操作と、プランへの機能の追加(同期)は、PLAPI の請求設定ハンドラーで完全にサポートされています。

何が必要ですか?

タスクリファレンス
<PricingTable /> の props、<CheckoutButton />、<Show> の課金パターンreferences/billing-components.md
B2C パターン(個人ユーザーのサブスクリプション、前提条件としての Membership optional)references/b2c-patterns.md
B2B パターン(組織のサブスクリプション、シート数上限プラン、管理者限定の課金 UI)references/b2b-patterns.md
Webhook イベントカタログ、ペイロード形式、ハンドラーテンプレートreferences/billing-webhooks.md

リファレンス

リファレンス説明
references/billing-components.md<PricingTable /> とサブスクリプション UI
references/b2c-patterns.mdB2C サブスクリプション課金パターン
references/b2b-patterns.md組織のサブスクリプションとシート数上限プランを使った B2B 課金
references/billing-webhooks.mdサブスクリプションのライフサイクルイベント処理

ドキュメント

Features と Plans:どちらを使うべきか

特定の機能、エクスポート、アナリティクス、API アクセス、監査ログを制限する場合は、has({ feature: 'slug' }) を使います。

プランのティアを制限する場合は、has({ plan: 'slug' }) を使います。プロ向けダッシュボードの表示、組織のサブスクリプションレベルの確認、無料ユーザーのリダイレクトなどが該当します。

シナリオ正しいチェック方法
「CSV をエクスポート」ボタンを制限するhas({ feature: 'export' })
「アナリティクス」セクションを制限するhas({ feature: 'analytics' })
/dashboard/pro 全体を制限するhas({ plan: 'pro' })
組織が team サブスクリプションに加入しているか確認するhas({ plan: 'org:team' })
SSO の設定を制限するhas({ feature: 'sso' })

「エクスポート機能を制限して」や「アナリティクスを制限して」と指示された場合は、必ず has({ feature }) を使います。has({ plan }) を使うのは、制限対象がプランのティアそのものであり、そのプラン内の特定の機能ではない場合だけです。

主なパターン

1. 料金表を表示する

単一のコンポーネントで、利用可能なプランをユーザーに表示します。

import { PricingTable } from '@clerk/nextjs'

export default function PricingPage() {
	return (
		<main>
			<h1>Choose a plan</h1>
			<PricingTable />
		</main>
	)
}

<PricingTable /> は、Clerk Dashboard で設定されているすべてのプランを自動的に表示します。プランを選択すると、Clerk のアプリ内チェックアウトドロワーが開きます。基本的な使い方では props は不要です。B2B の場合は for="organization" を渡すと、ユーザープランの代わりに組織レベルのプランが表示されます。

2. 機能の利用権限を確認する(サーバーサイド)

個々の機能を制限する場合は、こちらが推奨される方法です。

import { auth } from '@clerk/nextjs/server'

export default async function AnalyticsPage() {
	const { has } = await auth()

	const canViewAnalytics = has({ feature: 'analytics' })
	const canExport = has({ feature: 'export' })

	return (
		<div>
			{canViewAnalytics && <AnalyticsChart />}
			{canExport && <ExportButton />}
		</div>
	)
}

機能は Clerk Dashboard → Billing → Features で設定し、プランに割り当てます。細かな機能を制限する場合は has({ plan }) の代わりに has({ feature }) を使い、プランではなく機能を確認します。

3. 機能の利用権限を確認する(クライアントサイド)

クライアントサイドで機能を制限するには、useAuth() を使います。十分に保護するため、サーバーサイドのチェックと組み合わせてください。

'use client'
import { useAuth } from '@clerk/nextjs'

export function FeatureGatedUI() {
	const { has, isLoaded } = useAuth()
	if (!isLoaded) return null

	const canExport = has?.({ feature: 'export' })
	const canAnalytics = has?.({ feature: 'analytics' })

	return (
		<div>
			{canAnalytics && <AnalyticsSection />}
			{canExport ? <ExportButton /> : <UpgradeToExport />}
		</div>
	)
}

サーバーコンポーネントでは auth() を、クライアントコンポーネントでは useAuth() を使います。どちらも has({ feature }) と has({ plan }) に対応しています。

4. サブスクリプションプランをサーバーサイドで確認する

サブスクリプションプランでアクセスを制限します(個々の機能ではなく、ティアレベルで制限する場合に使います)。

import { auth } from '@clerk/nextjs/server'
import { redirect } from 'next/navigation'

export default async function ProDashboard() {
	const { has } = await auth()

	if (!has({ plan: 'pro' })) {
		redirect('/pricing')
	}

	return <ProFeatures />
}
5. クライアントサイドでプランを確認する

クライアントコンポーネントでは useAuth() フックを使います。

'use client'
import { useAuth } from '@clerk/nextjs'

export function UpgradePrompt() {
	const { has } = useAuth()

	if (has?.({ plan: 'pro' })) {
		return null
	}

	return (
		<div>
			<p>Upgrade to Pro to access this feature</p>
			<a href="/pricing">View plans</a>
		</div>
	)
}
6. 組織を使った B2B のシート数ベース課金

組織プランにはシート数上限(メンバーシップの上限)を設定でき、Clerk は招待時にこれを適用します。組織側のプランチェックでは org: スラッグプレフィックス(例:has({ plan: 'org:team' }))を使うと、制限が明確になります。B2B の料金ページには <PricingTable for="organization" /> を表示し、組織アカウントの課金 UI には <OrganizationProfile /> を使います。

プランのティア別命名、シート数上限の不変条件、管理者限定の課金、Webhook ハンドラーについては、references/b2b-patterns.md を参照してください。

7. サブスクリプションのステータスを表示する

特定のプランを確認するには has({ plan }) を使用するか、クライアントコンポーネントでサブスクリプションの詳細をすべて取得するには useSubscription() を使用します。sessionClaims からプラン情報を直接読み取らないでください。これはサポートされている方法ではありません。

サーバーコンポーネントで特定のプランを確認する場合:

import { auth } from '@clerk/nextjs/server'

export default async function AccountPage() {
	const { has } = await auth()

	const currentPlan = has({ plan: 'pro' })
		? 'pro'
		: has({ plan: 'starter' })
			? 'starter'
			: 'free'

	return (
		<div>
			<h2>Current Plan</h2>
			<p>You are on the {currentPlan} plan</p>
			{currentPlan === 'free' && <a href="/pricing">Upgrade</a>}
		</div>
	)
}

クライアントコンポーネントで useSubscription() を使ってサブスクリプションの詳細をすべて取得する場合:

'use client'
import { useSubscription } from '@clerk/nextjs/experimental'

export function SubscriptionDetails() {
	const { data: subscription, isLoading } = useSubscription()
	if (isLoading) return null
	if (!subscription) return <a href="/pricing">Choose a plan</a>

	return (
		<div>
			<p>Status: {subscription.status}</p>
			{subscription.nextPayment && (
				<p>Next payment: {subscription.nextPayment.date.toLocaleDateString()}</p>
			)}
		</div>
	)
}

useSubscription() は表示専用です。認可チェック(コンテンツやルートのアクセス制御)には、必ず has({ plan }) または has({ feature }) を使用してください。

8. プランに応じて API ルートを保護する

auth() を使って API ルートへのアクセスを制限します。

import { auth } from '@clerk/nextjs/server'
import { NextResponse } from 'next/server'

export async function GET() {
	const { has } = await auth()

	if (!has({ plan: 'pro' })) {
		return NextResponse.json({ error: 'Pro plan required' }, { status: 403 })
	}

	return NextResponse.json({ data: 'premium data' })
}
9. 請求 Webhook を処理する

Clerk のイベント名は Stripe のイベント名とは異なります。 Clerk の請求 Webhook では、Stripe のアンダースコア形式ではなく、ドット表記とキャメルケースを使用します。

subscription.canceled イベントはありません。キャンセルはアイテムレベルの subscriptionItem.canceled として発生します。

意図Stripe のイベント名Clerk のイベント名
サブスクリプション作成customer.subscription.createdsubscription.created
サブスクリプション更新customer.subscription.updatedsubscription.updated
サブスクリプション有効化(なし)subscription.active
サブスクリプションの支払い遅延(なし)subscription.pastDue
サブスクリプションアイテムのキャンセルcustomer.subscription.deletedsubscriptionItem.canceled
サブスクリプションアイテムの支払い遅延invoice.payment_failedsubscriptionItem.pastDue
サブスクリプションアイテムの更新(なし)subscriptionItem.updated
サブスクリプションアイテムの有効化(なし)subscriptionItem.active
サブスクリプションアイテムの更新予定(なし)subscriptionItem.upcoming
サブスクリプションアイテムの終了(なし)subscriptionItem.ended
サブスクリプションアイテムの放棄(なし)subscriptionItem.abandoned
サブスクリプションアイテムの期限切れ(なし)subscriptionItem.expired
サブスクリプションアイテムの未完了(なし)subscriptionItem.incomplete
無料トライアルの終了間近(なし)subscriptionItem.freeTrialEnding
支払い試行の作成(なし)paymentAttempt.created
支払い試行の更新(なし)paymentAttempt.updated

evt.type のチェックでは、必ず Clerk のイベント名を使用し、Stripe のイベント名は使用しないでください。

ペイロードの構造。 Clerk の請求 Webhook のペイロードはネストされています。サブスクリプションを申し込んだエンティティは evt.data.payer の下にあります(フィールド:user_id?、organization_id?)。プラン情報は各アイテムの evt.data.items[i].plan.slug にあります。サブスクリプション ID は evt.data.id です。サブスクリプションアイテムには、参照先の subscription_id フィールドがありません。そのため、subscriptionItem.* ハンドラーではアイテム ID(evt.data.id)を使ってレコードを特定するか、支払者とプランで検索します。

パターンを確認するための最小限のハンドラー(@clerk/nextjs/webhooks からインポートし、検証したうえで Clerk のイベント名に応じて分岐します):

import { verifyWebhook } from '@clerk/nextjs/webhooks'
import { NextRequest } from 'next/server'
import { db } from '@/lib/db'

export async function POST(req: NextRequest) {
	let evt
	try {
		evt = await verifyWebhook(req)
	} catch {
		return new Response('Verification failed', { status: 400 })
	}

	if (evt.type === 'subscription.created') {
		const { id, payer, items, status } = evt.data
		const entityId = payer.organization_id ?? payer.user_id
		const plan = items[0]?.plan?.slug
		await db.subscriptions.upsert({
			where: { subscriptionId: id },
			create: { subscriptionId: id, entityId, plan, status },
			update: { entityId, plan, status },
		})
	}

	// Add more branches per the event catalog above (subscription.updated,
	// subscriptionItem.canceled, subscriptionItem.pastDue, etc.)

	return new Response('OK', { status: 200 })
}

15 個すべてのイベントを網羅したテンプレート、@clerk/backend の TS 型宣言、proxy.ts による公開ルートの設定、サブスクリプションステータス値の表については、references/billing-webhooks.md を参照してください。

10. アップグレード/ダウングレードのフロー

アプリ内からユーザーがサブスクリプションを管理できるようにします。

import { PricingTable } from '@clerk/nextjs'
import { auth } from '@clerk/nextjs/server'

export default async function BillingPage() {
	const { has } = await auth()
	const isPro = has({ plan: 'pro' })

	return (
		<div>
			<h1>Billing</h1>
			{isPro ? (
				<div>
					<p>You are on the Pro plan</p>
					<PricingTable />
				</div>
			) : (
				<div>
					<p>Upgrade to access premium features</p>
					<PricingTable />
				</div>
			)}
		</div>
	)
}

<PricingTable /> はサブスクリプション登録済みのユーザーには異なる表示になり、現在のプランを表示するとともに、Clerk のアプリ内チェックアウトドロワーを通じてアップグレードやキャンセルを行えるようにします。

プランと機能の命名

プランスラッグと機能スラッグは Clerk Dashboard → Billing で定義します。一般的な規則は次のとおりです。

ティアプランスラッグ機能の例
Free(プランのチェックは不要)基本機能
Starterstarteranalytics、api_access
Proproanalytics、export、team
Enterpriseenterpriseすべての機能 + sso、audit_logs

ダッシュボードで定義した内容に合わせて、小文字のスラッグを使用してください。

B2B と B2C の課金

シナリオサブスクリプションを契約するユーザープランの確認
B2C SaaS個人ユーザーユーザーセッションで has({ plan: 'pro' })
B2B SaaS組織組織セッションで has({ plan: 'org:team' })
シート数に制限のある B2B組織プランにシート数の上限があります。料金はメンバー単位ではなくプラン単位です。大規模な組織向けにプランを段階分けしてください

B2B の場合、ユーザーがアクティブな組織セッションを持っていることを確認してください。has() チェックでは、アクティブなエンティティ(ユーザーまたは組織)が評価されます。

チェックアウトフロー

Clerk は <PricingTable /> と <CheckoutButton /> を使って、独自のチェックアウトドロワーを自動的に表示します。プランと料金は Clerk で管理します。サーバーアクションからチェックアウトを開始するには、<PricingTable /> を表示するページにリダイレクトします。

'use server'
import { redirect } from 'next/navigation'

export async function upgradeAction() {
	redirect('/pricing')
}

エラーのパターン(迅速な診断)

次のようなエラーや症状が発生した場合、ほとんどの場合、修正が必要なのはコードではなくダッシュボードの設定です。コンポーネントの編集から始めないでください。

エラー/症状根本原因修正方法
Clerk: 🔒 The <PricingTable/> component cannot be rendered when billing is disabled.(code: cannot_render_billing_disabled、開発環境のみ)このインスタンスで課金が有効になっていませんdashboard.clerk.com → Billing → Settings で課金を有効にするか、clerk enable billing を実行します。
<PricingTable /> が空で表示されるプランがない、またはプランが誤ったタブ(User と Organization)にある、または課金が有効になっていない対応するタブでプランを作成します。B2B の場合は for="organization" を指定し、Billing Settings を確認します
B2C と B2B を併用するアプリで、ユーザーが個人プランを契約できないメンバーシップ必須モード(2025-08-22 以降のデフォルト)では個人アカウントが無効になり、サインイン済みユーザーは choose-organization に強制されるため、個人サブスクリプションの状態になりません個人と組織のサブスクリプションを併用する必要がある場合:Dashboard → Organizations settings → Membership optional
Features ページが見つからないFeatures はプランごとに設定され、全体設定ではありませんDashboard → Billing → Plans → プランをクリック → Features
チェックアウトが成功した後も has({ plan: 'pro' }) が常に false を返す新しいプランを含めるようにセッショントークンが更新されていませんawait clerk.session?.reload() を実行するか、新しいセッションを取得するためにページを移動します
サブスクリプションを試す前に has({ plan: 'pro' }) が false を返すプランのスラッグが一致していない(大文字と小文字を区別)、課金が有効になっていない、または本番環境で決済ゲートウェイが接続されていないDashboard → Billing → Plans でスラッグを確認します。Billing → Settings で課金が有効になっていて、ゲートウェイが接続されていることを確認します
ロールが割り当てられているユーザーに対して has({ permission: 'org:x:y' }) が false を返すその権限に関連付けられた Feature が、組織のアクティブなプランに含まれていませんDashboard → Billing → Plans → Features で、その Feature をプランに追加します
Webhook の 401 または 404/署名検証に失敗するCLERK_WEBHOOK_SIGNING_SECRET が一致していない、または auth.protect() が Webhook のルートを対象としているDashboard → Webhooks から Signing Secret をコピーします。ミドルウェアまたはハンドラーで /api/webhooks(.*) に対して auth.protect() が呼び出されていないことを確認します

課金による権限の制御

課金が有効な場合、ユーザーのロールにその権限が割り当てられていても、その権限に関連付けられた Feature が組織のアクティブなプランに含まれていないと、has({ permission: 'org:posts:edit' }) は false を返します。これは意図された動作です。課金では Feature レベルで権限が制御されます。必要な Feature が Dashboard → Billing → Plans → Features でプランに追加されていることを必ず確認してください。

関連項目

  • clerk-setup - Clerk の初期インストール
  • clerk-orgs - B2B 組織(B2B の課金とシート数制限付きプランに必要)
  • clerk-webhooks - Webhook の署名検証とルーティング

コメント