# 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

# subscriptionStatus

A published property that indicates the subscription status of the user.

> **Info:** If you're using a custom [`PurchaseController`](/docs/ios/sdk-reference/PurchaseController), you must update this property whenever the user's entitlements change.

> **Note:** You can also observe changes via the [`SuperwallDelegate`](/docs/ios/sdk-reference/SuperwallDelegate) method `subscriptionStatusDidChange(from:to:)`.

## Purpose

Indicates the current subscription status of the user and can be observed for changes using Combine or SwiftUI.

## Signature

```swift
@Published
public var subscriptionStatus: SubscriptionStatus { get set }
```

## Parameters

This property accepts a `SubscriptionStatus` enum value:

* `.unknown` - Status is not yet determined
* `.active(Set<Entitlement>)` - User has active entitlements (set of entitlement identifiers)
* `.inactive` - User has no active entitlements

## Returns / State

Returns the current `SubscriptionStatus`. When using a [`PurchaseController`](/docs/ios/sdk-reference/PurchaseController), you must set this property yourself. Otherwise, Superwall manages it automatically.

## Usage

Set subscription status (when using PurchaseController):

```swift
Superwall.shared.subscriptionStatus = .active(["premium", "pro_features"])
Superwall.shared.subscriptionStatus = .inactive
```

Get current subscription status:

```swift
let status = Superwall.shared.subscriptionStatus
switch status {
case .unknown:
  print("Subscription status unknown")
case .active(let entitlements):
  print("User has active entitlements: \(entitlements)")
case .inactive:
  print("User has no active subscription")
}
```

Observe changes with Combine:

```swift
import Combine

class ViewController: UIViewController {
  private var cancellables = Set<AnyCancellable>()
  
  override func viewDidLoad() {
    super.viewDidLoad()
    
    Superwall.shared.$subscriptionStatus
      .sink { [weak self] status in
        self?.updateUI(for: status)
      }
      .store(in: &cancellables)
  }
  
  func updateUI(for status: SubscriptionStatus) {
    switch status {
    case .active:
      showPremiumContent()
    case .inactive:
      showFreeContent()
    case .unknown:
      showLoadingState()
    }
  }
}
```

SwiftUI observation:

```swift
struct ContentView: View {
  @StateObject var superwall = Superwall.shared
  
  var body: some View {
    VStack {
      switch superwall.subscriptionStatus {
      case .active(let entitlements):
        Text("Premium user with: \(entitlements.joined(separator: ", "))")
      case .inactive:
        Text("Free user")
      case .unknown:
        Text("Loading...")
      }
    }
  }
}
```