关于 stripe-stack
stripe-stack 是面向 Next.js 和 Supabase 项目的 Stripe 支付集成技能,提供 Webhook、订阅与积分系统的工作流和模板,并强调幂等处理及安全切换测试与正式环境。
主要功能
- 01Next.js 与 Supabase 集成工作流
- 02基于数据库的 Webhook 幂等处理
- 03Webhook 签名验证
- 04订阅与积分系统模式
- 05测试转正式环境检查清单
适用场景
- 01在新建的 Next.js 项目中接入 Stripe
- 02可靠地处理 Stripe Webhook
- 03实现周期性订阅
- 04构建按量付费积分
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ scientiacapital/ skills --skill stripe-stack安装后在 Claude Code 中输入/stripe-stack确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
config.jsonreference/common-errors.mdenvironment-vars.mdpricing-models.mdwebhook-patterns.mdtemplates/env-example.txtidempotency-migration.sqlplans-config.tsstripe-client.tswebhook-handler-nextjs.tswebhook-handler.test.tsworkflows/add-credit-system.mdadd-webhook-handler.mdgo-live-checklist.mdimplement-subscriptions.mdsetup-new-project.md详细介绍
<quick_start> 为 Next.js + Supabase 项目添加支付功能:
- 安装 Stripe:
npm install stripe @stripe/stripe-js - 添加环境变量(参见下方的 quick_reference)
- 创建幂等性表(参见下方的架构)
- 选择工作流:
setup-new-project.md或add-webhook-handler.md
// Lazy-loaded Stripe client
import Stripe from 'stripe';
let _stripe: Stripe | null = null;
export function getStripe(): Stripe {
if (!_stripe) {
_stripe = new Stripe(process.env.STRIPE_SECRET_KEY!, { apiVersion: '2025-12-15.clover' });
}
return _stripe;
}
</quick_start>
<success_criteria> 集成成功的标准:
- Webhook 处理程序使用基于数据库的幂等性机制(而非内存机制)
- 所有密钥都存储在环境变量中(绝不硬编码)
- 在部署任何正式模式配置前,测试模式已全面正常运行
- 所有 Webhook 端点都进行签名验证
- 处理事件前记录事件(先插入、后处理模式)
- 正式上线检查清单已完成 </success_criteria>
<essential_principles>
核心原则
-
幂等性不可妥协
- 所有 Webhook 处理程序都必须使用基于数据库的幂等性机制
- 绝不使用内存中的 Set(无服务器冷启动时会丢失)
- 处理事件前插入事件记录,而不是处理后插入
-
测试模式与正式模式分离
- 所有密钥都使用环境变量(绝不硬编码)
- 测试密钥:
sk_test_、pk_test_、whsec_test_ - 正式密钥:
sk_live_、pk_live_、whsec_live_ - 产品和价格必须在正式模式下重新创建
-
共享 Stripe 账户
- 所有 NetZero Suite 项目共用一个 Stripe 账户
- 多个项目可以使用同一个 Webhook 密钥
- 每个项目都有自己的 Webhook 端点 URL
-
延迟初始化客户端
- 绝不在模块级别初始化 Stripe(会导致构建错误)
- 服务端客户端使用工厂函数模式
- 创建实例前检查 API 密钥
</essential_principles>
你要构建什么?
继续前,请确定你的使用场景:
| 使用场景 | 工作流 | 描述 |
|---|---|---|
| 新项目 | setup-new-project.md | 从头开始集成 Stripe |
| 添加 Webhook | add-webhook-handler.md | 为现有项目添加 Webhook 处理程序 |
| 订阅 | implement-subscriptions.md | 使用套餐进行周期性计费 |
| 积分系统 | add-credit-system.md | 按需付费积分 |
| 正式上线 | go-live-checklist.md | 从测试模式迁移到正式模式 |
工作流路由
如果要在新项目中设置 Stripe:
→ 阅读 workflows/setup-new-project.md
→ 然后阅读 reference/environment-vars.md
→ 使用 templates/stripe-client.ts 和 templates/env-example.txt
如果要添加 Webhook 处理:
→ 阅读 workflows/add-webhook-handler.md
→ 然后阅读 reference/webhook-patterns.md
→ 使用 templates/webhook-handler-nextjs.ts 和 templates/idempotency-migration.sql
如果要实现订阅计费:
→ 阅读 workflows/implement-subscriptions.md
→ 然后阅读 reference/pricing-models.md
→ 使用 templates/plans-config.ts
如果要添加积分/用量计费系统:
→ 阅读 workflows/add-credit-system.md
→ 然后阅读 reference/pricing-models.md
如果要从测试模式迁移到正式模式:
→ 阅读 workflows/go-live-checklist.md
<quick_reference>
快速参考
环境变量(标准)
# Server-side (never expose to client)
STRIPE_SECRET_KEY=sk_test_...
STRIPE_WEBHOOK_SECRET=whsec_...
# Client-side (safe to expose)
NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY=pk_test_...
# Optional: Price IDs (for test→live switching)
STRIPE_PRICE_STARTER_MONTHLY=price_...
STRIPE_PRICE_PRO_MONTHLY=price_...
常见 Webhook 事件
| 事件 | 触发时机 | 操作 |
|---|---|---|
checkout.session.completed | 客户完成结账 | 创建订阅记录 |
customer.subscription.created | 新订阅开始 | 初始化用户限额 |
customer.subscription.updated | 套餐变更、续订 | 更新套餐/限额 |
customer.subscription.deleted | 取消订阅 | 降级为免费套餐 |
invoice.paid | 月度续订成功 | 重置用量计数器 |
invoice.payment_failed | 支付失败 | 标记为 past_due |
Stripe 客户端模式
let _stripe: Stripe | null = null;
export function getStripe(): Stripe {
if (!_stripe) {
const key = process.env.STRIPE_SECRET_KEY;
if (!key) throw new Error('STRIPE_SECRET_KEY not configured');
_stripe = new Stripe(key, {
apiVersion: '2025-12-15.clover',
typescript: true
});
}
return _stripe;
}
### 幂等性表结构
CREATE TABLE stripe_webhook_events (
id TEXT PRIMARY KEY, -- Use Stripe event ID directly
type TEXT NOT NULL, -- Event type
data JSONB NOT NULL, -- Full event payload
processed_at TIMESTAMPTZ DEFAULT NOW()
);
Webhook 处理器结构
export async function POST(request: NextRequest) {
const body = await request.text();
const signature = request.headers.get('stripe-signature');
// 1. Verify signature
const event = stripe.webhooks.constructEvent(body, signature, webhookSecret);
// 2. Check idempotency (BEFORE processing)
const { data: existing } = await supabase
.from('stripe_webhook_events')
.select('id')
.eq('id', event.id)
.single();
if (existing) return NextResponse.json({ duplicate: true });
// 3. Log event (INSERT before processing)
await supabase.from('stripe_webhook_events').insert({
id: event.id,
type: event.type,
data: event,
});
// 4. Process event
switch (event.type) {
case 'checkout.session.completed':
await handleCheckout(event.data.object);
break;
// ... other handlers
}
return NextResponse.json({ received: true });
}
</quick_reference>
<integration_notes>
集成说明
兼容
- Supabase:Webhook 处理器使用服务角色客户端(绕过 RLS)
- Prisma:Supabase 幂等性表的替代方案
- Vercel:为 Webhook 路由添加 runtime/maxDuration 配置
- Next.js App Router:使用
request.text()获取原始请求体
相关技能
supabase-sql-skill- 用于数据库迁移create-hooks-skill- 用于部署后的通知
GitHub 仓库
可获取私有模板和示例:
github.com/ScientiaCapital/stripe-stack
</integration_notes>
<reference_index>
参考文件
| 文件 | 用途 |
|---|---|
reference/webhook-patterns.md | 幂等性、事件处理、错误恢复 |
reference/pricing-models.md | 套餐、额度与按使用量计费 |
reference/environment-vars.md | 标准环境变量约定 |
reference/common-errors.md | 故障排查指南 |
模板文件
| 文件 | 用途 |
|---|---|
templates/webhook-handler-nextjs.ts | 完整的 Webhook 路由(复制粘贴即可使用) |
templates/stripe-client.ts | 延迟加载的客户端工厂 |
templates/plans-config.ts | 订阅套餐定义 |
templates/idempotency-migration.sql | Supabase 迁移 |
templates/webhook-handler.test.ts | 测试模板 |
templates/env-example.txt | 标准 .env 模板 |
工作流文件
| 文件 | 用途 |
|---|---|
workflows/setup-new-project.md | 全新 Stripe 集成 |
workflows/add-webhook-handler.md | 为现有项目添加 Webhook |
workflows/implement-subscriptions.md | 订阅计费 |
workflows/add-credit-system.md | 按需付费额度 |
workflows/go-live-checklist.md | 测试 → 生产环境迁移 |
</reference_index>
输出结果旁文件
最后一步,将内容写入 ~/.claude/skill-analytics/last-outcome-stripe-stack.json:
{"ts":"[UTC ISO8601]","skill":"stripe-stack","version":"1.0.0","variant":"default",
"status":"[success|partial|error]","runtime_ms":[estimated ms from start],
"metrics":{"webhooks_configured":[n],"products_created":[n],"checkout_flows_built":[n]},
"error":null,"session_id":"[YYYY-MM-DD]"}
如果部分阶段失败但已生成结果,请使用状态 "partial"。仅当未生成任何输出时,才使用 "error"。