Webhook endpoint **PHẢI** parse raw body bằng `await request.body()` TRƯỚC khi Pydantic parse. Nếu FastAPI parse thành Pydantic object trước → Stripe signature verify sẽ fail. Code hiện tại đã xử lý đúng.
`checkout.session.completed` và `customer.subscription.created` có thể fire gần như đồng thời. Dùng `stripe_subscription_id` unique constraint hoặc `updatedAt` timestamp check để tránh data đè lên nhau.
- [x] [Review][Patch] pages_limit never upgraded to Pro value on subscription activation — both `_activate_subscription_from_checkout` and `_handle_subscription_event` only set `monthly_token_limit`, not `pages_limit` [stripe_routes.py]
- [x] [Review][Patch] pages_limit downgrade ignores pages_used — sets `pages_limit=500` blindly, should use `max(pages_used, free_limit)` to avoid locking out existing content [stripe_routes.py]
- [x] [Review][Patch] `_activate_subscription_from_checkout` does not set `subscription_current_period_end` — stays NULL until `customer.subscription.created` fires [stripe_routes.py]
- [x] [Review][Patch] `_activate_subscription_from_checkout` does not set `token_reset_date` — stays NULL until first renewal invoice [stripe_routes.py]
- [x] [Review][Patch] `date.today()` used instead of `datetime.now(UTC).date()` in `_handle_invoice_payment_succeeded` — timezone mismatch with quota service [stripe_routes.py]
- [x] [Review][Patch] Unconfigured price ID env vars cause silent fallback to `plan_id="free"` for paying subscribers — should log warning when no price match [stripe_routes.py]
- [x] [Review][Patch] Idempotency check omits `plan_id` — mid-cycle plan change (monthly→yearly) with same status+period_end gets silently skipped [stripe_routes.py]
- [x] [Review][Patch] `billing_reason="subscription_create"` excluded from token reset — new subscribers inherit dirty free-tier token counter [stripe_routes.py]
- [x] [Review][Patch] `billing_reason="subscription_update"` included in token reset — plan changes mid-cycle incorrectly reset tokens to 0 [stripe_routes.py]
- [x] [Review][Patch] Out-of-order webhook delivery can overwrite newer `period_end` with older value — no "don't go backwards" guard [stripe_routes.py]
- [x] [Review][Patch] `SubscriptionStatus.FREE` in downgrade check is dead code — remove from set [stripe_routes.py]
- [x] [Review][Patch] Repeated `invoice.payment_failed` while PAST_DUE silently ignored without logging [stripe_routes.py]
- [x] [Review][Defer] Race between `checkout.session.completed` and `customer.subscription.deleted` can reactivate canceled subscription — deferred, requires Stripe API verification call
- [x] [Review][Defer] `invoice.payment_succeeded` does not update `subscription_current_period_end` — deferred, handled by `customer.subscription.updated` in same event sequence