Skip to content

Ramps deep link entry and affiliate provider priority - #6141

Open
j0ntz wants to merge 7 commits into
developfrom
jon/ramps-deeplink-provider-priority
Open

Ramps deep link entry and affiliate provider priority#6141
j0ntz wants to merge 7 commits into
developfrom
jon/ramps-deeplink-provider-priority

Conversation

@j0ntz

@j0ntz j0ntz commented Aug 6, 2026

Copy link
Copy Markdown
Contributor

Technical Design Document

ramps-deeplink-provider-priority.md

Description

Gives the ramps buy/sell flow a deep link entry point and provider priority.

Part A, link-scoped flow access. New edge://buy[/<providerId>[/<paymentType>]] and edge://sell[...] links (plus the https://deep.edge.app/... form) open RampCreateScene with an optional provider and payment type pinned to the top of the quote results. The pin lives in navigation params only: no saveAccountReferral call, and ?af= attribution keeps its existing independent behavior. It is scoped to the flow the link opened, so stepping to the option list and back keeps it while leaving the tab clears it. A pin that matches no quote logs and falls back to the normal ordering, which is what /buy/moonpay/cashapp does while the info server rampQuoteFilter disables Cash App. An unrecognized payment type in a link is dropped rather than failing the link, so a stale partner URL still opens the flow.

Part B, affiliate provider priority. useRampPreferredProviders reads the existing promoCards2 pluginPromotions.preferProviders through filterInfoCards (not getDisplayInfoCards), matched against accountReferral. Reading the ungated path is what makes silent priority config work: a card with localeMessages: {} and no ctaButton never renders in the carousel but still carries its behavior payload. No info server changes.

Both inputs feed one comparator (compareRampQuotes) shared by useRampQuotes and RampSelectOptionScene, so the payment-type group order and the provider order inside each group agree. Anything that makes a claim about the RATE stays out of that ordering: the select scene's "Best Rate" badge and the create scene's exchange rate (which also feeds the light-account purchase limit) both go through getBestRateRampQuote, so a pinned link changes which option the user lands on first, never what the app reports as the market rate.

Two ordering rules follow from that and are worth calling out. The "Best Rate" badge is scoped to the PAYMENT TYPE, not to the provider a card happens to display: a card is badged when the best quote lives anywhere in its provider group. Testing the displayed quote instead drops the badge from the entire list under any provider pin, because the pin moves the best quote out of index 0 in every group at once. And quotes that HAVE amounts outrank both preferences, so a preferred external provider's '0' placeholder (createExternalRampPlugin) cannot displace a priced quote or float its group up the list.

The design doc above carries both rules with worked examples: section 5.6 for the badge, decision 10.7 for the has-amounts tier.

The design doc above carries the paste-ready CouchDB promoCards2 blocks and the partner link instructions.

Asana: https://app.asana.com/0/1215088146871429/1217224633446931

CHANGELOG

Does this branch warrant an entry to the CHANGELOG?

  • Yes
  • No

Dependencies

none

Requirements

If you have made any visual changes to the GUI. Make sure you have:

  • Tested on iOS device
  • Tested on Android device
  • Tested on small-screen device (iPod Touch)
  • Tested on large-screen device (tablet)

Note

Medium Risk
Touches fiat on-ramp navigation and quote presentation (ordering vs. rate claims); behavior is mostly reordering with graceful fallbacks, but partners and marketing will rely on the new URLs and promo-driven priority.

Overview
Adds edge://buy / edge://sell (and https://deep.edge.app/...) deep links that open the ramps buy/sell flow, with optional path segments to pin a provider and payment type to the top of quote results for that visit only. Pins travel through navigation params (not referral state), forward into the option list, and clear when the user leaves the buy/sell tab; unknown payment types are ignored so partner URLs still open the flow.

Introduces shared quote ordering via compareRampQuotes / rampQuotePriority, fed by deep-link pins and by useRampPreferredProviders (info server promoCards2 preferProviders via filterInfoCards for silent affiliate priority). useRampQuotes and RampSelectOptionScene use that ordering; exchange rate and “Best Rate” still use getBestRateRampQuote so pinned UI does not misstate market rates.

Includes deep-link tests, rampQuotePriority unit tests, changelog entries, and a design doc for partners and CouchDB config.

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

@j0ntz

j0ntz commented Aug 6, 2026

Copy link
Copy Markdown
Contributor Author

📸 Test evidence

buy deeplink cold start

buy deeplink cold start

venmo pinned first

venmo pinned first

best rate badge on true best

best rate badge on true best

cashapp graceful fallback

cashapp graceful fallback

affiliate priority paybis first

affiliate priority paybis first

unaffiliated ordering unchanged

unaffiliated ordering unchanged

Captured by the agent's in-app test run (build-and-test).

@j0ntz
j0ntz marked this pull request as ready for review August 6, 2026 01:23
@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.

Comment thread src/components/scenes/RampCreateScene.tsx
Comment thread src/components/scenes/RampSelectOptionScene.tsx
@j0ntz
j0ntz force-pushed the jon/ramps-deeplink-provider-priority branch from 48dd57c to 5574137 Compare August 6, 2026 01:39
@j0ntz

