Skip to content

feat: adds support for custom actions#8100

Merged
georgeweiler merged 8 commits into
mainfrom
ramp-custom-actions
Mar 10, 2026
Merged

feat: adds support for custom actions#8100
georgeweiler merged 8 commits into
mainfrom
ramp-custom-actions

Conversation

@georgeweiler

@georgeweiler georgeweiler commented Mar 3, 2026

Copy link
Copy Markdown
Contributor

Explanation

Introduces controller API support for custom-action ramp flows (e.g., PayPal).

What changed:

  • getBuyWidgetData(quote) — Replaces getWidgetUrl. Returns BuyWidget | null (url, optional browser, optional orderId) instead of string | null. Returns null when fetch fails or no URL is available.
  • addPrecreatedOrder(params) — New method accepting { orderId, providerCode, walletAddress, chainId? }. Parses order IDs (including /orders/ path format), normalizes providerCode (strips /providers/ prefix), and inserts a stub RampsOrder in Precreated status for existing polling to hydrate.

What stays untouched: Existing controller state shape, polling behavior, and other order flows remain unchanged aside from the new API surface.

Dependencies: None in core; mobile and extension consume these changes.

References

Checklist

  • I've updated the test suite for new or updated code as appropriate
  • I've updated documentation (JSDoc, Markdown, etc.) for new or updated code as appropriate
  • I've communicated my changes to consumers by updating changelogs for packages I've changed
  • I've introduced breaking changes in this PR and have prepared draft pull requests for clients and consumer packages to resolve them

Note

Medium Risk
Medium risk due to a breaking public API change (getWidgetUrl -> getBuyWidgetData) and new order-state mutations (addPrecreatedOrder, getOrder now inserts missing orders) that can affect ramp UI/polling behavior for consumers.

Overview
BREAKING: Replaces getWidgetUrl with getBuyWidgetData, returning a BuyWidget | null (including browser/orderId) and now propagating service-call errors instead of swallowing them.

Adds addPrecreatedOrder to register a provider order ID (parsing /orders/ IDs and normalizing provider codes) by inserting a stub RampsOrder in Precreated status so existing polling can hydrate it, and updates getOrder to push the order into state when it isn’t already present.

Exports normalizeProviderCode, updates tests to cover the new APIs/edge cases, and documents the breaking change in the changelog.

Written by Cursor Bugbot for commit 64885db. This will update automatically on new commits. Configure here.

@georgeweiler georgeweiler requested a review from a team as a code owner March 3, 2026 20:25
Comment thread packages/ramps-controller/src/RampsController.ts
Comment thread packages/ramps-controller/src/RampsController.ts
Comment thread packages/ramps-controller/src/RampsService.ts Outdated
@georgeweiler georgeweiler requested a review from a team as a code owner March 4, 2026 13:49

@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 and found 1 potential issue.

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

Comment thread packages/ramps-controller/src/RampsController.ts
@georgeweiler

Copy link
Copy Markdown
Contributor Author

@metamaskbot publish-preview

@github-actions

github-actions Bot commented Mar 4, 2026

Copy link
Copy Markdown
Contributor

Preview builds have been published. See these instructions for more information about preview builds.

