clerk-billing について
Clerk Billing を利用する開発者向けに、プラン、機能の利用権限、アクセス制御、課金 Webhook の実装を説明するスキルです。ユーザーと組織の課金パターンに加え、CLI と API による設定管理も扱います。
主な機能
- 01ユーザーと組織の課金プラン
- 02料金表とサブスクリプション UI
- 03プランと機能の権限チェック
- 04組織プランの席数制限
- 05課金設定と Webhook 処理
活用シーン
- 01サブスクリプションプランを表示して選択を促す
- 02機能やプランに応じてページと API を制限する
- 03組織サブスクリプションの席数上限を設定する
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ clerk/ skills --skill clerk-billingインストール後、Claude Code で/clerk-billingを入力して読み込まれたか確認します。
同梱ファイル
SKILL.md と一緒にインストールされる参考資料やスクリプト。
evals/evals.jsonreferences/b2b-patterns.mdb2c-patterns.mdbilling-components.mdbilling-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スキルを参照してください。
クイックスタート
-
ダッシュボード → 請求 → 設定または
clerk enable billing(「エージェント優先」セクションを参照)で請求を有効にします。この手順を省略すると、開発環境ではcannot_render_billing_disabledがスローされ、本番環境では何も表示されません。 -
対応するタブでプランを作成します。ダッシュボード → 請求 → プランを開きます。タブは2つあり、スラッグはタブごとに設定され、作成後に移動できません。
- ユーザープラン →
<PricingTable />(デフォルトはfor="user") - 組織プラン →
<PricingTable for="organization" />
<PricingTable />が空になる最大の原因は、プランを間違ったタブで作成することです。プランは Clerk に保存され、Stripe とは同期されません。 - ユーザープラン →
-
プラン内に機能を追加します。 ダッシュボード → 請求 → プランでプランを開き、「機能」セクションを使用します。機能はプランごとに設定され、全体には適用されません。同じスラッグを複数のプランに追加できます。アクティブなプランにそのスラッグが含まれていれば、
has({ feature: 'export' })が一致します。 -
<PricingTable />を表示します(B2B の場合はfor="organization"を渡します)。 -
auth()のhas({ plan })またはhas({ feature })を使って、アクセスを制御します。 -
サブスクリプションのライフサイクルに関する 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.md | B2C サブスクリプション課金パターン |
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 | (プランのチェックは不要) | 基本機能 |
| Starter | starter | analytics、api_access |
| Pro | pro | analytics、export、team |
| Enterprise | enterprise | すべての機能 + 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 の署名検証とルーティング