Files
cursor/plans/callback_log_验签顺序_537cb63e.plan.md
ray zhou 225fb2bd28 ok
2026-05-29 19:26:16 +08:00

170 lines
8.6 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

---
name: callback_log 验签顺序
overview: 修订需求文档与 slot-pwa 实现:将「完整 callback_log 落库」从「收包第一时间」调整为「验签通过后」;验签失败仅保留应用日志,避免未授权请求刷写分库分表。
todos:
- id: update-docs-02
content: 修订 02_provider_callback_log.md流程图、§5 衔接、§6 验收、安全说明
status: completed
- id: update-docs-parent
content: 同步 game_gateway_risk_prd_v_2.md §4.3/§4.4 流程顺序
status: completed
- id: reorder-middleware
content: slot-pwaPOPAuth 在前ProviderCallbackLog 在后POPAuth 去掉 DB 验签回写
status: in_progress
- id: insert-verify-passed
content: insertReceived 验签通过态写入;清理 markVerifyPassed/Failed 在 POP 路径的调用
status: pending
- id: tests-verify
content: 更新/补充 callback_log 相关测试并跑 verify-slot-backend.sh
status: pending
isProject: false
---
# callback_log 改为验签通过后落库
## 问题判断
你的顾虑成立。当前设计与实现是:
```mermaid
sequenceDiagram
participant P as Provider
participant L as ProviderCallbackLogMiddleware
participant A as POPAuthMiddleware
participant C as Controller
P->>L: HTTP callback
L->>L: INSERT callback_log verify_status=0
L->>A: next
A->>A: verify signature
alt verify fail
A->>L: UPDATE verify_failed
A-->>P: 401 JSON
else verify pass
A->>C: next
end
```
- 需求:[02_provider_callback_log.md](docs/requirements/game_gateway/02_provider_callback_log.md) §3.1 / §5 要求「先 INSERT再验签」
- 实现:[middleware.php](slot-pwa/config/middleware.php) 中 `ProviderCallbackLogMiddleware` 排在 `POPAuthMiddleware` **之前**[ProviderCallbackLogMiddleware.php](slot-pwa/app/middleware/ProviderCallbackLogMiddleware.php) 注释也写明「须在验签之前」
- 验收 §6.2 要求「验签失败仍有 callback_log」——与防刷目标冲突
**原设计意图(合理但需边界)**:把每一次到达网关的请求都当作「证据」,包括验签失败,便于和 Provider 对账「对方声称发过回调」。代价是:公开 URL + 无 IP 白名单/限流时,攻击者可对 `user_transaction_*.game_provider_callback_log_`* 灌入大量 `MEDIUMTEXT``raw_body`/`raw_headers`)。
**你已确认的产品取舍**:验签通过后才 INSERT **完整** callback_log验签失败不落库仅应用日志后续可选轻量 reject 表,本期不做)。
这与资金安全主链路一致:父文档 [game_gateway_risk_prd_v_2.md](docs/requirements/game_gateway_risk_prd_v_2.md) §5.1 将「验签 / IP 校验」的目的写明为「防止非法回调」——应先挡住非法流量,再落审计库。
---
## 目标流程
```mermaid
sequenceDiagram
participant P as Provider
participant A as POPAuthMiddleware
participant L as ProviderCallbackLogMiddleware
participant C as Controller
P->>A: HTTP callback
A->>A: verify signature
alt verify fail
A->>A: structured error log only
A-->>P: auth error JSON
else verify pass
A->>L: next
L->>L: INSERT callback_log verify_status=1
L->>C: next
end
```
要点:
- **验签失败**`Log::error` 保留 `route``IP``body_hash`(可选)、`error`**不**调用 `markVerifyFailed`(因无 `log_id`
- **验签通过**`insertReceived` 时直接 `verify_status = VERIFY_PASSED(1)`,可删除事后 `markVerifyPassed` 的 UPDATE减少一次写
- **解析失败**:仍在验签通过且已落库后,由 Logic 更新 `parse_status` / `process_status`(现有 `markParseFailed` 等不变)
---
## 文档修订(必须)
### 1. [02_provider_callback_log.md](docs/requirements/game_gateway/02_provider_callback_log.md)
| 章节 | 修改 |
| ------------- | ----------------------------------------------------------------------------------------------------------------------------- |
| §1.2 In scope | 「第一时间落库」改为「**验签通过后**同步落库完整原始请求」 |
| §3.1 流程图 | `Provider callback → 验签 → 同步写 callback_log(verify_status=1) → 解析 → …` |
| §5 衔接表 | 「收到任意回调先 INSERT」改为「验签通过后 INSERT」删除「验签完成再 UPDATE verify_status」作为主路径保留解析/处理状态更新) |
| §6 验收 | **改写 §6.2**:验签失败不落 callback_log但应用日志可按 `request_id`/时间检索;**新增**:任意成功进入业务链路的 `provider_tx` 仍可 `callback_log_id` 追溯 `raw_body` |
| 新增 §(安全说明) | 明确:防刷靠「验签门禁 +(建议)网关 IP 白名单/限流」;若未来需要攻击审计,用独立 reject 表或日志平台,不污染主 callback_log |
### 2. [game_gateway_risk_prd_v_2.md](docs/requirements/game_gateway_risk_prd_v_2.md)
- §4.3 / §4.4 流程图中「同步写 callback_log」节点移到「验签」**之后**(与 02 子文档一致)
---
## 代码修订slot-pwa
### 1. 调整 Middleware 顺序
[config/middleware.php](slot-pwa/config/middleware.php) `pop` 组改为:
```php
'pop' => [
\app\middleware\POPAuthMiddleware::class,
\app\middleware\ProviderCallbackLogMiddleware::class,
],
```
### 2. [POPAuthMiddleware.php](slot-pwa/app/middleware/POPAuthMiddleware.php)
- 验签失败:仅 `Log::error` + 返回 JSON**移除** `markVerifyFailed`(依赖已落库的 `log_id`
- 验签成功:**移除** `markVerifyPassed`;直接 `$handler($request)` 进入下一 Middleware 落库
- 平台配置缺失:同样不落库,只打日志返回错误
### 3. [ProviderCallbackLogMiddleware.php](slot-pwa/app/middleware/ProviderCallbackLogMiddleware.php)
- 更新类注释:在验签 Middleware **之后**执行,仅对验签通过的请求落库
- `recordReceivedFromRequest` 失败时:当前 catch 后仍 `return $handler`——建议改为 **记录错误并返回 5xx**(避免无 callback_log 却继续扣款);与产品确认可放在实现阶段一并收紧
### 4. [ProviderCallbackLogService.php](slot-pwa/app/service/game/ProviderCallbackLogService.php) + [GameProviderCallbackLogModel.php](slot-pwa/app/model/shard/GameProviderCallbackLogModel.php)
- `insertReceived` 增加参数或拆方法:`insertReceivedAfterVerifyPassed()`,写入时 `verify_status = VERIFY_PASSED`
- `markVerifyPassed` / `markVerifyFailed`保留方法但标注「仅兼容旧路径」或删除未引用调用POP 路径不再使用 failed 回写)
- `recordReceivedFromRequest` 仅在验签后 Middleware 调用,语义与命名可在 PHPDoc 中写清
### 5. 测试
- [GameProviderCallbackLogShardModelTest.php](slot-pwa/tests/Feature/GameProviderCallbackLogShardModelTest.php) / [ProviderCallbackLogTestCase.php](slot-pwa/tests/Support/ProviderCallbackLogTestCase.php):若直接测 `insertReceived`,默认 `VERIFY_PENDING` 仍可用于 Model 单测;新增或调整用例覆盖「验签后插入为 PASSED」
- 可选Middleware 顺序集成测试(验签失败不触发 insert——若项目已有 HTTP 测试基建则补一条
---
## 不在本期但必须知晓的风险
| 风险 | 缓解 |
| ------------------------------- | --------------------------------------------------------------------------------- |
| 验签失败无 DB 记录,与 Provider 扯皮时只能靠日志 | 日志字段标准化:`provider_code``source_ip``body_hash``x-request-id`;日志保留期与 Provider 对齐 |
| CPU 层仍可被刷验签 | 网关层 Provider IP 白名单 + rate limitnginx/云 WAF本期文档 § 安全说明中写为 **建议项** |
| `uid=0` 分片仍可能被合法但解析失败的请求写入 | 仅发生在验签通过后,可接受;与刷库攻击不同类 |
---
## 实施顺序建议
1. 先改 02 子文档 + 父文档流程图(避免实现与需求再漂移)
2. 调整 middleware 顺序与 POP / CallbackLog 职责
3. Model/Service 插入时带 `VERIFY_PASSED`
4. `按php-clean-code.mdc 文件验证代码规范`
5.`verify-slot-backend.sh` + 相关 PHPUnit