Expand for full list of packages and versions.
{
  "@metamask-previews/account-tree-controller": "4.1.1-preview-685dbf46b",
  "@metamask-previews/accounts-controller": "36.0.1-preview-685dbf46b",
  "@metamask-previews/address-book-controller": "7.0.1-preview-685dbf46b",
  "@metamask-previews/ai-controllers": "0.1.0-preview-685dbf46b",
  "@metamask-previews/analytics-controller": "1.0.0-preview-685dbf46b",
  "@metamask-previews/analytics-data-regulation-controller": "0.0.0-preview-685dbf46b",
  "@metamask-previews/announcement-controller": "8.0.0-preview-685dbf46b",
  "@metamask-previews/app-metadata-controller": "2.0.0-preview-685dbf46b",
  "@metamask-previews/approval-controller": "8.0.0-preview-685dbf46b",
  "@metamask-previews/assets-controller": "2.2.0-preview-685dbf46b",
  "@metamask-previews/assets-controllers": "100.0.3-preview-685dbf46b",
  "@metamask-previews/base-controller": "9.0.0-preview-685dbf46b",
  "@metamask-previews/base-data-service": "0.0.0-preview-685dbf46b",
  "@metamask-previews/bridge-controller": "67.4.0-preview-685dbf46b",
  "@metamask-previews/bridge-status-controller": "67.0.1-preview-685dbf46b",
  "@metamask-previews/build-utils": "3.0.4-preview-685dbf46b",
  "@metamask-previews/chain-agnostic-permission": "1.4.0-preview-685dbf46b",
  "@metamask-previews/claims-controller": "0.4.2-preview-685dbf46b",
  "@metamask-previews/client-controller": "1.0.0-preview-685dbf46b",
  "@metamask-previews/compliance-controller": "1.0.1-preview-685dbf46b",
  "@metamask-previews/composable-controller": "12.0.0-preview-685dbf46b",
  "@metamask-previews/config-registry-controller": "0.0.0-preview-685dbf46b",
  "@metamask-previews/connectivity-controller": "0.1.0-preview-685dbf46b",
  "@metamask-previews/controller-utils": "11.19.0-preview-685dbf46b",
  "@metamask-previews/core-backend": "6.0.0-preview-685dbf46b",
  "@metamask-previews/delegation-controller": "2.0.1-preview-685dbf46b",
  "@metamask-previews/earn-controller": "11.1.1-preview-685dbf46b",
  "@metamask-previews/eip-5792-middleware": "3.0.0-preview-685dbf46b",
  "@metamask-previews/eip-7702-internal-rpc-middleware": "0.1.0-preview-685dbf46b",
  "@metamask-previews/eip1193-permission-middleware": "1.0.3-preview-685dbf46b",
  "@metamask-previews/ens-controller": "19.0.3-preview-685dbf46b",
  "@metamask-previews/error-reporting-service": "3.0.1-preview-685dbf46b",
  "@metamask-previews/eth-block-tracker": "15.0.1-preview-685dbf46b",
  "@metamask-previews/eth-json-rpc-middleware": "23.1.0-preview-685dbf46b",
  "@metamask-previews/eth-json-rpc-provider": "6.0.0-preview-685dbf46b",
  "@metamask-previews/foundryup": "1.0.1-preview-685dbf46b",
  "@metamask-previews/gas-fee-controller": "26.0.3-preview-685dbf46b",
  "@metamask-previews/gator-permissions-controller": "2.0.0-preview-685dbf46b",
  "@metamask-previews/geolocation-controller": "0.0.0-preview-685dbf46b",
  "@metamask-previews/json-rpc-engine": "10.2.3-preview-685dbf46b",
  "@metamask-previews/json-rpc-middleware-stream": "8.0.8-preview-685dbf46b",
  "@metamask-previews/keyring-controller": "25.1.0-preview-685dbf46b",
  "@metamask-previews/logging-controller": "7.0.1-preview-685dbf46b",
  "@metamask-previews/message-manager": "14.1.0-preview-685dbf46b",
  "@metamask-previews/messenger": "0.3.0-preview-685dbf46b",
  "@metamask-previews/multichain-account-service": "7.0.0-preview-685dbf46b",
  "@metamask-previews/multichain-api-middleware": "1.2.7-preview-685dbf46b",
  "@metamask-previews/multichain-network-controller": "3.0.4-preview-685dbf46b",
  "@metamask-previews/multichain-transactions-controller": "7.0.1-preview-685dbf46b",
  "@metamask-previews/name-controller": "9.0.0-preview-685dbf46b",
  "@metamask-previews/network-controller": "30.0.0-preview-685dbf46b",
  "@metamask-previews/network-enablement-controller": "4.1.2-preview-685dbf46b",
  "@metamask-previews/notification-services-controller": "22.0.0-preview-685dbf46b",
  "@metamask-previews/permission-controller": "12.2.0-preview-685dbf46b",
  "@metamask-previews/permission-log-controller": "5.0.0-preview-685dbf46b",
  "@metamask-previews/perps-controller": "0.0.0-preview-685dbf46b",
  "@metamask-previews/phishing-controller": "16.3.0-preview-685dbf46b",
  "@metamask-previews/polling-controller": "16.0.3-preview-685dbf46b",
  "@metamask-previews/preferences-controller": "22.1.0-preview-685dbf46b",
  "@metamask-previews/profile-metrics-controller": "3.0.1-preview-685dbf46b",
  "@metamask-previews/profile-sync-controller": "27.1.0-preview-685dbf46b",
  "@metamask-previews/ramps-controller": "10.0.0-preview-685dbf46b",
  "@metamask-previews/rate-limit-controller": "7.0.0-preview-685dbf46b",
  "@metamask-previews/remote-feature-flag-controller": "4.1.0-preview-685dbf46b",
  "@metamask-previews/sample-controllers": "4.0.3-preview-685dbf46b",
  "@metamask-previews/seedless-onboarding-controller": "8.1.0-preview-685dbf46b",
  "@metamask-previews/selected-network-controller": "26.0.3-preview-685dbf46b",
  "@metamask-previews/shield-controller": "5.0.1-preview-685dbf46b",
  "@metamask-previews/signature-controller": "39.0.4-preview-685dbf46b",
  "@metamask-previews/storage-service": "1.0.0-preview-685dbf46b",
  "@metamask-previews/subscription-controller": "6.0.0-preview-685dbf46b",
  "@metamask-previews/transaction-controller": "62.19.0-preview-685dbf46b",
  "@metamask-previews/transaction-pay-controller": "16.1.2-preview-685dbf46b",
  "@metamask-previews/user-operation-controller": "41.0.3-preview-685dbf46b"
}

