EasyApp

In-App Purchase

Learn how to use In-App Purchase in your app

inapppurchases

In-App Purchase Module

The InAppPurchases module provides a comprehensive in-app purchase solution, supporting multiple implementation approaches including both RevenueCat and StoreKit2 complete subscription management systems.

We will provide more beautiful in-app purchase UI implementations in the future, so stay tuned.

Module Structure

InAppPurchases/
├── View/
│   ├── InAppPurchasesView.swift                    # Main entry page
│   ├── CustomAPWithRevenueCatPaywallView.swift     # RevenueCat fully custom
│   ├── ASemiCustomWithRevenueCatPaywallView.swift  # RevenueCat semi-custom
│   └── CustomStoreKit2PaywallView.swift            # StoreKit2 fully custom
└── TestPremium/
    ├── TestRequirePremium.swift                    # Test Premium access control
    └── TestActionGoPaywall.swift                   # Test Paywall trigger

Four In-App Purchase Implementation Options

1. Fully Custom RevenueCat Paywall

Implement a fully customized paywall interface using RevenueCat SDK API:

Core Features:

  • Automatically calculate savings between subscription packages
  • Display monthly average price for annual subscriptions
  • Centralized pricing logic handling
struct CustomAPWithRevenueCatPaywallView: View {
    @EnvironmentObject private var revenueCatManager: RevenueCatManager
    @EnvironmentObject private var authManager: AuthManager
    @State private var selectedPackage: Package?
    
    var body: some View {
        NavigationStack {
            ScrollView {
                VStack {
                    // Subscription info display
                    if let productTitle = authManager.storeProduct?.localizedTitle,
                       let entitlement = authManager.customerInfo?.entitlements[
                        Constants.RevenueCat.entitlementID], entitlement.isActive {
                        SubscriptionInfoView(
                            productTitle: productTitle,
                            expirationDate: entitlement.expirationDate
                        )
                    }
                    
                    // Package selection interface
                    if let offerings = revenueCatManager.currentOfferings,
                       let currentOffering = offerings.current {
                        // Render package selection UI
                    }
                }
            }
        }
    }
}

2. Semi-Custom RevenueCat Paywall

Use RevenueCat's .paywallFooter() API, allowing custom design content:

struct ASemiCustomWithRevenueCatPaywallView: View {
    var body: some View {
        // Custom top content
        CustomPaywallContent()
            .paywallFooter()  // RevenueCat provided purchase button logic
    }
}

3. Fully Custom StoreKit2 Paywall

Implement a fully customized paywall using StoreKit2 API:

struct CustomStoreKit2PaywallView: View {
    @EnvironmentObject private var storeKit2Manager: StoreKit2Manager
    @EnvironmentObject private var authManager: AuthManager
    @State private var selectedProduct: Product?
    
    var body: some View {
        NavigationStack {
            ScrollView {
                VStack {
                    // Current subscription status
                    if let productTitle = authManager.storeKitProduct?.displayName,
                       let transaction = authManager.storeKitTransaction {
                        SubscriptionInfoView(
                            productTitle: productTitle,
                            expirationDate: transaction.expirationDate
                        )
                    }
                    
                    // Product list
                    ForEach(storeKit2Manager.products, id: \.id) { product in
                        ProductView(
                            product: product,
                            isSelected: selectedProduct?.id == product.id
                        ) {
                            selectedProduct = product
                        }
                    }
                }
            }
        }
    }
}

4. RevenueCatUI PaywallView

Directly use the standard paywall component provided by RevenueCatUI:

.sheet(isPresented: $displayPaywall) {
    PaywallView(
        displayCloseButton: true,
        performPurchase: { package in
            print("Purchase started: \(package.identifier)")
            HapticsEngine.impact()
            paySuccessCount += 1
            syncPurchases()
            return (userCancelled: false, error: nil)
        },
        performRestore: {
            print("Restore started")
            HapticsEngine.impact()
            paySuccessCount += 1
            syncPurchases()
            return (success: true, error: nil)
        }
    )
}

Premium Access Control

1. Declarative Access Control

