Skip to content

fix: url encode query prameters#3451

Merged
JZDesign merged 3 commits into
mainfrom
jzdesign/url-encode-for-paywalls2
May 7, 2026
Merged

fix: url encode query prameters#3451
JZDesign merged 3 commits into
mainfrom
jzdesign/url-encode-for-paywalls2

Conversation

@JZDesign

@JZDesign JZDesign commented May 7, 2026

Copy link
Copy Markdown
Contributor

Checklist

  • If applicable, unit tests
  • If applicable, create follow-up issues for purchases-ios and hybrids

Motivation

URL query parameters with spaces caused crashes

Description

url encode query parameters to prevent crashes


Note

Medium Risk
Changes how web checkout/custom URLs are constructed by encoding query components and inserting parameters before fragments, which could subtly affect downstream URL parsing and expectations in integrations.

Overview
Fixes URL construction when appending query params by URL-encoding parameter names/values and ensuring new params are inserted before any #fragment in URI.appendQueryParameter.

URL.appendQueryParameter is simplified to delegate through the URI implementation, and tests are updated/added to assert encoding behavior (including whitespace) and fragment handling; web-checkout URL expectations now reflect encoded values (e.g. %24rc_monthly).

Reviewed by Cursor Bugbot for commit a4a604b. Bugbot is set up for automated code reviews on this repo. Configure here.

@JZDesign JZDesign requested a review from a team as a code owner May 7, 2026 14:22
@JZDesign JZDesign added the pr:fix A bug fix label May 7, 2026
@JZDesign JZDesign requested review from tonidero and vegaro May 7, 2026 14:43
}

private fun String.encodeQueryParameterComponent(): String =
URLEncoder.encode(this, StandardCharsets.UTF_8.name()).replace("+", "%20")

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Just curious, why was the manual .replace("+", "%20") added? A + is allowed in URLs afaik.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

This was a GPT 5.5 change. The reasoning made sense to me. It is:

URLEncoder is for application/x-www-form-urlencoded, where spaces become +. In a URI query string, + is legal but not universally equivalent to a space unless the receiver applies form decoding. %20 is the safer URI encoding and avoids ambiguity.

This replacement is safe for literal plus signs too: URLEncoder turns + into %2B before our .replace("+", "%20"), so:

"A+B C" -> "A%2BB%20C"

So the current .replace("+", "%20") is intentional.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

but @tonidero had good feedback and I changed this

@codecov

codecov Bot commented May 7, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 79.48%. Comparing base (17eff89) to head (a4a604b).

Additional details and impacted files
@@           Coverage Diff           @@
##             main    #3451   +/-   ##
=======================================
  Coverage   79.48%   79.48%           
=======================================
  Files         363      363           
  Lines       14562    14562           
  Branches     1979     1979           
=======================================
  Hits        11574    11574           
  Misses       2190     2190           
  Partials      798      798           

☔ 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.

@tonidero tonidero left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Just some thoughts... But not blockers I would say. Thanks for fixing this!

return URI("$this$separator$name=$value")
val encodedParameter = "${name.encodeQueryParameterComponent()}=${value.encodeQueryParameterComponent()}"
val uriString = toString()
val fragmentIndex = uriString.indexOf('#')

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Hmm I'm thinking, do we support fragment in these urls? Just wondering if this is needed at all... If we have it in iOS though, I think it might make sense to support it. Otherwise, might be better to reduce complexity and leave it out? NABD.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I believe this to be how URLComponents handles things on iOS actually

return URL("$this$separator$name=$value")
}
internal fun URL.appendQueryParameter(name: String, value: String): URL =
toURI().appendQueryParameter(name, value).toURL()

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Good to avoid repeating the logic 👍 Thanks!

@JZDesign JZDesign enabled auto-merge May 7, 2026 15:30
@JZDesign JZDesign disabled auto-merge May 7, 2026 15:34
@JZDesign JZDesign enabled auto-merge May 7, 2026 15:34

@cursor cursor Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, have a team admin enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit a4a604b. Configure here.

assertEquals(updatedUri.toString(), "https://example.com/path?key=value")
}

@Test

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Test missing @RunWith for Android framework dependency

Medium Severity

URIExtensionsTest now exercises appendQueryParameter which calls android.net.Uri.encode(), but the test class lacks @RunWith(AndroidJUnit4::class). Every other test in this project that uses android.net.Uri includes this annotation to activate Robolectric. Without it, Uri.encode() may throw a stub RuntimeException depending on the test runner configuration, causing all tests in this file to fail.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit a4a604b. Configure here.

@JZDesign JZDesign added this pull request to the merge queue May 7, 2026
Merged via the queue into main with commit 25f3d85 May 7, 2026
38 checks passed
@JZDesign JZDesign deleted the jzdesign/url-encode-for-paywalls2 branch May 7, 2026 16:14
matteinn pushed a commit to matteinn/purchases-android that referenced this pull request Jun 5, 2026
**This is an automatic release.**

## RevenueCat SDK
### 🐞 Bugfixes
* fix: url encode query prameters (RevenueCat#3451) via Jacob Rakidzich
(@JZDesign)

## RevenueCatUI SDK
### 🐞 Bugfixes
* Fix: dismiss was called before onPurchaseComplete callback invocation
(RevenueCat#3353) via Jacob Rakidzich (@JZDesign)
* Propagate default package across workflow steps (RevenueCat#3431) via Cesar de
la Vega (@vegaro)
### Paywallv2
#### ✨ New Features
* feat: Allow disabling of automatic font scaling (RevenueCat#3438) via Jacob
Rakidzich (@JZDesign)

### 🔄 Other Changes
* Extract `PaywallComponentsImagePreDownloader` (RevenueCat#3448) via Cesar de la
Vega (@vegaro)
* Simplify `WorkflowTransitionState` with explicit from/to step fields
(RevenueCat#3441) via Cesar de la Vega (@vegaro)

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Low Risk**
> Low risk release bookkeeping: primarily flips version strings from
`10.5.0-SNAPSHOT` to `10.5.0` and updates docs/changelogs, with no
functional code changes beyond the reported version constant.
> 
> **Overview**
> Cuts the `10.5.0` release by switching the project from
`10.5.0-SNAPSHOT` to `10.5.0` across build metadata (`.version`,
`gradle.properties`, sample/test app `libs.versions.toml`, and
`Config.frameworkVersion`).
> 
> Updates release artifacts and documentation pointers: CircleCI docs
deploy now syncs the `10.5.0` docs folder to S3, `docs/index.html`
redirects to `10.5.0`, and changelogs are rolled forward with the
`10.5.0` entries in `CHANGELOG.md`/`CHANGELOG.latest.md`.
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
48537d6. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

pr:fix A bug fix

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants