Split from the payment reliability review around #4765.
Problem
When local entitlement state falls back to free, the product currently behaves like the user is simply a free user:
- Premium panels are re-gated through
panel-layout.ts:updatePanelGating().
- API/browser calls surface generic messages such as
Pro subscription required, Upgrade required, or API access requires an active subscription.
- The only explicit billing banner is for
subscription.status === on_hold in payment-failure-banner.ts.
That misses an important state: the customer may still be paying in Dodo, but our local renewal state is stale because a webhook was missed or exhausted. In that case, generic upgrade copy is misleading and can push the customer toward duplicate checkout or support confusion.
Proposed work
Introduce explicit billing states for the frontend/API boundary, separate from plain free-tier gating.
Suggested states:
free - never paid / no active subscription evidence.
active - entitlement valid now.
on_hold - payment failed / retry window, existing red banner behavior.
renewal_verification_pending - local paid subscription appears stale and reconciliation is in progress or queued.
renewal_verification_failed - provider check failed; ask user to refresh/contact support/manage billing.
lapsed - provider confirms no active paid coverage remains.
Acceptance criteria
Related
Priority: P1. This is customer-trust UX: do not tell a paying user to upgrade when the real state is we are verifying your renewal.
Split from the payment reliability review around #4765.
Problem
When local entitlement state falls back to free, the product currently behaves like the user is simply a free user:
panel-layout.ts:updatePanelGating().Pro subscription required,Upgrade required, orAPI access requires an active subscription.subscription.status === on_holdinpayment-failure-banner.ts.That misses an important state: the customer may still be paying in Dodo, but our local renewal state is stale because a webhook was missed or exhausted. In that case, generic upgrade copy is misleading and can push the customer toward duplicate checkout or support confusion.
Proposed work
Introduce explicit billing states for the frontend/API boundary, separate from plain free-tier gating.
Suggested states:
free- never paid / no active subscription evidence.active- entitlement valid now.on_hold- payment failed / retry window, existing red banner behavior.renewal_verification_pending- local paid subscription appears stale and reconciliation is in progress or queued.renewal_verification_failed- provider check failed; ask user to refresh/contact support/manage billing.lapsed- provider confirms no active paid coverage remains.Acceptance criteria
Upgrade to ProCTA while renewal verification is pending/failed.on_holdpayment-failure banner remains intact and is not conflated with missed-renewal reconciliation.Related
Priority: P1. This is customer-trust UX: do not tell a paying user to upgrade when the real state is
we are verifying your renewal.