Benefits never revoked when grace period expires while subscription stays past_due
Bug
When a subscription's org has benefit_revocation_grace_period > 0, benefit grants are never revoked once the grace period expires, as long as the subscription remains in past_due (still going through dunning retries). No benefit_grant.revoked / customer.state_changed webhook fires either. The customer-facing "Benefits were revoked on " message keeps showing the correct grace-period deadline, but nothing actually enforces it.
Root cause
In Subscription.enqueue_benefits_grants (server/polar/subscription/service.py), when a subscription becomes inactive, _is_within_revocation_grace_period is checked. If still within the grace window, the method just returns early without enqueuing the revoke job and without scheduling any follow-up check for when the grace period ends:
if task == "revoke":
...
if await self._is_within_revocation_grace_period(
session, subscription, organization
):
# Don't enqueue revocation yet, still within grace period
returnenqueue_benefits_grants is only invoked in response to subscription/order lifecycle events (status transitions, cancellations, dunning attempts, etc.), and there is no cron/scheduled task that re-checks past_due subscriptions once their grace period elapses. So if a subscription stays past_due throughout its dunning retry window (which routinely lasts longer than a short grace period), the grace-period expiry is never re-evaluated and the benefit grants stay is_granted: true indefinitely, until some unrelated event happens to call enqueue_benefits_grants again (e.g. the subscription is manually revoked, or dunning eventually ends and the subscription transitions to canceled/unpaid).
Repro reported by a customer
- Grace period set to "After 2 Days"
- Subscription went
past_dueon day 0 - Order page correctly displayed "Benefits were revoked on <day 2>"
GET /v1/benefit-grants?customer_id=…kept returning the grants asis_granted: truewell past day 2, with nocustomer.state_changedwebhook, until the grants were manually revoked from the backoffice
Expected behavior
At grace period expiry, Polar should revoke the benefit grants, update customer state, and emit benefit_grant.revoked / customer.state_changed, matching the displayed revocation date, even if the subscription is still past_due mid-dunning.
Suggested fix
Schedule a delayed re-check (e.g. via the existing delay param on enqueue_benefits_grants/benefit.enqueue_benefits_grants) for grace_period_ends_at when a past_due subscription is found to still be within its grace period, or add a periodic cron job that scans past_due/unpaid subscriptions whose grace period has elapsed and enqueues the revoke.
Sent by @allison-polar from Benefit revocation grace period bug investigation.
Source: polarsource/polar