# PurchaseController

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

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

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

## [Purpose](/content/docs/ios/sdk-reference/PurchaseController#purpose/index.html)

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](/content/docs/ios/sdk-reference/PurchaseController#signature/index.html)

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

@MainActor
  func restorePurchases() async -> RestorationResult
}
```

## [Parameters](/content/docs/ios/sdk-reference/PurchaseController#parameters/index.html)

| Prop                    | Type                   |
|-------------------------|------------------------|
| `purchase`product:     | StoreProduct           |
| `restorePurchases`     | None                   |

## [Returns / State](/content/docs/ios/sdk-reference/PurchaseController#returns--state/index.html)

- `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`](/content/docs/ios/sdk-reference/subscriptionStatus/index.html) yourself.

## [Handling Products](/content/docs/ios/sdk-reference/PurchaseController#handling-products/index.html)

- 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](/content/docs/ios/guides/custom-store-products/index.html).

## [Usage](/content/docs/ios/sdk-reference/PurchaseController#usage/index.html)

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

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