Skip to content

feat(referral): #11 退款追回 —— 整单退款扣回推广人奖励 - #3

Merged
worable233 merged 1 commit into
mainfrom
fix/referral-refund-clawback
Oct 24, 2026
Merged

worable233 merged 1 commit into
mainfrom
fix/referral-refund-clawback

Conversation

@worable233

Copy link
Copy Markdown
Owner

对应

跟踪表 #11「推广返佣」,审计 REVIEW §11。配套内核 PR:worable233/stackpanel#20。

六项验收复核(逐条对源码)

项 结论
SDK/依赖兼容 已满足:requires: store ^0.3.0 ↔ 商店 0.3.0;tsc --noEmit 干净
发奖幂等 已满足:账本 (referenceType, referenceId) 唯一 + P2002 视为已入账
并发重复事件 已满足:inviteeId 唯一索引吞重复归因 + 版本号乐观锁重试
退款追回 本轮补齐
反作弊 已满足:自我推广拒建、maxInvitesPerUser、sameIpLimit
完整界面 基本满足,本轮补 clawed_back 文案

退款追回口径(已拍板)

  • 只有 full === true 的整单退款追回推广人奖励
  • 部分退款不追回:按比例扣回无法向用户解释
  • 被邀请人奖励不追回:注册的对价没因退款消失
  • 余额不足不阻断退款:仍标 clawed_back,clawedBackAmount 记实际扣到的 0,留待人工补扣
  • 扣回幂等键 ${inviteeId}:inviter:clawback,不能复用入账那笔 —— 复用会撞唯一索引被当成「已入账」而静默跳过

顺带修掉的两个静默缺陷

  1. referral.reward.paid 未注册 EXTENSION_POINTS.notificationType → 非生产只打警告,通知照常投递且用户无法关闭。已在 onActivate 注册。
  2. 前端状态映射缺 clawed_back → 会显示原始 API 值。用户端/管理端两处补齐,四语言同步。

验证

  • src/referral.test.ts:13/13(新增 5 个追回用例)
  • 内核集成 referral-clawback.test.ts(真实 Postgres + 真实安装产物):4/4
  • packages/plugins/store/src/refund.test.ts:7/7
  • check-package-catalogs.mjs:OK(10 packages)
  • prettier:clean

未做(明确披露)

  • 后台人工补扣流程(插件外运营流程)
  • 部分退款的比例分摊口径(本轮拍板不做)
  • 真实 V10 联调(无凭据)

## 1. 退款追回(本轮核心)

- 订阅 `order.refunded`,新增 `clawbackByRefund`
- 只有 `full === true` 的整单退款才追回推广人奖励;部分退款不追回
- 被邀请人奖励不追回:那是注册的对价,注册事实没因退款消失
- 余额不足不阻断退款:状态标 `clawed_back`,`clawedBackAmount` 记实际扣到的 0
- 扣回用独立幂等键 `<inviteeId>:inviter:clawback`。复用入账那笔会撞
  `(referenceType, referenceId)` 唯一索引,被钱包当成「已入账」而静默跳过 ——
  表面幂等,实际扣款永远不生效
- 记录新增 `clawedBackAtMs` / `clawedBackAmount` / `clawedBackOrderId`

## 2. 封堵用户自设奖励金额(CI 暴露的真实缺陷)

`audit-referral-reward` 集成用例一直是红的(此前被 lint 失败掩盖):
`POST /referral/codes` 带 `rewardAmount: 1000000` 返回 200 而不是 400。

原实现让用户端建码/改码直接写入 `rewardAmount`,`resolveInviterReward` 又优先
采信这个覆盖值。等于任何人都能给自己开一个 10 亿分的推广码,再自导自演把平台
钱包套空。

改成:用户端 schema 不再接受 `rewardAmount`,并使用 `.strict()` 让多传的字段
直接 400,而不是被 Zod 默默剥掉、调用方误以为生效了。记录上的 `rewardAmount`
字段保留(老码可能带值),只是用户端不能再写。

## 3. 顺带修掉的两个静默缺陷

- `referral.reward.paid` 未注册 `EXTENSION_POINTS.notificationType`,
  非生产环境只打警告、通知照常投递且用户无法关闭。已在 `onActivate` 注册。
- 前端状态映射缺 `clawed_back`,会显示原始 API 值。用户端/管理端两处补齐,
  四语言同步。

## 验证

- 插件单测 `src/referral.test.ts`:13/13
- 内核集成 `referral-clawback.test.ts`(真实 Postgres):4/4
- 内核集成 `audit-referral-reward.test.ts`:1/1(修复前 400 断言失败)
- `check-package-catalogs.mjs`:OK(10 packages)
- prettier:clean
@worable233
worable233 force-pushed the fix/referral-refund-clawback branch from d8a64b9 to 11db6a8 Compare October 24, 2026 09:57
@worable233
worable233 merged commit 2a5004a into main Oct 24, 2026
1 check passed
@worable233
worable233 deleted the fix/referral-refund-clawback branch October 24, 2026 11:00
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant