Token导航 LogoToken导航TokenDH.com
待分类需要联网github未标认证来源可访问许可证需确认审计提醒

clerk-billing文员记账

Agent Skill

clerk-billing 用于处理 GitHub 仓库、Issue、Pull Request 和代码协作信息,适合在 Codex、Claude、Cursor、Gemini CLI 中需要围绕仓库状态、代码变更或协作事项进行整理时使用。可结合来源仓库、安装命令和原始 README 继续核验具体用法。安装前建议确认权限范围、维护状态,以及是否会触发联网、命令执行或文件读写。

总安装

1,934

周安装

79

GitHub Stars

39

下载量

626
CodexClaudeCursorGemini CLI

安装说明

本站只整理中文说明和来源信息,不托管安装包,也不代用户安装。

GitHub

来源数

2

许可证

unknown

最后核验

2026-05-01

来源状态

来源可访问

安装方式

通过对话安装

复制提示词发给支持本地命令或 Skills 的 AI 助手,先确认命令和权限,再让它执行。

请帮我安装这个 Agent Skill:clerk-billing(文员记账)
来源仓库:https://github.com/clerk/skills
仓库路径:skills/clerk-billing
安装命令:
npx skills add https://github.com/clerk/skills --skill clerk-billing
安装前请先检查当前环境是否支持对应 CLI,并向我确认将要执行的命令、安装目录、联网范围和文件读写权限;确认后再执行。

命令行安装

复制命令到本机终端执行。该命令会通过 npx skills 从第三方来源获取 Skill;本站只展示命令,不托管安装包,也不自动执行。

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

简介

管理 Clerk 账单功能启用状态,确保 pricing table 与 checkout 按钮正常工作。

  • 指出仅能通过仪表盘手动开启 Billing 功能,CLI 与 API 暂不支持切换。
  • 开发环境可使用共享网关,生产环境需绑定 Stripe 账户处理支付。
  • 注意 Billing APIs 仍处于实验阶段,建议锁定 SDK 版本避免兼容问题。
  • clerk-billing 属于待分类类 Skill,可作为该场景下的辅助能力补充。

SKILL.md

Billing

STOP, Dashboard-only prerequisite. Billing must be enabled from the Clerk Dashboard before any <PricingTable />, <CheckoutButton />, has({plan}), or has({feature}) usage works. The Clerk CLI and Backend API do not expose a toggle for this today, the only path is dashboard.clerk.com → your app → Billing → Settings. Dev instances can use the shared Clerk development gateway (no Stripe account needed); production requires a Stripe account for payment processing only. Note: Billing APIs are still experimental. Pin your @clerk/nextjs and clerk-js package versions. See clerk skill for the supported version table.

Quick Start

  1. Enable Billing, Dashboard → Billing → Settings. Dashboard-only; no CLI or API path. Skipping this throws cannot_render_billing_disabled in dev and renders empty in prod.
  2. Create plans in the matching tab, Dashboard → Billing → Plans. Two tabs, slugs scoped per tab, not movable after creation: Wrong-tab is the #1 cause of an empty <PricingTable />. Plans live in Clerk; not synced to Stripe.

- User Plans<PricingTable /> (default for="user") - Organization Plans<PricingTable for="organization" />

  1. Add features inside a plan, open the plan in Dashboard → Billing → Plans, use its Features section. Features are scoped per plan, not global. The same slug can attach to multiple plans; has({feature: 'export'}) matches if the active plan contains that slug.
  2. Render <PricingTable /> (pass for="organization" for B2B).
  3. Gate access with has({plan}) or has({feature}) from auth().
  4. Handle billing webhooks for subscription lifecycle.

Dashboard shortcuts

ActionURL
Enable Billinghttps://dashboard.clerk.com/last-active?path=billing/settings
Create / edit planshttps://dashboard.clerk.com/last-active?path=billing/plans
Membership mode (B2C + B2B coexistence)https://dashboard.clerk.com/last-active?path=organizations-settings
Edit featuresPlans → click a plan → Features section (no direct URL)

What Do You Need?

TaskReference
<PricingTable /> props, <CheckoutButton />, <Show> billing patternsreferences/billing-components.md
B2C patterns (individual user subscriptions, Membership optional prerequisite)references/b2c-patterns.md
B2B patterns (org subscriptions, seat-limit plans, admin-gated billing UI)references/b2b-patterns.md
Webhook event catalog, payload shapes, handler templatesreferences/billing-webhooks.md

References

ReferenceDescription
references/billing-components.md<PricingTable /> and subscription UI
references/b2c-patterns.mdB2C subscription billing patterns
references/b2b-patterns.mdB2B billing with organization subscriptions and seat-limit plans
references/billing-webhooks.mdSubscription lifecycle event handling

Documentation

Features vs Plans: When to Use Which

Use has({feature: 'slug'}) when gating a specific capability, export, analytics, API access, audit logs.

