Skip to content
This repository was archived by the owner on Oct 16, 2025. It is now read-only.

hyodotdev/openiap-apple

Repository files navigation

OpenIAP Apple

⚠️ DEPRECATED - This repository has been moved to a monorepo

This repository is no longer maintained. All development has moved to the OpenIAP monorepo.

πŸ”„ New Location

Swift Package: packages/apple in the OpenIAP monorepo

Installation:

  • Swift Package Manager: Use https://github.com/hyodotdev/openiap with product OpenIAP
  • CocoaPods: pod 'openiap' (still published from monorepo)

No code changes required - the package name and API remain the same.

πŸ“– Visit OpenIAP monorepo β†’


OpenIAP Apple Logo
A comprehensive Swift implementation of the OpenIAP specification for iOS, macOS, tvOS, and watchOS applications.

OpenIAP is a unified specification for in-app purchases across platforms, frameworks, and emerging technologies. This Apple ecosystem implementation standardizes IAP implementations to reduce fragmentation and enable consistent behavioral across all Apple platforms.

In the AI coding era, having a unified IAP specification becomes increasingly important as developers build applications across multiple platforms and frameworks with automated tools.

🌐 Learn More

Visit openiap.dev for complete documentation, guides, and the full OpenIAP specification.

✨ Features

  • βœ… StoreKit 2 support with full iOS 15+ compatibility
  • βœ… Cross-platform support (iOS, macOS, tvOS, watchOS)
  • βœ… Thread-safe operations with MainActor isolation
  • βœ… Explicit connection management with automatic listener cleanup
  • βœ… Multiple API levels - Use OpenIapModule.shared or OpenIapStore
  • βœ… Product management with intelligent caching
  • βœ… Purchase handling with automatic transaction verification
    • Processes only StoreKit 2 verified transactions and emits updates.
  • βœ… Subscription management with cancel/reactivate support
    • Opens App Store manage subscriptions UI for user cancel/reactivate and detects state changes.
  • βœ… Receipt validation and transaction security
    • Provides Base64 receipt and JWS; verifies latest transaction via StoreKit and supports server-side validation.
  • βœ… Event-driven purchase observation
  • βœ… Swift Package Manager and CocoaPods support

πŸ“‹ Requirements

Platform Minimum Version
iOS 15.0+
macOS 14.0+
tvOS 15.0+
watchOS 8.0+
Swift 5.9+

πŸ“¦ Installation

Swift Package Manager

Add OpenIAP to your Package.swift:

dependencies: [
    .package(url: "https://github.com/hyodotdev/openiap-apple.git", from: "1.2.23")
]

Or through Xcode:

  1. File β†’ Add Package Dependencies
  2. Enter: https://github.com/hyodotdev/openiap-apple.git
  3. Select version and add to your target

CocoaPods

Add to your Podfile:

pod 'openiap', '~> 1.2.23'

Then run:

pod install

πŸš€ Quick Start

OpenIAP provides multiple ways to integrate in-app purchases, from super simple one-liners to advanced control. Choose the approach that fits your needs!

Option 1: Shared Instance (Simplest)

Use OpenIapModule.shared for quick integration:

import OpenIAP

let module = OpenIapModule.shared

// Initialize connection
_ = try await module.initConnection()

// Fetch products
let products = try await module.fetchProducts(
    ProductRequest(skus: ["premium", "coins"], type: .all)
)

// Make a purchase
let purchase = try await module.requestPurchase(
    let purchase = try await module.requestPurchase(RequestPurchaseProps(request: .purchase(RequestPurchasePropsByPlatforms(android: nil, ios: RequestPurchaseIosProps(andDangerouslyFinishTransactionAutomatically: nil, appAccountToken: nil, quantity: 1, sku: "premium", withOffer: nil))), type: .inApp))
)

// Get available/restored purchases
let restored = try await module.getAvailablePurchases(nil)

// End connection when done
_ = try await module.endConnection()

Option 2: OpenIapStore (SwiftUI Ready)

For more control while keeping it simple:

import OpenIAP

@MainActor
class StoreViewModel: ObservableObject {
    private let iapStore: OpenIapStore

    init() {
        // Setup store with event handlers
        self.iapStore = OpenIapStore(
            onPurchaseSuccess: { purchase in
                print("Purchase successful: \(purchase.productId)")
            },
            onPurchaseError: { error in
                print("Purchase failed: \(error.message)")
            }
        )

        Task {
            // Initialize connection
            try await iapStore.initConnection()

            // Fetch products
            try await iapStore.fetchProducts(
                skus: ["product1", "product2"],
                type: .inApp
            )
        }
    }

    deinit {
        Task {
            // End connection when done
            try await iapStore.endConnection()
        }
    }
}

Option 3: OpenIapModule Direct (Low-level)

For complete control over the purchase flow:

import OpenIAP

@MainActor
func setupStore() async throws {
    let module = OpenIapModule.shared

    // Initialize connection first
    _ = try await module.initConnection()

    // Setup listeners
    let subscription = module.purchaseUpdatedListener { purchase in
        print("Purchase updated: \(purchase.productId)")
    }

    // Fetch and purchase
    let request = ProductRequest(skus: ["premium"], type: .all)
    let products = try await module.fetchProducts(request)

    let purchase = try await store.requestPurchase(sku: "premium")
    let purchase = try await module.requestPurchase(props)

    // When done, clean up
    module.removeListener(subscription)
    _ = try await module.endConnection()
}

