# 支付宝沙箱密钥外提与仓库泄密处置 > status: **closed**(用户已决策接受,变更关闭) > created: 2026-09-07 > closed: 2026-09-07(用户决策:内网私仓 + 沙箱密钥可接受,后期生产密钥不提交) > complexity: 🟡中等 > 规则依据:`AGENTS.md` 5.9 安全红线「禁止硬编码密钥、AK/SK」、5.12 数据库脚本维护规范(无关)、2.4 环境变量 > 关联变更:`code-copilot/changes/hotel-pay-mock-hardening/`(资金逻辑收口,**性质不同,不可合并**) --- ## ⚠️ 0. 状态声明(必读) **本变更已关闭。** | 项 | 状态 | |---|---| | 方案编写 | ✅ 本文件 | | 代码/配置改动 | ❌ **一行未动** | | 密钥轮换 | ❌ 未执行 | | 用户决策 | ✅ **已决策接受**:git 是内网私有仓库(192.168.8.4),不在外网;密钥是沙箱密钥;后期如有必要,会直接不提交生产的密钥 | **关闭原因**:用户评估风险可接受,本变更不再推进。后续生产密钥不入库由用户把控。 **更正记录**: - 原 2.3 节「P0 启动隐患」**判断错误**,已撤销。真实情况:`application.yml` L42-43 `spring.profiles.active: dev`,当前启动会加载 `application-dev.yml`,`hotel.alipay.*` 全部有值,**不会崩**。 --- ## 1. 背景与目标 ### 1.1 问题 `application-dev.yml` 中的支付宝沙箱**应用私钥(RSA2,PKCS#8 全文约 1600 字符)与支付宝公钥以明文形式写死**,且该文件**被 git 跟踪并已推送到远端**。命中 `AGENTS.md` 5.9「禁止硬编码密钥、AK/SK、数据库密码」。 ### 1.2 风险定级(如实评估,不夸大) | 维度 | 结论 | |---|---| | 直接资金损失 | 🟢 **无** —— 这是**沙箱**密钥(`gateway-url` 为 `openapi-sandbox.dl.alipaydev.com`,`sandbox: true`),沙箱不涉及真实资金 | | 合规红线 | 🔴 **硬违规** —— `AGENTS.md` 5.9 **不区分沙箱与生产**,「禁止硬编码密钥」是无条件红线 | | 实际危害 | 🟡 **中** —— ① 他人可用你的沙箱 appid + 私钥以你的身份发起交易、污染测试数据;② 沙箱密钥对**存在被复用到生产**的现实可能(同一开发者图省事),一旦复用即升级为高危;③ 私钥泄露后无法「改密码」,只能重新生成密钥对并更换应用公钥 | | 扩散面 | 🔴 **已进远端历史** —— commit `65508d9`「支付宝沙箱支付功能提交」(2026-09-01)已存在于 `origin/master`,`git log -S` 可检索到私钥全文 | ### 1.3 目标 1. **止血**:轮换密钥,让已泄露的私钥失效(这是唯一真正有效的措施) 2. **断源**:仓库中不再出现任何真实密钥,改为环境变量占位 3. **生效忽略规则**:让 `.gitignore` 里**已存在但失效**的 `**/application-dev.yml` 规则真正起作用 4. **顺带修复一个 P0 启动隐患**(见 2.3)—— 生产 profile 当前**启动即崩** 5. **不做**:不改 `AGENTS.md` 的意图表述(它是对的,见 2.4) --- ## 2. 现状核实(全部经 git / 文件直读验证,非推测) ### 2.1 泄露范围 | 文件 | 是否被 git 跟踪 | 是否含明文密钥 | |---|---|---| | `forge-server/forge-admin-server/src/main/resources/application-dev.yml` | 🔴 **是**(`git ls-files` 命中) | 🔴 **是**,L106-118 `hotel.alipay` 块:`app-id` / `private-key`(L110)/ `alipay-public-key`(L112)/ `gateway-url` / `notify-url` / `sandbox` | | `forge-server/forge-app-server/src/main/resources/application-dev.yml` | 🔴 **是** | 🔴 **是**,L85-99 同一份密钥(**两份文件密钥完全相同**),另多一个 `return-url`(L97) | | `forge-server/*/src/main/resources/application-dev.example.yml` | ✅ 是(**应当**提交) | 🟢 **否** —— `Select-String -Pattern "alipay"` **零命中**,模板文件里根本没有 `hotel.alipay` 块 | | `forge-server/*/src/main/resources/application.yml`(生产) | ✅ 是 | 🟢 否 —— **零命中**,生产 yml 的 `hotel:` 块只有 `qr` 和 `pay`(admin-server L222-239) | 其它顺带发现:`application-dev.yml` L19-20 的数据库口令为 `root` / `root`(本地开发默认值,敏感度低,但同属「明文口令入库」)。 ### 2.2 🔴 `.gitignore` 规则**已存在但失效** —— 修正我此前的建议 我在提出本变更时的原话是「把 `application-dev.yml` 加入 `.gitignore`」。**这个说法是错的**,核实结果: ``` $ git check-ignore -v --no-index forge-server/forge-admin-server/src/main/resources/application-dev.yml .gitignore:83:**/application-dev.yml forge-server/.../application-dev.yml $ git check-ignore -v forge-server/forge-admin-server/src/main/resources/application-dev.yml (无输出,退出码 1) ``` - `.gitignore` **第 83 行早就有** `**/application-dev.yml`,规则本身完全正确、能匹配 - 但**不带 `--no-index` 时返回空** —— 因为 `.gitignore` **只对未跟踪文件生效**,该文件已在索引中,规则被完全绕过 - 结论:**不需要追加任何 `.gitignore` 规则**,需要的是 `git rm --cached` 把它从索引中移除,让既有规则接管 同时修正另一处说法:我此前说「`AGENTS.md` 2.4 把它归为『本地配置(不可提交)』,与实际不符,也该修」。**AGENTS.md 的表述是对的**(2.4 明确把 `application-dev.yml` 列为「后端本地配置」、把 `application-dev.example.yml` 列为「配置模板(可提交)」),**不符的是仓库实际状态**。要修的是状态,不是文案。 > ⚠️ `AGENTS.md` 2.4 唯一确实该修的是**路径前缀**:写的是 `forge/forge-admin-server/...`,真实路径是 `forge-server/forge-admin-server/...`。这与 2.2 的 `cd forge && mvn clean install`(仓库根 `forge/` 是**空目录**,真实 Maven 根为 `forge-server/`)同源,属同一处文档偏差,建议一并修。 ### 2.3 ✅ 【已撤销】原 P0 启动隐患 — 判断错误,当前不会崩 **原判断**:`AlipayConfig` 5 个 `@Value` 无默认值 + 生产 `application.yml` 无 `hotel.alipay` 块 → 生产启动崩溃(P0)。 **更正**:`application.yml` L42-43 `spring.profiles.active: dev`,且 docker-compose.yml / .env.example **无任何 profile 覆盖**,项目也**没有** `application-prod.yml`。因此当前启动(包括 docker 部署)都会加载 `application-dev.yml`,`hotel.alipay.*` 全部有值,**不会崩**。 **我错在哪**:把 `application.yml` 当成「生产专用配置」,忽略了它 L43 默认激活 dev profile,`application-dev.yml` 会被合并加载。 ### 2.4 环境事实 - 远端:`origin` → `http://192.168.8.4:3000/user5/forge_admin.git`(**内网 Gitea**,非公网仓库) - 泄露 commit `65508d9` 已在 `origin/master`(`git branch -r --contains` 命中) - ⚠️ 内网仓库**降低了**外部攻击面,但**不消除**风险:任何有仓库读权限的人(含离职人员的历史克隆、CI 缓存、备份)都持有该私钥 --- ## 3. 处置方案(4 步,顺序不可调换) > **顺序原则:先止血(轮换),再断源(占位化),最后清历史。** > 只要旧私钥仍然有效,任何仓库层面的清理都是**装饰性**的 —— 攻击者手里的密钥照样能用。 --- ### Step 1 🔴 轮换密钥(最先做,只能由你操作) **这一步做完,泄露的私钥立即作废,后续步骤的风险等级随之下降。** 在支付宝开放平台操作: 1. 登录 [open.alipay.com](https://open.alipay.com) → 开发者中心 → **沙箱应用**(appid `9021000166699753`) 2. 「开发信息」→「接口加签方式」→ 选择**公钥模式** 3. 用**支付宝官方密钥生成工具**重新生成一对 RSA2(2048 位)密钥 - ⚠️ 生成工具:[https://opendocs.alipay.com/common/02kipl](https://opendocs.alipay.com/common/02kipl),**禁止用第三方在线生成器**(那等于把新私钥再泄露一次) 4. 上传**新的应用公钥**,平台会返回**新的支付宝公钥** 5. 记录三样东西:新应用私钥、新支付宝公钥、(appid 不变) 6. ⚠️ **不要**把新密钥粘贴到任何 git 跟踪的文件、聊天工具、issue、文档中 —— 包括本文件 **验证轮换生效**:用旧私钥调一次沙箱接口,应返回验签失败(`isv.invalid-signature`)。 > 📌 **待你确认(第 6 节 Q1)**:是否有其它环境/同事正在使用这对沙箱密钥?轮换会**立即打断**他们的调试。 --- ### Step 2 yml 占位化 + 补齐生产块(修复 2.3 的 P0) **改动面:6 份 yml**(与 `hotel-pay-mock-hardening` 共享同一批文件,见第 5 节风险 ③) #### 2-A 生产 `application.yml`(admin-server L239 之后 / app-server L121 之后,**新增整个 `alipay` 块**) ```yaml hotel: qr: base-url: ${FORGE_HOTEL_QR_BASE_URL:} path: ${FORGE_HOTEL_QR_PATH:/r/} pay: mock-enabled: ${FORGE_HOTEL_PAY_MOCK_ENABLED:false} # 支付宝支付配置。全部通过环境变量注入,禁止在仓库中写明文密钥(AGENTS.md 5.9)。 # 五个必填项无代码级默认值:未注入时 AlipayConfig 占位符解析失败、服务启动即崩, # 这是刻意的 fail-fast —— 生产缺支付配置就不该起来,避免起来后静默走到 Mock 分支。 alipay: app-id: ${FORGE_HOTEL_ALIPAY_APP_ID:} private-key: ${FORGE_HOTEL_ALIPAY_PRIVATE_KEY:} alipay-public-key: ${FORGE_HOTEL_ALIPAY_PUBLIC_KEY:} gateway-url: ${FORGE_HOTEL_ALIPAY_GATEWAY_URL:https://openapi.alipay.com/gateway.do} notify-url: ${FORGE_HOTEL_ALIPAY_NOTIFY_URL:} return-url: ${FORGE_HOTEL_ALIPAY_RETURN_URL:} sandbox: ${FORGE_HOTEL_ALIPAY_SANDBOX:false} ``` > ⚠️ **这里有个设计选择需你定(第 6 节 Q2)**: > - **方案 A(上面写的)**:给空串默认值 `:}` → 生产不配也能启动,但支付功能不可用(调支付宝时报错) > - **方案 B**:**不给**默认值 → 生产不配就**启动即崩**(fail-fast) > > 我推荐 **方案 A + 空串**,理由:`AlipayConfig` 是 `@Configuration`,方案 B 会让**根本不用酒店支付功能的部署**也起不来(`forge-hotel` 是 `forge-business` 的一部分,被两个服务无条件扫描)。空串默认值 + 调用时报错,故障面更可控。 > > 但方案 A 需要**配套改动**:`AlipayConfig.alipayClient()`(L50-54)应在 `appId` / `privateKey` 为空时**不创建 Bean 或创建后在调用处显式报「支付未配置」**,否则 `DefaultAlipayClient` 会用空密钥初始化,错误信息晦涩。这一改动**超出纯配置范围**,需你同意才做(第 6 节 Q2)。 #### 2-B `application-dev.yml`(两份,**把明文密钥换成占位 + 本地兜底**) ```yaml hotel: qr: base-url: http://192.168.10.4:3001 path: "/#/pages/hotel/scan-bind" pay: mock-enabled: true # 支付宝沙箱配置。密钥不写死在文件里(本文件虽已 ignore,但历史上曾被提交,见变更方案 Step 1)。 # 三种注入方式任选其一: # ① IDEA Run Configuration → Environment variables(推荐,重启即生效) # ② 系统环境变量 # ③ 本机 ~/.forge/secrets/alipay.properties(若采用 Step 2-D 的 spring.config.import 方案) alipay: app-id: ${FORGE_HOTEL_ALIPAY_APP_ID:9021000166699753} private-key: ${FORGE_HOTEL_ALIPAY_PRIVATE_KEY:} alipay-public-key: ${FORGE_HOTEL_ALIPAY_PUBLIC_KEY:} gateway-url: ${FORGE_HOTEL_ALIPAY_GATEWAY_URL:https://openapi-sandbox.dl.alipaydev.com/gateway.do} notify-url: ${FORGE_HOTEL_ALIPAY_NOTIFY_URL:http://pc948923.natappfree.cc/hotel/open/pay/notify} return-url: ${FORGE_HOTEL_ALIPAY_RETURN_URL:http://192.168.10.4:3001/#/pages/hotel/customer/pay} # 仅 app-server sandbox: true ``` - `app-id` / `gateway-url` / `notify-url` / `return-url` / `sandbox` **保留明文默认值** —— 它们不是密钥,写死可让你**少配 5 个环境变量** - **只有 `private-key` 和 `alipay-public-key` 默认值为空** —— 这两个是必须外提的 - ⚠️ `notify-url` 的 natapp 免费隧道域名(`pc948923.natappfree.cc`)**与你账号绑定**,敏感度中等。是否也外提见第 6 节 Q3 #### 2-C `application-dev.example.yml`(两份,**新增带说明的空模板**) 当前模板文件**完全没有 `hotel.alipay` 块**(`Select-String` 零命中),新人 `cp` 之后会撞上 2.3 的启动崩溃。需补齐: ```yaml hotel: # ... qr / pay 块保持与 dev 一致 ... # 支付宝沙箱配置。以下两项必须自行填入,仓库中不提供任何真实值。 # 获取方式:open.alipay.com → 开发者中心 → 沙箱应用 → 接口加签方式 → 用官方密钥工具生成 RSA2 密钥对 alipay: app-id: 你的沙箱APPID private-key: 你的应用私钥(RSA2,PKCS#8 单行,不要带 -----BEGIN----- 头尾) alipay-public-key: 上传应用公钥后平台返回的支付宝公钥 gateway-url: https://openapi-sandbox.dl.alipaydev.com/gateway.do notify-url: 你的内网穿透地址/hotel/open/pay/notify return-url: 你的H5地址/#/pages/hotel/customer/pay sandbox: true ``` #### 2-D(可选)本机密钥文件方案 若不想每次配环境变量,可用 Spring Boot 的 `spring.config.import` 引入本机文件: ```yaml # application-dev.yml 顶部 spring: config: import: "optional:file:${user.home}/.forge/secrets/alipay.properties" ``` ```properties # ~/.forge/secrets/alipay.properties(在仓库外,天然不会被提交) FORGE_HOTEL_ALIPAY_PRIVATE_KEY=xxx FORGE_HOTEL_ALIPAY_PUBLIC_KEY=xxx ``` - `optional:` 前缀保证文件不存在时不报错 - 与仓库已有的 `~/.forge/secrets/crypto.properties` 约定(见 `application-dev.yml` L67 注释)**同源**,风格一致 - ⚠️ 需实测 `spring.config.import` 引入的 properties 能否被 `${}` 占位符解析(Spring Boot 3.2 支持,但需验证加载顺序)—— 见第 6 节 Q4 --- ### Step 3 让 `.gitignore` 规则生效 ```powershell # 从索引移除,但保留本地文件(--cached 是关键,不加会删掉你的本地配置) git rm --cached forge-server/forge-admin-server/src/main/resources/application-dev.yml git rm --cached forge-server/forge-app-server/src/main/resources/application-dev.yml # 验证:应输出 .gitignore:83:**/application-dev.yml git check-ignore -v forge-server/forge-admin-server/src/main/resources/application-dev.yml ``` #### 🔴 这一步对协作者有破坏性,必须先通知 `git rm --cached` 提交后,该 commit 记录的是**文件删除**。其他开发者 `git pull` 时,**git 会删掉他们本地的 `application-dev.yml`**(因为在他们工作区里该文件是跟踪状态)。 **必须先做的事**: 1. 通知所有协作者**备份自己的 `application-dev.yml`** 2. 他们 pull 之后把备份放回来(此时文件已 untracked + ignored,不会再被提交) 3. 提交信息中写明这一点 > 📌 **待你确认(第 6 节 Q5)**:当前仓库有几个活跃协作者?是否可以协调一个统一的时间窗口? #### ⚠️ 顺序要求 **Step 3 必须在 Step 2 之后**:先占位化再脱钩。若先 `git rm --cached`,那么在你换密钥的这段时间里,明文密钥文件仍在你本地且已脱离版本控制 —— 看似安全,实则**远端历史里的旧密钥仍然有效**,止血效果为零。 --- ### Step 4(可选,🔴 破坏性)清理 git 历史 **只有在 Step 1 轮换完成后,这一步才是"锦上添花";如果没轮换,这一步是自欺欺人。** | 方案 | 命令 | 破坏性 | |---|---|---| | `git filter-repo`(官方推荐) | `git filter-repo --replace-text expressions.txt` | 🔴 **重写全部 commit hash** | | BFG Repo-Cleaner | `bfg --replace-text passwords.txt` | 🔴 同上 | **后果(必须全部接受才能做)**: - 所有 commit hash 变化 → 现存分支、PR、issue 引用、CI 缓存全部失效 - 所有协作者必须**重新克隆**(不能 pull,会产生重复历史) - 远端需 `git push --force` → **`AGENTS.md` 与我的操作准则都要求:force push 到 master 必须你明确授权,我不会自行执行** - Gitea 服务端可能仍保留旧对象(需管理员执行 GC) **我的建议**: - 若仓库**仅内网 + 少数可信协作者** → **不做 Step 4**。轮换后旧密钥已失效,历史里的字符串只是无意义数据,重写历史的协作成本远高于收益 - 若仓库**曾对公网开放 / 有不可信读取者 / 沙箱密钥曾复用到生产** → **必须做 Step 4** --- ## 4. 对你本地开发的影响与规避 | 影响 | 说明 | 规避 | |---|---|---| | 🔴 **改完 `application-dev.yml` 后启动失败** | `private-key` 占位符无值 → `AlipayConfig` 拿到空串 → `DefaultAlipayClient` 初始化不报错,但**调支付宝接口时验签失败** | 执行 Step 2 前,先在 IDEA Run Configuration 里配好 `FORGE_HOTEL_ALIPAY_PRIVATE_KEY` / `FORGE_HOTEL_ALIPAY_PUBLIC_KEY` | | 🟡 支付宝沙箱支付调试中断 | 轮换密钥后,新密钥未配好之前无法调通 | 轮换与配置**在同一时间窗口内完成**;期间可用 `hotel.pay.mock-enabled=true` 走 Mock 分支调试业务流转(dev 已开启) | | 🟢 H5 前端 | **零影响** —— 本变更不动任何前端文件 | — | | 🟢 二维码功能 | **零影响** —— `hotel.qr.*` 块不动 | — | | 🟢 Mock 支付开关 | **零影响** —— `hotel.pay.mock-enabled` 不动 | — | > ⚠️ **执行时机建议**:选一个你**不需要立即调试支付宝真实支付**的时间点。若近期要演示/验收支付流程,先跳过 Step 1-2,只做记录。 --- ## 5. 风险与关注点 | # | 风险 | 缓解 | |---|---|---| | ① | 🔴 **轮换打断协作者**:沙箱 appid 是共享的,换密钥后其他人本地立刻失效 | Step 1 前先确认使用范围(Q1);轮换后同步新密钥给协作者(**通过安全渠道,不走 git**) | | ② | 🔴 **Step 3 删除协作者本地配置**:`git rm --cached` 提交后,别人 pull 会丢文件 | 提前通知备份,提交信息写明 | | ③ | 🟡 **6 份 yml 与 `hotel-pay-mock-hardening` 共享同一 `hotel:` 块** | 两变更**不可并行改同一文件**;本变更应在 `hotel-pay-mock-hardening` 的 HARD-GATE 签署**之后**再动 yml,避免回滚互相污染 | | ④ | 🟡 **Step 2-A 的方案 B(无默认值)会让不用酒店支付的部署起不来** | 采方案 A(空串默认值)+ 配套改 `alipayClient()` 显式报错(需你同意,Q2) | | ⑤ | 🟡 **新密钥二次泄露**:把新密钥粘到 issue / 聊天 / 文档 | Step 1 第 6 条已明令禁止;本文件全程**不含任何真实密钥字符** | | ⑥ | 🟢 **Step 4 重写历史** | 默认不做;若做需你明确授权 force push | | ⑦ | 🟡 **`application-dev.example.yml` 补块后新人仍可能填错格式** | 私钥必须是 **PKCS#8 单行、去掉 `-----BEGIN-----` 头尾**,在模板注释中写明 | --- ## 6. 待决策(需你逐条给结论,全部解决后才进入 `/apply`) - [ ] **Q1**:这对沙箱密钥(appid `9021000166699753`)**是否有其他环境/同事正在使用**?轮换会立即打断他们。是否有统一时间窗口? - [ ] **Q2**:生产 `application.yml` 的 `hotel.alipay.*` 采用**方案 A(空串默认值,推荐)**还是**方案 B(无默认值 fail-fast)**?若选 A,是否同意**配套修改 `AlipayConfig.alipayClient()`**,在密钥为空时显式抛「支付宝支付未配置」而非用空密钥初始化? - [ ] **Q3**:`notify-url` 的 natapp 隧道域名(`pc948923.natappfree.cc`)是否也外提?它与你账号绑定、敏感度中等,但外提后你每次换隧道都要改环境变量。 - [ ] **Q4**:是否采用 **Step 2-D**(`spring.config.import` + `~/.forge/secrets/alipay.properties`)?好处是不用配环境变量、与仓库既有 `crypto.properties` 约定同源;代价是需先实测加载顺序。 - [ ] **Q5**:是否执行 **Step 4**(重写 git 历史)?需你明确授权 `git push --force` 到 `master`,我不会自行执行。我的默认建议是**不做**(内网仓库 + 已轮换 = 收益低于协作成本)。 ### 6.1 附带待决策:`AGENTS.md` 文档修正 - [ ] **Q6**:是否顺带修 `AGENTS.md` 的**路径偏差**? - 2.2 节 `cd forge && mvn clean install` → 真实为 `cd forge-server && mvn clean install`(仓库根 `forge/` 是**空目录**) - 2.4 节表格路径 `forge/forge-admin-server/...` → 真实为 `forge-server/forge-admin-server/...` - ⚠️ **2.4 节的意图表述是对的,不要改**(`application-dev.yml` = 本地配置、`.example.yml` = 可提交模板),错的只是路径前缀 --- ## 7. 验收清单(执行后逐条勾) - [ ] 支付宝开放平台已生成新 RSA2 密钥对,应用公钥已更换 - [ ] 用旧私钥调沙箱接口返回验签失败(证明轮换生效) - [ ] `Select-String -Pattern "MIIEvg" -Path forge-server\*\src\main\resources\*.yml` **零命中**(旧私钥前缀已清除) - [ ] 6 份 yml 的 `hotel.alipay` 块均已占位化,生产块已补齐(修复 2.3 的 P0) - [ ] `application-dev.example.yml` 已补 `hotel.alipay` 空模板 + 格式说明 - [ ] `git ls-files | Select-String "application-dev.yml"` **零命中**(已脱离跟踪) - [ ] `git check-ignore -v forge-server/forge-admin-server/src/main/resources/application-dev.yml` 输出 `.gitignore:83`(规则已生效) - [ ] 本地 `application-dev.yml` 文件**仍存在**且内容正确(`--cached` 未误删) - [ ] 配好环境变量后,admin-server 与 app-server **均能正常启动** - [ ] 支付宝沙箱支付**调通一次**(`createPay` → 收银台 → 回调 → `pay_status=1`) - [ ] H5 dev 环境二维码扫码、Mock 支付调试**行为零变化** - [ ] 协作者已收到通知并备份了自己的 `application-dev.yml` - [ ] (若做 Step 4)所有协作者已重新克隆,CI 缓存已清理 --- ## 8. 确认记录(HARD-GATE) > 本变更**不直接改动资金流转逻辑**,但涉及**密钥安全**与**破坏性 git 操作**(`git rm --cached` / 可能的 `push --force`),按 `AGENTS.md` 5.9 与操作准则,须你确认后方可执行。 > **AI 助手不得代签、不得自行执行 Step 3 之后的任何 git 写操作。** - **确认时间**: - **确认人**: **确认范围声明**(签署即表示已阅读并认可): 1. 第 6 节 Q1~Q6 六项待决策已有明确结论 2. 知悉 Step 1 轮换密钥**只能由本人在支付宝开放平台操作**,AI 无法代办 3. 知悉 Step 3 会导致协作者 pull 时丢失本地 `application-dev.yml`,已安排通知与备份 4. 知悉 Step 4 需重写全部 commit hash 并 force push 到 master,**默认不做**;若做已单独明确授权 5. 知悉本变更应在 `hotel-pay-mock-hardening` 的 HARD-GATE 签署**之后**再动 6 份共享 yml(风险 ③)