j0ntz commented Aug 6, 2026

Copy link
Copy Markdown
Contributor Author

📸 Test evidence (after fix)

create scene shows true best rate

create scene shows true best rate

venmo still pinned after fix

venmo still pinned after fix

Captured by the agent's in-app test run (build-and-test).

Comment thread src/actions/DeepLinkingActions.tsx
@j0ntz
j0ntz force-pushed the jon/ramps-deeplink-provider-priority branch from 5574137 to 0da646a Compare August 6, 2026 01:58
@j0ntz

j0ntz commented Aug 6, 2026

Copy link
Copy Markdown
Contributor Author

📸 Test evidence (pin no longer outlives the visit)

pin cleared on later visit

pin cleared on later visit

Captured by the agent's in-app test run (build-and-test).

Comment thread src/components/scenes/RampCreateScene.tsx Outdated
@j0ntz
j0ntz force-pushed the jon/ramps-deeplink-provider-priority branch from 0da646a to 8ab1b49 Compare August 6, 2026 02:42
@j0ntz

j0ntz commented Aug 6, 2026

Copy link
Copy Markdown
Contributor Author

📸 Test evidence (pin lifetime)

pin kept on in flow back

pin kept on in flow back

pin cleared after leaving tab

pin cleared after leaving tab

Captured by the agent's in-app test run (build-and-test).

Comment thread src/actions/DeepLinkingActions.tsx
j0ntz added 2 commits August 5, 2026 19:51
Convert RampCreateBuyScene/RampCreateSellScene to the React.FC form and
replace the deprecated getReceiveAddress call with getAddresses. Mark the
remaining NavigationBase deprecation as deferred v7 migration work.
Parses edge://buy[/<providerId>[/<paymentType>]] and the sell equivalent
into a new rampCreate link, along with the https://deep.edge.app form. An
unrecognized payment type is dropped rather than failing the whole link, so
a stale partner URL still opens the flow.
@j0ntz
j0ntz force-pushed the jon/ramps-deeplink-provider-priority branch from 8ab1b49 to e271ded Compare August 6, 2026 02:53
Comment thread src/hooks/useRampPreferredProviders.ts Outdated
@j0ntz
j0ntz force-pushed the jon/ramps-deeplink-provider-priority branch 2 times, most recently from a945a9e to 070dead Compare August 6, 2026 19:22

@cursor cursor Bot left a comment

Copy link
Copy Markdown

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 using high effort and found 1 potential issue.

Fix All in Cursor

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

Reviewed by Cursor Bugbot for commit 070dead. Configure here.

Comment thread src/components/scenes/RampCreateScene.tsx
Adds a shared quote comparator that floats preferred providers and a
preferred payment type above the best-rate ordering, used by both the ramp
create scene and the select option scene so the payment type groups and the
providers inside them follow the same preference. A preference that matches
no quote logs and leaves the ordering untouched.
@j0ntz
j0ntz force-pushed the jon/ramps-deeplink-provider-priority branch from 070dead to 1e83206 Compare August 6, 2026 19:49
@j0ntz

j0ntz commented Aug 6, 2026

Copy link
Copy Markdown
Contributor Author

📸🪓 Test evidence (after fix): option-list pin now clears on tab leave

🪓 Hack-forced evidence: the fix commit was stashed to capture the pre-fix frame; restored before committing. Temporary uncommitted edit, reverted before commit; the marked frames prove the rendering, not the trigger.

pin active after fix

pin active after fix

pin cleared after tab leave

pin cleared after tab leave

🪓 HACK-FORCED: pre fix pin survives tab leave

🪓 HACK-FORCED: pre fix pin survives tab leave

tdd mermaid renders on github

tdd mermaid renders on github

Captured by the agent's in-app test run (build-and-test).

@j0ntz
j0ntz force-pushed the jon/ramps-deeplink-provider-priority branch 2 times, most recently from d841b28 to 3df6818 Compare August 7, 2026 21:36
@j0ntz

j0ntz commented Aug 7, 2026

Copy link
Copy Markdown
Contributor Author

📸 Test evidence: followup test-gap closure (promo priority, expiry, af attribution, warm start, sell)

control no promo order

control no promo order

promo code activated

promo code activated

paybis first via activepromotions

paybis first via activepromotions

future enddate pin applies

future enddate pin applies

expired card reverts order

expired card reverts order

expired card prunes activepromotions

expired card prunes activepromotions

af attribution moonpay first

af attribution moonpay first

visible promo card cta

visible promo card cta

cta warm pin venmo first

cta warm pin venmo first

warm openurl delivery

warm openurl delivery

no af no referral write

no af no referral write

sell moonpay pinned

sell moonpay pinned

Captured by the agent's in-app test run (build-and-test).

@j0ntz

j0ntz commented Aug 7, 2026

Copy link
Copy Markdown
Contributor Author

Non-visual evidence from the same run, to go with the screenshots above.

