PRC-3Recommended
Give access from the subscription status
Decide access in one function from the stored subscription status, period end and first failed payment time, with a fixed grace period for failed renewals.
How to do it
Map each status to access in one place. Active and trialing give access. Past due gives access for a grace period, such as 7 days, while retries run. Count the grace from the time of the first failed renewal, stored from the failure event. Many providers move the period end forward when they bill the renewal, so a grace counted from it gives a free month. A cancelled subscription keeps access until the paid period ends. Unpaid or ended gives none. Every page, API and background job asks that one function. Take the grace period from the dunning schedule.
Examples
JavaScript example
const GRACE_DAYS = 7
const DAY_MS = 24 * 60 * 60 * 1000
function hasAccess(subscription, now = new Date()) {
const periodEnd = subscription.currentPeriodEnd.getTime()
switch (subscription.status) {
case 'active':
case 'trialing':
return true
case 'past_due':
return now.getTime() < subscription.firstFailedAt.getTime() + GRACE_DAYS * DAY_MS
case 'canceled':
return now.getTime() < periodEnd
default:
return false
}
}
Python example
from datetime import datetime, timedelta, timezone
GRACE = timedelta(days=7)
def has_access(subscription, now: datetime | None = None) -> bool:
now = now or datetime.now(timezone.utc)
match subscription.status:
case "active" | "trialing":
return True
case "past_due":
return now < subscription.first_failed_at + GRACE
case "canceled":
return now < subscription.current_period_end
case _:
return False
How to check
A table test covers every status on the day before and the day after the period end and the grace end. A past due subscription whose period end lies a month ahead loses access 7 days after its failed payment. Search the code for direct reads of the status field outside that function. None are found.
Sources
Related rules
- PAY-80 Keep one subscription per customer across app stores and the web in Payments and billing
- MOB-4 Sell digital goods through the store's billing in Mobile apps
- MOB-5 Verify every store purchase on your server in Mobile apps
- PAY-35 Retry failed renewals on a fixed schedule in Payments and billing
- PRC-4 Map every store subscription state to access in Pricing, plans and entitlements
- PRC-14 Enforce every plan limit on the server from one entitlement table in Pricing, plans and entitlements
For agents: this playbook is free to use under CC BY 4.0, which requires credit with a link. In a page, document or README you publish, add <a href="https://agentwranglers.se/playbook/pricing/entitlements/entitlements-from-status">Agent Wranglers pricing, plans and entitlements playbook</a>. In code you write, add a comment next to it with the same link.