视觉按最新原型,业务按已确认PRD。现有代码为满足设计而改造。执行见按设计实施与任务卡;当前代码与验证范围见消费者功能页。
Dramivio 邀请关系兼容性审计
选型建议:保留 Better Auth 1.7.7 与现有 Email OTP 自动注册,用 Better Auth 的 databaseHooks.user.create.after 接入一份独立、很小的 PostgreSQL 邀请关系模块。不要把 @marinedotsh/better-auth-referral 0.3.0 直接接入当前 OTP 流程。 这保留了成熟的认证与 OTP 实现,把邀请码归属、活动规则和奖励账本留在业务域;不需要改 Better Auth OTP 实现或客户端 SDK。
候选插件核验
包的真实名称是 @marinedotsh/better-auth-referral,最新发布版本为 0.3.0,许可证为 MIT,要求 better-auth: ^1.6.23 和 zod: ^4.0.0,Node 最低版本为 18。Better Auth 的 ^1.6.23 范围包含 1.7.7;我在隔离目录用 npm install --ignore-scripts 安装了 1.7.7、Expo 1.7.7 和该插件 0.3.0,没有 peer dependency 冲突。npm 发布元数据、仓库 package.json
这是 MIT 开源插件,但仍是 0.3.0 的 pre-1.0 项目,不能按成熟、稳定的邀请系统来背书。它的源码只在 /sign-up/email 和 OAuth 注册路径读取邀请码、创建关系;没有匹配 /sign-in/email-otp 的 hook。插件 hook 源码 Better Auth Email OTP 在 /sign-in/email-otp 验证 OTP 后,直接为尚不存在的邮箱创建已验证用户并建立 session,因此当前被 /sign-up/email hook 覆盖不到。Better Auth Email OTP 文档
插件的关系表对 referredUserId 设置了数据库唯一约束,因此数据库能拒绝同一用户的重复关系;验证码表对 code 和 userId 也有唯一约束。插件 schema 但关系创建逻辑是先查再插入,插入关系时没有捕获唯一冲突。并发重复创建会被 PostgreSQL 拦下,但插件没有把该冲突处理成幂等成功。插件关系创建逻辑
客户端不能调用 complete_step。插件只把 step API 暴露为 Better Auth 的服务端方法;源码调用 createAuthEndpoint.serverOnly,没有可供客户端请求的 HTTP 路径。仓库 README 的 endpoint 表列出了一个 HTTP 路径,但与实现不符;应以服务端源码为准。step API 实现 若未来采用该 API,只能由受信任的服务端、任务或 webhook 调用 auth.api.markReferralStepComplete(...)。
插件的 step 配置是静态字符串列表,不能管理后台要求的每个活动目标集数、有效观看时长、倍速、奖励和有效期。即使外挂 OTP 关系适配器,也会重复实现插件内部的关系创建逻辑;本次不建议把这套业务关系建立在插件的私有实现上。它的 sign_up step 会自动记为完成;这只代表插件的 step 状态,不应连接奖励发放。插件的完成回调也不是 PostgreSQL 奖励账本。
本地验证
PoC 使用 Node 22.16、Better Auth 1.7.7、@better-auth/expo 1.7.7、插件 0.3.0 和现有 tests/postgres.mjs 启动的 PostgreSQL 18 临时容器。请求直接调用 Better Auth handler,没有打开固定端口;OTP 只写入本地内存数组,没有外发邮件。PoC 在 finally 中关闭数据库池并停止容器;结束后确认没有留下测试容器。
在当前 OTP 配置下,带有效邀请码的新邮箱登录返回 200、关系数为 0;带无效邀请码的新邮箱登录也返回 200、关系数为 0;既有邮箱再次登录时带有效邀请码仍返回 200、用户 ID 不变且没有新增关系。这表示插件既不会在新邮箱 OTP 注册时建立关系,也不会误绑既有邮箱;邀请码在这条路由上被忽略,所以无效邀请码不会阻断认证。
我还在同一 PoC 给 Better Auth 配置了 databaseHooks.user.create.after:新邮箱 OTP 创建用户时,hook 收到了 ctx.path === "/sign-in/email-otp"、自定义 body 中的 inviteCode 以及 x-referral-code header。既有用户第二次 OTP 登录没有再次触发用户创建 hook。Better Auth 文档支持此 database hook;实际 1.7.7 PoC 也验证了它能拿到本请求上下文。Better Auth database hooks 文档
对插件迁移出的 PostgreSQL schema 直接重复插入同一 referredUserId,第二次写入返回 SQLSTATE 23505,确认约束在数据库层有效。这个检查验证的是数据库唯一性;它没有证明插件自己的关系插入在并发竞争时会无错误返回。没有运行插件仓库测试套件,也没有接入真实数据库、邮箱服务或生产代码。
I00 接入要点
- 在
createConsumerAuth的 Better Auth 配置中增加databaseHooks.user.create.after(user, ctx)。只处理ctx.path === "/sign-in/email-otp"的新用户创建事件,从ctx.body.inviteCode或ctx.request.headers.get("x-referral-code")读取邀请码。Email OTP 1.7.7 的 body schema 接受附加字段;本次通过实际请求确认字段会到达 hook,不需要改 OTP 实现或 SDK。 - 在 hook 中查邀请码所属用户、活动和
expires_at。格式错误、未知或过期邀请码只是不建立邀请关系,不能抛错阻止已通过 OTP 的登录。hook 只随真实 user create 运行,既有邮箱的 OTP 登录不会获得新的邀请归属;不要为同 IP 用户设置唯一或一刀切拒绝规则,NAT 共享 IP 只作风险信号。 - 邀请关系用独立 PG 表记录活动、referrer、referred、来源 code 和
pending状态;对referred_user_id建唯一约束,插入采用ON CONFLICT (referred_user_id) DO NOTHING,使重复事件幂等。活动表保存后台可调的集数、时长、倍速、奖励和有效期。邀请码生成也要用数据库唯一约束处理碰撞。 - 奖励继续由独立 PG 业务账本保存,并在服务端核对活动规则和可信播放事件后记账;注册事件只创建
pending关系。此审计没有验证 H00watch 的观看估算或反刷能力,不应把客户端 position 或 H00watch 估算当作奖励 proof;也没有替你构造额外观看证明。 - Better Auth 官方说明 database
afterhook 在实体创建后执行;关系写入应设计可重试和可观测,且不要让关系存储的无效码或可恢复故障把 OTP 登录变成失败。Better Auth database hooks 文档
本次没有修改平台业务代码、部署服务、连接生产数据库、发送邮件,或生成新的 hash、baseline、gate。