# PurchaseController

A protocol for handling Superwall's subscription-related logic with your own purchase implementation.

> **Info:** **This protocol is not required.** By default, Superwall handles all subscription-related logic automatically.

> **Warning:** When implementing PurchaseController, you must manually update [`subscriptionStatus`](/docs/ios/sdk-reference/subscriptionStatus) whenever the user's entitlements change.

## Purpose

Use this protocol only if you want complete control over purchase handling, such as when using RevenueCat, another third-party purchase framework, or your own external billing flow.

## Signature

```swift
public protocol PurchaseController: AnyObject {
  @MainActor
  func purchase(product: StoreProduct) async -> PurchaseResult
  
  @MainActor
  func restorePurchases() async -> RestorationResult
}
```

## Parameters

<TypeTable
  type="{
  purchase: {
    type: &#x22;product: StoreProduct&#x22;,
    description: &#x22;Called when user initiates purchasing. The `StoreProduct` may wrap an App Store product or, for custom paywall products in 4.15.0+, an API-backed product that must be purchased by your external billing system. Returns `PurchaseResult`.&#x22;,
    required: true,
  },
  restorePurchases: {
    type: &#x22;None&#x22;,
    description: &#x22;Called when user initiates restore. Implement your restore logic here. Returns `RestorationResult`.&#x22;,
    required: true,
  },
}"
/>

## Returns / State

* `purchase()` returns a `PurchaseResult` (`.purchased`, `.failed(Error)`, or `.cancelled`)
* `restorePurchases()` returns a `RestorationResult` (`.restored` or `.failed(Error?)`)

When using a PurchaseController, you must also manage [`subscriptionStatus`](/docs/ios/sdk-reference/subscriptionStatus) yourself.

## Handling Products

* For App Store-backed products, use `product.sk1Product` or `product.sk2Product`, or pass the product into your existing purchase SDK.
* For custom products introduced in `4.15.0`, Superwall will call your purchase controller with a `StoreProduct` that has no StoreKit backing product. In that case, use `product.productIdentifier` in your external billing system and return the matching `PurchaseResult`.
* Do not call `Superwall.shared.purchase(product)` for custom products. That helper is for StoreKit-backed purchases only.

For a complete guide, see [Custom Store Products](/docs/ios/guides/custom-store-products).

## Usage

For implementation examples and detailed guidance, see [Using RevenueCat](/docs/ios/guides/using-revenuecat).

> **Tip:** This is commonly used with RevenueCat, StoreKit 2, or other third-party purchase frameworks where you want to maintain your existing purchase logic.