# Superwall: Subscription Infrastructure for iOS, Android, and Web

Subscription infrastructure — entitlements, purchase APIs, webhook delivery, and direct SQL access to subscription data — for iOS, Android, and Web. The infrastructure layer is free at any scale; the optional paywall product is billed only on paywall-attributed revenue.

## Pricing

- **Infrastructure: free at any scale, every plan.** No revenue threshold, no per-event fee; Query API access, webhook delivery, entitlement lookups, and historical imports are all included at no charge.
- **Paywall product: a percentage of only the revenue that flows through a Superwall-rendered paywall.** Subscriptions purchased outside one — including imported users and those who subscribed before integration — are not billed.

Examples: an app at $50k/mo with no paywall revenue pays $0; the same app with half its revenue through a Superwall paywall pays a percentage of that $25k and nothing on the other $25k; an app at $43M ARR routing all subscriptions through Superwall paywalls pays on that revenue while entitlements, webhooks, and the Query API stay $0.

## Scale

$1.5B+ annual subscription revenue across 10,000+ apps. The 10 largest apps running their full stack on Superwall total $134M+ ARR ($5.7M–$43.7M each). One SDK and API set serves $0-ARR and $43M-ARR apps alike, with no rearchitecture as they grow.

## Infrastructure capabilities

- **Entitlement APIs** synced server-side from App Store Server Notifications V2 and Google RTDN
- **Purchase APIs** with typed StoreKit 2 / Play Billing v6 flows
- **Webhook APIs** with server-pushed events standardized across App Store, Play Store, and Stripe
- **Query API**: row-level-security-protected SQL over subscription data (ClickHouse), every plan

Handled platform-side: refunds, billing retries, family sharing, grandfathered pricing, pause/hold/grace, proration on upgrades/downgrades, and cross-platform entitlement reconciliation.

## Migration

Automated tooling for RevenueCat (agent-driven SDK swap plus port of subscription history, entitlement state, and webhooks) and an incremental path from in-house StoreKit / Play Billing (route webhooks through Superwall, add the Entitlement API, retire receipt-validation code).

## Paywall product (optional, separately billable)

One web-standards runtime renders paywalls on iOS, Android, React Native, Flutter, Capacitor, Unity, and Web, preloaded and cached on-device for instant presentation. Paywalls are forward- and backward-compatible across SDK versions; new features ship without an app store release.

## Architecture

Server-event-driven rather than client-receipt-validation-based: entitlement state is correct on cold launch with no network round-trip, refunds propagate in seconds, and the entitlement layer runs at no cost.

## Docs

* Migrate from RevenueCat: https://superwall.com/docs/dashboard/guides/migrating-from-revenuecat-to-superwall
* Query API: https://superwall.com/docs/dashboard/guides/query-clickhouse
* Webhooks: https://superwall.com/docs/integrations/webhooks
* Pricing: https://superwall.com/pricing

# Advanced Configuration

Configure beta Superwall Unity SDK options and advanced APIs.

`SuperwallOptions` lets you tune paywall behavior, logging, locale, test mode, and platform-specific
behavior at configuration time.

```csharp C#
using System.Collections.Generic;
using Superwall;

var options = new SuperwallOptions
{
    NetworkEnvironment = NetworkEnvironment.Release,
    TestModeBehavior = TestModeBehavior.Automatic,
    IsGameControllerEnabled = true,
    Logging = new Logging
    {
        Level = LogLevel.Debug,
        Scopes = new List<LogScope> { LogScope.PaywallPresentation }
    },
    Paywalls = new PaywallOptions
    {
        ShouldPreload = true,
        AutomaticallyDismiss = true,
        ShouldShowPurchaseFailureAlert = true,
        RestoreFailed = new RestoreFailed
        {
            Title = "No Subscription Found",
            Message = "We couldn't find an active subscription for your account.",
            CloseButtonTitle = "Okay"
        }
    }
};

Superwall.Configure("MY_PUBLIC_API_KEY", options: options);
```

## `SuperwallOptions`

