TPInAppReceipt

repository·master·Indexed 20 days ago

https://github.com/tikhop/tpinappreceipt

A lightweight, pure-Swift library for reading and validating Apple In-App Purchase receipts locally on the device. It provides tools to decode receipts, verify integrity via certificate chains and signatures, and access detailed app and IAP receipt fields. The library supports Swift 6.0+ and offers both modern async/await APIs and a blocking API for non-async contexts.

Tokens
5.8K
Snippets
22
Records
30
Agent score
68%

What's inside TPInAppReceipt

  1. Understand the TPInAppReceipt architecture

    master

    The library is structured into three distinct layers that separate data modeling, parsing, and verification:

    1. Core: Contains the data models. AppReceipt is the primary typealias representing the receipt structure. It uses generic PKCS#7 types (like ContentInfo, SignedData, and SignerInfo) to model the ASN.1 structure, while InAppReceiptPayload and InAppPurchase hold the actual decoded receipt data.
    2. Decoder: Responsible for parsing raw data. It uses an Engine to transform Data into an AppReceipt.
    3. Validator: Responsible for verifying the receipt. It composes multiple ReceiptVerifier implementations (such as chain, signature, hash, and meta verifiers) and runs them in parallel using a TaskGroup.

    All core types are Sendable and Hashable.

  2. Migrate from TPInAppReceipt v3 to v4

    master

    To upgrade to version 4.0.0 or later, update your Swift Package Manager dependency and ensure your project meets the new platform requirements. Note that v4 introduces significant breaking changes, moving from synchronous/callback-based APIs to Swift Concurrency (async/await) and renaming core types.

    .package(url: "https://github.com/tikhop/TPInAppReceipt.git", from: "4.0.0")
  3. Update Receipt Loading and Parsing (Type Changes)

    master

    The core type has changed from InAppReceipt to AppReceipt. Loading the local receipt and parsing from data are now asynchronous operations.

    // Loading local receipt
    // Before: let receipt = try InAppReceipt.localReceipt()
    let receipt = try await AppReceipt.local
    
    // Parsing from data
    // Before: let receipt = try InAppReceipt.receipt(from: data)
    let receipt = try AppReceipt.receipt(from: data)
  4. Install TPInAppReceipt via Swift Package Manager

    master

    To use TPInAppReceipt in your project, add it as a dependency in your Package.swift file.

    Requirements

    • Swift 6.0+ / Xcode 16+
    • macOS 10.15+ / iOS 13+ / tvOS 13+ / watchOS 6.2+ / visionOS 1+
    dependencies: [
        .package(url: "https://github.com/tikhop/TPInAppReceipt.git", from: "4.0.2")
    ]
    
    .target(
        name: "YourTarget",
        dependencies: ["TPInAppReceipt"]
    )
  5. Use Blocking Mode for synchronous receipt validation

    master

    If you are working in a context where asynchronous APIs are unavailable, you can use the Blocking SPI (System Programming Interface) to perform synchronous receipt retrieval and validation.

    Note that this requires using the @_spi(Blocking) attribute to access the synchronous methods.

    Key differences from Async mode:

    • Verifiers: Uses Security.framework verifiers (SecChainVerifier, SecSignatureVerifier) instead of the async swift-certificates/swift-crypto implementations.
    • Execution: Verifiers run sequentially rather than in parallel.
    • Consistency: Hash and metadata verification logic remains identical to the async version.
    @_spi(Blocking) import TPInAppReceipt
    
    let receipt = try AppReceipt.local_blocking
    let result = receipt.validate_blocking()
  6. Validate a receipt using default validation

    master

    To verify the authenticity and integrity of an App Store receipt using the standard suite of checks, call the validate() method on a receipt instance. This runs four checks in parallel: certificate chain, signature, hash, and metadata. The validator automatically selects the appropriate Apple root certificate based on the receipt's environment.

    let result = await receipt.validate()
    
    switch result {
    case .valid:
        // Proceed with trusted receipt
        break
    case .invalid(let error):
        print(error)
    }
  7. Decode App Store receipt data

    master

    You can decode a receipt using AppReceipt in two ways: by accessing the local receipt stored on the device or by decoding raw data (e.g., from a network response or file). AppReceipt is a typealias over ContentInfo<SignedData<InAppReceiptPayload>>, allowing access to the full PKCS#7 structure.

    import TPInAppReceipt
    
    // Local receipt
    let receipt = try await AppReceipt.local
    
    // From raw data
    let receipt = try AppReceipt.receipt(from: data)
  8. Query purchases and subscriptions

    master

    Use the AppReceipt instance to inspect the user's purchase history and active subscriptions. You can check for general purchase existence, list all purchases, or query specifically for auto-renewable subscriptions.

    receipt.purchases              // [InAppPurchase]
    receipt.hasPurchases
    receipt.autoRenewablePurchases
    receipt.activeAutoRenewableSubscriptionPurchases
    
    // Query by product identifier
    receipt.purchases(ofProductIdentifier: "com.example.sub")
    receipt.containsPurchase(ofProductIdentifier: "com.example.premium")
    
    // Subscription specific queries
    receipt.hasActiveAutoRenewableSubscription(
        ofProductIdentifier: "com.example.sub",
        forDate: Date()
    )
    
    receipt.lastAutoRenewableSubscriptionPurchase(
        ofProductIdentifier: "com.example.sub"
    )