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
}
}
}
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.
Last updated
Was this helpful?