Back to the RevenueCat homepage
RevenueCat SDKGoogle Play Billing

Chapter 13: Cancellations, Pauses, and Winback

Handling cancellations from scratch means writing code for user-initiated cancellation, developer-initiated cancellation via API, system cancellation, pause states, restoring canceled subscriptions, resubscribing, subscription deferrals, and revocations.

Most of these are passive events your app observes rather than initiates. RevenueCat reflects them all in CustomerInfo. The code you write to handle them is small.

Detecting Cancellation

kotlin
val entitlement = customerInfo.entitlements["pro_access"]

if (entitlement?.unsubscribeDetectedAt != null) {
    // User has canceled but may still have active access
    val expiry = entitlement.expirationDate
    if (entitlement.isActive && expiry != null) {
        showCancellationBanner(expiry)
    }
}

unsubscribeDetectedAt is set when RevenueCat receives the SUBSCRIPTION_CANCELED RTDN. isActive remains true until the billing period ends. Your app shows a "subscription ends on [date]" message without computing access manually.

Backend Webhooks for Cancellation

Your backend receives:

json
{ "type": "CANCELLATION", "cancel_reason": "UNSUBSCRIBE", "expiration_at_ms": 1702592000000 }

cancel_reason can be UNSUBSCRIBE (user), BILLING_ERROR (payment failure), DEVELOPER_INITIATED, or PRICE_INCREASE. Use this to segment churned users for win-back campaigns.

Paused Subscriptions

RevenueCat sets isActive = false when a subscription is paused. You do not need to call queryPurchasesAsync() with setIncludeSuspendedSubscriptions(true). The state comes from RevenueCat's server-side subscription state.

kotlin
// Just check isActive - RevenueCat handles the state resolution
val hasPremiumAccess = customerInfo.entitlements["pro_access"]?.isActive == true

If you want to show a "your subscription is paused, resuming on [date]" message, call the RevenueCat REST API (GET /v1/subscribers/{app_user_id}) and read the paused_expiration_time_ms field for the subscription. RevenueCat does not currently send a dedicated webhook event for subscription pauses, the pause state change is reflected via CustomerInfo when RevenueCat processes the Google Play RTDN.

Restoring a Canceled Subscription (Before Expiry)

If a user cancels and then re-subscribes before expiry (Google's "resubscribe before expiry" flow), RevenueCat detects the reactivation via the SUBSCRIPTION_RESTARTED RTDN and sets willRenew = true again. The unsubscribeDetectedAt is cleared. Your app reads customerInfo and the state is correct, no special handling needed.

Resubscribing After Expiry

A resubscription after expiry sends a RENEWAL webhook event, not INITIAL_PURCHASE. INITIAL_PURCHASE fires only for a user's first-ever purchase of a product. Ensure your webhook handler grants entitlement access on RENEWAL as well as INITIAL_PURCHASE, a handler that only listens for INITIAL_PURCHASE will not restore access for users who resubscribe after expiry.

Subscription Deferral (Extending Access)

Subscription deferral is a developer-side operation done through the Google Play Developer API. RevenueCat does not expose a deferral API from the SDK or dashboard. If you need to defer a subscription, you call purchases.subscriptionsv2.defer on the Google Play Developer API directly from your backend.

After deferral, RevenueCat will receive the updated expiry time via RTDN processing and reflect it in CustomerInfo automatically.

Revoking a Subscription

Revocation is also done through the Google Play Developer API directly (purchases.subscriptionsv2.revoke). RevenueCat processes the resulting RTDN and immediately sets isActive = false for the entitlement.

Opening the Subscription Management Screen

Let users cancel or manage their subscription through Google Play:

kotlin
customerInfo.managementURL?.let { url ->
    startActivity(Intent(Intent.ACTION_VIEW, url))
}

Win-Back: No SDK Changes Required

Win-back campaigns are configured in the Google Play Console (Play-managed win-back) or in the RevenueCat dashboard (custom targeting). You do not write code to implement win-back. When a win-back offer is accepted, it arrives as a normal purchase and surfaces in CustomerInfo like any other subscription.

Prefer building from scratch?

The Google Play Billing Handbook covers the same topics with raw BillingClient, Developer API, and RTDNs.

Related chapters

  • Chapter 11: Subscription States

    Seven complex subscription states are resolved to just one simple boolean check: isActive.

    Learn more
  • Chapter 12: Payment Recovery

    In-app messages are shown automatically by default. Just two lines needed to detect grace period.

    Learn more
  • Chapter 14: Price Changes

    Nothing to implement on your side. RevenueCat processes RTDNs, and CustomerInfo stays correct.

    Learn more
Cancellations, Pauses, and Winback | RevenueCat