维护说明:本文档已并入本站直接维护(单仓重构后原 backend/docs/*.md、前端 docs/*.md 不再随仓库发布)。
实现细节以 pay-unify 源码 为准,页面与源码的对应关系见相关资源。
SkillHub X402 支付集成指南
✅ 认证:/api/v2/* 一律使用 OAuth2 —— POST /oauth/token 换取 Authorization: Bearer <token> 后调用,见认证体系。
统一支付后端(Pay-Unify backend/)提供完整的 SkillHub X402 支付能力,任何 Skill 只需简单配置即可接入付费流程。
目录
1. 整体架构
┌─ 用户 / AI Agent ───────────────────────────────┐
│ │
│ 1. 触发 Skill(如 "epub转txt") │
│ 2. 收到 402 + WeixinPay-Required │
│ 3. 用户手机确认支付(AI 专属卡) │
│ 4. 携带 X-Out-Trade-No 重试获取内容 │
└──────────────────────┬───────────────────────────┘
│
┌──────────────────────▼───────────────────────────────────┐
│ SkillHub 平台 │
│ - 来源认证、内容完整性校验、可信调用入口 │
│ - 管理 X402 协议支付流程 │
│ - 将请求路由到商户后端 │
└──────────────────────┬───────────────────────────────────┘
│
┌──────────────────────▼───────────────────────────────────┐
│ 商户后端 (Pay-Unify backend) │
│ │
│ POST /api/v2/skillpay/resource ← 核心入口 │
│ │
│ ├─ 首次请求 (无 X-Out-Trade-No) │
│ │ 1. 微信支付 Native 下单(获取 prepay_id + code_url) │
│ │ 2. X402 L1 签名(SHA256withRSA) │
│ │ 3. 返回 402 + WeixinPay-Required Header │
│ │ │
│ └─ 付费后重试 (有 X-Out-Trade-No) │
│ 1. 查询微信支付确认已付 │
│ 2. 执行 Skill 业务脚本(如 epub_to_txt.py) │
│ 3. 返回 200 + 付费内容 │
│ │
│ GET /api/v2/skillpay/query/:no ← 查询订单状态 │
└──────────────────────────────────────────────────────────┘
核心角色
| 角色 |
说明 |
| AI Agent / SkillHub |
用户触发 Skill 的入口,处理 X402 支付协议的客户端交互 |
| Pay-Unify 后端 |
商户后端,处理微信支付下单、X402 签名、验证支付、执行业务 |
| 微信支付 |
实际资金通道,Native 二维码支付 |
| Skill 业务脚本 |
付费后执行的业务逻辑(如 epub_to_txt.py),由后端调用 |
2. 后端做了什么
2.1 支付层
internal/pkg/service/payment/ 目录下的基础设施:
| 组件 |
文件 |
职责 |
WechatPayService |
wepay_service.go |
微信支付 V3 API 封装:下单、查单、关闭、退款、回调验签 |
SkillHubService |
skillhub_service.go |
X402 协议签名:构造 L2 JSON → Base64 → SHA256withRSA → L1 5行签名串 |
AlipayService |
alipay_service.go |
支付宝支付封装(Pay Skill 可扩展使用) |
2.2 X402 协议签名(skillhub_service.go)
L2 载荷(JSON):
{
"out_trade_no": "WX402_20260724123456",
"amount": 50, // 单位:分
"description": "EPUB转文本: book.epub",
"skill_id": "epub-to-txt",
"wechat_params": {
"native_order": {
"prepay_id": "wx...",
"trade_type": "NATIVE"
}
}
}
↓ Base64 编码
SHA256withRSA 签名(使用 SkillHub 开发者私钥)
↓ Base64 编码
L1 5 行签名串:
sh-xxxxxxxx
PUB_KEY_xxxxxxxx
<L2_base64>
<signature_base64>
(空行)
这个 5 行 payment_code 是 X402 协议的核心凭据,通过 HTTP Header 返回给 Agent。
2.3 业务编排层(skillpay_handler.go)
POST /api/v2/skillpay/resource 是整个集成入口,使用 OAuth2 Bearer 认证(scope: skillpay:call)。
请求格式:
1{
2 "query": "/path/to/book.epub"
3}
请求头(OAuth2 Bearer 认证):
Authorization: Bearer <access_token>
Content-Type: application/json
X-Out-Trade-No: <X402订单号> ← 付费后重试时携带
处理流程(HandleResource):
收到请求
│
├─ 检查 X-Out-Trade-No Header
│ │
│ ├─ 无 → createPaymentOrder():
│ │ 1. Snowflake 生成 orderNo
│ │ 2. WeChat Pay Native 下单 → prepay_id + code_url
│ │ 3. X402 签名 → 5行 payment_code
│ │ 4. 保存订单到 MySQL(Extra 中存 query)
│ │ 5. 返回 402 + WeixinPay-Required Header
│ │
│ └─ 有 → verifyAndFulfill():
│ 1. 查 DB 获取订单(含 Extra.query)
│ 2. QueryOrder 查微信支付确认已付
│ 3. 更新订单状态为已支付
│ 4. executeBusiness(query) 执行业务脚本
│ 5. 返回 200 + 付费内容
2.4 业务执行层(executeBusiness)
后端通过配置的 ExecScriptPath 调用外部脚本执行实际的付费业务:
1cmd := exec.Command("python3", scriptPath, query)
2output, err := cmd.CombinedOutput()
3// 返回 output 作为付费内容
如果未配置脚本路径,返回模拟数据用于测试。
2.5 订单存储
所有 X402 支付订单存在 tb_payment_order 表,关键字段:
| 字段 |
示例值 |
说明 |
order_no |
202607241234567890 |
内部订单号(Snowflake 生成) |
subject |
EPUB转文本: book.epub |
支付描述(微信支付显示) |
amount |
0.50 |
支付金额(元) |
status |
1=未支付, 201=已支付 |
订单状态 |
pay_way |
wechat |
支付方式 |
trade_no |
wx... |
微信支付 prepay_id |
extra |
{"query":"...", "skill_id":"..."} |
扩展信息(存业务参数) |
app_id |
skillhub |
来源标识 |
3. Skill 如何结合后端
3.1 整体协作流程
SkillHub 层(用户触发 Skill)
│
▼
AI Agent 调用商户后端
│ POST /api/v2/skillpay/resource {"query": "..."}
│
▼
商户后端处理支付
│
├─ 未付 → 402(含 payment_code)
│ → Agent 展示给用户
│ → 用户手机确认支付
│ → Agent 带 X-Out-Trade-No 重试
│
└─ 已付 → 调用 Skill 业务脚本
→ 返回执行结果给 Agent
→ Agent 呈现给用户
3.2 Skill 需要做的
Skill 开发者只需要做两件事:
① 编写业务脚本(如 epub_to_txt.py)
提供一个可以通过命令行调用的脚本,接收业务参数(如文件路径),输出结果到 stdout:
1python3 my_skill_script.py /path/to/input.data
脚本的输出(stdout)会作为付费内容返回给 Agent。
② 配置 skill.yaml
1name: my-pay-skill
2pay:
3 enabled: true
4 price:
5 amount: 0.50 # 定价
6 currency: CNY
7 billing:
8 mode: per_call # 按次计费
9 x402:
10 auth_mode: per_payment # 笔笔确认
3.3 后端需要做的
在 config.toml 中配置:
1[SkillHubConfig]
2 Enabled = true
3 DeveloperId = "sh-xxxxxxxx" # SkillHub 商户号
4 PubKeyId = "PUB_KEY_xxxxxxxx" # 开发者公钥 ID
5 PrivateKey = "/etc/certs/skillhub/private_key.pem" # RSA 私钥
6 SkillId = "my-pay-skill" # Skill slug(与 skill.yaml 一致)
7 PriceAmount = 0.50 # 定价(与 skill.yaml 一致)
8 Description = "我的付费技能"
9 ExecScriptPath = "/app/my-skill/scripts/my_skill_script.py" # 脚本路径
10 ExecHandler = "cli" # 执行模式:cli 直接命令行调用
3.4 数据流详情
每次请求:
query = 业务参数(如 EPUB 文件路径)
↓
首次: query 存入 order.extra.query
重试: 从 order.extra.query 取出 → 传给 executeBusiness()
↓
executeBusiness():
exec.Command("python3", ExecScriptPath, query)
→ 脚本 stdout 作为响应内容返回
4. 快速接入一个新 Skill
步骤 1:编写业务脚本
创建一个可独立运行的脚本,接收一个参数,输出结果到 stdout:
1#!/usr/bin/env python3
2import sys
3
4def do_something(input_path):
5 # 你的业务逻辑
6 return f"处理结果: {input_path}"
7
8if __name__ == '__main__':
9 if len(sys.argv) < 2:
10 print('{"success":false,"error":"缺少参数"}')
11 sys.exit(1)
12 result = do_something(sys.argv[1])
13 print(result)
步骤 2:部署脚本到服务器
1# 将脚本部署到服务器
2scp my_skill_script.py user@host:/app/my-skill/scripts/
步骤 3:配置后端
编辑 config.toml(或部署的 config-prod.toml):
1[SkillHubConfig]
2 Enabled = true
3 SkillId = "my-pay-skill"
4 PriceAmount = 0.50
5 Description = "我的付费技能"
6 ExecScriptPath = "/app/my-skill/scripts/my_skill_script.py"
7 ExecHandler = "cli"
步骤 4:创建 skill.yaml
1name: my-pay-skill
2display_name: "我的付费技能"
3version: "1.0.0"
4actions:
5 - type: script
6 path: ./scripts/my_skill_script.py
7 handler: main
8pay:
9 enabled: true
10 price:
11 amount: 0.50
12 currency: CNY
13 billing:
14 mode: per_call
15 x402:
16 auth_mode: per_payment
步骤 5:测试
1# 首次调用(未付费,应返回 402)
2curl -s -X POST https://your-domain.com/api/v2/skillpay/resource \
3 -H "Content-Type: application/json" \
4 -H "Authorization: Bearer $TOKEN" \
5 -d '{"query": "/app/my-skill/input.data"}'
6# → 402 + WeixinPay-Required Header
认证方式见认证体系;完整 X402 调用顺序见第 7 节。
5. 配置参考
5.1 后端配置([SkillHubConfig])
| 配置项 |
必填 |
说明 |
示例值 |
Enabled |
是 |
启用 SkillHub 支付集成 |
true |
DeveloperId |
是 |
SkillHub 商户号 |
sh-xxxxxxxx |
PubKeyId |
是 |
开发者公钥 ID |
PUB_KEY_xxxxxxxx |
PrivateKey |
是 |
RSA 2048 私钥文件路径 |
/etc/certs/skillhub/private_key.pem |
SkillId |
是 |
Skill slug(与 skill.yaml 一致) |
epub-to-txt |
SkillVersion |
否 |
Skill 版本 |
1.0.0 |
PriceAmount |
否 |
定价(元,默认 0.50) |
0.50 |
Description |
否 |
支付描述前缀 |
EPUB转文本 |
ExecScriptPath |
否 |
付费后调用的脚本路径 |
/app/epub-to-txt/scripts/epub_to_txt.py |
ExecHandler |
否 |
执行模式(仅 cli) |
cli |
5.2 Skill 配置(skill.yaml)
| 配置项 |
说明 |
示例值 |
pay.price.amount |
定价 |
0.50 |
pay.billing.mode |
计费模式 |
per_call |
pay.billing.min_balance |
最低预存(减少弹窗) |
5.00 |
pay.trial.calls |
免费试用次数 |
3 |
pay.x402.auth_mode |
X402 授权模式 |
per_payment |
6. 接口文档
6.1 核心入口
POST /api/v2/skillpay/resource
认证: OAuth2 Bearer(与 /api/v2/payment/* 一致)
请求(首次 — 未付费):
1// Headers: Authorization: Bearer <token>
2// Body:
3{
4 "query": "/path/to/input.data"
5}
响应(402 Payment Required):
1// Headers:
2// WeixinPay-Required: <5行payment_code>
3// X-Out-Trade-No: WX402_<orderNo>
4{
5 "code": "PAYMENT_REQUIRED",
6 "message": "需要支付 ¥0.50 才能继续",
7 "WeixinPay": {
8 "WeixinPay-Required": "<5行payment_code>"
9 },
10 "out_trade_no": "WX402_20260724123456",
11 "skill_id": "epub-to-txt",
12 "amount": 50,
13 "orderNo": "20260724123456"
14}
请求(重试 — 已付费):
1// Headers: Authorization: Bearer <token>, X-Out-Trade-No: WX402_<orderNo>
2// Body:
3{
4 "query": "/path/to/input.data"
5}
响应(200 OK):
1{
2 "code": "SUCCESS",
3 "content": "业务脚本的输出内容(stdout)",
4 "orderNo": "20260724123456"
5}
6.2 订单查询
GET /api/v2/skillpay/query/:outTradeNo
响应(200 OK):
1{
2 "code": 200,
3 "message": "Success",
4 "data": {
5 "orderNo": "20260724123456",
6 "amount": 0.50,
7 "status": 201,
8 "payWay": "wechat",
9 "payTime": 1721812345
10 }
11}
订单状态:1=未支付, 2=已扫码, 201=已支付, 300=已关闭, 400=已退款
7. 端到端测试
7.1 测试前提
- 后端已部署运行
[SkillHubConfig] Enabled = true
[WechatPayConfig] Enabled = true 且微信商户配置正确
- 在「API 应用管理」创建了带
skillpay:call/refund scope 的应用
7.2 手动测试(curl)
1# === Step 1: 首次请求(未付费)===
2# 换取 OAuth2 Bearer token(scope 需含 skillpay:call)
3CLIENT_ID="your-client-id"
4CLIENT_SECRET="your-client-secret"
5TOKEN=$(curl -s -X POST http://localhost:8097/oauth/token \
6 -H "Content-Type: application/json" \
7 -d "{\"grant_type\":\"client_credentials\",\"client_id\":\"${CLIENT_ID}\",\"client_secret\":\"${CLIENT_SECRET}\"}" \
8 | jq -r .access_token)
9
10# 发送请求
11curl -s -w "\nHTTP %{http_code}\n" -X POST http://localhost:8097/api/v2/skillpay/resource \
12 -H "Content-Type: application/json" \
13 -H "Authorization: Bearer $TOKEN" \
14 -d '{"query": "/app/test/sample.epub"}'
15
16# 期望输出:HTTP 402 + WeixinPay-Required Header
17
18# === Step 2: 模拟付费后重试 ===
19# (将 <OUT_TRADE_NO> 替换为上一步返回的 out_trade_no)
20curl -s -w "\nHTTP %{http_code}\n" -X POST http://localhost:8097/api/v2/skillpay/resource \
21 -H "Content-Type: application/json" \
22 -H "Authorization: Bearer $TOKEN" \
23 -H "X-Out-Trade-No: <OUT_TRADE_NO>" \
24 -d '{"query": "/app/test/sample.epub"}'
25
26# 如果微信支付已付 → HTTP 200 + 付费内容
27# 如果未付 → HTTP 402 + "订单尚未支付完成"
7.3 验证 402 响应格式
1curl -s -D - -X POST http://localhost:8097/api/v2/skillpay/resource \
2 -H "Content-Type: application/json" \
3 -H "Authorization: Bearer $TOKEN" \
4 -d '{"query": "test.epub"}' | head -20
5
6# 必须包含:
7# ✅ HTTP/1.1 402 Payment Required
8# ✅ WeixinPay-Required: <payment_code>
9# ✅ Content-Type: application/json
10# ✅ Body 含 WeixinPay 节点
8. 现有集成示例:epub-to-txt
8.1 文件结构
pay-unify/backend/
├── config.toml # 后端配置(含 SkillHubConfig)
├── internal/
│ ├── handler/skillpay_handler.go # X402 处理器(核心)
│ └── pkg/service/payment/
│ ├── skillhub_service.go # X402 签名服务
│ └── wepay_service.go # 微信支付(含 CreateNativePrepayOrder)
└── ...
.claude/skills/epub-to-txt/
├── SKILL.md # Skill 文档
├── skill.yaml # SkillHub 发布配置
└── scripts/
└── epub_to_txt.py # 业务脚本(付费后执行)
8.2 已配置的内容
后端 config.toml:
1[SkillHubConfig]
2 Enabled = true
3 DeveloperId = "sh-xxxxxxxx"
4 PubKeyId = "PUB_KEY_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
5 PrivateKey = "/etc/certs/skillhub/private_key.pem"
6 SkillId = "epub-to-txt"
7 PriceAmount = 0.50
8 Description = "EPUB转文本"
9 ExecScriptPath = "/app/epub-to-txt/scripts/epub_to_txt.py"
10 ExecHandler = "cli"
Skill skill.yaml(已发布):
1name: epub-to-txt
2pay:
3 enabled: true
4 price:
5 amount: 0.50
6 billing:
7 mode: per_call
8 min_balance: 5.00
9 trial:
10 calls: 3
8.3 付费流程示例
用户: "帮我把这本书转换成文本"
Agent → POST /api/v2/skillpay/resource {"query": "/app/books/book.epub"}
│
├─ 后端创建微信支付订单 + X402 签名
├─ 返回 402 + WeixinPay-Required
│
├─ Agent 展示支付二维码给用户
├─ 用户微信扫码 → AI 专属卡 → 确认支付 ¥0.50
│
└─ Agent 携带 X-Out-Trade-No 重试
├─ 后端确认已付
├─ 执行: python3 epub_to_txt.py /app/books/book.epub
└─ 返回: {"success":true, "output_path":"...", "file_size":102400}
Agent: "转换完成!文件已保存到 /app/books/book.txt(100.0 KB)"
8.4 定价与定价策略参考
| 定价金额 |
适用场景 |
说明 |
| ¥0.10 – ¥0.50 |
单次查询/简单转换 |
低频低价值,薄利多销 |
| ¥0.50 – ¥5.00 |
内容生成/分析 |
按产出计费,价值感强 |
| ¥1.00 – ¥10.00 |
专业工具/数据 |
专业价值,用户愿付费 |
| ¥9.90 – ¥99.00/月 |
持续服务 |
订阅制,稳定现金流 |
建议新 Skill 从低价起步(¥0.10-¥0.50),设 3 次免费试用,根据数据逐步调价。