fix(trial): sync legacy agent_user_points on trial paid (LES-1184)
Bug
Fifi 报:mufifi32+99@gmail.com (s2) 付了 $4.99 trial 套餐,但 200 积分前端不到账。
根因(一句话)
Trial 支付的 webhook handler 只写了新链路(agent_user_points_item / _usage),没同步 legacy agent_user_points 表。前端余额 + 扣点路径都从那读 → 用户看到旧值,且消费时 trial 200 不会被扣(平台兜底白扣)。
证据链(4 数据源对账)
-
Stripe:customer
cus_UYCeu701aFPnpc(mufifi32+99@gmail.com, metadata.user_id=2048049070132150274),subsub_1TZ6NxQujuDadbsDoc7QGL5Nactive,invoicein_1TZ6NwQujuDadbsD3L002WEGpaid $4.99,priceId=price_1TWuyEQujuDadbsDurvm11Db(trial)。✅ -
agent_subscription:plan_id=5, total_points=200, has_purchased_trial=1, schedule 已挂。✅ -
agent_user_points_itemid=7628:type=plan_payment, points=200, biz_id=sub_1TZ6Nx…, expire=2026-05-27 17:25:52,未 expired。✅ -
agent_user_points(legacy 余额):plan_points=0, current_plan_id=1, current_points=100, update_time=2026-04-25。❌ 未同步
修复
TrialPurchaseFlowService.handleTrialPaidLocked 在写新链路之前,调一次 syncLegacyTrialPlanPoints(as) —— 镜像 StripeSubscriptionService.handleInvoicePaidForNewSubscriptionInternal 的 "旧链路优先" 约定:
// 1. AgentSubscription saveOrUpdate
// 2. 发 200 积分
syncLegacyTrialPlanPoints(as); // 新增:legacy 表
userPointsRechargeService.trialPurchase(as); // 既有:新链路
// 3. Schedule
syncLegacyTrialPlanPoints 镜像 StripeSubscriptionService.syncLegacyPlanPoints:updatePoints(reset, plan_payment, trial_purchase, 200) + updateUserPointsCurrentPlanId(5)。重放幂等由 updatePoints 的 reset 语义 + processSubscriptionRecharge CAS 兜底。
测试
- 145/145 unit test 通过
- 回归断言加在
TrialPurchaseFlowServiceTest#replay_reusesExistingSchedule_doesNotCreate:verify(legacyUserPointsService, times(1)).updatePoints(any()) verify(legacyUserPointsService, times(1)).updateUserPointsCurrentPlanId(eq(userId), eq(5)) -
TrialPortalCancelTest构造器同步加 mock 参数
已 backfill SIT 受影响测试号
4 个 trial active 用户 legacy 表已修:
| user_id | google_email | leg current_points | plan_points |
|---|---|---|---|
| 2048049070132150274 | mufifi32+99@gmail.com | 300 | 200 |
| 2056624916610408450 | mufifi32+8@gmail.com | 300 | 200 |
| 2053827934105038849 | initial1wu+free@gmail.com | 260 | 200 |
| 2054882213405990913 | initial1wu+sit0514@gmail.com | 200 | 200 |
Dubbo getCurrentUserPoints(2048049070132150274) 现在返回 currentPoints=300, planId=5, planPoints=200
范围
- 单文件改动 + 2 个测试构造器同步
- 不动 prod 行为:本 MR 只补本来就遗漏的一步,不改 Stripe / Schedule / 扣点路径
- 未触动 clawback 路径(trial refund 在 30min 窗口内会调
clawback;它写负 usage 但同样不动 legacy;与本次相同设计缺陷,单独 followup,避免 PR 膨胀)