> ## 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.

# 部署到 Vercel

> 五分钟 Vercel 部署——push、填环境变量、点 Deploy。

Vercel 是阻力最小的路径。仓库默认配置（Next.js 15、pnpm、Node runtime）和 Vercel 预设完全吻合，
导入即用，前提是环境变量已经填好。从 `git push` 到上线大约 5 分钟，大头是粘 secrets 和建数据库。

## 前置条件

* 一个带**连接池**的 Postgres 数据库。Neon、Supabase、Railway、Crunchy 都行。Serverless 函数
  不维持长连接，所以必须用 pooler URL（Supabase 的 6543 端口、Neon 的 `-pooler` 主机名）。
* 一个 32 位以上的随机 `BETTER_AUTH_SECRET`：`openssl rand -base64 32`。
* 当前支付 provider 的密钥。`siteConfig.payment.provider` 选哪个，对应的 `*_SECRET_KEY`、
  `*_WEBHOOK_SECRET` 和相关 `*_PRICE_*` id 就要填齐。
* 可选但建议：`RESEND_API_KEY` 用于事务邮件。没有的话邮件 facade 静默 no-op，注册会跳过验证步骤。
* Vercel 能读到的 GitHub 仓库。SSO、monorepo、fork 都行。

push 之前本地跑一遍 `pnpm typecheck && pnpm lint && pnpm build`。CI 会跑同样三条；
Vercel 也会在同一关卡拒掉坏 build。

## 步骤

### 1. Push 到 GitHub

```bash theme={null}
git push origin main
```

私有仓库需要在 GitHub app 设置里授权 Vercel。

### 2. 在 Vercel 导入

Vercel dashboard：Add New → Project → 选仓库。Framework Preset 自动识别为 **Next.js**。
build command 留空（Vercel 从 `package.json` 读 `pnpm build`）。Output directory 是 `.next`（默认）。

### 3. 填环境变量

Settings → Environment Variables。**必填**基线：

```
DATABASE_URL=postgres://user:pass@host:6543/db?sslmode=require
BETTER_AUTH_SECRET=<32+ 随机字符>
BETTER_AUTH_URL=https://your-domain.com
NEXT_PUBLIC_APP_URL=https://your-domain.com
ADMIN_EMAILS=you@example.com
```

加上**当前激活**支付 provider 的密钥（Stripe 示例）：

```
STRIPE_SECRET_KEY=sk_live_…
STRIPE_WEBHOOK_SECRET=whsec_…
STRIPE_PRICE_VIBESTRAP_PROMO=price_…
NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY=pk_live_…
```

可选但常用：`RESEND_API_KEY`、`GOOGLE_CLIENT_ID/SECRET`、`TURNSTILE_SECRET_KEY` +
`NEXT_PUBLIC_TURNSTILE_SITE_KEY`。完整清单见 [Env 参考](/zh/env-reference)。

### 4. 同步生产数据库

**首次部署前**先把 schema 同步过去。两种方式：

```bash theme={null}
# 方案 A —— 最快，dev 风格。把 schema 与 DB diff 后直接执行。
DATABASE_URL=<prod-url> pnpm db:push
```

```bash theme={null}
# 方案 B —— 提交式 migration。一旦有真实数据就用这个。
pnpm db:generate            # 在 drizzle/ 下写一份 SQL 文件
git add drizzle/ && git commit
DATABASE_URL=<prod-url> pnpm db:migrate
```

一旦有付费用户，**绝对不要**再对生产跑 `db:push`——它会无声丢列。改用方案 B，从 deploy hook
或一次性脚本跑 `db:migrate`，别从运行中的 app 跑。

### 5. 配置支付 webhook

在支付 provider 的 dashboard，加 webhook endpoint，指向：

```
https://your-domain.com/api/webhooks/stripe
https://your-domain.com/api/webhooks/creem
https://your-domain.com/api/webhooks/nowpayments
```

挑你激活 provider 对应的那条。订阅：`checkout.session.completed`、`invoice.paid`、
`customer.subscription.updated`、`customer.subscription.deleted`（Stripe 命名——
其他 provider 自行对应）。把签名密钥贴回 `STRIPE_WEBHOOK_SECRET`（或对应变量）。

### 6. Deploy

push 或点 Deploy。首次 build 2-3 分钟。Vercel 的日志会大声报 env 错误——`src/env.ts` 会在
启动时对必填项缺失抛错。

## 验证

逐个访问下面的 URL，红的就是哪里没配好。

* `https://your-domain.com/` —— 首页正常，hero 是你设的语言
* `https://your-domain.com/zh/` —— 中文 hero
* `https://your-domain.com/api/ping` —— 返回 `{ ok: true }`
* `https://your-domain.com/sitemap.xml` —— 所有公共路由都列出来
* `https://your-domain.com/robots.txt` —— `/admin`、`/api/`、`/settings` 全 disallow
* `https://your-domain.com/register` —— 注册可用，能收到验证邮件

然后用 live key 跑一笔真实的 \$1 充值（之后退给自己）：`payment` 和 `credit_transaction` 两张
表应该在结账后 5 秒内各出现一行。如果没出现，去支付 dashboard 的 webhook 日志看 4xx 响应。

## 常见坑

* **`BETTER_AUTH_URL` 不一致。** 必须是带 scheme 的完整 prod URL，不带末尾斜杠。和实际域名
  漂移时认证回调会静默失败。
* **prod 缺环境变量。** Vercel 把 Production / Preview / Development 分开。除非你刻意要分环境
  值，否则统一勾「All Environments」。
* **Webhook 签名密钥不对。** 旧的 `STRIPE_WEBHOOK_SECRET` 会让每个事件返回 400。在 dashboard
  轮换一次密钥，贴回 Vercel，重新部署。
* **Postgres 连接数撑满。** Serverless 函数烧连接很快。用 pooler URL（Supabase 6543 端口、
  Neon 的 `-pooler.region` 主机）。撞「max connections」？你用的是直连 URL。
* **忘了 `NEXT_PUBLIC_APP_URL`。** 被 sitemap、OG 图、OAuth redirect、支付成功 URL 用。
  生产环境必须和 `BETTER_AUTH_URL` 一致。
* **首次部署前没跑 `db:push`。** App 起得来，第一个 query 就 500，因为表不存在。

## 回滚

Vercel 永久保留每次部署。从 Deployments 列表把任意旧 build promote 回去——秒级回滚。
如果是 migration 的锅，用 DB provider 的 point-in-time 快照恢复（Neon 和 Supabase 所有套餐
都自带）。

## 官方文档

* Vercel Next.js 指南：[vercel.com/docs/frameworks/nextjs](https://vercel.com/docs/frameworks/nextjs)
* Env 变量：[vercel.com/docs/projects/environment-variables](https://vercel.com/docs/projects/environment-variables)
* Cron jobs（用于积分过期清扫）：[vercel.com/docs/cron-jobs](https://vercel.com/docs/cron-jobs)
* Drizzle migration：[orm.drizzle.team/docs/migrations](https://orm.drizzle.team/docs/migrations)
