Skip to content

Fix MoonPay "Send with Edge" sell link#6056

Merged
j0ntz merged 1 commit into
developfrom
jon/moonpay-payment-deeplink
Jul 14, 2026
Merged

Fix MoonPay "Send with Edge" sell link#6056
j0ntz merged 1 commit into
developfrom
jon/moonpay-payment-deeplink

Conversation

@j0ntz

@j0ntz j0ntz commented Jun 24, 2026

Copy link
Copy Markdown
Contributor

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)

Description

Asana task

Asana: https://app.asana.com/1/9976422036640/project/1207384676342554/task/1209649066660320

Fixes the MoonPay "Send with Edge" sell link. When a user starts a MoonPay sell order but has not completed the crypto deposit, MoonPay shows a "complete payment" button (in their email and in the buy.moonpay.com/trade_history page) that reflects back the deposit redirect URL our app hands MoonPay. That button dead-ended on a static "Redirecting to Payment..." page, so the user could not finish the deposit (the reported lead case is an XRP sell that also needs a destination tag).

Root cause (regression analysis). The redirect URL we pass to MoonPay is RETURN_URL_PAYMENT in src/plugins/gui/providers/common.ts. It has been https://edge.app/redirect/payment/ since it was introduced (Oct 2023); it was never a deep.edge.app value, so this is not a value regression. The apex edge.app host is not claimed as a universal link on iOS (ios/edge/edge.entitlements) or Android (AndroidManifest.xml) - only deep.edge.app, dl.edge.app, and return.edge.app are. The constant was designed as an in-app webview interception sentinel (uri.startsWith(RETURN_URL_PAYMENT)), which works in-app but cannot open the app from an external click. The buy redirects already use deep.edge.app; only the sell payment redirect used the unclaimed apex.

Fix.

  • Point RETURN_URL_PAYMENT at the claimed deep.edge.app host, matching the buy redirects. In-app interception is host-agnostic (prefix startsWith), so the existing webview flows (MoonPay and Paybis, ramp and legacy) are unaffected.
  • Parse the redirect/payment deep link into a new paymentRedirect link type and route it to the Send scene, resolving the wallet from the base currency code and pre-filling the deposit address, amount, and destination tag/memo (via uniqueIdentifier). Both the deep.edge.app and legacy edge.app hosts are parsed.

No new user-facing strings; reuses the existing Send scene and wallet picker.

Testing.

  • Unit tests for the parser (real-world MoonPay URL, XRP with destination tag, deep.edge.app host, and missing-param browser fallback).
  • verify-repo.sh (eslint + jest) passes; tsc clean.
  • Driven on the iOS simulator: firing edge://redirect/payment/?baseCurrencyCode=btc&baseCurrencyAmount=0.001&depositWalletAddress=bc1q... opens the wallet picker (all BTC wallets) and then the Send scene pre-filled with the deposit address and 0.001 BTC. Screenshots below. The slider is not confirmed (no broadcast).

Note

Medium Risk
External deep links now drive the Send flow with provider-supplied addresses and amounts; guards reduce bad pre-fills, but users still must confirm the transaction and ticker collisions rely on wallet picker disambiguation.

Overview
Fixes MoonPay "Send with Edge" (and similar ramp sell flows) when users tap complete payment links outside the app—those links used to hit an unclaimed edge.app host and dead-end instead of opening Edge.

Redirect URLs for payment, success, fail, and cancel now use the claimed deep.edge.app host (matching buy flows). isReturnUrl centralizes WebView interception so both deep.edge.app and legacy edge.app URLs still work for in-flight orders.

Deep linking: /redirect/payment/ parses to a new paymentRedirect type with deposit address, optional amount, and memo/tag; invalid or blank required params become a no-op instead of errors or browser fallback. success / fail / cancel open the app as no-ops. Handling resolves assets by currency symbol (native + tokens, including custom), runs the wallet picker when needed, converts amounts safely, then opens Send pre-filled via the existing URI path.

Unit tests cover parsing, return-URL matching, and edge cases (XRP tag, bad amounts, missing params).

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

@j0ntz

j0ntz commented Jun 24, 2026

Copy link
Copy Markdown
Contributor Author

📸 Test evidence (iOS sim)

agent proof 1209649066660320 01 wallet picker

agent proof 1209649066660320 01 wallet picker

agent proof 1209649066660320 02 send prefilled

agent proof 1209649066660320 02 send prefilled

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

Comment thread src/util/DeepLinkParser.ts
Comment thread src/actions/DeepLinkingActions.tsx Outdated
@j0ntz j0ntz force-pushed the jon/moonpay-payment-deeplink branch from 676c146 to 961a3fb Compare June 24, 2026 23:35
@j0ntz j0ntz force-pushed the jon/moonpay-payment-deeplink branch from 961a3fb to bf4d1ea Compare July 9, 2026 21:31
Comment thread src/plugins/gui/providers/common.ts Outdated
@j0ntz j0ntz force-pushed the jon/moonpay-payment-deeplink branch from bf4d1ea to d2676b2 Compare July 9, 2026 21:42

