How to Let Users Manage or Cancel a Subscription

Overview

RevenueCat cannot cancel a subscription for the user. Cancellations and plan changes always happen on the store side (the App Store or Google Play). Your job is to send the user to the correct management screen. There are two ways to do this:

  • iOS (native): showManageSubscriptions() presents the App Store manage subscriptions sheet directly inside your app.
  • Cross-platform: customerInfo.managementURL is a URL you open yourself. Use it on React Native, on Android SDK versions before 10.18.0, or whenever you want one code path across stores.
Prerequisites: The RevenueCat SDK must already be installed and configured with Purchases.configure(...). See the iOS codelab and the Configure the SDK guide for setup. You also need a way to fetch CustomerInfo; see Get CustomerInfo.

iOS: showManageSubscriptions() (Native)

On iOS, RevenueCat exposes a native helper that opens the App Store manage subscriptions sheet without you having to build or open a URL. Call it from a Restore or Manage Subscription button:

swift
import RevenueCat

func openManageSubscriptions() async {
    do {
        // Presents the native App Store manage subscriptions sheet.
        try await Purchases.shared.showManageSubscriptions()
    } catch {
        print("Could not open manage subscriptions: \(error.localizedDescription)")
    }
}

From SwiftUI you can call it from a button action:

swift
Button("Manage Subscription") {
    Task {
        do {
            try await Purchases.shared.showManageSubscriptions()
        } catch {
            print("Manage subscriptions failed: \(error)")
        }
    }
}
Native on iOS and Android. showManageSubscriptions() presents the App Store sheet on iOS. Android gained an equivalent Purchases.sharedInstance.showManageSubscriptions(context, callback) in purchases-android 10.18.0. On React Native, and on Android below 10.18.0, use managementURL instead (covered below).

Cross-platform: customerInfo.managementURL

Every CustomerInfo object carries a managementURL property. This URL points to the store's subscription management page: the App Store page for App Store subscriptions, and the Google Play page for Google Play subscriptions. You fetch CustomerInfo with getCustomerInfo() and then open the URL yourself with the platform's standard URL opener.

This is the right approach when:

  • You are on Android below 10.18.0, which has no native manage screen method.
  • You are on React Native, where you open the URL with Linking.
  • You want a single code path that works the same way across stores.
managementURL can be null. It is null when there is no active store subscription to manage. It does not go null merely because the subscription was bought on another platform: it points at the store that owns the subscription. Always check for null before opening it. See the When managementURL is null section.

React Native: Open managementURL with Linking

Fetch CustomerInfo with getCustomerInfo(), then open managementURL with React Native's Linking API. Guard against a null URL first:

typescript
import { Linking } from 'react-native';
import Purchases from 'react-native-purchases';

async function openManageSubscriptions() {
  try {
    const info = await Purchases.getCustomerInfo();
    if (info.managementURL) {
      await Linking.openURL(info.managementURL);
    } else {
      // No active store subscription to manage on this platform.
      console.log('No management URL available.');
    }
  } catch (e) {
    console.log('Failed to open manage subscriptions', e);
  }
}

In a component, wire it to a button and disable the button when there is nothing to manage:

tsx
import { useEffect, useState } from 'react';
import { Button, Linking } from 'react-native';
import Purchases from 'react-native-purchases';

export function ManageSubscriptionButton() {
  const [managementURL, setManagementURL] = useState<string | null>(null);

  useEffect(() => {
    Purchases.getCustomerInfo().then((info) => {
      setManagementURL(info.managementURL);
    });
  }, []);

  return (
    <Button
      title="Manage Subscription"
      disabled={!managementURL}
      onPress={() => {
        if (managementURL) Linking.openURL(managementURL);
      }}
    />
  );
}

Android: Open managementURL with an Intent

Since purchases-android 10.18.0 the Android SDK has a native Purchases.sharedInstance.showManageSubscriptions(context, callback). On older versions, read customerInfo.managementURL and open it with a standard ACTION_VIEW intent, which routes to the Google Play subscription management page:

kotlin
import android.content.Intent
import android.net.Uri
import com.revenuecat.purchases.Purchases
import com.revenuecat.purchases.getCustomerInfoWith

fun openManageSubscriptions(context: Context) {
    Purchases.sharedInstance.getCustomerInfoWith(
        onError = { error ->
            Log.e("Purchases", "Could not load CustomerInfo: ${error.message}")
        },
        onSuccess = { customerInfo ->
            val url = customerInfo.managementURL
            if (url != null) {
                val intent = Intent(Intent.ACTION_VIEW, Uri.parse(url.toString()))
                context.startActivity(intent)
            } else {
                // No active subscription to manage on this device.
                Log.d("Purchases", "managementURL is null")
            }
        }
    )
}

With coroutines you can use the suspend variant awaitCustomerInfo() instead:

kotlin
lifecycleScope.launch {
    try {
        val customerInfo = Purchases.sharedInstance.awaitCustomerInfo()
        val url = customerInfo.managementURL
        if (url != null) {
            startActivity(Intent(Intent.ACTION_VIEW, Uri.parse(url.toString())))
        } else {
            showMessage("No subscription to manage on this device.")
        }
    } catch (e: PurchasesException) {
        Log.e("Purchases", "Failed to load CustomerInfo: ${e.message}")
    }
}

When the Management URL is null

managementURL is not always present. Handle these cases so your UI never tries to open a missing URL:

Situation managementURL
Active store subscription on the current platform A URL to the store management page
No active store subscription to manage null
Subscription purchased on a different platform null
Cross-platform purchases. managementURL does not become null just because the subscription was bought on another platform. It points at the store that owns the subscription, so an App Store subscription still yields the App Store page when read from your Android app, and a customer with active subscriptions on several platforms gets the App Store page. Opening it sends the user to the right place, so this needs no special case.
Hide or disable the button. When managementURL is null and, on iOS, the user has no manageable subscription, hide or disable the manage button rather than showing an error. The URL points to the App Store or Google Play page depending on where the subscription was purchased, so there is nothing to open when it is absent.

FAQ

How do I open the manage subscriptions screen on iOS?
Call try await Purchases.shared.showManageSubscriptions(). It presents the native App Store manage subscriptions sheet where the user can change or cancel their plan.

Does React Native have a showManageSubscriptions method?
No. The native showManageSubscriptions() helper is part of the iOS SDK. On React Native and Android, open customerInfo.managementURL instead. On React Native that means Linking.openURL(info.managementURL).

Can RevenueCat cancel a subscription for me?
No. Cancellations always happen on the store (App Store or Google Play). RevenueCat only sends the user to the correct management page, either through showManageSubscriptions() on iOS or through managementURL elsewhere.

Why is managementURL null?
Because there is no active store subscription to manage, or because the active subscription was bought on a different platform than the current device. Check for null and hide the manage button when it is missing.

Related Guides