Logging Revenue with RevenueCat

Learn how to log conversion revenue from your in-app purchases if you're using 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:

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:

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:

// 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
        }
    }
}

Last updated

Was this helpful?