# 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 [#post-purchase-behavior-modes]

You can configure how users are redirected after checkout in your [Application Settings](/docs/web-checkout/web-checkout-configuring-stripe-keys-and-settings#post-purchase-behavior):

Redeem Mode (Default) [#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-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**:

* `app_user_id` - The user's identifier from your app
* `email` - User's email address
* `stripe_subscription_id` - The Stripe subscription ID, or the Stripe Checkout session ID for one-time purchases
* Any custom placement parameters you set

**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.

***

Setting Up Deep Links [#setting-up-deep-links]

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 [#prerequisites]

1. [Configuring Stripe Keys and Settings](/docs/web-checkout/web-checkout-configuring-stripe-keys-and-settings)
2. [Deep Links](/docs/flutter/quickstart/in-app-paywall-previews)

> **Warning**

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.



* [Using RevenueCat](/docs/flutter/guides/web-checkout/using-revenuecat)
* [Using a PurchaseController](/docs/flutter/guides/web-checkout/linking-membership-to-iOS-app#using-a-purchasecontroller)

***

Handling Redemption (Redeem Mode) [#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:

willRedeemLink [#willredeemlink]

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.

```dart
import 'package:superwallkit_flutter/superwallkit_flutter.dart';
import 'package:flutter/material.dart';

class MySuperwallDelegate extends SuperwallDelegate {
  @override
  void willRedeemLink() {
    // Show a loading indicator to the user
    print('Activating your purchase...');
    // You might show a SnackBar or loading dialog here
  }
}
```

You can manually dismiss the paywall at this point if needed, but note that the paywall will be dismissed automatically when the `didRedeemLink` method is called.

didRedeemLink [#didredeemlink]

After receiving a response from the network, we will call `didRedeemLink(result)` with the result of redeeming the code. The result is a `RedemptionResult` subclass, which can be one of:

* `RedemptionResultSuccess`: The redemption succeeded and contains information about the redeemed code.
* `RedemptionResultError`: An error occurred while redeeming. You can check the error message via the error parameter.
* `RedemptionResultExpiredCode`: The code expired and contains information about whether a redemption email has been resent and an optional obfuscated email address.
* `RedemptionResultInvalidCode`: The code that was redeemed was invalid.
* `RedemptionResultExpiredSubscription`: The subscription that the code redeemed has expired.

On network failure, the SDK will retry up to 6 times before returning a `RedemptionResultError` 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. If a paywall is presented, it will be dismissed automatically.

```dart
import 'package:superwallkit_flutter/superwallkit_flutter.dart';
import 'package:flutter/material.dart';

class MySuperwallDelegate extends SuperwallDelegate {
  final BuildContext context; // Pass context if you need to show dialogs/snackbars

  MySuperwallDelegate(this.context);

  @override
  void didRedeemLink(RedemptionResult result) {
    switch (result) {
      case RedemptionResultExpiredCode():
        _showMessage('Expired Link');
        print('[!] code expired: ${result.code}, ${result.info}');
        break;
      
      case RedemptionResultError():
        _showMessage(result.error.message);
        print('[!] error: ${result.code}, ${result.error}');
        break;
      
      case RedemptionResultExpiredSubscription():
        _showMessage('Expired Subscription');
        print('[!] expired subscription: ${result.code}, ${result.redemptionInfo}');
        break;
      
      case RedemptionResultInvalidCode():
        _showMessage('Invalid Link');
        print('[!] invalid code: ${result.code}');
        break;
      
      case RedemptionResultSuccess():
        final purchaserInfo = result.redemptionInfo.purchaserInfo;
        final email = purchaserInfo.email;
        if (email != null) {
          Superwall.shared.setUserAttributes({'email': email});
          _showMessage('Welcome, $email!');
        } else {
          _showMessage('Welcome!');
        }

        switch (purchaserInfo.storeIdentifiers) {
          case StripeStoreIdentifiers(subscriptionIds: final ids):
            print('[!] redeemed Stripe subscriptions: $ids');
          case PaddleStoreIdentifiers(subscriptionIds: final ids):
            print('[!] redeemed Paddle subscriptions: $ids');
          case UnknownStoreIdentifiers(store: final store):
            print('[!] redeemed purchase from $store');
        }
        break;
    }
  }

  void _showMessage(String message) {
    // Show a snackbar or toast message
    ScaffoldMessenger.of(context).showSnackBar(
      SnackBar(content: Text(message)),
    );
  }
}
```

Setting up the delegate [#setting-up-the-delegate]

Make sure to set your delegate when configuring Superwall:

```dart
void main() async {
  WidgetsFlutterBinding.ensureInitialized();
  
  await Superwall.configure('pk_your_api_key');
  
  // Set the delegate (you'll need access to BuildContext for UI operations)
  runApp(MyApp());
}

class MyApp extends StatefulWidget {
  @override
  State<MyApp> createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
  @override
  void initState() {
    super.initState();
    // Set the delegate after the widget is initialized
    WidgetsBinding.instance.addPostFrameCallback((_) {
      Superwall.shared.setDelegate(MySuperwallDelegate(context));
    });
  }

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: YourHomeScreen(),
    );
  }
}
```