关于 convex-billing
convex-billing 是一项技能,通过 @convex-dev/stripe 为 Convex 应用添加 Stripe 计费。它指导配置结账、签名验证 Webhook、订阅状态存储和服务端访问检查。
主要功能
- 01安装并配置 Stripe 组件
- 02设置订阅结账操作
- 03注册签名验证 Webhook
- 04在组件表中存储订阅状态
- 05通过服务端查询控制访问
适用场景
- 01为 Convex 应用添加 Stripe 订阅
- 02处理结账和订阅 Webhook 事件
- 03根据订阅状态限制服务端功能访问
安装
复制下面的命令,在终端中运行。
$
npx skills add https:/ / github.com/ get-convex/ agent-skills --skill convex-billing安装后在 Claude Code 中输入/convex-billing确认已加载。
详细介绍
添加计费 / 支付
使用 @convex-dev/stripe 将 Stripe 接入 Convex:创建结账 action、由组件注册的 httpAction webhook(自动验证签名)、存储在组件表中的订阅状态,以及通过 query 在服务端进行访问控制。
工作流
- 安装组件:
npm install @convex-dev/stripe。 - 创建
convex/convex.config.ts:import { defineApp } from 'convex/server'; import stripe from '@convex-dev/stripe/convex.config.js'; const app = defineApp(); app.use(stripe); export default app; - 将 Stripe 密钥存储在 Convex 环境变量中(使用
envmicro power):STRIPE_SECRET_KEY(sk_test_… / sk_live_…)和STRIPE_WEBHOOK_SECRET(whsec_…)。 - 创建
convex/http.ts以注册 webhook 路由(组件会自动处理签名验证):import { httpRouter } from 'convex/server'; import { components } from './_generated/api'; import { registerRoutes } from '@convex-dev/stripe'; const http = httpRouter(); registerRoutes(http, components.stripe, { webhookPath: '/stripe/webhook' }); export default http; - 创建
convex/billing.ts,其中包含结账 action 和订阅访问控制 query:import { action, query } from './_generated/server'; import { components } from './_generated/api'; import { StripeSubscriptions } from '@convex-dev/stripe'; import { v } from 'convex/values'; const stripeClient = new StripeSubscriptions(components.stripe, {}); export const createSubscriptionCheckout = action({ args: { priceId: v.string() }, returns: v.object({ sessionId: v.string(), url: v.union(v.string(), v.null()) }), handler: async (ctx, args) => { const identity = await ctx.auth.getUserIdentity(); if (!identity) throw new Error('Not authenticated'); const customer = await stripeClient.getOrCreateCustomer(ctx, { userId: identity.subject, email: identity.email, name: identity.name }); return await stripeClient.createCheckoutSession(ctx, { priceId: args.priceId, customerId: customer.customerId, mode: 'subscription', successUrl: `${process.env.SITE_URL ?? 'http://localhost:3000'}/?success=true`, cancelUrl: `${process.env.SITE_URL ?? 'http://localhost:3000'}/?canceled=true`, subscriptionMetadata: { userId: identity.subject } }); }, }); export const isSubscribed = query({ args: {}, returns: v.boolean(), handler: async (ctx) => { const identity = await ctx.auth.getUserIdentity(); if (!identity) return false; const subscriptions = await ctx.runQuery(components.stripe.public.listSubscriptionsByUserId, { userId: identity.subject }); return subscriptions.some((sub) => sub.status === 'active' || sub.status === 'trialing'); }, }); - 运行
npx convex dev --once— 它会安装组件并推送函数。确认输出中显示✔ Installed component stripe.。 - 在 Stripe Dashboard → Webhooks 中:添加端点
https://<deployment>.convex.site/stripe/webhook,订阅checkout.session.completed、customer.subscription.*、invoice.*、payment_intent.*。将签名密钥复制为STRIPE_WEBHOOK_SECRET。
规则
- 使用 @convex-dev/stripe(npm: @convex-dev/stripe@^0.1.4)— 它会通过 registerRoutes 在内部处理 webhook 签名验证;不要手动编写 constructEvent webhook。
- Stripe 密钥存储在 Convex 环境变量中(使用
envmicro power):STRIPE_SECRET_KEY 和 STRIPE_WEBHOOK_SECRET。 - 通过服务端存储的订阅状态进行访问控制,使用 isSubscribed query(读取组件表),而不是依赖客户端声明。
- convex/convex.config.ts 必须从 '@convex-dev/stripe/convex.config.js' 导入(不能使用 .ts)— Convex 打包器要求使用 .js 扩展名。
评论
支持 Markdown