# Logging Revenue with RevenueCat

ContextDecision supports logging conversions and revenue from any in-app purchase system, including RevenueCat.

Our revenue logging API supports both StoreKit 1 and StoreKit 2. RevenueCat abstracts away the StoreKit implementation, but it still exposes the underlying StoreKit objects if needed.

## StoreKit 2 Integration

To retrieve the StoreKit 2 product and log it, implement the `paywallViewController(_:didFinishPurchasingWith:)` delegate in your class that conforms to `PaywallViewControllerDelegate` as such:

```swift
extension MyDelegateClass: PaywallViewControllerDelegate {
    /// This delegate method notifies when a purchase has finished.
    func paywallViewController(_ controller: PaywallViewController, didFinishPurchasingWith customerInfo: CustomerInfo) {
        guard let latestEntitlement = extractLatestEntitlement(from: customerInfo) else {
            print("No entitlements found.")
            // Handle this scenario
            return
        }
        Purchases.shared.getProducts([latestEntitlement.productIdentifier]) { products in
            if let product = products.first {
                // Log or handle the StoreKit Product object
                if let context = ContextManager.recentContext(flowName: "my_upsell_flow") {
                    context.logRevenueOutcome(from: product.sk2Product)
                } else {
                    // Handle this scenario - make sure your Context is created before presenting the paywall, before reading it here
                }
            } else {
                print("Could not fetch product details for identifier: \(latestEntitlement.productIdentifier)")
                // Handle the error here
            }
        }
    }

    private func extractLatestEntitlement(from customerInfo: CustomerInfo) -> EntitlementInfo? {
        return customerInfo.entitlements.all.values
            .sorted(by: { ($0.latestPurchaseDate ?? .distantPast) > ($1.latestPurchaseDate ?? .distantPast) })
            .first
    }
}
```

## StoreKit 1 Integration

For apps still using StoreKit 1 (iOS 14 and earlier support), you can access the underlying SKProduct instead:

```swift
extension MyDelegateClass: PaywallViewControllerDelegate {
    /// This delegate method notifies when a purchase has finished.
    func paywallViewController(_ controller: PaywallViewController, didFinishPurchasingWith customerInfo: CustomerInfo) {
        guard let latestEntitlement = extractLatestEntitlement(from: customerInfo) else {
            print("No entitlements found.")
            // Handle this scenario
            return
        }
        Purchases.shared.getProducts([latestEntitlement.productIdentifier]) { products in
            if let product = products.first {
                // Log using StoreKit 1 SKProduct
                if let context = ContextManager.recentContext(flowName: "my_upsell_flow") {
                    context.logStoreKit1RevenueOutcome(from: product.sk1Product)
                } else {
                    // Handle this scenario - make sure your Context is created before presenting the paywall
                }
            } else {
                print("Could not fetch product details for identifier: \(latestEntitlement.productIdentifier)")
                // Handle the error here
            }
        }
    }

    private func extractLatestEntitlement(from customerInfo: CustomerInfo) -> EntitlementInfo? {
        return customerInfo.entitlements.all.values
            .sorted(by: { ($0.latestPurchaseDate ?? .distantPast) > ($1.latestPurchaseDate ?? .distantPast) })
            .first
    }
}
```

## Alternative: Using Transaction Information

If you need to log additional transaction-specific information (such as transaction state or date), you can also use the transaction-based approach:

```swift
// In your SKPaymentTransactionObserver implementation
func paymentQueue(_ queue: SKPaymentQueue, updatedTransactions transactions: [SKPaymentTransaction]) {
    for transaction in transactions {
        switch transaction.transactionState {
        case .purchased, .restored:
            // Get the associated product if available
            let productId = transaction.payment.productIdentifier
            Purchases.shared.getProducts([productId]) { products in
                let skProduct = products.first?.sk1Product
                
                if let context = ContextManager.recentContext(flowName: "my_upsell_flow") {
                    // Log with transaction and optional product context
                    context.logStoreKit1RevenueOutcome(from: transaction, product: skProduct)
                }
            }
            queue.finishTransaction(transaction)
        case .failed:
            // Handle failed transaction
            if let context = ContextManager.recentContext(flowName: "my_upsell_flow") {
                context.log(.negative)
            }
            queue.finishTransaction(transaction)
        default:
            break
        }
    }
}
```

{% hint style="success" %}
There's no need to call `context.log(.positive)` after logging a revenue outcome from a `Product` or `SKProduct`, as the positive outcome is already logged internally. Alternatively, you can customize the outcome to be logged directly, via `logStoreKit1RevenueOutcome(from:outcome:)` for StoreKit 1 or `logRevenueOutcome(from:outcome:)` for StoreKit 2.
{% endhint %}


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.contextsdk.com/context-decision/revenue-outcomes/revenuecat.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
