Skip to main content
This page documents all classes, methods, properties, and types available in the WhopCheckout SDK.
Looking for guides? See Build a Paywall or Check Entitlements.

Checkout

The main class for managing in-app purchases. An @Observable class that can be used with SwiftUI’s environment system.
@MainActor @Observable
public class Checkout

shared

The shared Checkout instance. Use this singleton to configure the SDK and manage purchases.
static let shared: Checkout
Example:
import WhopCheckout

// Configure at app startup
try await Checkout.shared.configure(...)

// Pass through SwiftUI environment
ContentView()
    .environment(Checkout.shared)

// Access in views
@Environment(Checkout.self) var checkout

configure(companyId:apiKey:planMappings:)

Configures and initializes the SDK. Call this once at app startup.
func configure(
    companyId: String,
    apiKey: String,
    planMappings: [PlanMapping] = []
) async throws
Parameters:
ParameterTypeDescription
companyIdStringYour Whop company ID (starts with biz_)
apiKeyStringYour API key with iap:read permission
planMappings[PlanMapping]Mapping of Whop plan IDs to Apple StoreKit product IDs. Pass an empty array if you don’t need StoreKit fallback.
Example:
try await Checkout.shared.configure(
    companyId: "biz_xxxxxxxxxxxxxx",
    apiKey: "your_api_key_here",
    planMappings: [
        .init(whopId: "plan_xxxxx", appleId: "monthly_sub"),
        .init(whopId: "plan_yyyyy", appleId: "yearly_sub")
    ]
)

isInitialized

Indicates whether the SDK has finished initializing.
var isInitialized: Bool { get }
Example:
if checkout.isInitialized {
    // SDK ready to use
}

deviceId

The unique device identifier managed by the SDK. Persists across app launches using the iOS Keychain.
var deviceId: String { get }

appUserId

The current logged-in user ID, or nil if no user is logged in.
var appUserId: String? { get }

plans

Array of available subscription plans for the configured products.
var plans: [CheckoutPlan] { get }
Example:
ForEach(checkout.plans) { plan in
    Text(plan.title ?? "Plan")
}

memberships

Array of active memberships for the current user or device.
var memberships: [CheckoutMembership] { get }

isSubscribed

Whether the user has any active membership.
var isSubscribed: Bool { get }
Example:
if checkout.isSubscribed {
    // Show premium content
}

supportsExternalPurchases

Whether the current App Store region supports external (non-StoreKit) purchases. Currently true for US users, false elsewhere.
var supportsExternalPurchases: Bool { get }
When true, the SDK defaults to Whop web checkout (lower fees). When false, it defaults to StoreKit.

refreshPlans()

Refreshes the available plans from the server.
@discardableResult
func refreshPlans() async throws -> [CheckoutPlan]
Returns: The refreshed plans with current pricing. Throws: WhopCheckoutError if the refresh fails. Call this to update pricing or plan availability after initialization. The plans property will also be updated with the latest data. Example:
// Refresh and use the return value
let plans = try await Checkout.shared.refreshPlans()

// Or just refresh (plans property updates automatically)
try await Checkout.shared.refreshPlans()

hasAccess(to:)

Checks if the user has access to a specific product.
func hasAccess(to productId: String) -> Bool
Parameters:
ParameterTypeDescription
productIdStringThe product ID to check (starts with prod_)
Returns: true if the user has an active membership for the product Example:
// Replace with your product ID from the Whop dashboard
if checkout.hasAccess(to: "prod_xxxxxxxxxxxxxx") {
    // Show pro features
}

purchase(_:method:)

Initiates a purchase flow for a plan. By default, uses Whop web checkout in the US (lower fees) and StoreKit elsewhere.
func purchase(_ whopPlanId: String, method: PaymentMethod? = nil) async throws -> CheckoutPurchaseResult
Parameters:
ParameterTypeDescription
whopPlanIdStringThe Whop plan ID to purchase (starts with plan_)
methodPaymentMethod?Override the payment method. Defaults based on region.
Returns: CheckoutPurchaseResult containing the receipt ID and membership information Throws:
  • WhopCheckoutError.cancelled if the user dismisses the checkout
  • WhopCheckoutError.notConfigured if the SDK is not configured
  • WhopCheckoutError.paymentFailed(String) if the payment fails
