Stripe 支付接入指南
路由说明
启用条件:PULSE_STRIPE_SECRET_KEY 非空 且 PULSE_SHOP_ENABLED=true
| 方法 | 路径 | 说明 |
|---|---|---|
POST |
/webhook/stripe |
Stripe Webhook 回调 |
GET |
/shop/plans |
套餐列表(JSON API) |
POST |
/shop/checkout |
创建 Stripe Checkout Session |
GET |
/panel/shop |
商店页面(用户侧) |
GET |
/panel/shop/success |
支付成功页 |
GET |
/panel/plans |
管理员套餐 CRUD |
环境变量
PULSE_STRIPE_SECRET_KEY=sk_test_... # Stripe Secret Key(必填)
PULSE_SHOP_ENABLED=true # 显式开启商店(必填)
PULSE_STRIPE_WEBHOOK_SECRET=whsec_... # Webhook 签名密钥(生产环境必填)
PULSE_STRIPE_WEBHOOK_SECRET留空时不验签,生产环境务必配置。
传入方式
项目不读 .env 文件,需手动传入:
# 方式一:命令前加变量
PULSE_STRIPE_SECRET_KEY=sk_test_xxx PULSE_SHOP_ENABLED=true make dev
# 方式二:export 到当前 shell(同一窗口运行 make dev)
export PULSE_STRIPE_SECRET_KEY=sk_test_xxx
export PULSE_SHOP_ENABLED=true
make dev
# 方式三:direnv(推荐,写 .env 文件后 direnv allow)
Stripe Dashboard 配置
1. 获取 Secret Key
dashboard.stripe.com/apikeys → 复制 Secret key(sk_test_...)
2. 配置 Webhook
dashboard.stripe.com/webhooks → Add endpoint:
- URL:
https://你的域名/webhook/stripe - 事件:至少勾选
checkout.session.completed - 创建后点进去 → Signing secret → Reveal → 复制
whsec_...
3. 创建 Product & Price
- Products → Add product
- 填写名称,添加价格(选择一次性或订阅)
- 创建后进入 Product 详情页 → Pricing 部分 → 复制
price_xxx
注意:
prod_xxx是 Product ID,price_xxx才是 Price ID,填错会报resource_missing。
测试模式
Stripe Dashboard 右上角开启 Sandbox(旧版叫 Test mode),此模式下:
- API Key 为
sk_test_...开头 - 支付不会真实扣款
测试卡号:
| 字段 | 值 |
|---|---|
| 卡号 | 4242 4242 4242 4242 |
| 有效期 | 任意未来日期(如 12/34) |
| CVC | 任意 3 位(如 123) |
套餐配置注意事项
套餐类型必须与 Stripe Price 类型匹配,否则报错:
| Pulse 套餐类型 | Stripe Price 类型 |
|---|---|
one_time |
One-time(一次性) |
subscription |
Recurring(订阅) |
错误示例:套餐类型为 one_time 但 Stripe Price 是订阅价格,会报:
You specified `payment` mode but passed a recurring price.
本地 Webhook 测试
方式一:ngrok(无需安装 Stripe CLI)
brew install ngrok
ngrok http 8080
# 将 ngrok URL 填入 Stripe Dashboard Webhook endpoint
方式二:Stripe CLI
brew install stripe/stripe-cli/stripe
stripe login
stripe listen --forward-to localhost:8080/webhook/stripe
# 终端会打印临时 whsec_...,用作 PULSE_STRIPE_WEBHOOK_SECRET
常见错误排查
| 错误 | 原因 | 解决 |
|---|---|---|
plan has no Stripe price configured |
套餐未填 Stripe Price ID | 编辑套餐填入 price_xxx |
No such price: 'prod_xxx' |
填了 Product ID 而非 Price ID | 在 Product 详情 Pricing 部分复制 price_xxx |
payment mode but passed a recurring price |
套餐类型与 Price 类型不匹配 | 统一为 subscription 或 one_time |
CSRF token invalid |
HTMX 请求未携带 CSRF token | 确认 shared.html 用 document.addEventListener 而非 document.body.addEventListener |
failed to create checkout session |
Stripe API 调用失败 | 查看服务端日志具体错误 |