diff --git a/Sources/SuperwallKit/Analytics/Internal Tracking/Trackable Events/TrackableSuperwallEvent.swift b/Sources/SuperwallKit/Analytics/Internal Tracking/Trackable Events/TrackableSuperwallEvent.swift index 97dbd5a3fa..8084d9d2ee 100644 --- a/Sources/SuperwallKit/Analytics/Internal Tracking/Trackable Events/TrackableSuperwallEvent.swift +++ b/Sources/SuperwallKit/Analytics/Internal Tracking/Trackable Events/TrackableSuperwallEvent.swift @@ -671,6 +671,15 @@ enum InternalSuperwallEvent { var params = paywallInfo.audienceFilterParams() if let product = product { params["abandoned_product_id"] = product.productIdentifier + let hasRicherProductAttributes = + !product.localizedSubscriptionPeriod.isEmpty + || !product.period.isEmpty + + if hasRicherProductAttributes { + for (key, value) in product.attributes { + params["abandoned_product_\(key.camelCaseToSnakeCase())"] = value + } + } } return params default: diff --git a/Tests/SuperwallKitTests/Analytics/Internal Tracking/TrackTests.swift b/Tests/SuperwallKitTests/Analytics/Internal Tracking/TrackTests.swift index 51decc4b4c..0deed330c7 100644 --- a/Tests/SuperwallKitTests/Analytics/Internal Tracking/TrackTests.swift +++ b/Tests/SuperwallKitTests/Analytics/Internal Tracking/TrackTests.swift @@ -1695,6 +1695,46 @@ struct TrackingTests { result.parameters.audienceFilterParams["presented_by_event_name"] as? String == paywallInfo.presentedByPlacementWithName) } + @Test func transaction_abandon() async { + let paywallInfo: PaywallInfo = .stub() + let productId = "abc" + let product = StoreProduct( + sk1Product: MockSkProduct( + subscriptionPeriod: SKProductSubscriptionPeriodMock(numberOfUnits: 1, unit: .month), + productIdentifier: productId + ), + entitlements: [.stub()] + ) + let dependencyContainer = DependencyContainer() + let skTransaction = MockSKPaymentTransaction(state: .purchased) + let transaction = await dependencyContainer.makeStoreTransaction(from: skTransaction) + let result = await Superwall.shared.track( + InternalSuperwallEvent.Transaction( + state: .abandon(product), + paywallInfo: paywallInfo, + product: product, + transaction: transaction, + source: .external, + isObserved: false, + storeKitVersion: .storeKit1 + ) + ) + + #expect(result.parameters.audienceFilterParams["$event_name"] as! String == "transaction_abandon") + #expect(result.parameters.audienceFilterParams["$product_period"] != nil) + #expect(result.parameters.audienceFilterParams["$product_period_months"] != nil) + + #expect( + result.parameters.audienceFilterParams["event_name"] as! String == "transaction_abandon") + #expect( + result.parameters.audienceFilterParams["abandoned_product_id"] as! String == productId) + #expect(result.parameters.audienceFilterParams["abandoned_product_identifier"] as! String == productId) + #expect(result.parameters.audienceFilterParams["abandoned_product_period"] as? String == "month") + #expect(result.parameters.audienceFilterParams["abandoned_product_period_months"] as? String == "1") + #expect(result.parameters.audienceFilterParams["abandoned_product_period_years"] as? String == "0") + #expect((result.parameters.audienceFilterParams["abandoned_product_localized_period"] as? String)?.isEmpty == false) + } + @Test func transaction_fail() async { let paywallInfo: PaywallInfo = .stub() let productId = "abc" diff --git a/Tests/SuperwallKitTests/StoreKit/Mocks/SKProductSubscriptionPeriodMock.swift b/Tests/SuperwallKitTests/StoreKit/Mocks/SKProductSubscriptionPeriodMock.swift index dc6bcd25ff..767e56a370 100644 --- a/Tests/SuperwallKitTests/StoreKit/Mocks/SKProductSubscriptionPeriodMock.swift +++ b/Tests/SuperwallKitTests/StoreKit/Mocks/SKProductSubscriptionPeriodMock.swift @@ -9,7 +9,22 @@ import Foundation import StoreKit final class SKProductSubscriptionPeriodMock: SKProductSubscriptionPeriod { + private let internalNumberOfUnits: Int + private let internalUnit: SKProduct.PeriodUnit + override var numberOfUnits: Int { - return 1 + return internalNumberOfUnits + } + + override var unit: SKProduct.PeriodUnit { + return internalUnit + } + + init( + numberOfUnits: Int = 1, + unit: SKProduct.PeriodUnit = .month + ) { + self.internalNumberOfUnits = numberOfUnits + self.internalUnit = unit } }