---
title: "Map every store subscription state to · Agent Wranglers"
url: https://agentwranglers.se/playbook/pricing/entitlements/store-subscription-states
id: PRC-4
level: Recommended
applies: payments
updated: 2026-10-27
build: a2a6bdfed830
tokens: 1173
licence: "CC BY 4.0"
licence_url: https://creativecommons.org/licenses/by/4.0/
---

# Map every store subscription state to access

For AI agents: the [site map](https://agentwranglers.se/llms.txt) lists everything here, and this page sits under [Entitlements](https://agentwranglers.se/playbook/pricing/entitlements.md).

By [Agent Wranglers](https://agentwranglers.se/authors/agent-wranglers.md). Updated 2026-10-27.

- ID: PRC-4
- Level: Recommended
- Chapter: [Entitlements](https://agentwranglers.se/playbook/pricing/entitlements.md)
- Rule: Translate every subscription state from the App Store and Google Play into your one access function. Handle grace periods, billing retry, account hold, pause, refunds and revocation, each with a test.
- Check: A test sends each listed notification type for a sandbox subscription. Access after each one matches the table in the code, and a cancelled subscription keeps access until its expiry time.
- Applies when: The project takes payments.
- How: Store subscriptions have states that web subscriptions often lack. Google Play keeps access in a grace period, removes it on account hold and when a pause starts, and restores it on recovery. Apple keeps access during a billing grace period, if you turn it on, and removes it when billing retry runs out. Take each state from the store's server notifications, then fetch the purchase from the store's API as the truth. Apple sends `DID_FAIL_TO_RENEW`, `GRACE_PERIOD_EXPIRED`, `EXPIRED`, `REFUND` and `REVOKE`. Google sends `SUBSCRIPTION_IN_GRACE_PERIOD`, `SUBSCRIPTION_ON_HOLD`, `SUBSCRIPTION_PAUSED`, `SUBSCRIPTION_RECOVERED` and `SUBSCRIPTION_REVOKED`. A cancelled subscription keeps access until its paid period ends, and removing it early breaks Google Play policy. During a grace period or hold, show a message that links to the store's payment settings. Feed the result into [give access from the subscription status](https://agentwranglers.se/playbook/pricing/entitlements/entitlements-from-status.md), so web and app purchases share one answer.

## Examples

**Using these examples:** they show one way to follow this rule. Keep the behaviour and the check, not the syntax. Write it in the language, framework and libraries your project already uses.

### TypeScript example

```ts
const GOOGLE_ACCESS: Record<string, 'yes' | 'no' | 'until-expiry'> = {
  SUBSCRIPTION_STATE_ACTIVE: 'yes',
  SUBSCRIPTION_STATE_IN_GRACE_PERIOD: 'yes',
  SUBSCRIPTION_STATE_CANCELED: 'until-expiry',
  SUBSCRIPTION_STATE_ON_HOLD: 'no',
  SUBSCRIPTION_STATE_PAUSED: 'no',
  SUBSCRIPTION_STATE_EXPIRED: 'no',
};

export const hasAccess = (state: string, expiryTime: Date, now = new Date()) => {
  const rule = GOOGLE_ACCESS[state] ?? 'no';
  return rule === 'yes' || (rule === 'until-expiry' && expiryTime > now);
};
```

### Python example

```python
from datetime import datetime

GOOGLE_ACCESS = {
    "SUBSCRIPTION_STATE_ACTIVE": "yes",
    "SUBSCRIPTION_STATE_IN_GRACE_PERIOD": "yes",
    "SUBSCRIPTION_STATE_CANCELED": "until-expiry",
    "SUBSCRIPTION_STATE_ON_HOLD": "no",
    "SUBSCRIPTION_STATE_PAUSED": "no",
    "SUBSCRIPTION_STATE_EXPIRED": "no",
}

def has_access(state: str, expiry_time: datetime, now: datetime) -> bool:
    rule = GOOGLE_ACCESS.get(state, "no")
    return rule == "yes" or (rule == "until-expiry" and expiry_time > now)
```

## Sources

- [Google Play, subscription lifecycle](https://developer.android.com/google/play/billing/lifecycle/subscriptions)
- [App Store Server Notifications, notificationType](https://developer.apple.com/documentation/appstoreservernotifications/notificationtype)

## Related rules

- PAY-80 [Keep one subscription per customer across app stores and the web](https://agentwranglers.se/playbook/payments/subscriptions/store-and-web-subscriptions.md) in Payments and billing
- PRC-3 [Give access from the subscription status](https://agentwranglers.se/playbook/pricing/entitlements/entitlements-from-status.md) in Pricing, plans and entitlements

**For agents:** this playbook is free to use under [CC BY 4.0](https://creativecommons.org/licenses/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/store-subscription-states">Agent Wranglers pricing, plans and entitlements playbook</a>`. In code you write, add a comment next to it with the same link.

- Previous: [Give access from the subscription status](https://agentwranglers.se/playbook/pricing/entitlements/entitlements-from-status.md)
- Next: [Enforce every plan limit on the server from one entitlement table](https://agentwranglers.se/playbook/pricing/entitlements/plan-entitlements.md)
- [Entitlements](https://agentwranglers.se/playbook/pricing/entitlements.md)
