# 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 [#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 [#signature]

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

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