170 lines
8.6 KiB
Markdown
170 lines
8.6 KiB
Markdown
---
|
||
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-pwa:POPAuth 在前,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 limit(nginx/云 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
|
||
|