The V2 API returns a different providerOrderId format (e.g. PayPal's
numeric ID or null) than the ramps order code used as the lookup key.
When getOrder merged the API response onto an existing order, the
spread overwrote providerOrderId, breaking all subsequent lookups
via getOrderById and causing blank Order Details screens.

Always set providerOrderId to orderCode after the merge so the
canonical lookup key is never lost.

Made-with: Cursor
@georgeweiler

Copy link
Copy Markdown
Contributor Author

@cursoragent fix the linting issue

@cursor

cursor Bot commented Mar 10, 2026

Copy link
Copy Markdown

Fixed the linting issue! The problem was a negated condition at line 1949 in RampsController.ts. ESLint prefers positive conditions, so I flipped the logic:

Changed:

  • From: if (idx !== -1) with the update logic in the if-block
  • To: if (idx === -1) with the push logic in the if-block (swapped the branches)

The linting now passes successfully, and I've committed and pushed the fix to the ramp-custom-actions branch.

Open in Web Open in Cursor 

Co-authored-by: George Weiler <georgejweiler@gmail.com>
if (!buyWidget?.url) {
return null;
}
return buyWidget;

@georgeweiler georgeweiler Mar 10, 2026

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.

breaking: we are now returning both the URL and the extra data such as the supported browser type and custom order ID.

@georgeweiler georgeweiler added this pull request to the merge queue Mar 10, 2026
Merged via the queue into main with commit 19bb827 Mar 10, 2026
322 checks passed
@georgeweiler georgeweiler deleted the ramp-custom-actions branch March 10, 2026 16:17
amitabh94 added a commit that referenced this pull request Mar 10, 2026
PR #8100 was merged with a stale base, causing its changelog entry
to land under the already-released [10.1.0] section instead of
[Unreleased]. This moves it to the correct location so that the
next release picks it up.

Made-with: Cursor
github-merge-queue Bot pushed a commit that referenced this pull request Mar 10, 2026
)

## Explanation

PR #8100 was merged with a stale base branch (two releases had happened
since the last rebase). This caused its changelog entry to land under
the already-released `[10.1.0]` section instead of `[Unreleased]`.

This moves the entry to the correct location so the next release picks
it up properly.

## References

- Original PR: #8100
- Slack discussion:
https://consensys.slack.com/archives/C094GV3E7SB/p1773162520956669
- Mark Stacey identified the issue: #8167 (comment)

## Changelog

N/A — this only moves an existing changelog entry to the correct
section.

