Skip to content

Conversation

tonidero
Copy link
Contributor

@tonidero tonidero commented Aug 18, 2025

Description

This adds some new internal APIs to be used by our hybrid SDKs that allow to pass a PresentedOfferingContext when launching paywalls and passing a specific offering.

  • Actually use presented offering context data

@tonidero tonidero added pr:other pr:force_patch Forces a patch release labels Aug 18, 2025
@tonidero tonidero marked this pull request as ready for review August 19, 2025 06:16
@tonidero tonidero requested review from a team August 19, 2025 06:16
Copy link

codecov bot commented Aug 19, 2025

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 78.38%. Comparing base (cec4b38) to head (2d6fbb4).
⚠️ Report is 1 commits behind head on main.

Additional details and impacted files
@@           Coverage Diff           @@
##             main    #2610   +/-   ##
=======================================
  Coverage   78.38%   78.38%           
=======================================
  Files         301      301           
  Lines       11229    11229           
  Branches     1561     1561           
=======================================
  Hits         8802     8802           
  Misses       1741     1741           
  Partials      686      686           

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

Copy link
Member

@JayShortway JayShortway left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for taking care of this! Just a question on whether we need to introduce the new "OfferingInfo" concept for this.

data class OfferingId(val offeringId: String) : OfferingSelection()
data class OfferingInfo(val offeringInfo: OfferingPresentationInfo) : OfferingSelection()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we need a new class (OfferingPresentationInfo) here? Can we not add the PresentedOfferingContext parameter to OfferingId? Like so:

data class OfferingId(
  val offeringId: String, 
  val presentedOfferingContext: PresentedOfferingContext?,
) : OfferingSelection()

Reasoning also being that it's not super clear (at first glance) how setOfferingInfo() is different from setOffering(). That could also remain setOfferingId() in this case:

