Customer Center

Give users a native screen to manage subscriptions, restore purchases, request refunds, and contact support.

The Customer Center is a self-service screen for managing purchases. Users can view their subscriptions and purchases, restore purchases, cancel or change a Google Play subscription, request a refund, manage a web checkout subscription, and contact support. It requires SDK version 2.8.5 or later and runs on every API level the SDK supports.

Presenting the Customer Center

Call presentCustomerCenter() from the main thread after configuring the SDK:

Superwall.instance.presentCustomerCenter(
    onDismiss = {
        // The Customer Center was dismissed.
    },
)

Only one Customer Center can be open at a time; presenting it while it's already open does nothing. Call dismissCustomerCenter() to dismiss it from your app. See presentCustomerCenter for the full signature.

The Customer Center opens as its own full-screen activity on top of your current one, so it can't be embedded in your own layout or navigation graph. Its toolbar has a close button on the main screen and a back arrow on a subscription's detail screen. The system back gesture closes a subscription's detail screen first, then the Customer Center itself. It keeps its place across configuration changes such as rotation.

Present it while your app is in the foreground. Android can refuse to start an activity from the background without reporting an error, so if the Customer Center hasn't appeared within five seconds, the SDK gives up and logs an error. Your onDismiss handler still runs so you aren't left waiting, but the delegate's customerCenterDidDismiss is not called, since nothing opened.

What users see

The SDK shows the management screen when the user has at least one subscription (active or expired), one-time purchase, or active entitlement, and the no purchases screen when they have none.

The management screen lists each subscription as a row that opens its own detail screen, alongside Restore purchases and account details. An active entitlement with no transaction behind it also opens a detail screen. One-time purchases appear as cards without one. Repeat renewals of the same subscription collapse into a single row.

Cancel subscription, Change plan, and Request a refund each act on one subscription, so they live on that subscription's detail screen rather than the management screen. Restore purchases is the reverse: it appears on the management and no purchases screens, never on a detail screen.

When the user comes back from Google Play or a web management page, the Customer Center reloads their purchases so a cancellation or plan change made there shows up right away.

Customizing the Customer Center

By default, SuperwallOptions.customerCenter uses CustomerCenterConfiguration.default, which works with zero setup. The only row that needs configuration is Contact support, which stays hidden until you set a support email.

To customize it, set customerCenter before calling configure:

val options = SuperwallOptions().apply {
    customerCenter = CustomerCenterConfiguration(
        managementScreen = CustomerCenterConfiguration.Screen(
            paths = listOf(
                CustomerCenterConfiguration.Path.restore(),
                CustomerCenterConfiguration.Path.changePlan(),
                CustomerCenterConfiguration.Path.refund(),
                CustomerCenterConfiguration.Path.manageSubscription(
                    survey = CustomerCenterConfiguration.FeedbackSurvey.cancellation,
                ),
                CustomerCenterConfiguration.Path.url(
                    url = "https://mycompany.com/faq",
                    title = "FAQ",
                ),
                CustomerCenterConfiguration.Path.contactSupport(),
            ),
        ),
        noPurchasesScreen = CustomerCenterConfiguration.Screen(
            paths = listOf(CustomerCenterConfiguration.Path.restore()),
        ),
        support = CustomerCenterConfiguration.Support(email = "support@mycompany.com"),
    )
}

Superwall.configure(
    applicationContext = this,
    apiKey = "MY_API_KEY",
    options = options,
)

Pass a configuration to presentCustomerCenter() to override it for one presentation. See CustomerCenterConfiguration for every screen, path, survey, support, and appearance option.

A configuration you supply replaces the default rather than merging with it, so the screen shows exactly what you list and nothing else. To keep the defaults and change one thing, copy the default:

options.customerCenter = CustomerCenterConfiguration.default.copy(
    support = CustomerCenterConfiguration.Support(email = "support@mycompany.com"),
)