Example:
do {
    // Use default payment method (Whop in US, StoreKit elsewhere)
    let result = try await checkout.purchase("plan_xxxxxxxxxxxxxx")
    print("Success! Receipt: \(result.receiptId)")
} catch WhopCheckoutError.cancelled {
    // User cancelled
} catch {
    // Handle error
}

// Or explicitly choose a payment method
let result = try await checkout.purchase("plan_xxx", method: .apple)

logIn(appUserId:)

Logs in a user and claims any unclaimed memberships associated with the device.
func logIn(appUserId: String) async throws
Parameters:
ParameterTypeDescription
appUserIdStringYour app’s user ID (should not change for the same user)
Example:
try await checkout.logIn(appUserId: "user_123")

logOut()

Logs out the current user and clears their memberships from the local state.
func logOut()
Example:
checkout.logOut()

restorePurchases()

Restores purchases from both StoreKit and Whop.
func restorePurchases() async throws -> Bool
Returns: true if any active subscription was found Example:
Button("Restore Purchases") {
    Task {
        let restored = try await checkout.restorePurchases()
        if restored {
            print("Purchases restored!")
        }
    }
}

Types

CheckoutPurchaseResult

The result of a successful purchase.
struct CheckoutPurchaseResult {
    let receiptId: String
    let membership: CheckoutMembership?
}
PropertyTypeDescription
receiptIdStringThe unique receipt ID for this purchase
membershipCheckoutMembership?The membership created by this purchase
The membership property depends on the payment method used:
Payment MethodRegionmembership value
Whop web checkoutUSPopulated immediately
StoreKitOutside USnil initially
Why StoreKit purchases return nil: StoreKit transactions are processed asynchronously by Apple. When the purchase completes, the SDK may not yet have the Whop membership synced. The SDK tracks StoreKit entitlements separately, so checkout.isSubscribed will still return true.How to handle both cases:
do {
    let result = try await checkout.purchase("plan_xxx")

    // Always check isSubscribed - works for both payment methods
    if checkout.isSubscribed {
        // Grant access
    }

    // Optionally use membership details if available
    if let membership = result.membership {
        print("Membership ID: \(membership.id)")
        print("Expires: \(membership.expiresAt?.formatted() ?? "N/A")")
    }
} catch WhopCheckoutError.cancelled {
    // User cancelled
}
Key point: Don’t rely on membership being non-nil to grant access. Always use checkout.isSubscribed or checkout.hasAccess(to:) to check subscription status.

PlanMapping

A mapping between a Whop plan and an Apple StoreKit product, used during SDK configuration. This tells the SDK which Apple product to use for StoreKit purchases outside the US.
PlanMapping vs CheckoutPlan: PlanMapping is for SDK configuration (mapping IDs). CheckoutPlan is what you display in your UI (has pricing, titles, etc.). You create PlanMapping objects in configure(), then access CheckoutPlan objects via checkout.plans.
struct PlanMapping: Sendable {
    let whopId: String
    let appleId: String

    init(whopId: String, appleId: String)
}
PropertyTypeDescription
whopIdStringThe Whop plan ID (e.g., plan_xxx)
appleIdStringThe Apple product ID from App Store Connect (e.g., com.yourapp.monthly)
Example:
// Used in configure() - just maps IDs
try await Checkout.shared.configure(
    companyId: "biz_xxx",
    apiKey: "your_key",
    planMappings: [
        .init(whopId: "plan_monthly", appleId: "com.app.monthly"),
        .init(whopId: "plan_yearly", appleId: "com.app.yearly")
    ]
)

// After configuration, use checkout.plans (CheckoutPlan array) for UI
ForEach(checkout.plans) { plan in  // These are CheckoutPlan objects
    Text(plan.title ?? "Plan")
    Text(plan.whopDisplayPrice)
}
Your Whop plans and Apple products should have matching pricing and billing periods. See Setup → Plan Mappings for details.