🎯 API Architecture

OpenIAP now has a simplified, minimal API with just 2 main components:

Core Components

  1. OpenIapModule (OpenIapModule.swift)

    • Core StoreKit 2 implementation
    • Shared instance for simple usage
    • Low-level instance methods for advanced control
  2. OpenIapStore (OpenIapStore.swift)

    • SwiftUI-ready with @Published properties
    • Explicit connection management (initConnection/endConnection)
    • Event callbacks for purchase success/error
    • Perfect for MVVM architecture

Why This Design?

  • No Duplication: Each component has a distinct purpose
  • Flexibility: Use the shared module or the SwiftUI store
  • Simplicity: Only 2 files to understand instead of 4+
  • Compatibility: Maintains openiap.dev spec compliance

πŸ§ͺ Testing

Run Tests

# Via Swift Package Manager
swift test

# Via Xcode
⌘U (Product β†’ Test)

Test with Sandbox

  1. Configure your products in App Store Connect
  2. Create a Sandbox Apple ID
  3. Use test card: 4242 4242 4242 4242

Server-Side Validation

OpenIAP provides comprehensive transaction verification with server-side receipt validation:

let store = OpenIapStore()
try await store.initConnection()

// Request purchase (validate server-side first)
let purchase = try await store.requestPurchase(
    try await store.requestPurchase(sku: "dev.hyo.premium")
)

// Validate on your server using purchase.purchaseToken
// Then finish the transaction manually
_ = try await store.finishTransaction(purchase: purchase, isConsumable: false)

πŸ”„ Connection Management

The library provides explicit connection management with automatic listener cleanup.

Key Benefits

  1. Explicit Connection Control: You decide when to connect and disconnect
  2. Automatic Listener Cleanup: Listeners are cleaned up on endConnection()
  3. Built-in Event Handling: Purchase success/error callbacks are managed for you
  4. SwiftUI Ready: Published properties for reactive UI updates
  5. Simplified API: All common operations with sensible defaults

Usage Pattern

class StoreViewModel: ObservableObject {
    private let iapStore = OpenIapStore()

    init() {
        Task {
            // Initialize connection
            try await iapStore.initConnection()

            // Fetch products
            try await iapStore.fetchProducts(skus: productIds)
        }
    }

    deinit {
        Task {
            // End connection (listeners cleaned up automatically)
            try await iapStore.endConnection()
        }
    }
}

πŸ“š Data Models

Our Swift data models are generated from the shared GraphQL schema in openiap-gql. Run ./scripts/generate-types.sh (or the equivalent tooling in that repo) to update Sources/Models/Types.swift, and every consumerβ€”including the example appβ€”should rely on those generated definitions instead of hand-written structs.

ProductIOS snapshot
struct ProductIOS {
    let id: String
    let title: String
    let description: String
    let type: ProductType
    let displayPrice: String
    let currency: String
    let price: Double?
    let platform: IapPlatform

    // iOS-specific properties
    let displayNameIOS: String
    let typeIOS: ProductTypeIOS
    let subscriptionInfoIOS: SubscriptionInfoIOS?
    let discountsIOS: [DiscountIOS]?
    let isFamilyShareableIOS: Bool
}
PurchaseIOS snapshot
struct PurchaseIOS {
    let id: String
    let productId: String
    let transactionDate: Double
    let purchaseToken: String?
    let purchaseState: PurchaseState
    let isAutoRenewing: Bool
    let quantity: Int
    let platform: IapPlatform

    // iOS-specific properties
    let appAccountToken: String?
    let environmentIOS: String?
    let storefrontCountryCodeIOS: String?
    let subscriptionGroupIdIOS: String?
    let transactionReasonIOS: String?
    let offerIOS: PurchaseOfferIOS?
}

DiscountOffer

struct DiscountOffer {
    let identifier: String
    let keyIdentifier: String
    let nonce: String
    let signature: String
    let timestamp: String
}

⚑ Error Handling

OpenIAP provides comprehensive error handling:

// Unified error model
struct PurchaseError: LocalizedError {
    let code: String
    let message: String
    let productId: String?

    var errorDescription: String? { message }
}

// Create errors with predefined codes
let error = PurchaseError(code: "E_USER_CANCELLED", message: "User cancelled the purchase")

🀝 Contributing

We welcome contributions! Please see our Contributing Guidelines for details.

πŸ“„ License

This project is licensed under the MIT License - see the LICENSE file for details.

πŸ”„ Best Practices

  1. Choose the right API level: Use OpenIapModule.shared for simple flows, or OpenIapStore for SwiftUI apps
  2. Handle errors appropriately: Always check for user cancellations vs actual errors
  3. Validate receipts server-side: Use andDangerouslyFinishTransactionAutomatically: false for server validation
  4. Test with Sandbox: Always test purchases in App Store Connect Sandbox environment
  5. Monitor events: Set up purchase listeners before making purchases

πŸ’¬ Support


Built with ❀️ for the OpenIAP community

About

In-App Purchase in Apple platform that confirms OpenIAP

Topics

Resources

License

Contributing

Stars

Watchers

Forks

Packages

No packages published

Contributors 3

  •  
  •  
  •  

Languages