Subscription State Diagram
Phase 0: Intent
Use this skill when you need a single place to look up:
- What states a Google Play subscription can be in.
- Which RTDN (Real Time Developer Notification) moves a subscription from one state to the next.
- Whether a given state grants access to paid content.
- What token behavior to expect across renewals, pauses, holds, and resubscribes.
This is a reference skill. It does not write code for you. It hands you the map so you can point at a node, point at an edge, and then make a decision about access or acknowledgement.
Phase 1: Locate
Tell the skill which part of the state machine you care about. Pick the shortest path that matches your question.
| You want to know... |
Jump to |
| What every state means and whether it grants access |
States at a Glance |
| Which RTDN fires on a specific transition |
Transition Table |
| Whether to grant access right now |
Access Decision Flowchart |
| What happens to the purchase token across events |
Token Lifecycle |
| The numeric code for a notification type |
RTDN Quick Reference |
| How long grace period, account hold, or token validity lasts |
Time Windows |
A typical lookup looks like this:
- Name the current state (for example
IN_GRACE_PERIOD).
- Name the event you observed (for example RTDN type 5).
- Find the row in the From
IN_GRACE_PERIOD table.
- Read the target state and the access rule.
Phase 2: Reference Diagram
States at a Glance
| State |
Access |
Description |
| PENDING |
No |
Purchase initiated, payment not yet processed |
| ACTIVE |
Yes |
Subscription is paid and current |
| IN_GRACE_PERIOD |
Yes |
Payment failed, Google is retrying, user keeps access |
| ON_HOLD |
No |
Grace period expired, access revoked, awaiting payment fix |
| PAUSED |
No |
User requested pause, access revoked |
| CANCELED |
Until expiry |
Canceled but not expired, access until expiryTime |
| EXPIRED |
No |
Subscription has ended |
Transition Table
Every edge in the diagram. Columns: origin state, destination state, trigger, RTDN name and numeric type.
| From |
To |
Trigger |
RTDN |
| PENDING |
ACTIVE |
Payment completes |
SUBSCRIPTION_PURCHASED (4) |
| PENDING |
EXPIRED |
Payment fails or times out |
SUBSCRIPTION_PENDING_PURCHASE_CANCELED (20) |
| ACTIVE |
ACTIVE |
Successful renewal |
SUBSCRIPTION_RENEWED (2) |
| ACTIVE |
IN_GRACE_PERIOD |
Renewal fails, grace period enabled |
SUBSCRIPTION_IN_GRACE_PERIOD (6) |
| ACTIVE |
ON_HOLD |
Renewal fails, no grace, account hold enabled |
SUBSCRIPTION_ON_HOLD (5) |
| ACTIVE |
CANCELED |
User or developer cancels |
SUBSCRIPTION_CANCELED (3) |
| ACTIVE |
EXPIRED |
Revoked or refunded |
SUBSCRIPTION_REVOKED (12) |
| ACTIVE |
PAUSED |
Pause takes effect at end of period |
SUBSCRIPTION_PAUSED (10) |
| IN_GRACE_PERIOD |
ACTIVE |
Payment recovered |
SUBSCRIPTION_RECOVERED (1) |
| IN_GRACE_PERIOD |
ON_HOLD |
Grace expires without payment |
SUBSCRIPTION_ON_HOLD (5) |
| IN_GRACE_PERIOD |
EXPIRED |
Grace expires, no account hold |
SUBSCRIPTION_EXPIRED (13) |
| ON_HOLD |
ACTIVE |
User fixes payment |
SUBSCRIPTION_RECOVERED (1) |
| ON_HOLD |
EXPIRED |
Account hold period expires |
SUBSCRIPTION_EXPIRED (13) |
| PAUSED |
ACTIVE |
Pause ends, payment succeeds |
SUBSCRIPTION_RECOVERED (1) |
| PAUSED |
ON_HOLD |
Pause ends, payment fails |
SUBSCRIPTION_ON_HOLD (5) |
| CANCELED |
ACTIVE |
User restores before expiration |
SUBSCRIPTION_RESTARTED (7) |
| CANCELED |
EXPIRED |
expiryTime reached |
SUBSCRIPTION_EXPIRED (13) |
| EXPIRED |
ACTIVE |
User resubscribes, new purchase token |
SUBSCRIPTION_PURCHASED (4) |
RTDN Quick Reference by Number
| # |
Name |
Common Usage |
| 1 |
RECOVERED |
Restore access after hold or pause |
| 2 |
RENEWED |
Extend access period |
| 3 |
CANCELED |
Note cancellation, keep access until expiry |
| 4 |
PURCHASED |
New subscription, grant access |
| 5 |
ON_HOLD |
Revoke access |
| 6 |
IN_GRACE_PERIOD |
Keep access, warn user |
| 7 |
RESTARTED |
Restore access, same token |
| 8 |
PRICE_CHANGE_CONFIRMED |
Deprecated |
| 9 |
DEFERRED |
Extend billing date |
| 10 |
PAUSED |
Revoke access at period end |
| 11 |
PAUSE_SCHEDULE_CHANGED |
Check pause status |
| 12 |
REVOKED |
Immediately revoke access |
| 13 |
EXPIRED |
Revoke access, clean up |
| 17 |
ITEMS_CHANGED |
Check updated line items |
| 18 |
CANCELLATION_SCHEDULED |
Note scheduled cancellation |
| 19 |
PRICE_CHANGE_UPDATED |
Check price change details |
| 20 |
PENDING_PURCHASE_CANCELED |
Clean up pending records |
| 22 |
PRICE_STEP_UP_CONSENT_UPDATED |
Check consent status |
Time Windows
| Window |
Duration |
What Happens |
| Acknowledgement |
3 days |
Unacknowledged purchases auto refund |
| Grace period |
1 to 30 days, configurable |
User keeps access while Google retries |
| Silent grace period |
1 day minimum |
Applies even when grace is set to 0 days |
| Account hold |
Up to 30 days, configurable |
User loses access, can recover by fixing payment |
| Token validity |
60 days post expiration |
API calls still succeed in this window |
| Pause duration |
1 week to 3 months |
Depends on billing period |
Phase 3: Handling Guidance Per Transition
Each row tells you what to do when the RTDN lands. Pair this with the access rule in Phase 2.
| Transition |
Your handler should |
| PENDING to ACTIVE |
Acknowledge within 3 days, provision entitlement, store the purchase token |
| PENDING to EXPIRED |
Clean up the pending record, do not provision access |
| ACTIVE to ACTIVE (renewal) |
Extend the entitlement to the new expiryTime, keep the same token |
| ACTIVE to IN_GRACE_PERIOD |
Keep access on, surface a payment warning, trigger In App Messaging |
| ACTIVE to ON_HOLD |
Revoke access, tell the user to fix payment in Play |
| ACTIVE to CANCELED |
Keep access until expiryTime, show an expiry countdown, log the cancel reason |
| ACTIVE to EXPIRED (revoke) |
Revoke access immediately, note the refund or revocation |
| ACTIVE to PAUSED |
Access ends at period end, show resume date on next open |
| IN_GRACE_PERIOD to ACTIVE |
Clear the warning banner, confirm expiryTime moved forward |
| IN_GRACE_PERIOD to ON_HOLD |
Revoke access, swap the warning for a fix payment prompt |
| IN_GRACE_PERIOD to EXPIRED |
Revoke access, offer resubscribe |
| ON_HOLD to ACTIVE |
Restore the entitlement, keep the existing token |
| ON_HOLD to EXPIRED |
Revoke, offer resubscribe, stop polling the token after 60 days |
| PAUSED to ACTIVE |
Restore access, same token |
| PAUSED to ON_HOLD |
Keep access revoked, switch message to fix payment |
| CANCELED to ACTIVE |
Restore, same token, remove the expiry countdown |
| CANCELED to EXPIRED |
Revoke, offer resubscribe |
| EXPIRED to ACTIVE |
Treat as a new purchase, new token, link it to the prior user identity |
Access Decision
Use the same rule across every handler.
fun shouldGrantAccess(
state: String,
expiryTimeMillis: Long
): Boolean = when (state) {
"SUBSCRIPTION_STATE_ACTIVE",
"SUBSCRIPTION_STATE_IN_GRACE_PERIOD" -> true
"SUBSCRIPTION_STATE_CANCELED" ->
System.currentTimeMillis() < expiryTimeMillis
else -> false
}
Token Lifecycle
| Event |
Token Behavior |
| New purchase |
New token |
| Renewal |
Same token, new Order ID |
| Upgrade or downgrade |
New token, old token linked via linkedPurchaseToken |
| Restore before expiry |
Same token |
| Resubscribe after expiry |
New token |
| Pause and resume |
Same token |
| Grace period recovery |
Same token |
| Account hold recovery |
Same token |
References
1---2name: subscription-state-diagram3description: Use this skill as the pull out reference for the full Google Play subscription state machine, showing every state and every RTDN triggered transition in one diagram.4license: Apache-2.0; see LICENSE5---67# Subscription State Diagram89## Phase 0: Intent1011Use this skill when you need a single place to look up:1213- What states a Google Play subscription can be in.14- Which RTDN (Real Time Developer Notification) moves a subscription from one state to the next.15- Whether a given state grants access to paid content.16- What token behavior to expect across renewals, pauses, holds, and resubscribes.1718This is a reference skill. It does not write code for you. It hands you the map so you can point at a node, point at an edge, and then make a decision about access or acknowledgement.1920## Phase 1: Locate2122Tell the skill which part of the state machine you care about. Pick the shortest path that matches your question.2324| You want to know... | Jump to |25|---------------------|---------|26| What every state means and whether it grants access | States at a Glance |27| Which RTDN fires on a specific transition | Transition Table |28| Whether to grant access right now | Access Decision Flowchart |29| What happens to the purchase token across events | Token Lifecycle |30| The numeric code for a notification type | RTDN Quick Reference |31| How long grace period, account hold, or token validity lasts | Time Windows |3233A typical lookup looks like this:34351. Name the current state (for example `IN_GRACE_PERIOD`).362. Name the event you observed (for example RTDN type 5).373. Find the row in the From `IN_GRACE_PERIOD` table.384. Read the target state and the access rule.3940## Phase 2: Reference Diagram4142### States at a Glance4344| State | Access | Description |45|-------|--------|-------------|46| PENDING | No | Purchase initiated, payment not yet processed |47| ACTIVE | Yes | Subscription is paid and current |48| IN_GRACE_PERIOD | Yes | Payment failed, Google is retrying, user keeps access |49| ON_HOLD | No | Grace period expired, access revoked, awaiting payment fix |50| PAUSED | No | User requested pause, access revoked |51| CANCELED | Until expiry | Canceled but not expired, access until `expiryTime` |52| EXPIRED | No | Subscription has ended |5354### Transition Table5556Every edge in the diagram. Columns: origin state, destination state, trigger, RTDN name and numeric type.5758| From | To | Trigger | RTDN |59|------|----|---------|------|60| PENDING | ACTIVE | Payment completes | `SUBSCRIPTION_PURCHASED` (4) |61| PENDING | EXPIRED | Payment fails or times out | `SUBSCRIPTION_PENDING_PURCHASE_CANCELED` (20) |62| ACTIVE | ACTIVE | Successful renewal | `SUBSCRIPTION_RENEWED` (2) |63| ACTIVE | IN_GRACE_PERIOD | Renewal fails, grace period enabled | `SUBSCRIPTION_IN_GRACE_PERIOD` (6) |64| ACTIVE | ON_HOLD | Renewal fails, no grace, account hold enabled | `SUBSCRIPTION_ON_HOLD` (5) |65| ACTIVE | CANCELED | User or developer cancels | `SUBSCRIPTION_CANCELED` (3) |66| ACTIVE | EXPIRED | Revoked or refunded | `SUBSCRIPTION_REVOKED` (12) |67| ACTIVE | PAUSED | Pause takes effect at end of period | `SUBSCRIPTION_PAUSED` (10) |68| IN_GRACE_PERIOD | ACTIVE | Payment recovered | `SUBSCRIPTION_RECOVERED` (1) |69| IN_GRACE_PERIOD | ON_HOLD | Grace expires without payment | `SUBSCRIPTION_ON_HOLD` (5) |70| IN_GRACE_PERIOD | EXPIRED | Grace expires, no account hold | `SUBSCRIPTION_EXPIRED` (13) |71| ON_HOLD | ACTIVE | User fixes payment | `SUBSCRIPTION_RECOVERED` (1) |72| ON_HOLD | EXPIRED | Account hold period expires | `SUBSCRIPTION_EXPIRED` (13) |73| PAUSED | ACTIVE | Pause ends, payment succeeds | `SUBSCRIPTION_RECOVERED` (1) |74| PAUSED | ON_HOLD | Pause ends, payment fails | `SUBSCRIPTION_ON_HOLD` (5) |75| CANCELED | ACTIVE | User restores before expiration | `SUBSCRIPTION_RESTARTED` (7) |76| CANCELED | EXPIRED | `expiryTime` reached | `SUBSCRIPTION_EXPIRED` (13) |77| EXPIRED | ACTIVE | User resubscribes, new purchase token | `SUBSCRIPTION_PURCHASED` (4) |7879### RTDN Quick Reference by Number8081| # | Name | Common Usage |82|---|------|--------------|83| 1 | RECOVERED | Restore access after hold or pause |84| 2 | RENEWED | Extend access period |85| 3 | CANCELED | Note cancellation, keep access until expiry |86| 4 | PURCHASED | New subscription, grant access |87| 5 | ON_HOLD | Revoke access |88| 6 | IN_GRACE_PERIOD | Keep access, warn user |89| 7 | RESTARTED | Restore access, same token |90| 8 | PRICE_CHANGE_CONFIRMED | Deprecated |91| 9 | DEFERRED | Extend billing date |92| 10 | PAUSED | Revoke access at period end |93| 11 | PAUSE_SCHEDULE_CHANGED | Check pause status |94| 12 | REVOKED | Immediately revoke access |95| 13 | EXPIRED | Revoke access, clean up |96| 17 | ITEMS_CHANGED | Check updated line items |97| 18 | CANCELLATION_SCHEDULED | Note scheduled cancellation |98| 19 | PRICE_CHANGE_UPDATED | Check price change details |99| 20 | PENDING_PURCHASE_CANCELED | Clean up pending records |100| 22 | PRICE_STEP_UP_CONSENT_UPDATED | Check consent status |101102### Time Windows103104| Window | Duration | What Happens |105|--------|----------|--------------|106| Acknowledgement | 3 days | Unacknowledged purchases auto refund |107| Grace period | 1 to 30 days, configurable | User keeps access while Google retries |108| Silent grace period | 1 day minimum | Applies even when grace is set to 0 days |109| Account hold | Up to 30 days, configurable | User loses access, can recover by fixing payment |110| Token validity | 60 days post expiration | API calls still succeed in this window |111| Pause duration | 1 week to 3 months | Depends on billing period |112113## Phase 3: Handling Guidance Per Transition114115Each row tells you what to do when the RTDN lands. Pair this with the access rule in Phase 2.116117| Transition | Your handler should |118|------------|----------------------|119| PENDING to ACTIVE | Acknowledge within 3 days, provision entitlement, store the purchase token |120| PENDING to EXPIRED | Clean up the pending record, do not provision access |121| ACTIVE to ACTIVE (renewal) | Extend the entitlement to the new `expiryTime`, keep the same token |122| ACTIVE to IN_GRACE_PERIOD | Keep access on, surface a payment warning, trigger In App Messaging |123| ACTIVE to ON_HOLD | Revoke access, tell the user to fix payment in Play |124| ACTIVE to CANCELED | Keep access until `expiryTime`, show an expiry countdown, log the cancel reason |125| ACTIVE to EXPIRED (revoke) | Revoke access immediately, note the refund or revocation |126| ACTIVE to PAUSED | Access ends at period end, show resume date on next open |127| IN_GRACE_PERIOD to ACTIVE | Clear the warning banner, confirm `expiryTime` moved forward |128| IN_GRACE_PERIOD to ON_HOLD | Revoke access, swap the warning for a fix payment prompt |129| IN_GRACE_PERIOD to EXPIRED | Revoke access, offer resubscribe |130| ON_HOLD to ACTIVE | Restore the entitlement, keep the existing token |131| ON_HOLD to EXPIRED | Revoke, offer resubscribe, stop polling the token after 60 days |132| PAUSED to ACTIVE | Restore access, same token |133| PAUSED to ON_HOLD | Keep access revoked, switch message to fix payment |134| CANCELED to ACTIVE | Restore, same token, remove the expiry countdown |135| CANCELED to EXPIRED | Revoke, offer resubscribe |136| EXPIRED to ACTIVE | Treat as a new purchase, new token, link it to the prior user identity |137138### Access Decision139140Use the same rule across every handler.141142```kotlin143fun shouldGrantAccess(144 state: String,145 expiryTimeMillis: Long146): Boolean = when (state) {147 "SUBSCRIPTION_STATE_ACTIVE",148 "SUBSCRIPTION_STATE_IN_GRACE_PERIOD" -> true149 "SUBSCRIPTION_STATE_CANCELED" ->150 System.currentTimeMillis() < expiryTimeMillis151 else -> false152}153```154155### Token Lifecycle156157| Event | Token Behavior |158|-------|----------------|159| New purchase | New token |160| Renewal | Same token, new Order ID |161| Upgrade or downgrade | New token, old token linked via `linkedPurchaseToken` |162| Restore before expiry | Same token |163| Resubscribe after expiry | New token |164| Pause and resume | Same token |165| Grace period recovery | Same token |166| Account hold recovery | Same token |167168## References169170- [Full chapter](https://www.revenuecat.com/guides/google-play-billing/appendix-subscription-state-diagram)