Use the .requirePremium() modifier to protect subscription-required content:

struct TestRequirePremium: View {
    @EnvironmentObject private var authManager: AuthManager
    
    var body: some View {
        VStack {
            Text("This is Premium-only content")
                .requirePremium(
                    subscriptionType: .storeKit2,
                    sheetOrFullScreenCover: .fullScreenCover,
                    onCancelPaywall: {
                        print("User cancelled subscription")
                    },
                    onPaySuccess: {
                        print("Subscription successful")
                    }
                )
        }
    }
}

2. Action-Triggered Access Control

Trigger subscription checks through button clicks and other actions:

struct TestActionGoPaywall: View {
    @EnvironmentObject private var authManager: AuthManager
    
    var body: some View {
        VStack {
            // RevenueCat implementation
            Button("Use RevenueCat Paywall") {
                authManager.actionGoPaywall(
                    subscriptionType: .revenueCat,
                    sheetOrFullScreenCover: .sheet
                ) {
                    // Actions to execute when user has subscription
                    print("Execute Premium feature")
                }
            }
            
            // StoreKit2 implementation
            Button("Use StoreKit2 Paywall") {
                authManager.actionGoPaywall(
                    subscriptionType: .storeKit2,
                    sheetOrFullScreenCover: .fullScreenCover
                ) {
                    // Actions to execute when user has subscription
                    print("Execute Premium feature")
                }
            }
        }
    }
}

Core Feature Characteristics

1. Dual Subscription System Support

  • RevenueCat - Feature-rich subscription management platform
  • StoreKit2 - Apple's native subscription API
  • Choose one based on project requirements

2. Price Calculation and Display

  • Automatically calculate savings between different packages
  • Display monthly average price for annual subscriptions
  • Support multi-currency and localized price display

3. Subscription Status Management

  • Real-time display of current subscription status
  • Support for restore purchase functionality

4. User Experience Optimization

  • Celebration animations (Confetti effects)
  • Haptic feedback
  • Loading state indicators
  • Error handling and retry mechanisms

5. Flexible Display Options

  • Sheet modal mode
  • FullScreenCover full-screen mode

Manager Integration

RevenueCat Manager

@EnvironmentObject private var revenueCatManager: RevenueCatManager

// Get subscription products
let offerings = revenueCatManager.currentOfferings
let packages = offerings?.current?.availablePackages

// Purchase handling
try await revenueCatManager.purchase(package: selectedPackage)

StoreKit2 Manager

@EnvironmentObject private var storeKit2Manager: StoreKit2Manager

// Get product list
let products = storeKit2Manager.products

// Purchase handling
try await storeKit2Manager.purchase(product: selectedProduct)

Testing and Debugging

The module provides complete testing components:

  1. Test Premium Access - Verify access control logic
  2. Test Paywall Trigger - Verify paywall display logic
  3. Simulate Purchase Flow - Sandbox testing

During development, in-app purchases are tested in the sandbox environment. No actual charges occur during purchases.

Please use a real device for testing, not the simulator.

  • First, we need to create a sandbox test account in App Store Connect's Users and Access

If you already have a test account, follow the image below:

addSandboxTestAcc

Click the + button to create a sandbox test account.

If you don't have a test account and are creating one for the first time, follow the image below:

addSandboxTestAcc2

In the subsequent popup, enter your test account information and click the Create button to save.

addSandboxTestAcc3

  • Run the app on a real device for in-app purchase testing

How to view subscription data in real-time?

Return to RevenueCat's Dashboard page, where you can see all your subscription information.

In the sandbox environment, you need to turn on the Sandbox data switch.

paySuccess

Native StoreKit2 revenue needs to be viewed in App Store Connect. Data is usually delayed, typically N+1 days.

Localization Support

  • Multi-language price display
  • Localized error messages
  • Regionalized currency formatting

Through the InAppPurchases module, you can quickly implement professional-grade in-app purchase functionality, supporting multiple technical solutions and display methods to meet the needs of different application scenarios.

For More Learning Resources, Please Refer To:

Last updated on