Section 6 JSON blocks against the real cleaners. Both blocks were extracted from the committed doc and run through edge-info-server's asInfoCard and asPromoCards2 (asHealingArray(asInfoCard)) in node, against edge-info-server@3.12.0 from the gui's own node_modules (byte-identical asInfoCard to the repo checkout):

PASS  asInfoCard  6.1 visible card with a deep link CTA
      promoId="moonpay" pluginPromotions=[{"pluginType":"buy","preferProviders":["moonpay"]}]
      round-trip stable: yes
PASS  asInfoCard  6.2 silent priority card
      promoId="moonpay" pluginPromotions=[{"pluginType":"buy",...},{"pluginType":"sell",...}]
      localeMessages keys=[] ctaButton=undefined
      round-trip stable: yes
PASS  asPromoCards2 (asHealingArray): 2 in -> 2 out (0 silently dropped required)
PASS  negative control: 3 in (1 missing "background") -> 2 out (drop expected)

The negative control matters because the healing array drops a malformed card silently; without it a green result proves nothing.

AC2, now runtime rather than code reading. A temporary uncommitted marker inside saveAccountReferral wrote one line per call into the app container, and CreationReason.json was hashed before and after:

Launch saveAccountReferral calls installerId / activePromotions CreationReason.json
baseline, no deep link 1 undefined / [] unchanged
edge://buy/moonpay/venmo, driven to the option list 1 undefined / [] unchanged (same sha1)
https://deep.edge.app/buy/moonpay/cashapp?af=moonpay 1 undefined / ["moonpay"] unchanged

So the pin path adds no referral write at all, while the af form does exactly one, and it lands in activePromotions rather than installerId because the referral server 404s the code. Both hacks were reverted; git status is clean of them.

One caveat worth recording, not a defect in this PR. The "Best Rate" badge is attached to getBestRateRampQuote(allQuotes), while each card renders its payment-type group's first quote. When the pinned provider is not the best-rate provider, the best-rate quote stops leading any group and the badge renders nowhere rather than moving. Visible in the Paybis-pinned screenshot. The design doc's testing section now says so.

Testing was done on a throwaway account rather than edge-funds for everything that writes account-synced state (activePromotions, attribution), so no shared roster account was dirtied. The sell drive used edge-funds because it needs a balance.

@j0ntz
j0ntz force-pushed the jon/ramps-deeplink-provider-priority branch from 3df6818 to c69d8cc Compare August 7, 2026 21:54

@claude claude Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Claude Code Review

Claude Code Review is paused for this repository. To reconnect it, an admin of this repository's GitHub organization (or the account owner, for personal repositories) who can also manage your Claude organization's Code Review settings needs to re-link GitHub in Code Review settings. This is a one-time step.

Tip: disable this comment in your organization's Code Review settings.

@j0ntz
j0ntz force-pushed the jon/ramps-deeplink-provider-priority branch from c69d8cc to 2744bfe Compare August 7, 2026 22:18
@j0ntz
j0ntz force-pushed the jon/ramps-deeplink-provider-priority branch from 2744bfe to 90ec73b Compare August 7, 2026 22:48

@peachbits peachbits 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.

Two notes on the ordering precedence in compareRampQuotes (the group-sort comment and the amounts-tier comment below are the same root cause: the priority ranks are compared before everything else), plus one nit on the debug-log effect.

Comment thread src/components/scenes/RampSelectOptionScene.tsx
Comment thread src/plugins/ramps/utils/rampQuotePriority.ts
Comment thread src/components/scenes/RampSelectOptionScene.tsx Outdated
Comment thread src/components/scenes/RampCreateScene.tsx
j0ntz added 4 commits August 7, 2026 18:30
Navigates to the buy or sell tab and threads the optional provider and
payment type through as link-scoped pins.
Documents the link format, the ordering rules, the promoCards2 setup blocks
for the info server document, and the partner link instructions.
@j0ntz
j0ntz force-pushed the jon/ramps-deeplink-provider-priority branch from 90ec73b to 0499f75 Compare August 8, 2026 01:57
@j0ntz

j0ntz commented Aug 8, 2026

Copy link
Copy Markdown
Contributor Author

📸🪓 Test evidence (review fixes)

🪓 Hack-forced evidence: temporarily filtered ACH quotes out of RampSelectOptionScene so the globally-best quote landed inside a multi-provider group (live quotes put it in the single-provider ACH group, where a pin cannot displace it); frame 41 additionally restores the pre-fix isBestOption rule. Temporary uncommitted edit, reverted before commit; the marked frames prove the rendering, not the trigger.

unpinned control badge on ach

unpinned control badge on ach

🪓 HACK-FORCED: before fix no badge anywhere

🪓 HACK-FORCED: before fix no badge anywhere

🪓 HACK-FORCED: after fix badge on group holding best

🪓 HACK-FORCED: after fix badge on group holding best

paybis pinned badge on ach

paybis pinned badge on ach

pin cleared after tab leave

pin cleared after tab leave

Captured by the agent's in-app test run (build-and-test).

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.

2 participants