Commet
Commet 是针对 SaaS 和 AI 产品的账单与支付解决方案。作为记录商(Merchant of Record),Commet 处理订阅、按使用量计费、税务合规和全球支付 - 让你几分钟内即可开始盈利。
这个插件由 Commet 团队维护。如遇到漏洞、问题或功能请求,请联系 Commet 支持。
特性
【Features】
- 注册时自动创建客户
- 客户门户用于自助账单管理
- 订阅管理(获取、更改计划、取消)
- 功能访问控制(布尔值、计量、座位)
- 计量计费的使用追踪
- 每用户定价的座位管理
- 可选的带签名验证的 Webhook 处理
安装
【Installation】
pnpm add better-auth @commet/better-auth @commet/node准备
【Preparation】
从 Commet 控制面板 获取你的 API 密钥。
【Get your API key from the Commet dashboard.】
COMMET_API_KEY=ck_...
COMMET_ENVIRONMENT=sandbox # or production服务器配置
【Server Configuration】
import { betterAuth } from "better-auth";
import {
commet,
portal,
subscriptions,
features,
usage,
seats,
} from "@commet/better-auth";
import { Commet } from "@commet/node";
const commetClient = new Commet({
apiKey: process.env.COMMET_API_KEY,
environment: process.env.COMMET_ENVIRONMENT, // 'sandbox' or 'production'
});
export const auth = betterAuth({
// ... your config
plugins: [
commet({
client: commetClient,
createCustomerOnSignUp: true,
use: [
portal(),
subscriptions(),
features(),
usage(),
seats(),
],
}),
],
});客户端配置
【Client Configuration】
import { createAuthClient } from "better-auth/react";
import { commetClient } from "@commet/better-auth";
export const authClient = createAuthClient({
plugins: [commetClient()],
});配置选项
【Configuration Options】
commet({
client: commetClient, // Required: Commet SDK instance
createCustomerOnSignUp: true, // Auto-create customer on signup
getCustomerCreateParams: ({ user }) => ({
legalName: user.name,
metadata: { source: "web" },
}),
use: [/* plugins */],
})当启用 createCustomerOnSignUp 时,会自动创建一个 Commet 客户,并将 externalId 设置为用户的 ID。无需数据库映射。
【When createCustomerOnSignUp is enabled, a Commet customer is automatically created with externalId set to the user's ID. No database mapping required.】
门户插件
【Portal Plugin】
将用户重定向到 Commet 客户门户,自助管理账单。
【Redirects users to the Commet customer portal for self-service billing management.】
import { commet, portal } from "@commet/better-auth";
commet({
client: commetClient,
use: [
portal({ returnUrl: "/dashboard" }),
],
})// Redirects to Commet customer portal
await authClient.customer.portal();订阅插件
【Subscriptions Plugin】
管理客户订阅。
【Manage customer subscriptions.】
import { commet, subscriptions } from "@commet/better-auth";
commet({
client: commetClient,
use: [subscriptions()],
})// Get current subscription
const { data: subscription } = await authClient.subscription.get();
// Change plan
await authClient.subscription.changePlan({
subscriptionId: "sub_xxx",
planCode: "enterprise",
billingInterval: "yearly",
});
// Cancel subscription
await authClient.subscription.cancel({
subscriptionId: "sub_xxx",
reason: "Too expensive",
immediate: false, // Cancel at period end
});功能插件
【Features Plugin】
检查经过身份验证的用户的功能访问权限。
【Check feature access for the authenticated user.】
import { commet, features } from "@commet/better-auth";
commet({
client: commetClient,
use: [features()],
})// List all features
const { data: featuresList } = await authClient.features.list();
// Get specific feature
const { data: feature } = await authClient.features.get("api_calls");
// Check if feature is enabled (boolean)
const { data: check } = await authClient.features.check("sso");
// Check if user can use one more unit (metered)
const { data: canUse } = await authClient.features.canUse("api_calls");
// Returns: { allowed: boolean, willBeCharged: boolean }使用插件
【Usage Plugin】
跟踪计量计费的使用事件。
【Track usage events for metered billing.】
import { commet, usage } from "@commet/better-auth";
commet({
client: commetClient,
use: [usage()],
})await authClient.usage.track({
eventType: "api_call",
value: 1,
idempotencyKey: `evt_${Date.now()}`,
properties: { endpoint: "/api/generate" },
});经过身份验证的用户会自动与该事件关联。
【The authenticated user is automatically associated with the event.】
座位插件
【Seats Plugin】
管理基于席位的许可证。
【Manage seat-based licenses.】
import { commet, seats } from "@commet/better-auth";
commet({
client: commetClient,
use: [seats()],
})// List all seat balances
const { data: seatBalances } = await authClient.seats.list();
// Add seats
await authClient.seats.add({ seatType: "member", count: 5 });
// Remove seats
await authClient.seats.remove({ seatType: "member", count: 2 });
// Set exact count
await authClient.seats.set({ seatType: "admin", count: 3 });
// Set all seat types at once
await authClient.seats.setAll({ admin: 2, member: 10, viewer: 50 });Webhooks 插件(可选)
【Webhooks Plugin (Optional)】
处理 Commet 的 webhooks。这是可选的,因为你始终可以直接查询状态。
【Handle Commet webhooks. This is optional since you can always query state directly.】
import { commet, webhooks } from "@commet/better-auth";
commet({
client: commetClient,
use: [
webhooks({
secret: process.env.COMMET_WEBHOOK_SECRET,
onPayload: (payload) => {
// Catch-all handler
},
onSubscriptionCreated: (payload) => {},
onSubscriptionActivated: (payload) => {},
onSubscriptionCanceled: (payload) => {},
onSubscriptionUpdated: (payload) => {},
}),
],
})在你的 Commet 仪表板中配置 webhook 端点:/api/auth/commet/webhooks
【Configure the webhook endpoint in your Commet dashboard: /api/auth/commet/webhooks】
完整示例
【Full Example】
import { betterAuth } from "better-auth";
import { drizzleAdapter } from "better-auth/adapters/drizzle";
import {
commet as commetPlugin,
portal,
subscriptions,
features,
usage,
seats,
} from "@commet/better-auth";
import { Commet } from "@commet/node";
import { db } from "./db";
import * as schema from "./schema";
const commetClient = new Commet({
apiKey: process.env.COMMET_API_KEY!,
environment: process.env.COMMET_ENVIRONMENT as "sandbox" | "production",
});
export const auth = betterAuth({
database: drizzleAdapter(db, { provider: "pg", schema }),
emailAndPassword: { enabled: true },
plugins: [
commetPlugin({
client: commetClient,
createCustomerOnSignUp: true,
getCustomerCreateParams: ({ user }) => ({
legalName: user.name,
}),
use: [
portal({ returnUrl: "/dashboard" }),
subscriptions(),
features(),
usage(),
seats(),
],
}),
],
});import { createAuthClient } from "better-auth/react";
import { commetClient } from "@commet/better-auth";
export const authClient = createAuthClient({
baseURL: process.env.NEXT_PUBLIC_BETTER_AUTH_URL,
plugins: [commetClient()],
});
export const { signIn, signUp, signOut, useSession } = authClient;"use client";
import { authClient } from "@/lib/auth-client";
export function BillingSection() {
const handlePortal = async () => {
await authClient.customer.portal();
};
const checkFeature = async () => {
const { data } = await authClient.features.canUse("api_calls");
if (data?.allowed) {
// Proceed with action
await authClient.usage.track({ eventType: "api_call" });
}
};
return (
<div>
<button onClick={handlePortal}>Manage Billing</button>
<button onClick={checkFeature}>Use Feature</button>
</div>
);
}