Expose abandoned transaction product params in filters#459
Expose abandoned transaction product params in filters#459SebastianSzturo wants to merge 2 commits intosuperwall:developfrom
Conversation
| let hasRicherProductAttributes = | ||
| !product.localizedPrice.isEmpty | ||
| || !product.localizedSubscriptionPeriod.isEmpty | ||
| || !product.period.isEmpty |
There was a problem hiding this comment.
Guard condition always passes for real App Store products
localizedPrice is non-empty for every real App Store product (subscriptions, consumables, non-consumables alike), so the hasRicherProductAttributes OR-condition is effectively always true for any StoreKit product. This means non-subscription products will also have their subscription-specific period fields (e.g. abandoned_product_period_months: "", abandoned_product_period_days: "") emitted as empty strings in the audience filter params, which may create noise in the filter picker UI.
If the intent is to expose duration fields only for subscription products, consider checking localizedSubscriptionPeriod alone (or check !product.period.isEmpty), since those are empty for non-subscription products:
let hasRicherProductAttributes = !product.localizedSubscriptionPeriod.isEmpty
|| !product.period.isEmptyPrompt To Fix With AI
This is a comment left during a code review.
Path: Sources/SuperwallKit/Analytics/Internal Tracking/Trackable Events/TrackableSuperwallEvent.swift
Line: 674-677
Comment:
**Guard condition always passes for real App Store products**
`localizedPrice` is non-empty for every real App Store product (subscriptions, consumables, non-consumables alike), so the `hasRicherProductAttributes` OR-condition is effectively always `true` for any StoreKit product. This means non-subscription products will also have their subscription-specific period fields (e.g. `abandoned_product_period_months: ""`, `abandoned_product_period_days: ""`) emitted as empty strings in the audience filter params, which may create noise in the filter picker UI.
If the intent is to expose duration fields only for subscription products, consider checking `localizedSubscriptionPeriod` alone (or check `!product.period.isEmpty`), since those are empty for non-subscription products:
```swift
let hasRicherProductAttributes = !product.localizedSubscriptionPeriod.isEmpty
|| !product.period.isEmpty
```
How can I resolve this? If you propose a fix, please make it concise.| #expect(result.parameters.audienceFilterParams["abandoned_product_period"] != nil) | ||
| #expect(result.parameters.audienceFilterParams["abandoned_product_period_months"] != nil) | ||
| #expect(result.parameters.audienceFilterParams["abandoned_product_period_years"] != nil) | ||
| #expect(result.parameters.audienceFilterParams["abandoned_product_localized_period"] != nil) |
There was a problem hiding this comment.
!= nil assertions don't verify period values are meaningful
periodMonthsString, periodYearsString, etc. return "" (empty string) for non-subscription products, and an empty string is still non-nil — so these assertions pass even when the feature has not populated real period data. Consider asserting the expected string values from MockSkProduct's configured period, or at least asserting as? String != "" to confirm the fields are actually populated.
Prompt To Fix With AI
This is a comment left during a code review.
Path: Tests/SuperwallKitTests/Analytics/Internal Tracking/TrackTests.swift
Line: 1729-1732
Comment:
**`!= nil` assertions don't verify period values are meaningful**
`periodMonthsString`, `periodYearsString`, etc. return `""` (empty string) for non-subscription products, and an empty string is still non-nil — so these assertions pass even when the feature has not populated real period data. Consider asserting the expected string values from `MockSkProduct`'s configured period, or at least asserting `as? String != ""` to confirm the fields are actually populated.
How can I resolve this? If you propose a fix, please make it concise.
Problem
When configuring a paywall trigger for
transaction_abandon, the dashboard filter UI only exposesabandoned_product_id.The SDK already includes richer abandoned product metadata in the internal
$product_*parameter set, but those fields are not copied into the explicit event parameter payload that powers the filter picker. As a result, you cannot target different paywalls based on the abandoned product duration.Intended use case
The goal is to show a different rescue paywall depending on which subscription duration was abandoned. For example:
Right now that workflow is blocked because
transaction_abandondoes not expose duration-related fields in the filter UI.Solution
abandoned_product_*event parametersabandoned_product_idfieldtransaction_abandonparameter setNotes
This addresses the App Store abandon path where a real
StoreProductis available. Stripe/web checkout abandon still only has product ID in the current model, so duration-based filtering for that path would require additional Stripe product metadata to be tracked.Greptile Summary
This PR copies
StoreProduct.attributesintoabandoned_product_*audience filter params when a transaction is abandoned, enabling dashboard filter rules based on product period and pricing. Existingabandoned_product_idis preserved, and ahasRicherProductAttributesguard skips attribute copying for blank Stripe/web checkout placeholder products.Confidence Score: 5/5
Safe to merge; changes are additive and isolated to the transaction abandon event parameter set with no risk of regression.
All findings are P2: one design observation about the guard condition semantics and one test quality note. No P0/P1 issues exist. The core logic correctly copies product attributes and preserves backward compatibility with
abandoned_product_id.No files require special attention.
Vulnerabilities
No security concerns identified.
Important Files Changed
abandoned_product_*params by iteratingproduct.attributesfor the.abandontransaction state; thehasRicherProductAttributesguard useslocalizedPricewhich is always non-empty for real App Store products, so empty subscription period fields get emitted for non-subscription products too.transaction_abandontest covers key presence but uses!= nilchecks for period fields that would pass even for empty strings, reducing confidence that the feature works correctly for subscription products.Flowchart
%%{init: {'theme': 'neutral'}}%% flowchart TD A[Transaction abandoned] --> B{product != nil?} B -- No --> E[Return paywallInfo.audienceFilterParams only] B -- Yes --> C[Set abandoned_product_id] C --> D{hasRicherProductAttributes?\nlocalizedPrice.isEmpty OR\nlocalizedSubscriptionPeriod.isEmpty OR\nperiod.isEmpty} D -- False\nStripe/web blank product --> E D -- True\nReal App Store product --> F[Iterate product.attributes\nadd abandoned_product_ prefix\ncamelCase to snake_case] F --> G[Return merged params] E --> H[Audience filter evaluation] G --> HPrompt To Fix All With AI
Reviews (1): Last reviewed commit: "Expose abandoned product params" | Re-trigger Greptile
(2/5) Greptile learns from your feedback when you react with thumbs up/down!