Skip to content

fix(deeplink): keep URL parameters on a direct open - #9

Merged
onamfc merged 1 commit into
mainfrom
brandon/sit-429-merge-url-params
Sep 10, 2026
Merged

fix(deeplink): keep URL parameters on a direct open#9
onamfc merged 1 commit into
mainfrom
brandon/sit-429-merge-url-params

Conversation

@onamfc

@onamfc onamfc commented Sep 10, 2026

Copy link
Copy Markdown
Member

Summary

A link shared as ?slug=titanic reached the app after a deferred install but not on a direct open. This fixes that, so customParameters carries URL parameters on both paths.

Fifth and last of five, completing SIT-429: React Native LinkForty/mobile-sdk-react-native#7, Expo LinkForty/mobile-sdk-expo#6, iOS LinkForty/mobile-sdk-ios#6, Android LinkForty/mobile-sdk-android#5.

What was happening

_resolveUrl(uri, fallback: localData) returned the server payload on success and used the local parse only as a failure fallback — so a successful resolve discarded the parsed URL parameters entirely.

That payload is the link's stored configuration. The server cannot know what was appended to the URL that was actually tapped; only the SDK does, having just parsed it.

The value was never lost from the callback — the Uri is still delivered — but it was absent from the field the deferred path teaches people to read, so the two open types disagreed.

The rules it follows

  • URL values win on a collision — the precedence the server already applies on the deferred path.
  • mergingUrlParameters returns a copy touching customParameters alone. linkId, deepLinkPath, appScheme, the store URLs and utmParameters remain server truth.
  • The resolve-failure fallback is unchanged.

A live leak this also closes

extractCustomParameters excluded the five UTM keys by exact match but not fp_ or lf_click.

Those are LinkForty plumbing: fp_* are fingerprint signals the redirect reads server-side for attribution, lf_click is the id the redirect appends to a destination URL for downstream analytics. Neither is the customer's data.

Not hypothetical — the resolve-failure path already returns the local parse directly, so an app hitting it today can receive fp_tz and lf_click inside customParameters as though the customer had set them.

Matching is now by prefix, so any utm_ or fp_ name is covered whatever the suffix; the previous exact-match set would have missed utm_id. Tests pin the near-misses (utmost, fps, lf_clicks) as not reserved.

Verification

  • dart analyze libno issues found
  • flutter test109 tests, all passing
  • 8 new tests: reserved names covered, case-insensitive matching, near-misses excluded, custom extraction filtering, merging into an empty payload, URL overriding a configured key, no-op on null and empty, and server-only fields never overwritten
  • Every pre-existing test passes unchanged

Worth noting across all five

All five SDKs had the same fp_/lf_click gap. Four independent codebases in four languages sharing one defect is a design signal, not four coincidences: each parser was written to exclude "UTM" rather than "everything LinkForty reserves". The server-side extractor had it right; the SDKs each re-derived a narrower rule.

Part of SIT-429 — the last one.

A link shared as ?slug=titanic delivered that value to the app after a
deferred install but not when the app was already installed. _resolveUrl
returned the server payload and dropped the local parse — and that payload
is the link's stored configuration, which cannot know what was appended to
the URL that was tapped.

customParameters now carries both, with URL values winning on a collision:
the precedence the server already applies on the deferred path.

mergingUrlParameters returns a copy touching customParameters alone.
linkId, deepLinkPath, appScheme, the store URLs and utmParameters stay
server truth; a local parse cannot know them.

Also aligns the reserved-name filter with the server's. It excluded the
five UTM keys by exact match but not fp_ or lf_click, so fingerprint
signals and the click-correlation id could reach an app as if the customer
had set them — already possible on the resolve-failure path, which returns
the local parse directly. Matching is now by prefix, so any utm_ or fp_
name is covered whatever the suffix.

Completes the same change across all five SDKs.
@onamfc
onamfc merged commit 5f68a33 into main Sep 10, 2026
@onamfc
onamfc deleted the brandon/sit-429-merge-url-params branch September 10, 2026 05:59
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant