skillpay-upgrade — 普通 Skill → Pay Skill 一键升级
> 依据 SkillHub 官方指引 https://skillhub.cn/tutorials#agent-pay-upgrade 与
> 商户改造最佳实践 https://skillhub.cn/tutorials#agent-pay-best-practice(均已通读)。
> 本 skill 只做「改造」:把普通 Skill 加上微信 Agent Pay 触发能力(含支付后重试/查单);
> 发布请用 skill-publish-audit → skillhub-auto-publish。
1. 一句话价值
把「把一个普通 Skill 改造成付费 Skill」这一流程标准化成可重复运行的生成器:你输入关键信息 → 它自动改写目标 Skill(注入支付触发模块 + 配置模板 + 改写 SKILL.md 付费契约段)→ 跑密钥扫描安检。改完可一键接续你已有的发布链路。
它解决的是教程里的三段开发者侧代码(教程称 Step 1~4),并补齐支付后闭环:
- (a) 微信支付下单 → 拿
code_url(Native)/prepay_id(JSAPI)/h5_url(H5),订单号自动生成(WX402_前缀,≤32 位)。internal 模式自带 v3 签名(wx_v3_auth.py),external 模式接你已有的下单服务 - (b) X402 AI 预下单 → 用 SkillHub RSA 私钥对请求做
SHA256withRSA签名,把pay_value换成payment_code - (c) 返回支付触发 → 响应带
WeixinPay-Required: <payment_code>(服务端:HTTP 402 + Header/Body;本地:CLI 输出 JSON) - (d) 支付后重试/查单 → Agent 支付完成后凭
X-Out-Trade-No重试,服务端查单验证后履约(SQLite 幂等,跨进程/重启不重复发内容)
Agent 侧(WorkBuddy + 微信支付)会自动识别 WeixinPay-Required 并拉起支付卡片——开发者侧不需要对接 weixinpay MCP。
2. 触发词 → 执行映射
| 用户说 | 执行 |
|---|---|
| "升级为付费skill" / "payskill改造" / "skillpay升级" / "把skill改成收费" / "接入微信支付" | 跑 pay_upgrade.py:解析(可 load-profile)→ 预检 → 渲染 → 写入 → 密钥扫描 → 提示 |
| 只想先看不改 | 加 --dry-run(渲染到临时目录预览,不写目标 Skill) |
| 跳过交互确认 | 加 --skip-preflight(CI/批量场景) |
| "保存我的商户配置" / "复用上次配置" / "换个skill再改造一次" | 用 --save-profile <name> / --load-profile <name>(详见 references/profile_store.md) |
3. 输入关键信息清单
生成器需要以下信息(支持 --spec <json> 一次性喂入,或拆成 --target/--mode/--slug/... CLI 参数):
| 类别 | 字段 | 说明 |
|---|---|---|
| 目标 | TARGET_SKILL_DIR |
待升级的普通 Skill 绝对路径(不存档) |
| 形态 | DEPLOY_MODE |
server(HTTP/MCP 服务端)或 local(CLI 脚本) |
| 下单 | WX_ORDER_PROVIDER |
internal(生成器自带微信 v3 下单签名,默认)或 external(接你已有下单服务,豁免微信下单字段) |
| SkillHub | SKILL_ID(slug)、SKILL_VERSION |
已发布的 slug 与版本 |
| SkillHub | DEVELOPER_ID(sh-XXX)、PUB_KEY_ID(PUB_KEY_...) |
商户号与公钥 ID(公开信息,可写入) |
| 微信商户 | WX_APPID、WX_MCHID、NOTIFY_URL |
下单用(公开/半公开;external 模式豁免)。AppID 为 v3 下单必填字段,须与商户号绑定 |
| 微信商户 | WX_API_KEY_ENV / WX_CERT_PATH_ENV |
仅环境变量名,运行时读取,不写包 |
| 微信商户 | WX_CERT_SERIAL |
证书序列号;留空自动从证书提取(internal 模式推荐) |
| 定价 | AMOUNT(CNY 分,正整数)、CURRENCY(CNY)、PRODUCT_ID(SPxxx) |
订单金额与商品 |
| 支付 | PAY_DATA_TYPE |
code_url(Native,默认) / prepay_id(JSAPI,需 openid) / h5_url(H5,需 scene_info) |
| 支付 | WX_OPENID、WX_H5_SCENE |
JSAPI 默认 openid(可选,生产建议运行时传参);H5 scene_info JSON(可选) |
| 密钥 | PRIVATE_KEY_ENV |
持有 private_key_pem 的环境变量名(唯一私钥入口) |
> 安全红线:公钥类字段可写入包内;private_key_pem、微信 API_KEY、证书路径一律只存「环境变量名 / 外部路径」,运行时读取,生成器绝不把明文写入目标 Skill。
> 💾 配置存档(反复使用免重复输入):--save-profile <name> 把本次配置(公开信息 + 环境变量名,绝无明文密钥)存档到 ~/.workbuddy/skillpay-profiles.json;下次改造其他 Skill 用 --load-profile <name> 一键复用,有变化用 CLI 参数单字段覆盖。详见 references/profile_store.md。
3a. 改造时与用户交互收集信息(协议)
> 完整「向用户收取的配置信息清单(含类型/内容/注意事项/获取来源)」见 references/config_collection_checklist.md——本段是它的执行协议,决定怎么问、按什么顺序问、红线在哪。
改造一个 Skill 时,不要一次性抛一大堆字段名让用户填,而是按下列 4 个阶段与用户交互,每阶段只问当前需要的项:
阶段 0|开场对齐(必做,对应清单 §0)
先讲清三句话(直接复用清单 §6 话术):
1. 三套钥匙互不通用:SkillHub 开发者密钥(X402 签名)/ 微信商户 API 证书(v3 签名)/ 微信支付平台公钥(验签回调)——任一对不上都报验签失败。
2. 私钥/Token 绝不写进包:生成器只写"环境变量名"或"公钥明文",真私钥/APIv3 密钥/Token 只经环境变量注入。
3. 缺什么问什么,不编造:标"🔴 硬缺口"的项给不出就先完成不依赖它的部分,明确告知阻塞点。
阶段 1|资质确认(对应 preflight_checklist.md 四项硬门槛)
先确认企业认证 / 绑商户 / 下单能力 / 已发布定价是否齐备;任一缺失→提示暂停,不强行改代码。
阶段 2|公开信息收集(直接可写包,对应清单 §1~§3、§5)
逐项询问并直接填入:TARGET_SKILL_DIR / DEPLOY_MODE / WX_ORDER_PROVIDER(internal/external,先定这个再决定问不问微信下单字段) / SKILL_ID / SKILL_VERSION / 定价(AMOUNT分) / PRODUCT_ID / PAY_DATA_TYPE / DEVELOPER_ID / PUB_KEY_ID / 开发者公钥 / WX_MCHID / WX_APPID / NOTIFY_URL / 微信支付平台公钥+ID。这些无外部敏感来源,用户给得出就填。
> 若用户说「我已有下单服务」→ 切 WX_ORDER_PROVIDER=external,跳过微信下单字段(APPID/MCHID/NOTIFY_URL/商户密钥),只收 SkillHub 侧字段 + PRIVATE_KEY_ENV。
> 若已有存档(--list-profiles 可见)→ 先 --load-profile 载入,逐项确认即可,不必重问。
阶段 3|敏感信息收集(⚠️ 只收"环境变量名/注入方式",绝不收明文,对应清单 §2.4、§4)
向用户明确:SkillHub Token、微信商户 API 私钥、APIv3 密钥、SkillHub 开发者私钥都只通过环境变量注入运行时。
- 若用户把私钥明文直接发来 → 拒绝入包,改为请其 export WXPAY_API_KEY=... 等形式注入;生成器保留的只能是环境变量名。
- 记录下"该私钥对应的环境变量名"即可(如 WXPAY_API_KEY、SKILLHUB_RSA_PRIVATE_KEY)。
阶段 4|回填 + 安全自检 + 交付判定
- 渲染时:公开字段填入;私钥/密钥只写
os.environ[...]引用。 - 写入后必跑
secret_scanner.py(P0=0 才算过)。 - 交付时按清单 §7 给出"哪些齐了 / 哪些 🔴 阻断 / 哪些 🟡 可占位",绝不臆造缺失的真实值。
> 一句话:公开项直接问直接填;敏感项只问"怎么注入"、不问"密钥是什么";三套钥匙先对齐再动手。
4. 标准流程(8 步)
- 解析入参:可
--load-profile <name>载入存档作基底;再读--spec <json>;最后 CLI 参数覆盖(优先级:CLI > spec > 存档)。 - 取值校验:按
WX_ORDER_PROVIDER动态校验必填(internal 需微信下单字段组,external 豁免);PAY_DATA_TYPE三选一;AMOUNT必须为正整数(分)。 - 前置预检:打印 SkillHub 四项硬门槛(企业认证 / 绑定微信商户 / 微信支付下单能力 / 已发布且设定价)。任一缺失 → 提示暂停,不强行改代码。
- 选模板:按
DEPLOY_MODE选server/local模板;共用pay_lib.py+wx_v3_auth.py(模板已内联于scripts/templates_builtin.py,发布包不再含 .tmpl 文件)。 - 渲染:把公开字段填入;私钥/API Key 只写
os.environ[...]引用。 - 写入目标 Skill:渲染并写入支付触发模块(内置
pay_lib.py→ 目标pay_lib.py,wx_v3_auth.py→ 目标wx_v3_auth.py,pay_trigger_{mode}.py→ 目标pay_trigger_{mode}.py,pay_config.example.json→ 目标pay_config.example.json;模板字符串统一存于scripts/templates_builtin.py),并改写目标SKILL.md(注入付费契约段)。 - 安全自检:调用
secret_scanner.py扫描目标目录——命中真实 PEM 私钥 /skh_Token → 拒绝完成并报告文件/行。 - 收尾:可选
--save-profile存档;给出skill-publish-audit→skillhub-auto-publish的确切命令(不自动发布)。
--dry-run:渲染到临时目录、打印预览,不写目标 Skill,用于先审后改。
5. 支付模块核心逻辑(注入代码必含)
注入目标 Skill 的支付模块(渲染自内置模板 templates_builtin.py 中的 pay_lib.py,零三方依赖:标准库 + openssl 子进程;wx_v3_auth.py 提供微信 v3 签名):
generate_out_trade_no():生成合规订单号WX402_+ 时间戳(14位) + 随机串(12位) = 32 位(微信支付 ≤32 位要求)。create_wx_order():按PAY_DATA_TYPE三路下单——Native→code_url/ JSAPI→prepay_id(自动补payer.openid)/ H5→h5_url(自动补scene_info.h5_info);internal 模式 v3 Authorization 签名由wx_v3_auth.build_v3_auth_header完成(WECHATPAY2-SHA256-RSA2048,签名串method\nurl\ntimestamp\nnonce\nbody\n),证书序列号自动提取;external 模式委托create_external_order钩子。query_wx_order(out_trade_no):微信 v3 查单(GET out-trade-no),返回trade_state(SUCCESS/NOTPAY/CLOSED 等);external 模式委托query_external_order钩子。check_and_fulfill(out_trade_no):查单验证 →SUCCESS则调用generate_paid_content履约;SQLite 幂等(.pay_fulfilled.db,跨进程/重启有效;PAY_FULFILLED_DB环境变量可换路径),重复请求返回already_fulfilled: true。x402_preorder(pay_value):
1. 构造 L2 业务 JSON(skill_info/pay_type=SKILL_PAY/pay_mode=AUTH_AND_PAY/pay_items[].pay_data.type/expires_at≤15min);
2. 标准 Base64(非 URL-safe、不带换行)→payment_required;
3. 拼 5 行签名串(每行以\n结尾,含末行):POST\n/palmpayminiapp/clawagentpay/preorder\n{timestamp}\n{nonce_str}\n{payment_required}\n;
4.SHA256withRSA(PKCS#1 v1.5)用os.environ[PRIVATE_KEY_ENV]私钥签名 → Base64 →signature;
5. 组装 L1 Body(纯 Body 鉴权,无 Authorization 头)→ POST → 取payment_code。emit_pay_required_dict(code):返回{WeixinPay-Required, prompt}契约。run_pay_flow_with_order():端到端首次流程,返回(payment_code, out_trade_no)——统一接口模式的核心入口。
local 模板签名走 openssl dgst -sha256 -sign(Git Bash / macOS / Linux 自带),HTTP 走 urllib——零 pip 依赖,最易被 skillhub 接受。
5a. 支付后重试与查单(统一接口模式,对齐最佳实践)
生成的服务端模板采用单一端点 + X-Out-Trade-No 头区分两种场景:
| 场景 | 请求特征 | 响应 |
|---|---|---|
| 首次请求 | 无 X-Out-Trade-No 头 |
HTTP 402 + WeixinPay-Required + X-Out-Trade-No(含 amount/currency) |
| 支付后重试 | 带 X-Out-Trade-No 头(Body 与首次一致) |
查单 SUCCESS → HTTP 200 + content + already_fulfilled;未支付 → 再次 402(NOT_PAID) |
- local 形态对应
--retry <out_trade_no>参数。 - 幂等:同一订单只履约一次,重复请求
already_fulfilled: true。 - 完整 9 步协议、两套密钥区分、生产改造点见
references/best_practice.md。
5b. CLI 查单模式关键认知(极易踩坑 ★)
local 形态采用「CLI 输出 + 主动查单履约」时,下面三点必须想清楚再动手:
- 两个正交维度别混:「触发标识返回方式(CLI 输出 vs HTTP Header)」与「回调 URL(notify_url)」是两条平行线。local 形态天然是 CLI 输出(已做对),但 notify_url 是另一维度,选 CLI 输出不会让它消失(M17)。
- 四把凭证归属不同:下单/查单签名只用「商户 API 证书私钥 + 序列号」;APIv3 密钥 + 平台公钥只在 SCF 接收回调时被消耗(解密+验签),本地主链路不读,且 APIv3 密钥不参与 v3 签名(M19)。
- notify_url 必须填真实 HTTPS:主履约虽不经过它(走
--paid查单),但它仍是下单 API 必填 + 审核「真接入」证据,绝不能填example.com占位(M16)。腾讯云 SCF 老「API 网关触发」2025-06-30 已停服,改用「函数 URL」(*.tencentscf.com,已备案)(M15)。
> 完整凭证归属图(交易流程图)+ CLI 查单模式数据流 + 平台公钥 vs 平台证书 + 真实改造案例,见 references/wechat_pay_credential_flow.md。
6. 安全红线(★ 不可省略)
private_key_pem、微信API_KEY、证书只以环境变量名/外部路径形式存在;生成器任何位置都不出现明文。secret_scanner.py在写入后必跑;命中真实密钥 → 中止并定位,绝不带密发布。- 发布包只给
pay_config.example.json(占位符),真实配置由作者本地环境变量提供。 - 注入的 SKILL.md 段明确告知:私钥仅展示一次、SkillHub 不存储、切勿放前端或公开仓库。
7. 与发布链路集成(可选接续)
升级完成后,推荐顺序:
1. skill-publish-audit(确认无私有耦合/无 secret/清 pyc)→
2. skillhub-auto-publish(发布/更新)。
生成器在收尾打印这两步命令,不自动执行(避免越权发布)。
下次复用:pay_upgrade.py --load-profile <name> --target <新skill目录> --skip-preflight——同一套商户配置改造多个 Skill 只需这一条命令。
> 注:audit→auto-publish 链路不会把多余二进制打进发布包——auto-publish 的 make_clean_zip.py 强制排除 __pycache__/.pyc 等,zip 输出在 skill 目录之外。本 skill 的私钥防线更前一道、更关键。
7a. 改造完成后检查(5 项)
升级并注入付费契约段后,按以下清单核对:
- 付费契约段已注入:目标
SKILL.md出现「💳 付费能力(Pay Skill)」段,且四步法指引完整(重复运行不会重复追加)。 - 密钥未落盘:目标包内搜不到
PRIVATE KEY/skh_/ 商户 API Key 明文(secret_scanner.py已跑且 P0=0)。 - 订单号合规:
generate_out_trade_no()产出 ≤32 位(WX402_+ 14 位时间戳 + 12 位随机串)。 - 重试链路闭环:server 模板含
X-Out-Trade-No双场景分支;local 模板支持--retry <out_trade_no>(pay_trigger_local.py --help可查)。 - 回调验签就绪:server 模板含
/pay/notify端点(verify_wx_signature接线);internal 模式下单/查单走wx_v3_auth签名(非 TODO)。
7b. 停止条件(立即停止,不写目标)
以下任一情形出现,立即停止,不写入目标 Skill、不继续渲染:
| 条件 | 触发点 | 处置 |
|---|---|---|
| 前置预检未过 | preflight() 任一门槛未满足(企业认证/绑商户/下单能力/已发布定价) |
提示先完成前置,sys.exit(3) |
| 必填字段缺失 | 缺 SKILL_ID / WX_MCHID / AMOUNT 等任一 REQUIRED_KEYS |
报错列出缺失字段,sys.exit(2) |
| 目标目录不存在 | 非 dry-run 且 TARGET_SKILL_DIR 不是目录 |
报错,sys.exit(2) |
| 密钥扫描发现 P0 | 写入后 secret_scanner.py 命中真实私钥/skh_ Token |
中止并报告文件:行,sys.exit(1) |
| 渲染残留占位符 | render_all() 后产物仍含 ${VAR} |
报错列出残留,不进入写入阶段 |
> dry-run 模式不写目标,可安全预览;确认无误后去掉 --dry-run 正式执行。
8. 能力边界
| 能做什么 | 不能做什么 | 适用范围 |
|---|---|---|
| 把普通 Skill 改造为 Pay Skill(注入模块 + 配置 + 文档) | 不替你完成微信商户入驻/认证(需人工在 SkillHub 后台做) | 用户级 Skill 升级为 SkillHub Pay Skill |
| 生成 server / local 两种形态模板 | external 模式不替你接已有下单服务(提供 create_external_order/query_external_order 钩子) |
Native / JSAPI / H5 下单 |
微信 v3 下单签名(wx_v3_auth:Authorization 头 + 证书序列号自动提取 + 敏感字段加密辅助) |
不实现真实业务内容(generate_paid_content 需开发者替换) |
internal 模式开箱即用下单/查单 |
生成支付后重试/查单框架(X-Out-Trade-No 模式 + SQLite 幂等履约) |
不自动发布(只提示命令) | Agent 支付后取回付费内容 |
配置存档复用(--save-profile/--load-profile,公开信息+环境变量名) |
不存明文密钥(私钥始终在环境变量里) | 同一套商户配置改造多个 Skill |
| 密钥扫描安检(防私钥泄漏) | 微信回调生产级防重放(模板已验签,需叠加时间戳/金额核对) | 发布前最后闸 |
9. 参考文档指引
| 文档 | 何时读 | 路径 |
|---|---|---|
| X402 签名规范速查 + 易错点 + 两套密钥区分 | 排查签名/验签失败、理解 5 行签名串 | references/x402_signing.md |
| 四项前置门槛核对表 | 改造前确认资质齐备 | references/preflight_checklist.md |
| 向用户收取的配置信息清单(类型/内容/注意事项/来源/话术) | 改造时逐项与用户交互收集 | references/config_collection_checklist.md |
| 入驻 SkillPay 精简指南 | 企业认证 + 微信商户号绑定流程 | references/onboard_guide.md |
| 对外发布 Pay Skill 精简指南 | 上传、定价、审核、通过率建议 | references/publish_guide.md |
| 商户改造最佳实践 | 9 步闭环 / 统一接口 / 幂等 / 生产化 | references/best_practice.md |
| 配置存档机制(Profile Store) | 反复使用场景:存档/复用/覆盖/安全边界 | references/profile_store.md |
| 错误台账(负向约束优先) | 动手前先看「别这样做」,踩坑排障 | references/MISTAKES.md |
| 微信支付凭证归属全图(CLI 查单模式·交易流程图) | 分清触发方式/回调URL两维度、四把凭证归属、平台公钥vs证书、函数URL替代API网关 | references/wechat_pay_credential_flow.md |
| 常见问题(密钥轮换/过期/验签/重试) | 排障、运维 | references/faq.md |