# 第三方人机验证优化改造计划 约束:不写双路/兼容迁移代码;顺手清理死代码;不做真实视觉效果验收(用语法检查、契约测试、构建产物校验代替)。 已确认决策:范围全做(P0+P1+P2);下载场景 pass 放行 10 分钟;凭据不齐时仅加后台警告、前台保持放行。 ## 改动文件总览 | 文件 | 动作 | |---|---| | `inc/modules/captcha.php` | 重写校验层 + 瘦身渲染 + 后台警告 + 静态缓存 | | `assets/js/captcha-front.js` | 新建(从内联脚本迁移,~340 行) | | `assets/css/shared/captcha.css` | 保留不动(改为进 bundle) | | `tools/build-public-assets.mjs` | publicScripts 加 captcha-front.js;public.css 加 captcha.css | | `src/Admin/Schema/SchemaPermissions.php` | 删除 'mode' 死字段(L32-34) | | `assets/admin/src/schema/sections/permissions/index.js` | 删除 'mode' 死字段(L18 defaults、L50-56) | | `src/Admin/SchemaProvider.php` | L403 白名单删 'mode';保存后 flush 配置静态缓存 | | `tests/widget-system/captcha-cloud-contract.test.cjs` | 新建契约测试 | 不动的文件及原因:`aksrt-header-userbox.php`(toast-contract 测试锁定 signin 接线)、`data-aksrt-download-captcha` 挂载点(`templates/parts/resource-download-box.php:467`、`inc/helpers/aksrt-pay.php:1586`)、所有业务 JS(auth/signin/comments/cms-download/user-center/front-submit 只消费 `AKSRTCaptchaState.scenes[scene]` 与 mount 内 token input,接口不变即零改动)。 ## 1. inc/modules/captcha.php — 校验层 **P0-1 腾讯云改走 WP HTTP API**:`aksrt_verify_tencent()` 删除 curl 块(L374-390,含 `CURLOPT_SSL_VERIFYPEER => false`),改用 `wp_remote_post('https://captcha.tencentcloudapi.com/', ['timeout'=>5, 'headers'=>aksrt_tencent_tc3_sign(...)生成的一维数组, 'body'=>$payload])`,与另外三家一致。签名函数不动。 **P1-5 错误信息收敛**:四家服务商的传输层错误(wp_remote_post 返回 WP_Error、响应不可解析)统一 `error_log('[AKSRT Captcha] {provider}: ' . 详情)` 后返回通用文案 `__('人机验证服务暂不可用,请稍后重试', 'aksrt')`;保留对用户有行动意义的映射文案(腾讯 CaptchaCode 表、阿里 F0xx 表)。腾讯 API Error 分支(签名错误等)也走 log+通用文案。 **P1-4**:阿里云 `SignatureNonce` 由 `uniqid('', true)` 改 `wp_generate_uuid4()`。 **P1-8**:四家 `'timeout'` 统一 5 秒(现 10 秒)。 **P1-6 Turnstile 绑定场景**:`aksrt_verify_captcha($post_data, $scene = '')` 增加第二参数(唯一调用方是 `aksrt_require_captcha`,无散落调用点);Cloudflare 成功响应校验:`action !== 'aksrt-verify'` 或 `cdata !== $scene`(存在才比对,lenient)→ 失败。客户端配套见 §2。 **P0-2 下载 pass 放行(10 分钟)**:`aksrt_require_captcha()` 内,scene 命中放行名单时先查 transient、验证通过后写 transient: - `aksrt_captcha_pass_ttl($scene)`:仅 `download` 返回 `10 * MINUTE_IN_SECONDS`,其余返回 0(单一代码路径,无散落分支) - key = `'aksrt_captcha_pass_' . md5($scene . '|' . get_current_user_id() . '|' . aksrt_get_client_ip() . '|' . md5(UA))`,仿 `aksrt_auth_rate_` 既有模式 ## 2. assets/js/captcha-front.js — 新建(P2-9 + P1-7 客户端) 把 `aksrt_render_captcha()` 内联 `