一个用 Node.js 编写的静态博客生成器,并通过 Cloudflare Workers 接入 x402 支付协议,让 AI agent、自动化程序和真人都能按次付费访问内容。
项目由两部分组成:
- Hono SSG 静态站点生成器:Markdown(带 YAML frontmatter)经构建期 Hono JSX app 渲染为
dist/,可托管在任意静态平台。 - Cloudflare Worker:托管
dist/静态资源,并在边缘拦截付费路由,用 x402 协议完成基于 USDC 的按次结算。
它提供四种能力:普通静态博客、面向 agent 的付费 JSON API、面向 AI 爬虫的付费门,以及面向真人的付费文章。
- Hono SSG 静态博客。响应式页面,支持标签、归档、RSS。
- agent 付费 API(
/api/content/:slug)。把免费文章以结构化 JSON 提供给 agent,按次付费调用。 - 爬虫付费门(
/posts/*)。已知 AI 爬虫按 User-Agent 识别后付费读全文,真人浏览免费。 - 人类付费文章(
/premium/:slug)。真人在浏览器里连钱包付款读全文,付款后签发通行证,有效期内免重付。购买记录写入 KV,清 cookie 或换设备后可用钱包签名免费恢复访问。
前置条件:
- Node.js 24+ 与 pnpm。
- Cloudflare 账号(可选,部署 Worker 时使用)。
启动本地开发站点:
pnpm run dev其他常用任务:
pnpm run typecheck # 检查 Node/Hono/browser 与 Worker TypeScript
pnpm run build # 构建到 dist/
pnpm run preview # 预览构建产物项目已统一到 Node.js + pnpm 工具链:
package.json管理静态站点构建脚本、测试脚本、Worker/Wrangler 命令和全部 npm 依赖。pnpm-lock.yaml用于锁定依赖版本,CI 通过pnpm install --frozen-lockfile可复现安装。- 构建端、Hono SSG 与浏览器脚本已统一为 TypeScript / TSX;
main.ts通过tsxloader 运行,public/JavaScript/index.ts在构建时由 esbuild 编译为版本化浏览器脚本。
用脚本生成一篇新文章:
pnpm run new-post -- --title "标题" --categories "Tech" --tags "Node,x402" --summary "一句话摘要"生成的文件位于 src/posts/,frontmatter 字段如下:
| 字段 | 必填 | 说明 |
|---|---|---|
title |
是 | 文章标题 |
date |
是 | 发布时间,YYYY-MM-DD HH:mm:ss |
categories |
是 | 分类 |
tags |
否 | 标签列表 |
summary |
是 | 摘要,用于列表页与付费墙预览 |
updateAt |
是 | 更新时间,构建时按 Git 记录自动维护 |
paid |
否 | 设为 true 则成为付费文章 |
price |
否 | 付费文章的单篇价格,例如 $1;仅在 paid: true 时生效 |
付费文章:在 frontmatter 加 paid: true。构建时,公开页面只输出摘要与解锁入口,全文改由 Worker 在付款后返回,不会落进任何免费文件。
免费文章不需要额外配置。只要不写 paid,或显式写 paid: false,文章就会按普通静态页面构建,并进入 /api/content/:slug 的付费 JSON API。
---
title: 免费文章
summary: 这篇文章公开可读。
paid: false
---付费文章需要写 paid: true。构建时,公开页面只保留 summary 和解锁入口;完整 HTML 会写进 Worker 的 premium 数据表,并只通过 /premium/:slug 在付款后返回。
---
title: 付费文章
summary: 摘要免费可见,全文需解锁。
paid: true
---如需单篇定价,继续添加 price。这个价格会同时用于公开 teaser 的显示和 Worker 的实际收费。
---
title: 深度文章
summary: 这篇文章单独定价。
paid: true
price: "$1"
---未设置 price 的付费文章使用 Worker 环境变量 PREMIUM_PRICE。为了避免显示价和实收价不一致,未设置 price 时,公开 teaser 不写死具体金额。
付费文章不会进入 /api/content/:slug 的免费文章 map,也不会把全文落到公开静态文件。这样可以避免 agent API 或静态回源绕过 /premium/:slug 的付款门。
-
安装 Worker 依赖:
pnpm install
-
新建
.dev.vars(已在.gitignore中),放入本地密钥与测试网覆盖:ACCESS_TOKEN_SECRET=你的随机长字符串 NETWORK=eip155:84532 FACILITATOR_URL=https://x402.org/facilitator
-
启动 Worker(会先自动构建
dist/与worker/content.generated.js):pnpm run worker:dev
三个端到端测试脚本用于验证付款链路(付款腿需一个持有 Base Sepolia 测试网 USDC 的钱包)。默认网络是 eip155:84532,需要覆盖时可设置 X402_NETWORK。
# agent 付费 API:未付款 402,付款后 200
PRIVATE_KEY=<测试钱包私钥> node scripts/pay-test.ts
# 爬虫付费门:真人 200、爬虫未付款 402、爬虫付款 200
PRIVATE_KEY=<测试钱包私钥> node scripts/crawl-test.ts
# 人类付费文章:付款页、摘要页、付款、通行证免重付
PRIVATE_KEY=<测试钱包私钥> node scripts/premium-test.ts
# 钱包恢复访问(零费用):预置 KV 购买记录 + 签名恢复 + 未购地址被拒
node scripts/restore-test.ts零费用测试付款成功路径:scripts/mock-facilitator.ts 是一个对所有 verify/settle 都放行的本地 mock,用来在不花测试币的情况下走通「结算成功 → 发通行证 → 写购买记录」:
# 终端 1:起 mock(监听 :4402)
node scripts/mock-facilitator.ts
# 把 .dev.vars 的 FACILITATOR_URL 临时改为 http://localhost:4402,
# 重启 wrangler dev 后即可用任意签名走完付款成功路径。测完记得改回。mock 会无条件放行付款,只能用于本地开发,不要指向生产。
wrangler.jsonc 的 vars 为非机密配置:
| 变量 | 含义 | 示例 |
|---|---|---|
NETWORK |
CAIP-2 结算网络;本地测试网用 eip155:84532,生产主网用 eip155:8453 |
eip155:84532 |
FACILITATOR_URL |
测试网 HTTP facilitator 地址;主网分支会改用 Coinbase CDP facilitator | https://x402.org/facilitator |
PRICE |
agent API 单次价格 | $0.001 |
CRAWL_PRICE |
爬虫单页价格 | $0.001 |
PREMIUM_PRICE |
人类付费文章价格 | $0.01 |
PASS_TTL_SECONDS |
通行证有效期(秒) | 2592000(30 天) |
PAY_TO_ADDRESS |
收款地址 | 0x... |
机密项不写进 wrangler.jsonc:
ACCESS_TOKEN_SECRET:通行证的 HMAC 签名密钥。本地放.dev.vars,生产用npx wrangler secret put ACCESS_TOKEN_SECRET。CDP_API_KEY_ID、CDP_API_KEY_SECRET:生产主网用 Coinbase CDP facilitator 验证与结算真实 USDC,本地测试网不需要。
kv_namespaces 里的 PURCHASES 绑定保存付费文章的购买记录(钱包即账户):
-
本地
wrangler dev由 miniflare 自动模拟,无需创建。 -
生产部署前需要创建命名空间,并把返回的 id 填进
wrangler.jsonc:npx wrangler kv namespace create PURCHASES
站点公开信息配置在 site.config.json:
author、website、title、description、keywords:站点基础元信息。profile:头像、主页链接、about 页签名、标签、技术栈、项目和社交链接。footer:版权年份、协议链接和 Powered by 文案。sponsor.url:导航栏赞助按钮和文章赞助按钮跳转地址;留空则文章页不插入赞助按钮。ads:页脚前的外链广告配置;enabled: false可关闭广告,items中每项支持title、url和可选description。giscus:评论区配置;enabled: false可关闭评论。
.env 只保留本地运行相关的小型覆盖项:
WEBSITE=https://www.example.com/
AUTHOR=your-name
PORT=3000WEBSITE 和 AUTHOR 会覆盖 site.config.json 的同名字段,方便本地和部署环境用不同域名。构建器不再生成 dist/CNAME;如果你仍使用 GitHub Pages 自定义域名,请在部署平台或仓库设置里维护 CNAME。
本地运行 npx wrangler dev 时,Wrangler 会读取 .dev.vars。因此本地默认覆盖为 NETWORK=eip155:84532,也就是 Base Sepolia 测试网。你在本地依旧可以用测试网 USDC 付款,这是设计如此,用来避免开发时误花真实资金。
部署到 Cloudflare Workers 时,.dev.vars 不会上传。生产 Worker 使用 wrangler.jsonc 里的 vars,所以当前默认是 NETWORK=eip155:8453,也就是 Base mainnet。
主网付款会结算真实 USDC。部署前需要确认这些生产配置:
PAY_TO_ADDRESS是你控制的 Base mainnet 收款地址。CDP_API_KEY_ID和CDP_API_KEY_SECRET已用npx wrangler secret put写入 Worker secrets。ACCESS_TOKEN_SECRET已用npx wrangler secret put写入 Worker secrets。PRICE、CRAWL_PRICE、PREMIUM_PRICE或文章 frontmatterprice已设置为真实想收取的价格。
本地看到 eip155:84532 不代表生产没有切主网。判断生产网络应以部署后的 Worker 响应为准:解码 PAYMENT-REQUIRED 响应头,确认 accepts[0].network 是否为 eip155:8453。
首次部署按以下步骤执行:
-
创建购买记录的 KV 命名空间,并把输出的 id 填进
wrangler.jsonc的kv_namespaces:npx wrangler kv namespace create PURCHASES
-
写入三个生产机密:
npx wrangler secret put ACCESS_TOKEN_SECRET npx wrangler secret put CDP_API_KEY_ID npx wrangler secret put CDP_API_KEY_SECRET
-
确认
wrangler.jsonc里的PAY_TO_ADDRESS是你控制的收款地址,PRICE、CRAWL_PRICE、PREMIUM_PRICE是想实收的价格。 -
构建产物与 Worker 内容模块由同一条命令生成:
pnpm run build # 生成 dist/ 与 worker/content.generated.js -
部署到 Cloudflare Workers:
pnpm run worker:deploy
后续更新只需重复第 4、5 步。
只想验证 Worker 能否被 Wrangler 打包时,可以运行:
pnpm run worker:dry-run也可以走 GitHub Actions 的 deploy-worker 任务:在仓库配置 CLOUDFLARE_API_TOKEN 与 CLOUDFLARE_ACCOUNT_ID secret 后,推送即触发。
本地开发默认通过公开 https://x402.org/facilitator 在 Base Sepolia(eip155:84532)测试,不需要 CDP keys,也不会动真实资金。生产配置使用 Base mainnet(eip155:8453)和 Coinbase CDP facilitator,需要设置 CDP_API_KEY_ID、CDP_API_KEY_SECRET、真实收款地址,并把域名 DNS 迁到 Cloudflare。
| 端点 | 受众 | 价格变量 | 返回 |
|---|---|---|---|
/api/content/:slug |
agent | PRICE |
免费文章的结构化 JSON |
/posts/* |
AI 爬虫(按 UA 识别) | CRAWL_PRICE |
文章 HTML(真人免费) |
/premium/:slug |
人类浏览器 / agent | 文章 price 或 PREMIUM_PRICE |
付费文章全文 HTML |
main.ts 位于根目录,是构建入口:收集 Markdown 数据,创建构建期 Hono TSX app,编译 public assets,然后把路由响应写入 dist/。
.
├── main.ts # 构建入口
├── site.config.json # 站点公开 profile、社交链接、评论与赞助配置
├── wrangler.jsonc # Cloudflare Worker 配置
├── worker # Worker 源码(Cloudflare 运行时)
│ ├── index.ts # 路由与 x402 付款门
│ ├── access.ts # 通行证签发与校验
│ └── crawlers.ts # AI 爬虫 UA 列表
├── scripts # 建帖与端到端测试脚本
├── src
│ ├── blog # Hono SSG 构建期 app、数据收集与路由输出
│ │ ├── data.tsx # collectBlogData + worker/content.generated.js 输出
│ │ ├── app.tsx # createBlogApp,定义静态页面路由
│ │ ├── components.tsx # Hono JSX 组件
│ │ └── emit.ts # emitStaticRoutes,把 Hono 响应写入 dist
│ ├── build
│ │ └── assets.ts # emitAssets,处理 CSS 与浏览器 TS 版本化输出
│ ├── about # 关于页
│ ├── picture # 图片
│ ├── posts # 博客文章
│ └── util # 模板、站点配置、remark 插件与 Node 工具
├── public
│ ├── JavaScript/index.ts # 浏览器交互脚本,构建为 dist/public/JavaScript/index.<version>.js
│ └── css # Editorial HIG token-first 样式
└── dist # 构建产物