internal fun setOfferingId(
  offeringId: String?,
  presentedOfferingContext: PresentedOfferingContext?,
)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah... we could add it as a new parameter, the main reason I joined them into a new class was because both types of data should move together through all the plumbing, and creating a new entity ensured that better than separating into different parameters and moving both around. That could lead to easily forgetting passing/setting one of them IMO. It's true it's a bit confusing with the other setOffering APIs though... not sure if we could come up with a better naming...

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What about IdAndPresentedOfferingContext? 😅 (It's already namespaced to OfferingSelection, so maybe we can get away with omitting "Offering" in the name.)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm it's a bit of a longer name... certainly more explicit, but not sure if we win much that much clarity TBH... But I guess it could improve on the readability for the setOfferingId method... So will do the rename.

@@ -86,6 +94,15 @@ class PaywallActivityLauncher(resultCaller: ActivityResultCaller, resultHandler:
* @param edgeToEdge Whether to display the paywall in edge-to-edge mode.
* Default is true for Android 15+, false otherwise.
*/
@Deprecated(
message = "Use launch with presented offering context instead",
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is the PresentedOfferingContext always available? Should we default to null?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For an offering, it should always be available yes. I had to make it nullable for backwards compatibility reasons, but otherwise, it would be good to assume that it should always be there.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, but what is the purpose of the "offering ID" API, if devs will always need to have a full Offering (because they now need the PresentedOfferingContext)? That makes the "offering ID" API obsolete no, or am I misunderstanding something?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The main reason is that it's tricky to create a full Offering native object from the hybrids, since it has a lot of data and even some non-parcelable objects like the Store's product entities. So we can't easily recreate the offerings from the data in the hybrids, that's the reason we were only passing the offering id.

This PR adds the data we were missing for proper traceability purposes. Otherwise, any placement/targeting data would be lost.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right got it, but this is a public API too. Before this PR, it had its utility. You could launch a paywall with just an Offering ID, without having to get Offerings first. But now the utility of this public API is gone, as devs will need to get Offerings to be able to pass a PresentedOfferingContext.

I was gonna suggest:

  1. we deprecate launch(offeringIdentifier: String) in favor of launch(offering: Offering) instead, and
  2. we make launch(offeringIdentifier: String, presentedOfferingContext: PresentedOfferingContext) an @InternalRevenueCatAPI.

But I see you already did 2 😄 So in that case I think we should still do 1, as the current deprecation message encourages the use of an internal API.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right... our docs did say not to use this method, but yeah, we probably should have made it internal from the beginning... In any case, I do agre that the ReplaceWith should point to the method taking an offering instead of the internal API. Good catch!

@tonidero
Copy link
Contributor Author

In this PR I wasn't using the newly provided presented offering context yet. So I added that in a separate PR: #2612

@tonidero tonidero requested a review from JayShortway August 19, 2025 15:07
Copy link
Member

@JayShortway JayShortway left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good! Just the OriginalTemplatePaywallFooterView deprecation message mostly.

this.offeringSelection = offeringId?.let { OfferingSelection.OfferingId(it) }
?: OfferingSelection.None
internal fun setOfferingIdAndPresentedOfferingContext(
offeringInfo: OfferingSelection.IdAndPresentedOfferingContext?,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit:

Suggested change
offeringInfo: OfferingSelection.IdAndPresentedOfferingContext?,
idAndPresentedOfferingContext: OfferingSelection.IdAndPresentedOfferingContext?,

Comment on lines 135 to 140
@Deprecated(
"Use setOfferingInfo instead.",
ReplaceWith(
"setOfferingInfo(offeringId, presentedOfferingContext)",
),
)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

setOfferingInfo does not exist anymore, and setOfferingIdAndPresentedOfferingContext is internal. Should we add a setOffering function here? If not, we should remove the ReplaceWith from the deprecation message.

Comment on lines +163 to +164
// WIP: We do not support presentedOfferingContext when using the view in XML layouts.
presentedOfferingContext = null,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not for this PR, just thinking out loud: what if we support passing a placement in XML layouts?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah we could... TBH, I'm not sure how many people are using paywalls through XMLs... but it's certainly something we could add if needed.

Comment on lines +113 to +114
@JvmOverloads
fun setOfferingId(offeringId: String?, presentedOfferingContext: PresentedOfferingContext? = null) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we need a setOffering(Offering) too?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe we needed this for RN if I remember correctly, that required us to set the offering AFTER actually creating the paywall, which was pretty convoluted. We didn't need the setOffering API until now, and in general, I think it's better to just pass it in the constructor.

@tonidero tonidero enabled auto-merge August 20, 2025 07:41
@tonidero tonidero added this pull request to the merge queue Aug 20, 2025
Merged via the queue into main with commit 0c1aaa4 Aug 20, 2025
20 checks passed
@tonidero tonidero deleted the add-hybrid-apis-set-presented-offering-context branch August 20, 2025 08:15
github-merge-queue bot pushed a commit that referenced this pull request Aug 20, 2025
### Description
Based on #2610 

This actually makes use of the presented offering context to pass a
modified offering to the paywall. This data will be sent as part of the
post receipt when a purchase is made.
This was referenced Aug 20, 2025
tonidero pushed a commit that referenced this pull request Aug 21, 2025
**This is an automatic release.**

## RevenueCat SDK
### 🐞 Bugfixes
* Use `Block store` to backup anonymous user ids across installations
(#2595) via Toni Rico (@tonidero)

## RevenueCatUI SDK
### Paywallv2
#### 🐞 Bugfixes
* Fixes price formatting discrepancies on Paywalls for `{{
product.price_per_[day|week|month|year] }}` (#2604) via JayShortway
(@JayShortway)

### 🔄 Other Changes
* Revert dokka 2 and gradle 9 update (#2618) via Toni Rico (@tonidero)
* Introduce runtime annotations library and add stability annotations
for increasing UI performances (#2608) via Jaewoong Eum (@skydoves)
* Override presented offering context paywalls without offering (#2612)
via Toni Rico (@tonidero)
* Add APIs for hybrid SDKs to set presentedOfferingContext (#2610) via
Toni Rico (@tonidero)
* Bump Baseline Profiles to 1.4.0 and update profiles (#2611) via
Jaewoong Eum (@skydoves)
* Migrate deprecated kotlinOptions to compilerOptions (#2607) via
Jaewoong Eum (@skydoves)
* [AUTOMATIC][Paywalls V2] Updates paywall-preview-resources submodule
(#2613) via RevenueCat Git Bot (@RCGitBot)
* Migrate amazon & debugview modules to KTS (#2327) via Jaewoong Eum
(@skydoves)
* Update to Dokka 2.0.0 (#2609) via Toni Rico (@tonidero)
* Add log when restoring purchases finds no purchases with some
troubleshooting (#2599) via Toni Rico (@tonidero)

Co-authored-by: revenuecat-ops <ops@revenuecat.com>
tonidero added a commit that referenced this pull request Aug 25, 2025
This adds some new internal APIs to be used by our hybrid SDKs that
allow to pass a `PresentedOfferingContext` when launching paywalls and
passing a specific offering.

- [ ] Actually use presented offering context data
tonidero added a commit that referenced this pull request Aug 25, 2025
Based on #2610

This actually makes use of the presented offering context to pass a
modified offering to the paywall. This data will be sent as part of the
post receipt when a purchase is made.
@tonidero tonidero mentioned this pull request Aug 25, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
pr:force_patch Forces a patch release pr:other
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants