# GET /api/v1/ad/pangle-callback — 穿山甲 GroMore 激励视频发奖回调(S2S)

> 所属:Ad 组（前缀 `/api/v1/ad`） | 鉴权:**无 JWT,靠验签**（穿山甲 GroMore 服务器调用） | 限流:同 IP ≤300 次/分 | [← 返回 API 索引](./README.md)
>
> ⚠️ 我们客户端用 `useMediation(true)`(GroMore 融合),回调走 **GroMore 广告位层级**(规范见 supportcenter/26240),**不是**联盟代码位层级(5416)。两者密钥、响应格式都不同,别混。后台配置入口:**GroMore 聚合管理 → 搜广告位ID → 编辑 → 勾选「服务端激励回调」**(广告位层级配了就别再在代码位层级重复配,会冲突)。
>
> 集成实现:见 [integrations/pangle](../integrations/pangle.md)（验签算法、m-key 来源、设计动机）。

## 入参（query，由 GroMore 拼装）
GroMore 以 GET 回调,关键参数:

| 字段 | 类型 | 说明 |
|---|---|---|
| `user_id` | string | 客户端 `setUserID` 传入的用户标识（须为数字 = 本系统 user.id） |
| `trans_id` | string | 交易号（**幂等键** + **唯一参与签名的字段**） |
| `reward_name` | string | 奖励名（广告位配置,入库备注） |
| `ecpm` | string\|null | GroMore 回调携带的 eCPM。普通激励视频优先用它计算金币 |
| `extra` / `gromoreExtra` / `gromore_extra` | string | 客户端透传 JSON。支持 `ad_session_id`、`reward_scene`;`reward_scene=signin_boost` 表示签到膨胀 |
| `mediation_rit` | string | 代码位 ID（GroMore 带,目前仅入 raw 备查） |
| `prime_rit` | string | 广告位 ID（同上） |
| `adn_name` | string | 实际出广告的 ADN 名（同上,可用于收益分析） |
| `ecpm` | string | 本次广告 eCPM（同上,可用于收益分析） |
| `sign` | string | 签名,见下 |

**验签**:`sign = SHA256("{m-key}:{trans_id}")` 十六进制（只签 `trans_id`，其余参数不参与）。多激励位共用同一回调 URL → 服务端把各位的 m-key 都配上,`verify_callback_sign_any` 逐个试、任一过即接受。算法细节、m-key 配置项(`PANGLE_REWARD_SECRET_TEST/_TEST_DEDICATED/_PROD`)、为什么这样设计 → 见集成文档 [integrations/pangle](../integrations/pangle.md)。

## 出参
响应 `200`,**响应体必须是 `{"is_verify": bool, "reason": int}`**（GroMore 规范）。

| 字段 | 类型 | 说明 |
|---|---|---|
| `is_verify` | bool | `true`=校验通过、发放奖励（发奖成功 或 当日达上限,均算已处理、不重试） |
| `reason` | int | `is_verify=false` 时的错误码,透传客户端 SDK:`1`=参数缺/坏,`2`=user 不存在;成功为 `0` |

## 错误码
- `403` 验签失败（`bad sign`,留给真请求重试）
- `503` 回调未配置（`pangle_callback_configured=false`）

## 说明
**发奖唯一可信入口**:验签 → 取 `user_id`/`extra` → 按 `reward_scene` 分流 → 幂等处理（按 `trans_id` 去重）。客户端不直接发奖,被破解也刷不到钱。

- `reward_scene=reward_video` 或缺省:普通激励视频。金币按 `eCPM / 1000 * eCPM因子 * 当日次数因子 * 10000` 计算;若回调没有 `ecpm`,会按 `extra.ad_session_id` 查客户端 `/ad/ecpm-report` 的上报值;两边都没有 eCPM 时不发币,记录 `status=ecpm_missing`。
- `reward_scene=signin_boost`:签到膨胀。要求用户当天已签到且不是 Day14;看完视频固定发 `2000` 金币,写 `signin_boost_record` 与 `coin_transaction.biz_type=signin_boost`。
- 未知 `reward_scene`:不发币,记录 `status=unknown_scene`,返回 `is_verify=false/reason=1`。
- 验签过但参数缺/坏或 user 不存在 → 不发（`is_verify=false` + `reason`）;granted / capped / ecpm_missing / 业务不满足已记录 → `is_verify=true` + `reason=0`。
