2026-04-01 打卡
1. 今天学了什么 / 做了哪些具体操作
Webhook 签名验证(核心安全工作)
背景: 昨天 Lemon Squeezy 支付集成上线,但 webhook 没有签名验证,任何人都能伪造支付成功通知来给自己加字数。
实现过程:
-
第一版签名验证(错误): 参考了 Stripe 的格式,以为 LS 也是
timestamp=xxx,hmac_sha256=yyy的复合格式。部署后全部 401 失败。 -
查文档修正: 从 LS 官方文档确认实际格式——LS 的签名非常简单,就是纯
HMAC-SHA256(signing_secret, raw_body)的 hex digest,没有 timestamp,没有拼接。X-Signature header 直接就是 64 位十六进制字符串。 -
调试过程: CF Pages 的
wrangler tail无法使用(打包后的_worker.js不被识别为 Pages Function),本地也没有好的方式看线上日志。最终方案:在 webhook 响应里临时返回 debug 信息(签名比对结果),在 LS Dashboard 的 webhook 记录里查看。 -
验证结果:
match: true,签名完全正确。
最终实现(Edge Runtime 兼容):
- 使用 Web Crypto API(
crypto.subtle.importKey+crypto.subtle.sign),不用 Node.js crypto - 常量时间比较防时序攻击
- 环境变量
LEMONSQUEEZY_WEBHOOK_SECRET配置签名密钥
清理 PayPal 残留代码
删除了 4 个文件,全局搜索确认零残留引用:
lib/paypal.tsapp/api/paypal/create-order/route.tsapp/api/paypal/capture-order/route.tsapp/api/paypal/config/route.tscomponents/ui/paypal-button.tsx
修复 JWT 不刷新 plan 的 bug
问题: 用户买了 Pro 套餐后,pricing 页面仍然能买 Basic。根本原因是 NextAuth 的 JWT callback 只在首次登录时查 DB 拿 plan,后续 session 续期都用缓存的旧值。
修复: 把 DB 查询从 if (user) 块里移出来,改为每次 JWT callback 都查 DB。这样 webhook 更新 plan 后,下次 session 刷新就能拿到最新值。
附带修复:
- 注册默认字数从 30 改成 3000(和页面文案"3,000 free words"一致)
- session callback 里把
usage_count换成credits(字数)
CF Pages 日志排查踩坑
wrangler pages deployment tail报错 "does not have a Pages Function"- 原因:
@cloudflare/next-on-pages把所有路由打包成单个_worker.js,wrangler 不认为有 Functions - CF API 也没有日志拉取接口
- 最终用"响应里返回 debug info"的方式绕过
2. 解决了什么问题 / 有什么新认识 / 哪些地方需要改进
解决的问题:
- Webhook 安全漏洞 → HMAC-SHA256 签名验证,防止伪造支付通知
- PayPal 残留代码 → 全部清理干净
- 购买后套餐状态不同步 → JWT 每次 callback 都查 DB
新认识:
- 不同支付平台的签名格式完全不同——Stripe 用 timestamp+hmac,Lemon Squeezy 用纯 hmac。不能想当然地照搬。
- CF Pages 的日志能力很弱——没有实时 tail,没有历史日志 API。对于调试 webhook 这种场景,只能在响应里返回 debug 信息。
- Edge Runtime 的 crypto API 和 Node.js 不同——不能用
require('crypto'),必须用crypto.subtle。 - NextAuth JWT callback 的触发时机——不只是首次登录,每次 session 刷新都会触发。所以把 DB 查询放外面不会增加太多开销,但能保证数据实时性。
需要改进:
- 部署后应该有个自动化冒烟测试(curl webhook endpoint),不能每次都靠手动测试
- CF Pages 的日志问题需要找个长期方案,比如接入 Sentry 或 Loki
3. 收获
- 完整实现了 HMAC-SHA256 签名验证(Edge Runtime 版本)
- 学会了在 CF Pages 无法看日志时的调试技巧
- 加深了对 NextAuth JWT callback 生命周期的理解
- 养成了"先查官方文档再写代码"的习惯(签名格式踩坑的教训)
4. 接下来要学什么 / 做什么(可执行的规划)
- [ ] 切 Live Mode:生成 Live API Key,publish 3 个产品,替换代码里的 Test API Key
- [ ] SEO 优化:生成 sitemap.xml,补全各页面 meta tags(title/description/OG/Twitter Card)
- [ ] Product Hunt 发布准备:准备文案、截图、tagline
- [ ] 监控方案:接入 Sentry 或类似工具,解决 CF Pages 日志问题