Use has({plan: 'slug'}) when gating a tier, showing the pro dashboard, checking org subscription level, redirecting free users.

ScenarioCorrect check
Gate the "Export CSV" buttonhas({feature: 'export'})
Gate the "Analytics" sectionhas({feature: 'analytics'})
Gate all of /dashboard/prohas({plan: 'pro'})
Check if org has team subscriptionhas({plan: 'org:team'})
Gate SSO configurationhas({feature: 'sso'})

When a user says "gate the export feature" or "gate analytics", always use has({feature}). Only use has({plan}) when the gate is the plan tier itself, not a specific capability within it.

Key Patterns

1. Render the Pricing Table

Show available plans to users with a single component:

import { PricingTable } from '@clerk/nextjs'

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

<PricingTable /> automatically renders all plans configured in the Clerk Dashboard. Selecting a plan opens Clerk's in-app checkout drawer. No props needed for basic usage. For B2B, pass for="organization" to render org-level plans instead of user plans.

2. Check Feature Entitlements (Server-Side)

Gate by individual features, this is the preferred approach for specific capabilities:

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>
	)
}

Features are configured in Clerk Dashboard → Billing → Features and assigned to plans. Use has({feature}) instead of has({plan}) when gating granular capabilities, check the feature, not the plan.

3. Check Feature Entitlements (Client-Side)

Use useAuth() for client-side feature gating. Combine with server-side checks for full coverage:

'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>
	)
}

Server Components use auth(), Client Components use useAuth(). Both support has({feature}) and has({plan}).

4. Check Subscription Plan Server-Side

Gate access by subscription plan (use this for tier-level gates, not individual features):

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. Client-Side Plan Checks

Use useAuth() hook for client components:

'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 Seat-Based Billing with Organizations

Org plans can carry a seat limit (membership cap) that Clerk enforces at invite time. Use the org: slug prefix on org-side plan checks (e.g. has({plan: 'org:team'})) to keep gating unambiguous. Render the B2B pricing page with <PricingTable for="organization" />, and use <OrganizationProfile /> for the org account billing UI.

See references/b2b-patterns.md for tiered plan naming, seat-limit invariants, admin-only billing, and webhook handlers.

7. Display Subscription Status

Check specific plans with has({plan}), or use useSubscription() for full subscription details in client components. Do not read plan information from sessionClaims directly, that is not the supported path.

Server component, check for specific plans:

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>
	)
}

Client component, full subscription details via 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() is for display only. For authorization checks (gating content or routes), always use has({plan}) or has({feature}).

8. Protect API Routes by Plan

Gate API routes using auth():

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. Handle Billing Webhooks

Clerk event names differ from Stripe event names. Clerk billing webhooks use dot-notation and camelCase, not Stripe's underscore format. There is no subscription.canceled event. Cancellation fires at the item level as subscriptionItem.canceled. | Intent | Stripe event name | Clerk event name | | --- | --- | --- | | Subscription created | customer.subscription.created | subscription.created | | Subscription updated | customer.subscription.updated | subscription.updated | | Subscription active | (none) | subscription.active | | Subscription past due | (none) | subscription.pastDue | | Subscription item canceled | customer.subscription.deleted | subscriptionItem.canceled | | Subscription item past due | invoice.payment_failed | subscriptionItem.pastDue | | Subscription item updated | (none) | subscriptionItem.updated | | Subscription item active | (none) | subscriptionItem.active | | Subscription item upcoming renewal | (none) | subscriptionItem.upcoming | | Subscription item ended | (none) | subscriptionItem.ended | | Subscription item abandoned | (none) | subscriptionItem.abandoned | | Subscription item expired | (none) | subscriptionItem.expired | | Subscription item incomplete | (none) | subscriptionItem.incomplete | | Free trial ending soon | (none) | subscriptionItem.freeTrialEnding | | Payment attempt created | (none) | paymentAttempt.created | | Payment attempt updated | (none) | paymentAttempt.updated | Always use Clerk's event names, never Stripe's, in evt.type checks.
Payload shape. Clerk billing webhook payloads are nested. The subscribing entity lives under evt.data.payer (fields: user_id?, organization_id?). The plan info is on each item under evt.data.items[i].plan.slug. The subscription id is simply evt.data.id. Subscription items do not carry a subscription_id field back-reference, so in subscriptionItem.* handlers you identify the record by the item id (evt.data.id) or look up by payer plus plan.

Minimal handler to anchor the pattern (import from @clerk/nextjs/webhooks, verify, branch on Clerk event name):

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 })
}

For the full template covering all 15 events, the TS type declarations from @clerk/backend, the proxy.ts public-route setup, and the subscription status value table, see references/billing-webhooks.md.

10. Upgrade / Downgrade Flow

Let users manage their subscription from inside the app:

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 /> renders differently for subscribed users, it shows the current plan and allows upgrades or cancellations, all through Clerk's in-app checkout drawer.

