Post-Checkout Redirecting

Learn how to handle users redirecting back to your app after a web purchase.

After a user completes a web purchase, Superwall needs to redirect them back to your app. You can configure this behavior in two ways:

Post-Purchase Behavior Modes

You can configure how users are redirected after checkout in your Application Settings:

Redeem Mode (Default)

Superwall manages the entire redemption experience:

  • Users are automatically deep linked to your app with a redemption code
  • Fallback to App Store/Play Store if the app isn't installed
  • Redemption emails are sent automatically
  • The SDK handles redemption via delegate methods (detailed below)

This is the recommended mode for most apps.

Redirect Mode

Redirect users to your own custom URL with purchase information:

  • When to use: You want to show a custom success page, perform additional actions before redemption, or have your own deep linking infrastructure
  • What you receive: Purchase data is passed as query parameters to your URL

Query Parameters Included:

Prop

Type

Example:

https://yourapp.com/success?
  app_user_id=user_123&
  email=user@example.com&
  stripe_subscription_id=sub_1234567890&
  campaign_id=summer_sale

You'll need to implement your own logic to handle the redirect and deep link users into your app.

Paywall-level overrides

The app-level Post-Purchase Behavior setting is the default for checkout links and web checkout paywalls. This is separate from the paywall editor's Web Checkout Location setting, which controls where the checkout opens.

For a specific web checkout paywall button, you can override the app-level default by adding a Redeem Purchase action under the button's After purchase behavior. The action can use the app-level post-purchase behavior, open Superwall's redemption page, deep link directly into your app, or redirect to a custom URL.

See Redeem Purchase for web checkout for the dashboard setup.


Whether checkout starts from a web link or from a paywall that opens an external browser, the Superwall SDK relies on deep links to redirect back to your app.

Prerequisites

  1. Configuring Stripe Keys and Settings
  2. Deep Links

If you're not using Superwall to handle purchases, then you'll need to follow extra steps to redeem the web purchase in your app.


Handling Redemption (Redeem Mode)

When using Redeem mode (the default), handle the user experience when they're redirected back to your app using SuperwallDelegate methods:

When your app opens via the deep link, we will call the delegate method willRedeemLink() before making a network call to redeem the code. At this point, you might wish to display a loading indicator in your app so the user knows that the purchase is being redeemed.

import { SuperwallDelegate } from 'expo-superwall/compat';
import { Toast } from 'react-native-toast-message'; // or your preferred toast library

export class SWDelegate extends SuperwallDelegate {
  willRedeemLink(): void {
    // Show a loading indicator to the user
    Toast.show({
      type: 'info',
      text1: 'Activating your purchase...',
    });
  }
}

You can manually dismiss the paywall at this point if needed, or let the SDK finish redemption and apply the paywall behavior described below.

After receiving a response from the network, we will call didRedeemLink(result) with the result of redeeming the code. This result can be one of the following:

  • RedemptionResult with type: 'success': The redemption succeeded and contains information about the redeemed code.
  • RedemptionResult with type: 'error': An error occurred while redeeming. You can check the error message via the error parameter.
  • RedemptionResult with type: 'expiredCode': The code expired and contains information about whether a redemption email has been resent and an optional obfuscated email address.
  • RedemptionResult with type: 'invalidCode': The code that was redeemed was invalid.
  • RedemptionResult with type: 'expiredSubscription': The subscription that the code redeemed has expired.

On network failure, the SDK will retry up to 6 times before returning an error RedemptionResult in didRedeemLink(result).

Here, you should remove any loading UI you added in willRedeemLink and show a message to the user based on the result. A presented paywall normally dismisses automatically after redemption.

On iOS with Expo SDK v1.6.0 and later, a purchase from a paywall's checkout page is the exception. The paywall runs the purchase button's after-purchase actions and is dismissed with a purchased result only when those actions close it. willRedeemLink and didRedeemLink are still called.

import { SuperwallDelegate, RedemptionResult } from 'expo-superwall/compat';
import Superwall from 'expo-superwall/compat';
import { Toast } from 'react-native-toast-message'; // or your preferred toast library

export class SWDelegate extends SuperwallDelegate {
  didRedeemLink(result: RedemptionResult): void {
    switch (result.type) {
      case 'expiredCode':
        Toast.show({
          type: 'error',
          text1: 'Expired Link',
        });
        console.log('[!] code expired', result.code, result.expiredInfo);
        break;
      
      case 'error':
        Toast.show({
          type: 'error',
          text1: result.error.message,
        });
        console.log('[!] error', result.code, result.error);
        break;
      
      case 'expiredSubscription':
        Toast.show({
          type: 'error',
          text1: 'Expired Subscription',
        });
        console.log('[!] expired subscription', result.code, result.redemptionInfo);
        break;
      
      case 'invalidCode':
        Toast.show({
          type: 'error',
          text1: 'Invalid Link',
        });
        console.log('[!] invalid code', result.code);
        break;
      
      case 'success':
        const email = result.redemptionInfo?.purchaserInfo?.email;
        if (email) {
          Superwall.shared.setUserAttributes({ email });
          Toast.show({
            type: 'success',
            text1: `Welcome, ${email}!`,
          });
        } else {
          Toast.show({
            type: 'success',
            text1: 'Welcome!',
          });
        }
        break;
    }
  }
}

Access detailed product data

Successful redeems now populate result.redemptionInfo?.paywallInfo?.product with the exact product the customer purchased. Use it to show localized pricing on your success screen or to pass metadata to your billing system.

Prop

Type

function trackProduct(result: RedemptionResult) {
  if (result.type !== "success") return;

  const product = result.redemptionInfo?.paywallInfo?.product;
  if (!product) return;

  analytics.track("web_checkout_completed", {
    productId: product.identifier,
    price: product.price,
    period: product.periodly,
    currency: product.currencyCode,
  });
}

productIdentifier remains for backwards compatibility but will be removed in a future Expo release, so migrate to the richer product object now.

How is this guide?

On this page