PaymentMethod

The payment method to use for a purchase.
enum PaymentMethod: Sendable {
    case whop   // Web checkout via Whop (lower fees)
    case apple  // StoreKit purchase via Apple
}
CaseDescription
whopWeb checkout via Whop. 2.7% + $0.30 fees (vs Apple’s 15-30%). Available in the US.
appleStoreKit purchase via Apple (15-30% fees). Required outside the US.

CheckoutPlan (Display)

A subscription plan available for purchase, accessed via checkout.plans. Use this for displaying plan information in your UI and for making purchases.
CheckoutPlan vs Plan: CheckoutPlan contains full plan details (pricing, titles, trial info) for your UI. Plan is just an ID mapping used during SDK configuration. You never create CheckoutPlan objects—they come from checkout.plans after configuration.
struct CheckoutPlan: Identifiable {
    let id: String
    let productId: String?
    let title: String?
    let description: String?
    let planType: PlanType
    let billingPeriodDays: Int?
    let baseCurrency: String
    let initialPrice: Double
    let renewalPrice: Double
    let trialPeriodDays: Int?

    var renewalPeriod: RenewalPeriod? { get }
    var whopDisplayPrice: String { get }
    var appleDisplayPrice: String? { get }
}

enum PlanType: String {
    case oneTime = "one_time"
    case renewal
    case unknown
}

enum RenewalPeriod: Equatable {
    case weekly      // 7 days
    case monthly     // 30 days
    case quarterly   // 90 days
    case semiAnnual  // 180 days
    case yearly      // 365 days
    case custom(Int) // Custom period in days
}
PropertyTypeDescription
idStringThe plan ID
productIdString?The product this plan belongs to
titleString?Display name of the plan
descriptionString?Description text for the plan
planTypePlanTypeWhether this is a one-time or recurring plan
billingPeriodDaysInt?Number of days in billing cycle (30 = monthly, 365 = yearly)
baseCurrencyStringCurrency code (e.g., “usd”)
initialPriceDoubleInitial price of the plan
renewalPriceDoublePrice for renewals
trialPeriodDaysInt?Number of days in trial period
renewalPeriodRenewalPeriod?Computed renewal period (.monthly, .yearly, etc.)
whopDisplayPriceStringFormatted Whop price (e.g., “$9.99”)
appleDisplayPriceString?Localized StoreKit price, if a plan mapping exists

CheckoutMembership

An active subscription membership.
struct CheckoutMembership: Identifiable {
    let id: String
    let productId: String
    let planId: String
    let status: Status
    let isClaimed: Bool
    let createdAt: Date
    let expiresAt: Date?
    let renewalPeriodEnd: Date?
    let cancelAtPeriodEnd: Bool
    let receiptId: String?

    var isActive: Bool { get }
}

enum Status: String {
    case active
    case canceled
    case canceling
    case completed
    case drafted
    case expired
    case pastDue = "past_due"
    case trialing
    case unresolved
    case unknown
}
PropertyTypeDescription
idStringThe membership ID
productIdStringThe product this membership belongs to
planIdStringThe plan this membership is for
statusStatusCurrent status of the membership
isActiveBoolWhether the membership grants access (true for active, trialing, canceling, pastDue, completed)
isClaimedBoolWhether the membership has been claimed by a user
createdAtDateWhen the membership was created
expiresAtDate?When the membership expires
renewalPeriodEndDate?End of the current renewal period
cancelAtPeriodEndBoolWhether the membership will cancel at period end
receiptIdString?The receipt ID for this membership

WhopCheckoutError

Errors thrown by the SDK.
enum WhopCheckoutError: Error {
    case cancelled
    case notConfigured
    case paymentFailed(String)
}
CaseDescription
cancelledUser dismissed the checkout sheet without completing purchase
notConfiguredSDK is not configured. Call Checkout.shared.configure() first.
paymentFailed(String)Payment failed. The associated string contains the error message.

Requirements

  • iOS 17.0+
  • Xcode 15.0+
  • Swift 5.10+