Plan and Feature Naming

Plan slugs and feature slugs are defined in Clerk Dashboard → Billing. Common conventions:

TierPlan SlugExample Features
Free(no plan check needed)basic features
Starterstarteranalytics, api_access
Proproanalytics, export, team
Enterpriseenterpriseall features + sso, audit_logs

Use lowercase slugs matching what you define in the dashboard.

B2B vs B2C Billing

ScenarioWho subscribesPlan check
B2C SaaSIndividual userhas({plan: 'pro'}) on user session
B2B SaaSOrganizationhas({plan: 'org:team'}) on org session
Seat-limited B2BOrganizationPlan has a seat cap; pricing is per-plan, not per-member, tier your plans for bigger orgs

For B2B, ensure the user has an active org session. The has() check evaluates the active entity (user or org).

Checkout Flows

Clerk renders its own checkout drawer automatically through <PricingTable /> and <CheckoutButton />. Plans and pricing live in Clerk. To trigger checkout from a server action, redirect to a page that renders <PricingTable />:

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

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

Error Signatures (diagnose fast)

When you see any of these errors or symptoms, the fix is almost always a Dashboard toggle, not a code change. Do not start editing components.

Error / symptomRoot causeFix
Clerk: 🔒 The <PricingTable/> component cannot be rendered when billing is disabled. (code: cannot_render_billing_disabled, dev only)Billing is not enabled for this instanceEnable Billing at dashboard.clerk.com → Billing → Settings. No CLI path.
<PricingTable /> renders emptyNo plans, OR plan in the wrong tab (User vs Organization), OR Billing not enabledCreate plan in matching tab; pass for="organization" for B2B; check Billing Settings
Users can't subscribe to a personal plan on a B2C + B2B appMembership required mode (default since 2025-08-22) disables personal accounts, signed-in users are forced into choose-organization and never land on a personal-subscription stateIf you need personal + org subscriptions coexisting: Dashboard → Organizations settings → *Membership optional*
Can't find a Features pageFeatures are per-plan, not globalDashboard → Billing → Plans → click plan → Features
has({plan: 'pro'}) always returns false after a successful checkoutSession token hasn't been refreshed to include the new planawait clerk.session?.reload() or navigate to force a new session
has({plan: 'pro'}) returns false before any subscribe attemptPlan slug mismatch (case-sensitive), OR Billing not enabled, OR payment gateway not connected in productionVerify slug in Dashboard → Billing → Plans; confirm Billing → Settings shows enabled + connected gateway
has({permission: 'org:x:y'}) returns false for a user who has the roleThe Feature tied to that permission is not included in the organization's active PlanAdd the Feature to the Plan in Dashboard → Billing → Plans → Features
Webhook 401 / signature verification failedCLERK_WEBHOOK_SIGNING_SECRET mismatch or route protected by middlewareCopy the Signing Secret from Dashboard → Webhooks; add the webhook route to createRouteMatcher(['/api/webhooks(.*)'])

Billing Gates Permissions

When Billing is enabled, has({permission: 'org:posts:edit'}) returns false if the Feature associated with that permission is not included in the organization's active Plan, even if the user has the permission assigned via their role. This is by design: billing gates permissions at the feature level. Always ensure the required Feature is attached to the Plan in Dashboard → Billing → Plans → Features.

See Also

  • clerk-setup - Initial Clerk install
  • clerk-orgs - B2B organizations (required for B2B billing and seat-limit plans)
  • clerk-webhooks - Webhook signature verification and routing

适合场景

01

用户想查找某类 Agent Skill 时

02

需要根据任务场景推荐可安装能力包时

03

需要对比不同来源的安装命令和来源信息时

能力概览

能力 1

按任务关键词查找相关 Skills

能力 2

展示可复制的安装命令

能力 3

保留来源站点、仓库和原始说明,方便继续核验

能力 4

展示第三方安全扫描或审计结果

安装后应在对应宿主中按原始 README 的触发条件使用;具体调用方式请以来源页面和 README 为准。

平台分布

Codex

38.26%
按下载量换算240

Claude

28.77%
按下载量换算180

Cursor

17.82%
按下载量换算112

Gemini CLI

9.68%
按下载量换算61

安全审计

Gen Agent Trust Hub

通过

Socket

通过

Snyk

可疑

权限和风险

需要联网

该 Skill 可能需要联网访问来源站点、仓库或外部 API;具体网络访问范围需要结合源码和 README 复核。

安装前确认

本站仅展示第三方公开信息,不托管安装包,不提供自动安装或运行环境。安装前应自行审查源码、依赖和命令行为。来源安全扫描存在 warning/failed 结果,不能写成本站确认安全。当前只有一个来源,正式发布前建议补源仓库或其他目录站核验。

来源信息

继续浏览同类 Skills