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

Paywall-level overrides [#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](/docs/dashboard/dashboard-creating-paywalls/paywall-editor-styling-elements#redeem-purchase-for-web-checkout) for the dashboard setup.

***

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

Whether checkout starts from a web link or from an iOS paywall that opens Safari, 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)

:::ios
2) [Deep Links](/docs/sdk/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.



:::ios
* [Using RevenueCat](/docs/sdk/guides/web-checkout/using-revenuecat)
* [Using a PurchaseController](/docs/sdk/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.

```swift
func willRedeemLink() {
  ToastView.show(message: "Activating...", showActivityIndicator: true)
}
```

To present your own loading UI on top of the paywall, you can access the view controller of the paywall via `Superwall.shared.presentedViewController`. You can manually dismiss the paywall here, but note that the completion block of the original `register` call won't be triggered. 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. This is an enum that has the following cases:

* `success(code: String, redemptionInfo: RedemptionInfo)`: The redemption succeeded and `redemptionInfo` contains information about the redeemed code.
* `error(code: String, error: ErrorInfo)`: An error occurred while redeeming. You can check the error message via the `error` parameter.
* `expiredCode(code: String, expired: ExpiredCodeInfo)`: The code expired and `ExpiredCodeInfo` contains information about whether a redemption email has been resent and an optional obfuscated email address that the redemption email was sent to.
* `invalidCode(code: String)`: The code that was redeemed was invalid.
* `expiredSubscription(code: String, redemptionInfo: RedemptionInfo)`: 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. If a paywall is presented, it will be dismissed automatically.

```swift
func didRedeemLink(result: RedemptionResult) {
  switch result {
    case .expiredCode(let code, let expiredInfo):
      ToastView.show(message: "Expired Link", systemImageName: "exclamationmark.square.fill")
      print("[!] code expired", code, expiredInfo)
      break
    case .error(let code, let error):
      ToastView.show(message: error.message, systemImageName: "exclamationmark.square.fill")
      print("[!] error", code, error)
      break
    case .expiredSubscription(let code, let redemptionInfo):
      ToastView.show(message: "Expired Subscription", systemImageName: "exclamationmark.square.fill")
      print("[!] expired subscription", code, redemptionInfo)
      break
    case .invalidCode(let code):
      ToastView.show(message: "Invalid Link", systemImageName: "exclamationmark.square.fill")
      print("[!] invalid code", code)
      break
    case .success(_, let redemptionInfo):
      if let email = redemptionInfo.purchaserInfo.email {
        Superwall.shared.setUserAttributes(["email": email])
        ToastView.show(message: email, systemImageName: "person.circle.fill")
      } 
      else {
        ToastView.show(message: "Welcome!", systemImageName: "person.circle.fill")
      }
      break
  }
}
```