Add placement and targeting context to paywall events#3253
Merged
Conversation
2f8a6fe to
a7a725f
Compare
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #3253 +/- ##
==========================================
+ Coverage 79.36% 79.39% +0.02%
==========================================
Files 361 361
Lines 14458 14473 +15
Branches 1964 1968 +4
==========================================
+ Hits 11475 11491 +16
+ Misses 2190 2188 -2
- Partials 793 794 +1 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
12fc978 to
d872299
Compare
tonidero
approved these changes
Mar 23, 2026
tonidero
left a comment
Contributor
There was a problem hiding this comment.
Looking great! If we've tested this and the backend is already accepting the new propert, let's 🚢
4 tasks
9b96692 to
d7bb203
Compare
Include presentedOfferingContext (placementIdentifier, targetingRevision, targetingRuleId) in BackendEvent.Paywalls serialization so the backend can calculate conversion rates per placement. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add fromContext factory on PresentedOfferingContextBackend that encapsulates the nil-check logic, removing duplication between BackendStoredEvent and PaywallStoredEvent conversion paths.
Verify that placement and targeting context survives the full PaywallStoredEvent serialize → deserialize → toBackendEvent flow, and that events without placement produce null backend context.
- Add placement-only round-trip test (no targeting) - Add targeting-only round-trip test (no placement) - Add test through PaywallEvent.toBackendStoredEvent() path verifying placement and targeting are preserved
Align naming with iOS for cross-platform consistency.
- Use import instead of fully-qualified name for PresentedOfferingContext - Assert full PresentedOfferingContextData objects instead of individual fields - Add backward compat test for old stored events without presented_offering_context
d7bb203 to
9e64fda
Compare
matteinn
pushed a commit
to matteinn/purchases-android
that referenced
this pull request
May 5, 2026
**This is an automatic release.** ## RevenueCat SDK ### ✨ New Features * Unified StoreReplacementMode API (RevenueCat#3234) via Will Taylor (@fire-at-will) * Add placement and targeting context to paywall events (RevenueCat#3253) via Dan Pannasch (@dpannasch) ### 🐞 Bugfixes * Fix null Placements when offering_ids_by_placement is absent (RevenueCat#3254) via Dan Pannasch (@dpannasch) ## RevenueCatUI SDK ### Paywallv2 #### ✨ New Features * Wire multipage workflow navigation into PaywallViewModel (RevenueCat#3381) via Cesar de la Vega (@vegaro) ### 🔄 Other Changes * Add `triggerType` to `WorkflowTrigger` (RevenueCat#3393) via Cesar de la Vega (@vegaro) * Extract private function `NavigateTo.toPaywallAction` (RevenueCat#3392) via Cesar de la Vega (@vegaro) * Bump revenucatui-tests gradle cache key (RevenueCat#3391) via Toni Rico (@tonidero) * Create `WorkflowTriggerType` and `WorkflowTriggerActionType` (RevenueCat#3386) via Cesar de la Vega (@vegaro) * Update baseline profiles (RevenueCat#3390) via RevenueCat Git Bot (@RCGitBot) * Plumb `componentId` through buttons on workflow interactions (RevenueCat#3380) via Cesar de la Vega (@vegaro) * Add `ButtonComponent.Action.Workflow` (RevenueCat#3385) via Cesar de la Vega (@vegaro) * Add `componentId` to `ButtonCoomponentStyle` (RevenueCat#3384) via Cesar de la Vega (@vegaro) * Migrate all suspendCoroutine usages to suspendCancellableCoroutine (RevenueCat#3365) via Jaewoong Eum (@skydoves) * Add `WorkflowNavigator` for multipage workflow step navigation (RevenueCat#3379) via Cesar de la Vega (@vegaro) * build(deps): bump fastlane-plugin-revenuecat_internal from `b822f01` to `d24ab26` (RevenueCat#3383) via dependabot[bot] (@dependabot[bot]) * Add `id` field to `ButtonComponent` (RevenueCat#3377) via Cesar de la Vega (@vegaro) * Add CI workflows for generating Baseline Profiles (RevenueCat#3372) via Jaewoong Eum (@skydoves) * add min sdk level for paywalls and customer center (RevenueCat#2465) via Muhammad-Sharif Moustafa (@mshmoustafa) <!-- CURSOR_SUMMARY --> --- > [!NOTE] > **Low Risk** > Low risk release bookkeeping: updates version strings, changelogs, and documentation deployment targets without changing runtime logic beyond the reported version identifier. > > **Overview** > Cuts the `10.3.0` release by updating all version references from `10.3.0-SNAPSHOT` to `10.3.0` across build config (`gradle.properties`, `.version`, `Config.frameworkVersion`) and sample/test app dependency pins. > > Updates release documentation publishing to sync Dokka output to the `10.3.0` S3 path and changes `docs/index.html` to redirect to `10.3.0`. Also promotes release notes by moving `CHANGELOG.latest.md` entries into a new `CHANGELOG.md` section for `10.3.0`. > > <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit 056ce62. Bugbot is set up for automated code reviews on this repo. Configure [here](https://www.cursor.com/dashboard/bugbot).</sup> <!-- /CURSOR_SUMMARY -->
rickvdl
added a commit
to RevenueCat/purchases-js
that referenced
this pull request
Jun 2, 2026
## Summary - Include `placement_identifier`, `targeting_revision`, and `targeting_rule_id` in paywall events under a `presented_offering_context` nested object - Field is omitted when no placement or targeting data exists - Consistent with iOS and Android implementations ## Related PRs - iOS: RevenueCat/purchases-ios#6476 - Android: RevenueCat/purchases-android#3253 ## Test plan - [x] Unit tests for all context permutations (full, placement-only, targeting-only, none, empty string) - [x] JSON round-trip serialization tests - [x] Integration test verifying context flows from offering to event payload - [x] Manual testing with targeting rules and placements in demo app
matteinn
pushed a commit
to matteinn/purchases-android
that referenced
this pull request
Jun 5, 2026
) ### Description Adds `placement_identifier`, `targeting_revision`, and `targeting_rule_id` to the custom paywall impression event. Mirrors the event payload work in iOS PR RevenueCat/purchases-ios#6707 for Android. The internal paywall events already received this context via RevenueCat#3253; this PR brings the same placement/targeting attribution to custom paywall events. ### What's new - `CustomPaywallImpressionParams` gains an `Offering`-based constructor. Passing an `Offering` lets the SDK derive the offering identifier and `PresentedOfferingContext` from the offering's first available package. - The params object stores `paywallId`, `offeringId`, and `presentedOfferingContext`; it does not store or expose the `Offering` object. This keeps the public params surface focused on the values needed for event tracking. - The string `offeringId` constructor is deprecated in favor of passing an `Offering`, because an offering identifier alone cannot reliably carry placement and targeting context. - An `@InternalRevenueCatAPI` constructor accepts `paywallId`, `offeringId`, and `presentedOfferingContext` directly, so hybrid SDKs can pass the context through PHC to native without exposing `Offering` on the public params API. - `Purchases.trackCustomPaywallImpression(params)` resolves event data with this fallback chain: | Developer passes | `offering_id` sent | context sent | | --- | --- | --- | | Nothing (or only `paywallId`) | from `cachedOfferings.current` | from current offering | | Deprecated `offeringId` matching cache | passed string | from cached match | | Deprecated `offeringId` not in cache | passed string | nil | | `Offering` object | from offering | derived from offering | | Internal `presentedOfferingContext` | passed `offeringId` | passed context | - `CustomPaywallEvent.Impression.Data` carries flat `placementIdentifier` / `targetingRevision` / `targetingRuleId` fields, mirroring the existing internal `PaywallEvent` Android shape. - The wire payload nests these under `presented_offering_context`. The custom paywall event has its own `BackendEvent.CustomPaywallPresentedOfferingContextData` rather than reusing the internal paywall event's struct, matching the iOS approach where each event owns its own. <!-- CURSOR_SUMMARY --> --- > [!NOTE] > **Medium Risk** > Public API surface changes (new constructors, deprecation) and analytics event schema updates; behavior is localized to custom paywall tracking rather than purchases or auth. > > **Overview** > Custom paywall impression tracking now sends **placement and targeting context** (placement identifier, targeting revision, rule id) on the backend event, nested as `presented_offering_context`, aligning custom paywalls with internal paywall analytics. > > **`CustomPaywallImpressionParams`** adds constructors that take an **`Offering`** (or offering-only) so the SDK can set `offeringId` and `presentedOfferingContext` from the offering’s first package. The **`offeringId` string constructor is deprecated** in favor of `Offering`. Params expose `presentedOfferingContext` but not the offering object itself. > > **`Purchases.trackCustomPaywallImpression`** resolves what gets tracked: explicit context from params, lookup from **cached offerings** when only an offering id or nothing is passed, or values taken directly from a passed **`Offering`**. > > Wire and storage paths extend **`CustomPaywallEvent.Impression.Data`** and **`BackendEvent.CustomPaywall`** with the new fields; **`cachedOfferings`** is exposed on the orchestrator/offerings manager to support resolution. > > <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit ea3369c. Bugbot is set up for automated code reviews on this repo. Configure [here](https://www.cursor.com/dashboard/bugbot).</sup> <!-- /CURSOR_SUMMARY -->
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Include presentedOfferingContext (placementIdentifier, targetingRevision, targetingRuleId) in paywall event serialization so that events & charts can utilize those dimensions.
Checklist
purchases-iosand hybridsMotivation
Description
Note
Medium Risk
Changes the schema of emitted paywall events (new optional field), which could impact backend analytics/parsing if assumptions are strict, though the field is nullable and covered by compatibility tests.
Overview
Paywall backend event payloads now optionally include a nested
presented_offering_contextobject (placement + targeting metadata) so downstream processing can segment paywall events by placement/targeting.Conversion paths (
PaywallEvent.toBackendStoredEventandPaywallStoredEvent.toBackendEvent) populate this new field viaPresentedOfferingContextData.fromContext, which omits the object entirely when no placement/targeting is present, and tests were added to validate request JSON, round-trip serialization, and backward-compatible deserialization of older stored events without the new field.Reviewed by Cursor Bugbot for commit 9e64fda. Bugbot is set up for automated code reviews on this repo. Configure here.