The SDK logs a warning when a configuration renders but renders badly: two paths sharing an id on one screen, a URL path with no title (the row falls back to the URL's host), a survey with no title on any path other than manageSubscription, or an accent color that isn't valid hex.

When each path appears

Paths only appear when they make sense for the user's purchases, so you don't have to gate any of this yourself:

PathOpensShows when
restore()A progress overlay, then a result dialog. If your delegate declines the restore, nothing appears.Always, on the management and no purchases screens.
manageSubscription()The subscription's page in Google Play, labeled "Cancel subscription". For a web subscription, your web management page, labeled "Manage subscription".A Google Play subscription that's active and set to renew, or an active web subscription.
changePlan()The subscription's page in Google Play.An active Google Play subscription, including one the user has cancelled that hasn't expired yet.
refund()Google Play's order history, where the user submits the request.A Google Play subscription that isn't in a free trial, already refunded, or free, and is still inside windowMillis if you set one. It still shows on an expired subscription.
contactSupport()A pre-filled support email.support.email is set.
url()The URL, in a Custom Tab by default.Always.
custom()Nothing. Handle it in your delegate.Always.

changePlan(productIds) accepts productIds, but it has no effect on Android: the path always opens the subscription's page in Google Play.

contactSupport(), url(), and custom() don't depend on a purchase, so they appear on every detail screen as well as the main screens. When a user taps one there, the delegate receives that subscription as purchase.

When no path applies to a purchase, its detail screen shows a line of text instead of an action list. contactSupport() (with support.email set), url(), and custom() always apply, so this only happens when the management screen lists none of them. An active subscription the same user bought through your iOS app reads "Manage this subscription through App Store." One that's still active through any other store the SDK can't manage reads "Manage this subscription where you bought it." Anything else reads "There's nothing to manage for this purchase."

A survey attached to a path is shown before the action runs, except when the path leads to web subscription management, whether or not a management page was found. A survey with no options is also skipped.

Support email

Contact support opens the user's mail app with a message pre-filled with diagnostics: their user ID, app version, OS version, device model, SDK version, active entitlements, and whether they're in the sandbox. If no mail app is available, the Customer Center shows the address so the user can still reach you.

Prompting users to update

The Customer Center can show a banner asking the user to update your app, which is useful when someone contacts support about a bug you've already fixed. On Android the SDK doesn't look up your published version, so the banner only appears when you set latestAppVersion and the installed version is older:

options.customerCenter = CustomerCenterConfiguration.default.copy(
    support = CustomerCenterConfiguration.Support(
        email = "support@mycompany.com",
        latestAppVersion = "2.1.0",
    ),
)

Versions are compared on their first three numeric components, so 2.1 and 2.1.0 are equal. Update opens your app's Google Play listing, and Continue hides the banner. The banner appears on the management screen only. When a restore finds nothing while the banner would show, the result dialog also offers Update, on the management or no purchases screen. Set warnsAboutUpdates to false to turn off both the banner and that button. After the user taps Continue, neither appears again until the Customer Center is presented again.

Appearance

By default the Customer Center tints its rows, buttons, and spinners with your app's colorPrimary, taken from the theme of the activity you present it from (or your application's theme), and matches light and dark mode automatically. A theme you apply at runtime with setTheme isn't visible to the SDK, so it uses the manifest theme instead. If your theme leaves colorPrimary at a Material or AppCompat default, it falls back to a system blue. Set appearance.accent to choose the tint yourself.

Web checkout purchases

Subscriptions purchased through web checkout appear in the Customer Center alongside Google Play purchases, labeled with the store they came from: Stripe and Paddle purchases show "Web", and Superwall-store purchases show "Superwall". An entitlement granted outside any store is also labeled "Superwall". For all of them, the manage subscription path opens their web management page in a Custom Tab instead of Google Play. The SDK derives that page automatically for Superwall-hosted checkouts; set support.webManagementUrl to point somewhere else. If no management page can be resolved, the row stays visible and tells the customer to use the link in their emailed receipt. An entitlement granted outside any store is the exception: its row only appears when a management page resolves.

When the user is actively subscribed both on the web and through Google Play, the Customer Center shows a banner warning them they may be paying twice, so they can cancel one. Disable this with warnsAboutDuplicateSubscriptions = false.

Reacting to what users do

Pass a CustomerCenterDelegate to presentCustomerCenter() to gate restores, handle custom paths, and observe survey answers, refund requests, and dismissal. Custom paths do nothing on their own, so route them from customerCenterDidSelectAction:

val delegate = object : CustomerCenterDelegate {
    override fun customerCenterDidSelectAction(
        action: CustomerCenterAction,
        pathId: String,
        purchase: CustomerCenterPurchase?,
    ) {
        if (action is CustomerCenterAction.Custom) {
            openBillingSettings(action.identifier)
        }
    }
}

Superwall.instance.presentCustomerCenter(delegate = delegate)

Google Play takes refund requests on its own pages, so the SDK can't see how one ends. A refund reported as SUCCESS means Google Play's order history opened, not that a refund was granted. If it couldn't be opened, the status is ERROR and the purchase card shows an error message.

Events

The Customer Center tracks five SuperwallEvent cases. Observe them via the Superwall delegate to forward them to your analytics, and see them in your Superwall dashboard like any other event:

EventFires whenParameters
customerCenter_openThe Customer Center is presented.screen (management or no_purchases), presentation (always sheet on Android)
customerCenter_closeThe Customer Center is dismissed.
customerCenter_actionThe user taps a path.action, path_id, product_id, plus url or custom_identifier when relevant
customerCenter_surveyResponseThe user answers a survey.survey_id, option_id, action, path_id, product_id
customerCenter_refundRequestA refund request is handed off to Google Play, or fails to open.product_id, status (success or error)

The url parameter has its query string and fragment removed, since either can carry a token.

Localization

All Customer Center strings ship localized in 41 locales, including regional variants such as pt-BR, fr-CA, and Traditional Chinese. The language follows your app's locale, including per-app language settings. Any titles you set in the configuration are used verbatim, so localize custom titles yourself.

To reword a built-in string, declare a string resource with the same name in your app. For example, to relabel the restore row:

<!-- res/values/strings.xml -->
<string name="superwall_customer_center_path_restore">Restore my purchases</string>

An app string in values/ only replaces the SDK's default. The SDK ships its own translations, including en-GB and en-AU, and those still show for their locales unless you also override the string in each matching values-<locale> folder.

Limitations

  • The Customer Center always presents as its own full-screen activity. It can't be embedded in your own layout or navigation.
  • A purchase whose product has no display name is titled with the entitlement it unlocks, or left untitled. It is never titled with the product identifier. Today that applies to every web purchase, because the product catalog does not return a name yet.
  • Promotional and win-back offers are not yet supported as a path type.
  • The Customer Center is configured in code today. Remote configuration from the Superwall dashboard is coming.

FAQ

How is this guide?

On this page