Files
shaguabijia-app-server/docs/ad_reward_golive_checklist.md
T
OuYingJun1024 0722d7b0d5 feat: 福利钱包+签到+省钱战绩+激励广告发奖+微信提现 后端
- 福利钱包: 金币/现金余额、流水、兑换 (api/v1/wallet.py, crud/wallet.py, models/wallet.py, schemas/welfare.py)
- 每日签到: 连续天数 + 档位奖励 (api/v1/signin.py, crud/signin.py, models/signin.py)
- 任务系统: "打开消息提醒"等任务领奖 (api/v1/tasks.py, crud/task.py, models/task.py)
- 省钱战绩: 省钱汇总/战绩/店铺菜品 (api/v1/savings.py, crud/savings.py, models/savings.py)
- 激励广告发奖: 穿山甲服务端回调 + 发奖规则 + 限流 (api/v1/ad.py, core/pangle.py, core/rewards.py, core/ratelimit.py, crud/ad_reward.py, schemas/ad.py, docs/ad_reward_golive_checklist.md)
- 微信提现: 商家转账到零钱 V3 (core/wxpay.py); user 表加微信 openid/nickname/avatar
- DB 迁移: 8 个 alembic (welfare 表/cash_transaction/openid 唯一约束/savings 店铺菜品/savings_record/ad_reward/withdraw_order+openid/user 微信字段)
- 运维脚本: reconcile_withdraws(对账) + reset_signin/reset_welfare + sim_pangle_callback(模拟穿山甲回调)
- 测试: test_welfare / test_withdraw / test_ad_reward
- 配置: .env.example + config.py 新增福利/广告/微信支付项; main.py 挂 5 个新路由

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-27 17:38:08 +08:00

4.9 KiB

「看广告赚金币」上线 Checklist

跨两个仓库:shaguabijia-app-server(后端发奖)+ shaguabijia-app-android(客户端看广告)。 截至 2026-05-26:本地 + debug 包已端到端验证通过(出广告 → 发奖 → 金币到账 → 明细显示「看广告奖励」), 但尚未在生产上线。本文件记录上线前必须做的事,尤其是一堆"仅本地/debug 用、上线前必须清理"的脚手架。

设计要点(别改坏)

  • 发奖只走服务端:激励视频播完,穿山甲服务器 S2S 回调后端 /api/v1/ad/pangle-callback 发金币。 客户端不发奖,onRewardArrived 只触发"去后端刷余额"。客户端被破解也刷不到钱。
  • 后端发奖幂等(按 trans_id 去重)+ 每日上限(DAILY_AD_REWARD_LIMIT,按北京时间)。
  • 关键常量:app/core/rewards.pyAD_REWARD_COIN=100DAILY_AD_REWARD_LIMIT=10(占位值,上线前按 eCPM 实测收益重定)。

A. ⚠️ 上线前必须清理的"测试/调试专用"脚手架

这些是为了"没部署公网也能本地验证"加的,带到生产 = 体验差 / 绕过反作弊 / 引入废弃工具,务必清理。

A1. 后端:本地模拟发奖接口

  • .env 删除 / 设为 false:AD_REWARD_TEST_GRANT_ENABLED=false(默认就是 false;它一开,已登录客户端就能自助发奖)
  • (可选)确认生产 .env 没这行;/api/v1/ad/test-grant 在 false 时返回 404
  • 涉及:app/api/v1/ad.pytest_grantapp/core/config.pyAD_REWARD_TEST_GRANT_ENABLED

A2. 客户端:test-grant 调用

  • WelfareViewModel.onAdRewardEarned()if (BuildConfig.DEBUG) { repo.adTestGrant() } —— release 包(BuildConfig.DEBUG=false)本就跳过,确认 release 构建不含该调用即可(结构上已隔离,无需改码;打 release 包验一次)
  • 涉及:WelfareViewModel.ktWelfareRepository.adTestGrant()WelfareApi.adTestGrantTestGrantDto(留着无害,只要 release 不调用 + 后端 404)

A3. 客户端:穿山甲测试工具(广告预览)

  • app/build.gradle.kts:移除 debugImplementation(files("libs/tools-release.aar"))
  • 删除 app/libs/tools-release.aar
  • gradle.properties:移除 android.enableJetifier=true(仅为兼容 tools-release.aar 的旧 support 库而开,Jetifier 已废弃)
  • 删除 app/src/debug/.../ad/TestToolLauncher.kt + app/src/release/.../ad/TestToolLauncher.kt(空桩)
  • WelfareScreen.kt:移除「【调试】唤起穿山甲测试工具」入口(if (BuildConfig.DEBUG){...} 那段)+ TestToolLauncher import
  • 注:这些都 debug-only,不影响 release 功能;清掉是为了不留废弃依赖

B. 生产真正发奖链路(S2S)—— 阻塞于穿山甲后台

  • 穿山甲后台配 S2S 回调 URLhttps://app-api.shaguabijia.com/api/v1/ad/pangle-callback
  • 拿到真实验签方案 / 公钥(后台奖励校验密钥;大概率 RSA)
  • 换验签:app/core/pangle.pyverify_callback_sign 从占位 HMAC-SHA256 改成穿山甲真实方案
  • 对齐回调字段名(user_id / trans_id / 奖励数量等);客户端已透传 setUserID + setMediaExtra("uid:...")
  • 生产 .env:PANGLE_CALLBACK_ENABLED=true + PANGLE_REWARD_SECRET=<真实密钥>(配齐才不返 503)

C. 部署 + 包名

  • 后端部署到公网(由服务器管理员;/opt/shaguabijia-app-server,uvicorn 127.0.0.1:8770,nginx 反代)
  • 跑迁移:alembic upgrade head(建 ad_reward_record 表,迁移 c8d9e0f1a2b3)
  • 包名定稿:当前 com.jishisongfu.shaguabijia。穿山甲(APP_ID 5830519)、极光、微信都绑"包名 + 签名",定了再上,别再换
    • 微信提现链路当前因复用 elderhelper 的 appid + 包名切换已 dead,要恢复需申请傻瓜比价自己的微信 appid(另见客户端 build.gradle 注释)
  • (可选,提升真实填充)集成 MSA OAID SDK:申请证书(绑包名、审核几天)。App 侧当前 getDevOaid=null,有 OAID 后投放匹配 + 填充会明显改善

D. 提现对账(顺带,与本功能独立但同属上线必做)

  • 生产给 scripts/reconcile_withdraws.pycron / systemd timer(每 5~10 分钟跑一次)
    • 否则"扣款后微信无回调"的孤儿提现单没人兜底退款(客户端已加 ON_RESUME 无条件查单,但 cron 是最后防线)

E. 端到端验收(生产链路通后)

  • 真机看完一条真实激励视频 → 穿山甲 S2S 回调 → 后端发奖 → 客户端余额真到账
  • 幂等:同一 trans_id 不重复发
  • 每日上限:超过 DAILY_AD_REWARD_LIMIT 返回 capped、不发币、按钮显示「已达上限」
  • 中途退出广告 → 客户端提示「未看完视频,本次没有奖励哦」,且不发奖
  • 收益明细显示「看广告奖励」(biz_type=ad_reward)

关键阻塞链

B(拿验签方案/配回调)→ E(端到端验收);A(清理)可随时做;C(部署)与 B 并行。