refactor(integrations): 穿山甲验签改 GroMore SHA256 + pangle/wxpay 从 core 移入 integrations
- 验签: pangle.build_sign/verify_callback_sign 改为 GroMore 官方
sign=SHA256("{m-key}:{trans_id}")(普通 SHA256,只签 trans_id);
回调响应改成 GroMore 要求的 {is_verify, reason}
- 发奖: 按回调 reward_amount 发金币(rewards 带回退/夹紧),ad_reward 幂等 + 每日上限
- 分层: app/core/{pangle,wxpay}.py → app/integrations/(对齐 jiguang/meituan/sms);
更新 ad.py / crud/wallet.py / scripts/sim_pangle_callback.py / 测试的 import
(含 test_withdraw 的 monkeypatch 路径 app.core.wxpay→app.integrations.wxpay)
- 测试: test_ad_reward + test_withdraw 共 17 项通过
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
+19
-10
@@ -15,8 +15,9 @@ import uuid
|
||||
from fastapi import APIRouter, Depends, HTTPException, Request, status
|
||||
|
||||
from app.api.deps import CurrentUser, DbSession
|
||||
from app.core import pangle
|
||||
from app.core import rewards
|
||||
from app.core.config import settings
|
||||
from app.integrations import pangle
|
||||
from app.core.ratelimit import rate_limit
|
||||
from app.crud import ad_reward as crud_ad
|
||||
from app.schemas.ad import AdRewardStatusOut, PangleCallbackOut, TestGrantOut
|
||||
@@ -25,6 +26,11 @@ logger = logging.getLogger("shagua.ad")
|
||||
|
||||
router = APIRouter(prefix="/api/v1/ad", tags=["ad"])
|
||||
|
||||
# GroMore 回调 is_verify=false 时透传给客户端 SDK 的错误码(自定义,仅用于排查/客户端提示)
|
||||
REASON_OK = 0
|
||||
REASON_BAD_PARAMS = 1 # 验签过但缺 trans_id / user_id 非数字
|
||||
REASON_UNKNOWN_USER = 2 # user_id 不存在(可能伪造)
|
||||
|
||||
|
||||
@router.get(
|
||||
"/pangle-callback",
|
||||
@@ -33,11 +39,11 @@ router = APIRouter(prefix="/api/v1/ad", tags=["ad"])
|
||||
dependencies=[Depends(rate_limit(300, 60, "pangle-callback"))],
|
||||
)
|
||||
def pangle_callback(request: Request, db: DbSession) -> PangleCallbackOut:
|
||||
"""穿山甲服务器在激励视频播完后回调,带 user_id / trans_id / sign 等 query 参数。
|
||||
"""穿山甲 GroMore 在激励视频播完后回调,带 user_id / trans_id / reward_amount / sign 等 query 参数。
|
||||
|
||||
流程:开关/密钥就绪 → 验签 → 解析 user_id/trans_id → 幂等发金币。
|
||||
验签失败 403(穿山甲会重试,留给真请求);user_id 非法/不存在则受理但不发(is_valid 仍 True,
|
||||
避免穿山甲无意义重试)。
|
||||
流程:开关/密钥就绪 → 验签(SHA256(m-key:trans_id)) → 解析 user_id/reward_amount → 幂等发金币。
|
||||
验签失败 403(留给真请求重试);参数缺/坏或 user 不存在 → is_verify=false + reason(不发,不重试)。
|
||||
granted / capped → is_verify=true + reason=0。
|
||||
"""
|
||||
if not settings.pangle_callback_configured:
|
||||
raise HTTPException(
|
||||
@@ -53,24 +59,27 @@ def pangle_callback(request: Request, db: DbSession) -> PangleCallbackOut:
|
||||
trans_id = params.get("trans_id") or ""
|
||||
raw_user_id = params.get("user_id") or ""
|
||||
if not trans_id or not raw_user_id.isdigit():
|
||||
# 验签过了但参数缺/坏:受理不发奖,不让穿山甲重试
|
||||
# 验签过了但参数缺/坏:不发奖(is_verify=false),带错误码
|
||||
logger.warning("pangle callback bad params: %s", params)
|
||||
return PangleCallbackOut(is_valid=False)
|
||||
return PangleCallbackOut(is_verify=False, reason=REASON_BAD_PARAMS)
|
||||
|
||||
user_id = int(raw_user_id)
|
||||
coin = rewards.resolve_ad_reward_coin(params.get("reward_amount"))
|
||||
raw = "&".join(f"{k}={v}" for k, v in sorted(params.items()) if k != "sign")
|
||||
try:
|
||||
rec = crud_ad.grant_ad_reward(
|
||||
db, user_id, trans_id, reward_name=params.get("reward_name"), raw=raw[:1024]
|
||||
db, user_id, trans_id, coin=coin,
|
||||
reward_name=params.get("reward_name"), raw=raw[:1024],
|
||||
)
|
||||
except crud_ad.UnknownUserError:
|
||||
logger.warning("pangle callback unknown user_id=%d trans_id=%s", user_id, trans_id)
|
||||
return PangleCallbackOut(is_valid=False)
|
||||
return PangleCallbackOut(is_verify=False, reason=REASON_UNKNOWN_USER)
|
||||
|
||||
logger.info(
|
||||
"ad reward user_id=%d trans_id=%s status=%s coin=%d", user_id, trans_id, rec.status, rec.coin
|
||||
)
|
||||
return PangleCallbackOut(is_valid=True)
|
||||
# granted / capped 均算"已处理":is_verify=true 不让穿山甲重试(capped 只是没加币)
|
||||
return PangleCallbackOut(is_verify=True, reason=REASON_OK)
|
||||
|
||||
|
||||
@router.get("/reward-status", response_model=AdRewardStatusOut, summary="今日看广告发奖进度")
|
||||
|
||||
Reference in New Issue
Block a user