Back to the RevenueCat homepage
Google Play Billing

Appendix E: Subscription State Diagram (Pull Out Reference)

This appendix provides the complete subscription state machine as a quick reference card. Print it, pin it to your wall, or keep it open in a tab while building your billing integration.

States at a Glance

State

Access

Icon

Description

PENDING

No

Purchase initiated but payment not yet processed

ACTIVE

Yes

Subscription is paid and current

IN_GRACE_PERIOD

Yes

⚠️

Payment failed, retrying, user keeps access

ON_HOLD

No

🔒

Grace period expired, access revoked, awaiting payment fix

PAUSED

No

User requested pause, access revoked

CANCELED

Yes*

🔄

Canceled but not expired, access until expiryTime

EXPIRED

No

Subscription has ended

*CANCELED grants access only until expiryTime.

All State Transitions

From PENDING

To

Trigger

RTDN

ACTIVE

Payment completes

SUBSCRIPTION_PURCHASED (4)

EXPIRED

Payment fails or times out

SUBSCRIPTION_PENDING_PURCHASE_CANCELED (20)

From ACTIVE

To

Trigger

RTDN

ACTIVE

Successful renewal

SUBSCRIPTION_RENEWED (2)

IN_GRACE_PERIOD

Renewal payment fails (grace period enabled)

SUBSCRIPTION_IN_GRACE_PERIOD (6)

ON_HOLD

Renewal payment fails (no grace period, account hold enabled)

SUBSCRIPTION_ON_HOLD (5)

CANCELED

User or developer cancels

SUBSCRIPTION_CANCELED (3)

EXPIRED

Revoked or refunded

SUBSCRIPTION_REVOKED (12)

PAUSED

Pause takes effect at end of period

SUBSCRIPTION_PAUSED (10)

From IN_GRACE_PERIOD

To

Trigger

RTDN

ACTIVE

Payment recovered

SUBSCRIPTION_RECOVERED (1)

ON_HOLD

Grace period expires without payment

SUBSCRIPTION_ON_HOLD (5)

EXPIRED

Grace period expires (no account hold)

SUBSCRIPTION_EXPIRED (13)

From ON_HOLD

To

Trigger

RTDN

ACTIVE

User fixes payment

SUBSCRIPTION_RECOVERED (1)

EXPIRED

Account hold period expires

SUBSCRIPTION_EXPIRED (13)

From PAUSED

To

Trigger

RTDN

ACTIVE

Pause ends, payment succeeds

SUBSCRIPTION_RECOVERED (1)

ON_HOLD

Pause ends, payment fails

SUBSCRIPTION_ON_HOLD (5)

From CANCELED

To

Trigger

RTDN

ACTIVE

User restores before expiration

SUBSCRIPTION_RESTARTED (7)

EXPIRED

expiryTime reached

SUBSCRIPTION_EXPIRED (13)

From EXPIRED

To

Trigger

RTDN

ACTIVE

User resubscribes (new purchase token)

SUBSCRIPTION_PURCHASED (4)

Access Decision Flowchart

Use this logic to determine whether to grant access:

kotlin
1. Is subscriptionState == ACTIVE?
   → YES: Grant access

2. Is subscriptionState == IN_GRACE_PERIOD?
   → YES: Grant access (show payment warning)

3. Is subscriptionState == CANCELED?
   → Is current time < expiryTime?
     → YES: Grant access (show expiry notice)
     → NO: Revoke access

4. All other states (ON_HOLD, PAUSED,
   EXPIRED, PENDING):
   → Revoke access

Access Decision Code

kotlin
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 created

Renewal

Same token, new Order ID

Upgrade/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

RTDN Quick Reference by Number

#

Name

Common Usage

1

RECOVERED

Restore access after hold/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 Reference

Window

Duration

What Happens

Acknowledgement

3 days

Unacknowledged purchases are auto refunded

Grace period

Configurable (1-30 days)

User retains access while Google retries payment

Silent grace period

1 day minimum

Even with 0 day setting, there is a minimum 1 day silent period

Account hold

Configurable (up to 30 days)

User loses access, can fix payment to recover

Token validity

60 days post expiration

API calls still work for this window

Pause duration

1 week to 3 months

Depends on billing period configuration

Checklist: Do I Handle Every State?

  • PENDING: Withhold access, show "pending" message
  • ACTIVE: Grant full access
  • IN_GRACE_PERIOD: Grant access + show payment warning + use In App Messaging
  • ON_HOLD: Revoke access + show "fix payment" message
  • PAUSED: Revoke access + show "paused" message with resume date
  • CANCELED: Grant access until expiry + show expiry countdown
  • EXPIRED: Revoke access + show resubscribe option

Want a simpler approach?

The RevenueCat SDK Handbook covers the same topics — with less code and a managed backend.

Related chapters

  • Chapter 11: The Subscription State Machine

    Seven states (ACTIVE through PENDING), every transition, which states grant access, and queryPurchasesAsync() behavior.

    Learn more
  • Chapter 12: Payment Recovery: Grace Period and Account Hold

    Two-stage recovery: grace period with access retained, then account hold with access revoked. Configuration and code.

    Learn more
  • Chapter 13: Cancellations, Pauses, and Winback

    Cancellation ≠ immediate revocation. Pause mechanics, restore vs. resubscribe, deferral API, and winback strategies.

    Learn more
Subscription State Diagram (Pull Out Reference) | RevenueCat