billing-security

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Billing & Security Integration Patterns

计费与安全集成模式

"Configuration is not reality. Verification must be active, not passive."
This skill codifies lessons from 3 prod incidents (chrondle, bibliomnomnom, volume) where Stripe integrations failed despite passing code review.
"配置不等于实际情况。验证必须主动进行,而非被动。"
本技能总结了3起生产环境事件(chrondle、bibliomnomnom、volume)的经验教训,这些事件中Stripe集成虽通过代码审查但仍出现故障。

Core Principle

核心原则

Code reviews catch code bugs, not configuration bugs. External service integrations require:
  1. Format validation (API key patterns, URL formats)
  2. Reachability verification (can we actually reach the webhook URL?)
  3. Cross-deployment parity (Vercel and Convex must have same config)
  4. Runtime reconciliation (compare external state vs database state)

代码审查能发现代码缺陷,但无法发现配置缺陷。 外部服务集成需要:
  1. 格式验证(API密钥格式、URL格式)
  2. 可达性验证(能否实际访问webhook URL?)
  3. 跨部署一致性(Vercel和Convex必须配置相同)
  4. 运行时协调(对比外部状态与数据库状态)

Critical Patterns

关键模式

1. Environment Variable Hygiene

1. 环境变量规范

Always trim env vars:
typescript
// WRONG - trailing whitespace causes "Invalid character in header"
const key = process.env.STRIPE_SECRET_KEY;

// RIGHT - always trim
const key = process.env.STRIPE_SECRET_KEY?.trim();
Validate format before use:
typescript
const STRIPE_KEY_PATTERN = /^sk_(test|live)_[a-zA-Z0-9]+$/;
if (!STRIPE_KEY_PATTERN.test(key)) {
  throw new Error("Invalid STRIPE_SECRET_KEY format");
}
始终修剪环境变量:
typescript
// 错误写法 - 末尾空格会导致"请求头中存在无效字符"
const key = process.env.STRIPE_SECRET_KEY;

// 正确写法 - 始终修剪
const key = process.env.STRIPE_SECRET_KEY?.trim();
使用前验证格式:
typescript
const STRIPE_KEY_PATTERN = /^sk_(test|live)_[a-zA-Z0-9]+$/;
if (!STRIPE_KEY_PATTERN.test(key)) {
  throw new Error("Invalid STRIPE_SECRET_KEY format");
}

2. Webhook URL Validation

2. Webhook URL验证

Stripe does NOT follow redirects for POST requests. If your webhook URL returns 3xx, webhooks silently fail.
bash
undefined
Stripe不会跟随POST请求的重定向。 如果你的webhook URL返回3xx状态码,webhook会静默失败。
bash
undefined

Check for redirects BEFORE configuring webhook

配置webhook前检查是否存在重定向

curl -s -o /dev/null -w "%{http_code}" -I -X POST "https://your-domain.com/api/webhooks/stripe"
curl -s -o /dev/null -w "%{http_code}" -I -X POST "https://your-domain.com/api/webhooks/stripe"

Must return 4xx or 5xx, NOT 3xx

必须返回4xx或5xx,不能是3xx


**Use canonical domain:**
- If `example.com` redirects to `www.example.com`, configure webhook for `www.example.com`

**使用规范域名:**
- 如果`example.com`重定向到`www.example.com`,请为`www.example.com`配置webhook

3. Cross-Deployment Parity

3. 跨部署一致性

When using Vercel + Convex (or similar split architectures):
  • Env vars must be set on BOTH platforms
  • Use
    --prod
    flag:
    npx convex env set --prod KEY value
  • Verify parity: compare
    vercel env ls
    with
    npx convex env list --prod
当使用Vercel + Convex(或类似的拆分架构)时:
  • 必须在两个平台上都设置环境变量
  • 使用
    --prod
    标志:
    npx convex env set --prod KEY value
  • 验证一致性:对比
    vercel env ls
    npx convex env list --prod
    的输出

4. Stripe Parameter Constraints

4. Stripe参数约束

Mode-dependent parameters:
ParameterValid ModesInvalid Modes
customer_creation
payment, setupsubscription
subscription_data
subscriptionpayment, setup
Common trap: Using
customer_creation: "always"
in subscription mode throws an error at checkout time, not at compile time.

依赖模式的参数:
参数有效模式无效模式
customer_creation
payment, setupsubscription
subscription_data
subscriptionpayment, setup
常见陷阱: 在订阅模式下使用
customer_creation: "always"
会在结账时抛出错误,而非编译时。

Pre-Deployment Checklist

部署前检查清单

Before deploying any billing integration:
  • All env vars trimmed at read time
  • API key formats validated
  • Webhook URL returns non-3xx status
  • Vercel and Convex have matching config
  • Signature verification enabled
  • Error handling returns 200 (prevent Stripe infinite retries)

部署任何计费集成前:
  • 所有环境变量在读取时已修剪
  • API密钥格式已验证
  • Webhook URL返回非3xx状态码
  • Vercel和Convex配置一致
  • 已启用签名验证
  • 错误处理返回200状态码(避免Stripe无限重试)

Debugging Workflow (OODA-V)

调试工作流(OODA-V)

When billing integration fails:
  1. Observe - Check if request reaches server (look for logs)
  2. Orient - If no logs, it is network/redirect, not code
  3. Decide - Run
    curl -I
    on webhook URL
  4. Act - Fix configuration
  5. Verify - Resend event, watch
    pending_webhooks
    decrease

当计费集成出现故障时:
  1. 观察 - 检查请求是否到达服务器(查看日志)
  2. 定位 - 如果没有日志,问题出在网络/重定向,而非代码
  3. 决策 - 对webhook URL执行
    curl -I
    命令
  4. 行动 - 修复配置
  5. 验证 - 重新发送事件,观察
    pending_webhooks
    数量减少

References

参考资料

  • Stripe Constraints - API parameter rules
  • Env Var Hygiene - Validation patterns
  • Incident Patterns - Real failures from 3 projects
  • Stripe Constraints - API参数规则
  • Env Var Hygiene - 验证模式
  • Incident Patterns - 3个项目的真实故障案例

Scripts

脚本

  • scripts/verify-webhook-url.sh <url>
    - Check for redirects
  • scripts/verify-env-parity.sh
    - Compare Vercel/Convex config
  • scripts/audit-stripe-config.py
    - Full Stripe diagnostic
  • scripts/verify-webhook-url.sh <url>
    - 检查是否存在重定向
  • scripts/verify-env-parity.sh
    - 对比Vercel/Convex配置
  • scripts/audit-stripe-config.py
    - 完整Stripe诊断