跳转到主要内容
Stripe 是 vibestrap 的默认 Provider —— 开发体验最好、文档最全、对北美/欧洲 栈来说费率最低。代价是:你不是 Merchant of Record,所以欧盟 VAT 和美国销售税 得自己处理(开 Stripe Tax 加 0.5% 服务费可以解决大部分场景)。

前置条件

  • 一个 Stripe 账号(stripe.com)—— 免费,测试模式不需要 实体公司。
  • Stripe CLI 用来本地转发 webhook —— 见 docs.stripe.com/stripe-cli
  • 一个跑起来的 Postgres(pnpm db:push 已经执行过)。

1. 选定当前 Provider

打开 src/config/site.ts
payment: {
  provider: 'stripe' as 'stripe' | 'paddle' | 'lemonsqueezy' | 'creem',
  currency: 'usd',
},
默认就是 'stripe'。如果之前改过,改回来。

2. 在 Stripe Dashboard 建商品和价格

进 Stripe Dashboard,Products → Add product。每个要卖的 scene 建一个商品。 卖 vibestrap 脚手架本体时同一个商品下挂两个价格(或者拆成两个商品):
Scene说明
Vibestrap promo限时价(例如 $49 一次性)
Vibestrap standard原价(例如 $99 一次性)
自家的 buyer-app 通常会加一个 pro_monthly 订阅、一个 pro_yearly 订阅、 一个 lifetime 一次性,再加 1–4 个 credits_* 一次性。 记下每个 price_… ID,下一步要塞到环境变量里。

3. 配环境变量

.env.local(变量名严格对齐 src/env.ts):
STRIPE_SECRET_KEY=sk_test_...
STRIPE_WEBHOOK_SECRET=whsec_...        # 见第 5 步
NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY=pk_test_...

# vibestrap 本体
STRIPE_PRICE_VIBESTRAP_PROMO=price_...
STRIPE_PRICE_VIBESTRAP_STANDARD=price_...

# buyer-app 自身的定价(卖给你客户的)
STRIPE_PRICE_PRO_MONTHLY=price_...
STRIPE_PRICE_PRO_YEARLY=price_...
STRIPE_PRICE_LIFETIME=price_...
STRIPE_PRICE_CREDITS_BASIC=price_...
STRIPE_PRICE_CREDITS_STANDARD=price_...
STRIPE_PRICE_CREDITS_PREMIUM=price_...
STRIPE_PRICE_CREDITS_ENTERPRISE=price_...
UI 上没有露出来的商品对应的变量可以留空。

4. 本地用 Stripe CLI 联调

stripe login
stripe listen --forward-to localhost:3000/api/webhooks/stripe
CLI 会打印一个形如 whsec_… 的 webhook 签名密钥。把它贴到 .env.localSTRIPE_WEBHOOK_SECRET,重启 pnpm dev。之后所有测试支付都会带正确签名打到 你本地的路由。 不走 UI 直接造一个事件:
stripe trigger checkout.session.completed

5. 生产环境 webhook

Stripe Dashboard:Developers → Webhooks → Add endpoint
  • URLhttps://your-domain.com/api/webhooks/stripe
  • Events(至少订阅):checkout.session.completedinvoice.paidcustomer.subscription.updatedcustomer.subscription.deleted
  • 创建完点进去复制 Signing secretwhsec_…),作为生产环境的 STRIPE_WEBHOOK_SECRET

验证

  1. stripe listen 开着,启动 dev。
  2. 打开 /pricing,点 “Get vibestrap”,会跳到 Stripe Checkout。
  3. 用测试卡 4242 4242 4242 4242,过期日填未来任意月份,CVC 和邮编随便。
  4. 应该回跳到你设置的 successUrl
  5. 查数据库:
    select id, provider, scene, status, amount from payment
    order by created_at desc limit 1;
    
    能看到 provider='stripe'status='paid'、对应金额。
  6. stripe trigger checkout.session.completed 一次 —— 不会有重复行, 因为 payment.invoiceId / sessionId 做了幂等。

常见坑

  • Webhook 密钥对不上 —— stripe listen 打印的密钥每次会话都会变,不要拿 到生产环境。生产用 Dashboard 上 endpoint 的那一个。
  • 测试 vs 正式模式混用 —— 测试模式 sk_test_* 和正式模式 sk_live_* 下的 价格 ID 不通用,一个 price ID 只在一个模式里存在。
  • 没开 Stripe Tax —— 卖到欧盟没开 Stripe Tax 的话 VAT 会从你利润里扣。 要么打开 Stripe Tax,要么换成 MoR(Paddle / Lemon Squeezy)。
  • successUrl / cancelUrl 缺失 —— 任一个为空 createCheckout 都会抛 错。必须是绝对 URL(https://…)。
  • 订阅丢 subscription_data.metadata —— 续订发的 invoice.paid 事件本身 metadata 是空的。Stripe Provider 会把 checkout 的 metadata 复制到 subscription_data.metadata,让续订也带着 userId / scene,别去掉这条 路径。

官方文档