> ## Documentation Index
> Fetch the complete documentation index at: https://docs.vibestrap.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# Stripe

> 将 Stripe 接入为当前支付 Provider —— key、商品、webhook、本地联调。

Stripe 是 Vibestrap 的默认 Provider —— 开发体验最好、文档最全、对北美/欧洲
栈来说费率最低。代价是：你不是 Merchant of Record，所以欧盟 VAT 和美国销售税
得自己处理（开 Stripe Tax 加 0.5% 服务费可以解决大部分场景）。

## 前置条件

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

## 1. 选定当前 Provider

打开 `src/config/site.ts`：

```ts theme={null}
payment: {
  provider: 'stripe' as 'stripe' | 'creem' | 'nowpayments',
  currency: 'usd',
},
```

默认就是 `'stripe'`。如果之前改过，改回来。

## 2. 在 Stripe Dashboard 建商品和价格

进 Stripe Dashboard，**Products → Add product**。每个要卖的 scene 建一个商品。
卖 Vibestrap 脚手架本体时同一个商品下挂两个价格（或者拆成两个商品）：

| Scene              | 说明               |
| ------------------ | ---------------- |
| Vibestrap promo    | 限时价（例如 \$49 一次性） |
| Vibestrap standard | 原价（例如 \$99 一次性）  |

demo SaaS 在 `siteConfig.demoPlans` 里自带 4 档样板，覆盖常见定价模式：
`lifetime_promo` + `lifetime_standard`（一次性，复用 VIBESTRAP 的 price ID）
和 `pro_monthly` + `pro_yearly`（订阅，要单独建 Stripe 价格）。要加新档位
直接扩展数组即可。

记下每个 `price_…` ID，下一步要塞到环境变量里。

## 3. 配环境变量

`.env.local`（变量名严格对齐 `src/env.ts`）：

```bash theme={null}
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_...

# demo 订阅档位（recurring；一次性档位复用上面的 VIBESTRAP_* 价格）
STRIPE_PRICE_PRO_MONTHLY=price_...
STRIPE_PRICE_PRO_YEARLY=price_...
```

UI 上没有露出来的商品对应的变量可以留空。

## 4. 本地用 Stripe CLI 联调

```bash theme={null}
stripe login
stripe listen --forward-to localhost:3000/api/webhooks/stripe
```

CLI 会打印一个形如 `whsec_…` 的 webhook 签名密钥。把它贴到 `.env.local` 的
`STRIPE_WEBHOOK_SECRET`，重启 `pnpm dev`。之后所有测试支付都会带正确签名打到
你本地的路由。

不走 UI 直接造一个事件：

```bash theme={null}
stripe trigger checkout.session.completed
```

## 5. 生产环境 webhook

Stripe Dashboard：**Developers → Webhooks → Add endpoint**。

* **URL**：`https://your-domain.com/api/webhooks/stripe`
* **Events**（至少订阅）：`checkout.session.completed`、`invoice.paid`、
  `customer.subscription.updated`、`customer.subscription.deleted`。
* 创建完点进去复制 **Signing secret**（`whsec_…`），作为生产环境的
  `STRIPE_WEBHOOK_SECRET`。

## 验证

1. `stripe listen` 开着，启动 dev。
2. 打开 `/pricing`，点 "Get Vibestrap"，会跳到 Stripe Checkout。
3. 用测试卡 `4242 4242 4242 4242`，过期日填未来任意月份，CVC 和邮编随便。
4. 应该回跳到你设置的 `successUrl`。
5. 查数据库：
   ```sql theme={null}
   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，要么自己用 Creem 这类做地区分流。
* **`successUrl` / `cancelUrl` 缺失** —— 任一个为空 `createCheckout` 都会抛
  错。必须是绝对 URL（`https://…`）。
* **订阅丢 `subscription_data.metadata`** —— 续订发的 `invoice.paid` 事件本身
  `metadata` 是空的。Stripe Provider 会把 checkout 的 `metadata` 复制到
  `subscription_data.metadata`，让续订也带着 `userId` / `scene`，别去掉这条
  路径。

## 官方文档

* Stripe 文档：[stripe.com/docs](https://stripe.com/docs)
* Stripe CLI：[docs.stripe.com/cli](https://docs.stripe.com/cli)
* Webhooks 参考：[docs.stripe.com/webhooks](https://docs.stripe.com/webhooks)
* 测试卡号：[docs.stripe.com/testing](https://docs.stripe.com/testing)
* Stripe Tax：[stripe.com/tax](https://stripe.com/tax)