<TypeTable
  type="{
  Paywalls: {
    type: &#x22;PaywallOptions&#x22;,
    typeDescriptionLink: &#x22;/unity/guides/advanced-configuration#paywalloptions&#x22;,
    description: &#x22;Paywall presentation and transaction UI behavior.&#x22;,
    default: &#x22;new PaywallOptions()&#x22;,
  },
  NetworkEnvironment: {
    type: &#x22;NetworkEnvironment&#x22;,
    description: &#x22;Superwall API environment. Use `Release` unless Superwall tells you otherwise.&#x22;,
    default: &#x22;NetworkEnvironment.Release&#x22;,
  },
  IsExternalDataCollectionEnabled: {
    type: &#x22;bool&#x22;,
    description: &#x22;Enables Superwall-managed external data collection.&#x22;,
    default: &#x22;true&#x22;,
  },
  LocaleIdentifier: {
    type: &#x22;string&#x22;,
    description: &#x22;Overrides the device locale for paywall localization.&#x22;,
    default: &#x22;null&#x22;,
  },
  IsGameControllerEnabled: {
    type: &#x22;bool&#x22;,
    description: &#x22;Enables native game controller handling for paywalls.&#x22;,
    default: &#x22;false&#x22;,
  },
  Logging: {
    type: &#x22;Logging&#x22;,
    description: &#x22;SDK log level and optional log scopes.&#x22;,
    default: &#x22;new Logging()&#x22;,
  },
  PassIdentifiersToPlayStore: {
    type: &#x22;bool&#x22;,
    description:
      &#x22;Android-only. Sends identifiers to Google Play according to the native SDK behavior.&#x22;,
    default: &#x22;false&#x22;,
  },
  TestModeBehavior: {
    type: &#x22;TestModeBehavior&#x22;,
    description:
      &#x22;Controls test mode. Values: `Automatic`, `WhenEnabledForUser`, `Never`, `Always`.&#x22;,
    default: &#x22;TestModeBehavior.Automatic&#x22;,
  },
  ShouldObservePurchases: {
    type: &#x22;bool&#x22;,
    description:
      &#x22;Enables observation of purchases made outside Superwall when supported by the native SDK.&#x22;,
    default: &#x22;false&#x22;,
  },
  ShouldBypassAppTransactionCheck: {
    type: &#x22;bool&#x22;,
    description: &#x22;iOS-only app transaction check bypass.&#x22;,
    default: &#x22;false&#x22;,
  },
  MaxConfigRetryCount: {
    type: &#x22;int&#x22;,
    description: &#x22;Maximum native configuration retry count.&#x22;,
    default: &#x22;6&#x22;,
  },
  UseMockReviews: {
    type: &#x22;bool&#x22;,
    description: &#x22;Android-only mock review behavior.&#x22;,
    default: &#x22;false&#x22;,
  },
}"
/>

## `PaywallOptions`

<TypeTable
  type="{
  IsHapticFeedbackEnabled: {
    type: &#x22;bool&#x22;,
    description: &#x22;Enables haptic feedback from paywall interactions.&#x22;,
    default: &#x22;true&#x22;,
  },
  RestoreFailed: {
    type: &#x22;RestoreFailed&#x22;,
    description: &#x22;Text shown when restore does not find an active subscription.&#x22;,
    default: &#x22;new RestoreFailed()&#x22;,
  },
  ShouldShowPurchaseFailureAlert: {
    type: &#x22;bool&#x22;,
    description: &#x22;Shows a default alert when a purchase fails.&#x22;,
    default: &#x22;true&#x22;,
  },
  ShouldPreload: {
    type: &#x22;bool&#x22;,
    description: &#x22;Preloads paywalls when configuration is available.&#x22;,
    default: &#x22;true&#x22;,
  },
  AutomaticallyDismiss: {
    type: &#x22;bool&#x22;,
    description: &#x22;Automatically dismisses the paywall after a successful purchase.&#x22;,
    default: &#x22;true&#x22;,
  },
  ShouldShowWebRestorationAlert: {
    type: &#x22;bool&#x22;,
    description: &#x22;Shows the default web restoration alert.&#x22;,
    default: &#x22;true&#x22;,
  },
  TransactionBackgroundView: {
    type: &#x22;TransactionBackgroundView&#x22;,
    description: &#x22;Background view shown during transactions. Values: `Spinner`, `None`.&#x22;,
    default: &#x22;TransactionBackgroundView.Spinner&#x22;,
  },
  OverrideProductsByName: {
    type: &#x22;Dictionary<string, string>&#x22;,
    description: &#x22;Maps product names in paywalls to specific store product identifiers.&#x22;,
    default: &#x22;null&#x22;,
  },
  ShouldShowWebPurchaseConfirmationAlert: {
    type: &#x22;bool&#x22;,
    description: &#x22;Shows the web purchase confirmation alert.&#x22;,
    default: &#x22;false&#x22;,
  },
  UseCachedTemplates: {
    type: &#x22;bool&#x22;,
    description: &#x22;Uses cached paywall templates when available.&#x22;,
    default: &#x22;false&#x22;,
  },
  TimeoutAfter: {
    type: &#x22;float?&#x22;,
    description: &#x22;Optional paywall request timeout in seconds.&#x22;,
    default: &#x22;null&#x22;,
  },
}"
/>

## Preloading

```csharp C#
Superwall.Shared.PreloadAllPaywalls();

Superwall.Shared.PreloadPaywallsForPlacements(new List<string>
{
    "onboarding",
    "upgrade"
});
```

## Locale

```csharp C#
Superwall.Shared.LocaleIdentifier = "es_ES";
```

## Local Resources

Android builds can map paywall asset names to local file paths.

```csharp C#
Superwall.Shared.SetLocalResources(new Dictionary<string, string>
{
    { "hero_image", "/absolute/path/to/hero.png" }
});
```

> **Note:** `SetLocalResources` is Android-only in the current beta. It is a no-op on iOS.

## Programmatic Purchase and Products

```csharp C#
Superwall.Shared.GetProducts(new List<string> { "monthly", "annual" }, products =>
{
    foreach (var product in products)
    {
        Debug.Log($"{product.Key}: {product.Value.LocalizedPrice}");
    }
});

Superwall.Shared.Purchase("monthly", result =>
{
    Debug.Log($"Purchase result: {result.Type}");
});
```

For normal paywall flows, prefer `RegisterPlacement`.