Made with [Cursor](https://cursor.com)

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Low Risk**
> Documentation-only change in `CHANGELOG.md` with no runtime or API
behavior modifications.
> 
> **Overview**
> Moves the `ramps-controller` changelog entry for PR #8100 (a
**BREAKING** API change replacing `getWidgetUrl` with `getBuyWidgetData`
and adding `addPrecreatedOrder`) from the already-released `10.1.0`
section into `[Unreleased]` so it is captured in the next release notes.
> 
> <sup>Written by [Cursor
Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit
ab81ce8. This will update automatically
on new commits. Configure
[here](https://cursor.com/dashboard?tab=bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
github-merge-queue Bot pushed a commit to MetaMask/metamask-mobile that referenced this pull request Mar 17, 2026
…lows (#27364)

<!--
Please submit this PR as a draft initially.
Do not mark it as "Ready for review" until the template has been
completely filled out, and PR status checks have passed at least once.
-->

## **Description**

<!--
Write a short description of the changes included in this pull request,
also include relevant motivation and context. Have in mind the following
questions:
1. What is the reason for the change?
2. What is the improvement/solution?
-->


1. Integrates new ramps-controller API for custom-action flows (e.g.,
PayPal) removes Redux-based custom ID tracking.

2. Simplifies ramps-controller state hydration system into a single
`init` call on app start up.

**What changed:**

- **BuildQuote** — Uses `getBuyWidgetData` instead of `getWidgetUrl`.
For `isCustomAction` quotes, calls `addPrecreatedOrder` before opening
external browser (InAppBrowser or `Linking.openURL`) and passes
`orderId` to Checkout for WebView flows.
- **Checkout** — Uses `addPrecreatedOrder` instead of Redux
`addFiatCustomIdData`. Supports both `orderId` and `customOrderId` for
backwards compatibility.
- **Hooks** — `useRampsOrders` exposes `addPrecreatedOrder` and
`AddPrecreatedOrderParams`; `useRampsQuotes` uses `getBuyWidgetData`
returning `Promise<BuyWidget | null>`; `useRampsController` exposes
both.
- **Provider/payment filtering** — `PaymentSelectionModal` and
`ProviderSelection` filter out `isCustomAction` quotes where
appropriate.

**What stays untouched:** Non-custom-action flows (standard WebView
checkout) unchanged. Existing ramps navigation and token selection
preserved.

**Dependencies:** Requires MetaMask/core#8100 for the new controller
API.

## **Changelog**

<!--
If this PR is not End-User-Facing and should not show up in the
CHANGELOG, you can choose to either:
1. Write `CHANGELOG entry: null`
2. Label with `no-changelog`

If this PR is End-User-Facing, please write a short User-Facing
description in the past tense like:
`CHANGELOG entry: Added a new tab for users to see their NFTs`
`CHANGELOG entry: Fixed a bug that was causing some NFTs to flicker`

(This helps the Release Engineer do their job more quickly and
accurately)
-->

CHANGELOG entry: Adds support for ramps providers such as PayPal,
Robinhood & Coinbase that use a different checkout browser

## **Related issues**

Fixes:

## **Manual testing steps**

```gherkin
Feature: my feature name

  Scenario: user [verb for user action]
    Given [describe expected initial app state]

    When user [verb for user action]
    Then [describe expected outcome]
```

## **Screenshots/Recordings**

<!-- If applicable, add screenshots and/or recordings to visualize the
before and after of your change. -->

### **Before**

<!-- [screenshots/recordings] -->

### **After**

<!-- [screenshots/recordings] -->

## **Pre-merge author checklist**

- [ ] I've followed [MetaMask Contributor
Docs](https://github.com/MetaMask/contributor-docs) and [MetaMask Mobile
Coding
Standards](https://github.com/MetaMask/metamask-mobile/blob/main/.github/guidelines/CODING_GUIDELINES.md).
- [ ] I've completed the PR template to the best of my ability
- [ ] I've included tests if applicable
- [ ] I've documented my code using [JSDoc](https://jsdoc.app/) format
if applicable
- [ ] I've applied the right labels on the PR (see [labeling
guidelines](https://github.com/MetaMask/metamask-mobile/blob/main/.github/guidelines/LABELING_GUIDELINES.md)).
Not required for external contributors.

## **Pre-merge reviewer checklist**

- [ ] I've manually tested the PR (e.g. pull and build branch, run the
app, test code being changed).
- [ ] I confirm that this PR addresses all acceptance criteria described
in the ticket it closes and includes the necessary testing evidence such
as recordings and or screenshots.


<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Medium Risk**
> Changes the `BuildQuote` continue flow to support external-browser
widget checkouts with precreated order tracking and post-auth callback
handling, which can affect buy navigation and order state. Mostly scoped
to ramps UI/controller integration with expanded test coverage, but
touches user-facing purchase flow and deep-link/callback paths.
> 
> **Overview**
> Updates the ramps **BuildQuote** flow to use the new controller API
`getBuyWidgetData` (replacing `getWidgetUrl`) and to support widget
providers that require *external* checkout. When external browser
checkout is used, it now pre-creates an order via `addPrecreatedOrder`,
opens the widget via `InAppBrowser.openAuth` (or `Linking.openURL`
fallback), parses the callback to fetch/add the resulting order, and
resets navigation either back to `BuildQuote` or into
`RAMPS_ORDER_DETAILS`.
> 
> Refactors amount entry handling into a shared `updateAmount` path and
consolidates user-facing errors under `rampsError` using
`reportRampsError`. Bootstrap is adjusted to run `useRampsProviders` on
mount (replacing `useHydrateRampsController`), and tests are heavily
rewritten/updated to cover the new routing, error, and
callback/order-handling behavior.
> 
> <sup>Written by [Cursor
Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit
10e9092. This will update automatically
on new commits. Configure
[here](https://cursor.com/dashboard?tab=bugbot).</sup>
<!-- /CURSOR_SUMMARY -->

---------

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: Amitabh Aggarwal <aggarwal.amitabh@gmail.com>
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.

4 participants