Skip to content

Commit af58537

Browse files
chore(ramp): adopt core-owned Headless Buy default redirect URL (MetaMask#34207)
<!-- Please submit this PR as a draft initially. Do not mark it as "Ready for review" until this PR meets the canonical Definition of Ready For Review in `docs/readme/ready-for-review.md`. --> ## **Description** <!-- mms-check: type=text required=true --> Adopts the TRAM-3757 core change from MetaMask/core#9752 so Headless Buy and UB2 share one finish-line (fake-callback) URL derivation. - Bumps `@metamask/ramps-controller` to the stable published `^19.0.0` release. - Removes the client `getDefaultRedirectUrl` injection from `ramps-controller-init.ts`. Core now supplies the widened-path default through `RampsService:getDefaultRedirectCallbackUrl`. - Spreads `RAMPS_CONTROLLER_REQUIRED_SERVICE_ACTIONS` in the controller messenger so the new action cannot be forgotten on future upgrades. - Rewrites `getRampCallbackBaseUrl()` as `getDefaultRedirectCallbackUrl(getRampsEnvironment())`, deleting the duplicated host table. BuildQuote, Continue rewrite, Checkout completion matching, and the controller default now all use the same environment source (`RAMPS_ENVIRONMENT`, then `METAMASK_ENVIRONMENT`). ## **Changelog** <!-- mms-check: type=changelog required=true blocking=true --> CHANGELOG entry: null ## **Related issues** <!-- mms-check: type=issue-link required=true --> Fixes: TRAM-3757 Refs: MetaMask/core#9752 ## **Manual testing steps** <!-- mms-check: type=manual-testing required=true --> N/A - unit-covered wiring and package bump only. Covered by unit tests for `getRampCallbackBaseUrl` (parity with `getDefaultRedirectCallbackUrl(getRampsEnvironment())`, including `RAMPS_ENVIRONMENT` override), `ramps-controller-init`, and messenger construction. ## **Screenshots/Recordings** <!-- mms-check: type=screenshot required=true --> N/A - no user-visible UI change; wiring/package bump only. ### **Before** N/A ### **After** N/A, simply proving that UB2 still works. https://github.com/user-attachments/assets/61bf61fb-765c-4fe1-923c-3ce03d4e5d1e ## **Pre-merge author checklist** <!-- mms-check: type=checklist required=true --> - [x] 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). - [x] I've completed the PR template to the best of my ability - [x] I've included tests if applicable - [x] I've documented my code using [JSDoc](https://jsdoc.app/) format if applicable - [x] 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. #### Performance checks (if applicable) - [ ] I've tested on Android - Ideally on a mid-range device; emulator is acceptable - [ ] I've tested with a power user scenario - Use these [power-user SRPs](https://consensyssoftware.atlassian.net/wiki/spaces/TL1/pages/edit-v2/401401446401?draftShareId=9d77e1e1-4bdc-4be1-9ebb-ccd916988d93) to import wallets with many accounts and tokens - [ ] I've instrumented key operations with Sentry traces for production performance metrics - See [`trace()`](/app/util/trace.ts) for usage and [`addToken`](/app/components/Views/AddAsset/components/AddCustomToken/AddCustomToken.tsx#L274) for an example For performance guidelines and tooling, see the [Performance Guide](https://consensyssoftware.atlassian.net/wiki/spaces/TL1/pages/400085549067/Performance+Guide+for+Engineers). ## **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 ramp buy/checkout redirect URL resolution for headless and UB2 flows; behavior should match prior mapping but depends on core v19 and messenger delegation being correct. > > **Overview** > Bumps **`@metamask/ramps-controller`** to **^19.0.0** (TRAM-3757 / core#9752) so the widened Headless Buy quote path gets its default redirect from core instead of the mobile client. > > **`getRampCallbackBaseUrl()`** no longer maintains a local host switch on `METAMASK_ENVIRONMENT`; it delegates to **`getDefaultRedirectCallbackUrl(getRampsEnvironment())`**, aligning UB2 BuildQuote, checkout completion, and Continue rewrite with the same env source (`RAMPS_ENVIRONMENT`, then `METAMASK_ENVIRONMENT`). **`ramps-controller-init`** drops the **`getDefaultRedirectUrl`** injection into **`RampsController`**. > > The ramps controller messenger now spreads **`RAMPS_CONTROLLER_REQUIRED_SERVICE_ACTIONS`** instead of a hand-maintained action list, so **`RampsService:getDefaultRedirectCallbackUrl`** stays delegated on upgrades. Tests assert parity with core and **`RAMPS_ENVIRONMENT`** override behavior. > > <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit 721b70e. Bugbot is set up for automated code reviews on this repo. Configure [here](https://www.cursor.com/dashboard/bugbot).</sup> <!-- /CURSOR_SUMMARY -->
1 parent 2c3f5da commit af58537

6 files changed

Lines changed: 74 additions & 103 deletions

File tree

Lines changed: 42 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
import { getDefaultRedirectCallbackUrl } from '@metamask/ramps-controller';
2+
3+
import { getRampsEnvironment } from '../../../../core/Engine/controllers/ramps-controller/ramps-service-init';
14
import { getRampCallbackBaseUrl } from './getRampCallbackBaseUrl';
25

36
const PRODUCTION_CALLBACK =
@@ -8,49 +11,55 @@ const DEVELOPMENT_CALLBACK =
811
'https://on-ramp.dev-api.cx.metamask.io/regions/fake-callback';
912

1013
describe('getRampCallbackBaseUrl', () => {
11-
const originalEnv = process.env.METAMASK_ENVIRONMENT;
14+
const originalMetamaskEnvironment = process.env.METAMASK_ENVIRONMENT;
15+
const originalRampsEnvironment = process.env.RAMPS_ENVIRONMENT;
1216

1317
afterEach(() => {
14-
process.env.METAMASK_ENVIRONMENT = originalEnv;
18+
process.env.METAMASK_ENVIRONMENT = originalMetamaskEnvironment;
19+
if (originalRampsEnvironment !== undefined) {
20+
process.env.RAMPS_ENVIRONMENT = originalRampsEnvironment;
21+
} else {
22+
delete process.env.RAMPS_ENVIRONMENT;
23+
}
1524
});
1625

17-
it('returns production content callback for production', () => {
18-
process.env.METAMASK_ENVIRONMENT = 'production';
19-
expect(getRampCallbackBaseUrl()).toBe(PRODUCTION_CALLBACK);
26+
beforeEach(() => {
27+
delete process.env.RAMPS_ENVIRONMENT;
2028
});
2129

22-
it('returns production content callback for beta', () => {
23-
process.env.METAMASK_ENVIRONMENT = 'beta';
24-
expect(getRampCallbackBaseUrl()).toBe(PRODUCTION_CALLBACK);
25-
});
30+
it.each([
31+
['production', PRODUCTION_CALLBACK],
32+
['beta', PRODUCTION_CALLBACK],
33+
['rc', PRODUCTION_CALLBACK],
34+
['dev', DEVELOPMENT_CALLBACK],
35+
['exp', STAGING_CALLBACK],
36+
['test', STAGING_CALLBACK],
37+
['e2e', STAGING_CALLBACK],
38+
] as const)(
39+
'matches getDefaultRedirectCallbackUrl(getRampsEnvironment()) for METAMASK_ENVIRONMENT=%s',
40+
(metamaskEnvironment, expected) => {
41+
process.env.METAMASK_ENVIRONMENT = metamaskEnvironment;
42+
expect(getRampCallbackBaseUrl()).toBe(expected);
43+
expect(getRampCallbackBaseUrl()).toBe(
44+
getDefaultRedirectCallbackUrl(getRampsEnvironment()),
45+
);
46+
},
47+
);
2648

27-
it('returns production content callback for rc', () => {
28-
process.env.METAMASK_ENVIRONMENT = 'rc';
29-
expect(getRampCallbackBaseUrl()).toBe(PRODUCTION_CALLBACK);
30-
});
31-
32-
it('returns Dev API fake-callback for dev (no content.dev-api host)', () => {
33-
process.env.METAMASK_ENVIRONMENT = 'dev';
34-
expect(getRampCallbackBaseUrl()).toBe(DEVELOPMENT_CALLBACK);
35-
});
36-
37-
it('returns staging content callback for exp', () => {
38-
process.env.METAMASK_ENVIRONMENT = 'exp';
39-
expect(getRampCallbackBaseUrl()).toBe(STAGING_CALLBACK);
40-
});
41-
42-
it('returns staging content callback for test', () => {
43-
process.env.METAMASK_ENVIRONMENT = 'test';
49+
it('returns staging content callback when METAMASK_ENVIRONMENT is unset', () => {
50+
delete process.env.METAMASK_ENVIRONMENT;
4451
expect(getRampCallbackBaseUrl()).toBe(STAGING_CALLBACK);
52+
expect(getRampCallbackBaseUrl()).toBe(
53+
getDefaultRedirectCallbackUrl(getRampsEnvironment()),
54+
);
4555
});
4656

47-
it('returns staging content callback for e2e', () => {
57+
it('prefers RAMPS_ENVIRONMENT over METAMASK_ENVIRONMENT', () => {
4858
process.env.METAMASK_ENVIRONMENT = 'e2e';
49-
expect(getRampCallbackBaseUrl()).toBe(STAGING_CALLBACK);
50-
});
51-
52-
it('returns staging content callback when METAMASK_ENVIRONMENT is unset', () => {
53-
delete process.env.METAMASK_ENVIRONMENT;
54-
expect(getRampCallbackBaseUrl()).toBe(STAGING_CALLBACK);
59+
process.env.RAMPS_ENVIRONMENT = 'production';
60+
expect(getRampCallbackBaseUrl()).toBe(PRODUCTION_CALLBACK);
61+
expect(getRampCallbackBaseUrl()).toBe(
62+
getDefaultRedirectCallbackUrl(getRampsEnvironment()),
63+
);
5564
});
5665
});
Lines changed: 9 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,15 @@
1+
import { getDefaultRedirectCallbackUrl } from '@metamask/ramps-controller';
2+
3+
import { getRampsEnvironment } from '../../../../core/Engine/controllers/ramps-controller/ramps-service-init';
4+
15
/**
26
* Callback base URL for ramp quote/order redirects (UNIFIED_BUY_2).
3-
* Defined here so the unified BuildQuote / Checkout flow does not import the
4-
* legacy aggregator SDK (which has top-level initialization side effects).
57
*
6-
* Production/staging use the on-ramp-content hosts (same as Aggregator).
7-
* Mobile `dev` uses the RAM Dev API host because there is no
8-
* `on-ramp-content.dev-api` deployment; `/regions/fake-callback` is served
9-
* from `on-ramp.dev-api` and returns 200.
8+
* Thin wrapper around core's canonical environment-to-callback map so BuildQuote,
9+
* Checkout completion detection, Continue rewrite, and the controller's widened
10+
* default all resolve from the same `getRampsEnvironment()` source (which honors
11+
* `RAMPS_ENVIRONMENT` from builds.yml, then `METAMASK_ENVIRONMENT`).
1012
*/
11-
const RAMP_CALLBACK_URL_PRODUCTION =
12-
'https://on-ramp-content.api.cx.metamask.io/regions/fake-callback';
13-
const RAMP_CALLBACK_URL_STAGING =
14-
'https://on-ramp-content.uat-api.cx.metamask.io/regions/fake-callback';
15-
const RAMP_CALLBACK_URL_DEVELOPMENT =
16-
'https://on-ramp.dev-api.cx.metamask.io/regions/fake-callback';
17-
1813
export function getRampCallbackBaseUrl(): string {
19-
const env = process.env.METAMASK_ENVIRONMENT;
20-
switch (env) {
21-
case 'production':
22-
case 'beta':
23-
case 'rc':
24-
return RAMP_CALLBACK_URL_PRODUCTION;
25-
case 'dev':
26-
return RAMP_CALLBACK_URL_DEVELOPMENT;
27-
default:
28-
return RAMP_CALLBACK_URL_STAGING;
29-
}
14+
return getDefaultRedirectCallbackUrl(getRampsEnvironment());
3015
}

app/core/Engine/controllers/ramps-controller/ramps-controller-init.ts

Lines changed: 3 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@ import {
55
getDefaultRampsControllerState,
66
} from '@metamask/ramps-controller';
77
import type { RampsControllerInitMessenger } from '../../messengers/ramps-controller-messenger';
8-
import { getRampCallbackBaseUrl } from '../../../../components/UI/Ramp/utils/getRampCallbackBaseUrl';
98
import { handleOrderStatusChangedForNotifications } from './event-handlers/notification';
109
import { handleOrderStatusChangedForMetrics } from './event-handlers/analytics';
1110

@@ -40,14 +39,9 @@ export const rampsControllerInit: MessengerClientInitFunction<
4039
// The all-providers widening is driven by the `moneyHeadlessAllProviders`
4140
// remote feature flag, which the controller reads itself through the
4241
// `RemoteFeatureFlagController:getState` messenger action per quote call.
43-
// Default redirect URL for the widened quote fetch. MM Pay's quote
44-
// request omits `redirectUrl`, so aggregator quotes would come back without
45-
// the buy-widget URL the headless Checkout WebView needs; supply the same
46-
// callback base the UB2 flow uses so the widened path can open the WebView.
47-
// TODO(TRAM-3757): Derive this default redirect URL inside RampsController
48-
// from its RampsEnvironment (RampsService.getBaseUrl) instead of injecting
49-
// it from the client here; this callback duplicates a value core already has.
50-
getDefaultRedirectUrl: () => getRampCallbackBaseUrl(),
42+
// The widened-path default redirect URL is now derived inside core via
43+
// `RampsService:getDefaultRedirectCallbackUrl` (same environment as the
44+
// service). Keep that action delegated in the controller messenger.
5145
});
5246

5347
let orderSubscriptionsRegistered = false;

app/core/Engine/messengers/ramps-controller-messenger/ramps-controller-messenger.ts

Lines changed: 4 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import {
2+
RAMPS_CONTROLLER_REQUIRED_SERVICE_ACTIONS,
23
RampsControllerMessenger,
34
type RampsControllerOrderStatusChangedEvent,
45
} from '@metamask/ramps-controller';
@@ -42,40 +43,9 @@ export function getRampsControllerMessenger(
4243
// The controller reads the `moneyHeadlessAllProviders` feature flag
4344
// itself for quote widening.
4445
'RemoteFeatureFlagController:getState',
45-
'RampsService:getGeolocation',
46-
'RampsService:getCountries',
47-
'RampsService:getTokens',
48-
'RampsService:getProviders',
49-
'RampsService:getPaymentMethods',
50-
'RampsService:getQuotes',
51-
'RampsService:getBuyWidgetUrl',
52-
'RampsService:getOrder',
53-
'RampsService:getOrderFromCallback',
54-
'TransakService:setApiKey',
55-
'TransakService:setAccessToken',
56-
'TransakService:clearAccessToken',
57-
'TransakService:sendUserOtp',
58-
'TransakService:verifyUserOtp',
59-
'TransakService:logout',
60-
'TransakService:getUserDetails',
61-
'TransakService:getBuyQuote',
62-
'TransakService:getKycRequirement',
63-
'TransakService:getAdditionalRequirements',
64-
'TransakService:createOrder',
65-
'TransakService:getOrder',
66-
'TransakService:getUserLimits',
67-
'TransakService:requestOtt',
68-
'TransakService:generatePaymentWidgetUrl',
69-
'TransakService:createWidgetUrl',
70-
'TransakService:submitPurposeOfUsageForm',
71-
'TransakService:patchUser',
72-
'TransakService:submitSsnDetails',
73-
'TransakService:confirmPayment',
74-
'TransakService:getTranslation',
75-
'TransakService:getIdProofStatus',
76-
'TransakService:cancelOrder',
77-
'TransakService:cancelAllActiveOrders',
78-
'TransakService:getActiveOrders',
46+
// Spread the package-owned required list so new service actions
47+
// (e.g. getDefaultRedirectCallbackUrl) cannot be forgotten at upgrade.
48+
...RAMPS_CONTROLLER_REQUIRED_SERVICE_ACTIONS,
7949
],
8050
events: [],
8151
});

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -337,7 +337,7 @@
337337
"@metamask/preinstalled-example-snap": "^0.8.1",
338338
"@metamask/profile-metrics-controller": "^4.0.1",
339339
"@metamask/profile-sync-controller": "^28.3.0",
340-
"@metamask/ramps-controller": "^18.0.0",
340+
"@metamask/ramps-controller": "^19.0.0",
341341
"@metamask/react-data-query": "^0.2.2",
342342
"@metamask/react-native-acm": "patch:@metamask/react-native-acm@npm%3A1.2.0#~/.yarn/patches/@metamask-react-native-acm-npm-1.2.0-944bf863eb.patch",
343343
"@metamask/react-native-actionsheet": "2.4.2",

yarn.lock

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9957,7 +9957,7 @@ __metadata:
99579957
languageName: node
99589958
linkType: hard
99599959

9960-
"@metamask/ramps-controller@npm:^18.0.0, @metamask/ramps-controller@npm:^18.0.1":
9960+
"@metamask/ramps-controller@npm:^18.0.1":
99619961
version: 18.0.1
99629962
resolution: "@metamask/ramps-controller@npm:18.0.1"
99639963
dependencies:
@@ -9970,6 +9970,19 @@ __metadata:
99709970
languageName: node
99719971
linkType: hard
99729972

9973+
"@metamask/ramps-controller@npm:^19.0.0":
9974+
version: 19.0.0
9975+
resolution: "@metamask/ramps-controller@npm:19.0.0"
9976+
dependencies:
9977+
"@metamask/base-controller": "npm:^9.1.0"
9978+
"@metamask/controller-utils": "npm:^12.3.0"
9979+
"@metamask/messenger": "npm:^2.0.0"
9980+
"@metamask/profile-sync-controller": "npm:^28.3.0"
9981+
"@metamask/remote-feature-flag-controller": "npm:^5.0.0"
9982+
checksum: 10/332c9f58fbc77ef53dbebb9488d65d1195d8c5afe43758a4f65e1cb36383bf181f9c9557a492fe740ce9ca198b35ae8b90d68cb5d152626a04349ca3502bf180
9983+
languageName: node
9984+
linkType: hard
9985+
99739986
"@metamask/react-data-query@npm:^0.2.2":
99749987
version: 0.2.2
99759988
resolution: "@metamask/react-data-query@npm:0.2.2"
@@ -36165,7 +36178,7 @@ __metadata:
3616536178
"@metamask/profile-metrics-controller": "npm:^4.0.1"
3616636179
"@metamask/profile-sync-controller": "npm:^28.3.0"
3616736180
"@metamask/providers": "npm:^18.3.1"
36168-
"@metamask/ramps-controller": "npm:^18.0.0"
36181+
"@metamask/ramps-controller": "npm:^19.0.0"
3616936182
"@metamask/react-data-query": "npm:^0.2.2"
3617036183
"@metamask/react-native-acm": "patch:@metamask/react-native-acm@npm%3A1.2.0#~/.yarn/patches/@metamask-react-native-acm-npm-1.2.0-944bf863eb.patch"
3617136184
"@metamask/react-native-actionsheet": "npm:2.4.2"

0 commit comments

Comments
 (0)