@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/moonpay-payment-deeplink branch from d2676b2 to 88e8509 Compare July 9, 2026 21:49
Comment thread src/actions/DeepLinkingActions.tsx Outdated
Comment thread src/util/DeepLinkParser.ts Outdated
Comment thread src/actions/DeepLinkingActions.tsx
Comment thread src/actions/DeepLinkingActions.tsx
Comment thread src/util/DeepLinkParser.ts Outdated
Comment thread src/util/DeepLinkParser.ts
Comment thread src/actions/DeepLinkingActions.tsx
Comment thread src/plugins/gui/providers/common.ts Outdated
@j0ntz j0ntz force-pushed the jon/moonpay-payment-deeplink branch from 88e8509 to 2b9c97f Compare July 10, 2026 19:28
Comment thread src/util/DeepLinkParser.ts Outdated
@j0ntz j0ntz force-pushed the jon/moonpay-payment-deeplink branch from 2b9c97f to a654892 Compare July 10, 2026 19:48
Comment thread src/util/DeepLinkParser.ts
@j0ntz j0ntz force-pushed the jon/moonpay-payment-deeplink branch from a654892 to fd3bad0 Compare July 10, 2026 19:54
@j0ntz

j0ntz commented Jul 10, 2026

Copy link
Copy Markdown
Contributor Author

📸 Test evidence — payment-redirect deep link drives a populated Send scene (on-sim, slot UDID 15772779), plus cancel redirect no-ops with no error

agent proof 1209649066660320 01 payment send scene populated

agent proof 1209649066660320 01 payment send scene populated

agent proof 1209649066660320 02 wallet picker eth

agent proof 1209649066660320 02 wallet picker eth

agent proof 1209649066660320 03 cancel noop no error

agent proof 1209649066660320 03 cancel noop no error

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

@j0ntz j0ntz force-pushed the jon/moonpay-payment-deeplink branch from fd3bad0 to dba233e Compare July 13, 2026 22:35
@j0ntz j0ntz enabled auto-merge July 13, 2026 22:35
@j0ntz j0ntz disabled auto-merge July 13, 2026 22:49
MoonPay surfaces a "Send with Edge" button for pending sell orders (in
their email and order-history page) that reflects back the deposit
redirect URL our app hands them. That URL was RETURN_URL_PAYMENT =
https://edge.app/redirect/payment/, but the apex edge.app host is NOT
claimed as a universal link on iOS or Android (only deep.edge.app, dl,
and return are), so the button dead-ended on a static redirect page and
the user could not complete the deposit. The buy redirects already use
deep.edge.app; only the sell payment redirect used the unclaimed apex.

Point RETURN_URL_PAYMENT at the claimed deep.edge.app host (in-app
webview interception is host-agnostic via startsWith, so it is
unaffected) and parse the redirect/payment deep link into a new
paymentRedirect link that opens the Send scene, resolving the wallet
from the base currency code and pre-filling the deposit address, amount,
and destination tag (uniqueIdentifier).

Give the sibling ramp redirect URLs the same treatment: RETURN_URL_SUCCESS,
RETURN_URL_FAIL, and RETURN_URL_CANCEL now also live on the claimed
deep.edge.app host (was the apex edge.app), so an externally-reflected
terminal redirect opens the app instead of dead-ending. The host swap is
symmetric for the in-webview interception (Banxa matches by exact ===,
Paybis/MoonPay by startsWith), so no ramp flow changes behavior. These
terminal states carry no actionable payload, so the deep-link parser
resolves edge://redirect/{success,fail,cancel} to a no-op rather than the
former "Unknown deep link format" error, on the edge://, deep.edge.app,
and legacy apex edge.app hosts.
@j0ntz j0ntz force-pushed the jon/moonpay-payment-deeplink branch from dba233e to 77117ab Compare July 13, 2026 22:52
@j0ntz j0ntz enabled auto-merge July 13, 2026 22:52

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

Want higher recall? High effort reviews run extra passes and find more bugs. A team admin can switch effort levels in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 77117ab. Configure here.

const rawAmount = query.baseCurrencyAmount ?? undefined
const amountNum = rawAmount != null ? Number(rawAmount) : Number.NaN
const amount =
Number.isFinite(amountNum) && amountNum > 0 ? rawAmount : undefined

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Padded amount passes parser, breaks mul

Medium Severity

parsePaymentRedirect treats baseCurrencyAmount as valid when Number(rawAmount) is finite and positive, but still forwards the untrimmed query string as amount. Values that JavaScript parses successfully (leading or trailing whitespace, some exponential forms) can fail in biggystring mul after the wallet picker, surfacing an error instead of opening Send without an amount like other rejected amounts.

Additional Locations (1)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 77117ab. Configure here.

@j0ntz j0ntz merged commit 5c83012 into develop Jul 14, 2026
7 checks passed
@j0ntz j0ntz deleted the jon/moonpay-payment-deeplink branch July 14, 2026 00:29
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