diff --git a/packages/ramps-controller/src/RampsController.ts b/packages/ramps-controller/src/RampsController.ts index 5c8612135c0..910a0df2970 100644 --- a/packages/ramps-controller/src/RampsController.ts +++ b/packages/ramps-controller/src/RampsController.ts @@ -18,9 +18,10 @@ import type { PaymentMethodsResponse, QuotesResponse, Quote, + BuyWidget, RampsToken, RampsServiceActions, - BuyWidget, + RampsOrder, } from './RampsService'; import type { RampsServiceGetGeolocationAction, @@ -30,6 +31,8 @@ import type { RampsServiceGetPaymentMethodsAction, RampsServiceGetQuotesAction, RampsServiceGetBuyWidgetUrlAction, + RampsServiceGetOrderAction, + RampsServiceGetOrderFromCallbackAction, } from './RampsService-method-action-types'; import type { RequestCache as RequestCacheType, @@ -117,6 +120,8 @@ export const RAMPS_CONTROLLER_REQUIRED_SERVICE_ACTIONS: readonly ( 'RampsService:getPaymentMethods', 'RampsService:getQuotes', 'RampsService:getBuyWidgetUrl', + 'RampsService:getOrder', + 'RampsService:getOrderFromCallback', 'TransakService:setApiKey', 'TransakService:setAccessToken', 'TransakService:clearAccessToken', @@ -478,6 +483,8 @@ type AllowedActions = | RampsServiceGetPaymentMethodsAction | RampsServiceGetQuotesAction | RampsServiceGetBuyWidgetUrlAction + | RampsServiceGetOrderAction + | RampsServiceGetOrderFromCallbackAction | TransakServiceSetApiKeyAction | TransakServiceSetAccessTokenAction | TransakServiceClearAccessTokenAction @@ -1865,6 +1872,52 @@ export class RampsController extends BaseController< } } + /** + * Fetches an order from the unified V2 API endpoint. + * Returns a normalized RampsOrder for all provider types (aggregator and native). + * + * @param providerCode - The provider code (e.g., "transak", "transak-native", "moonpay"). + * @param orderCode - The order identifier. + * @param wallet - The wallet address associated with the order. + * @returns The unified order data. + */ + async getOrder( + providerCode: string, + orderCode: string, + wallet: string, + ): Promise { + return await this.messenger.call( + 'RampsService:getOrder', + providerCode, + orderCode, + wallet, + ); + } + + /** + * Extracts an order from a provider callback URL. + * Sends the callback URL to the V2 backend for provider-specific parsing, + * then fetches the full order. This is the V2 equivalent of the aggregator + * SDK's `getOrderFromCallback`. + * + * @param providerCode - The provider code (e.g., "transak", "moonpay"). + * @param callbackUrl - The full callback URL the provider redirected to. + * @param wallet - The wallet address associated with the order. + * @returns The unified order data. + */ + async getOrderFromCallback( + providerCode: string, + callbackUrl: string, + wallet: string, + ): Promise { + return await this.messenger.call( + 'RampsService:getOrderFromCallback', + providerCode, + callbackUrl, + wallet, + ); + } + // === TRANSAK METHODS === // // Auth state is managed at two levels: diff --git a/packages/ramps-controller/src/RampsService-method-action-types.ts b/packages/ramps-controller/src/RampsService-method-action-types.ts index 6765d66bf86..a1a56ac298d 100644 --- a/packages/ramps-controller/src/RampsService-method-action-types.ts +++ b/packages/ramps-controller/src/RampsService-method-action-types.ts @@ -109,6 +109,35 @@ export type RampsServiceGetBuyWidgetUrlAction = { handler: RampsService['getBuyWidgetUrl']; }; +/** + * Fetches an order from the unified V2 API endpoint. + * Returns a normalized RampsOrder for all provider types. + * + * @param providerCode - The provider code (e.g., "transak", "transak-native", "moonpay"). + * @param orderCode - The order identifier. + * @param wallet - The wallet address associated with the order. + * @returns The unified order data. + */ +export type RampsServiceGetOrderAction = { + type: `RampsService:getOrder`; + handler: RampsService['getOrder']; +}; + +/** + * Extracts an order from a provider callback URL. + * Sends the callback URL to the V2 backend for provider-specific parsing, + * then fetches the full order. + * + * @param providerCode - The provider code (e.g., "transak", "moonpay"). + * @param callbackUrl - The full callback URL the provider redirected to. + * @param wallet - The wallet address associated with the order. + * @returns The unified order data. + */ +export type RampsServiceGetOrderFromCallbackAction = { + type: `RampsService:getOrderFromCallback`; + handler: RampsService['getOrderFromCallback']; +}; + /** * Union of all RampsService action types. */ @@ -119,4 +148,6 @@ export type RampsServiceMethodActions = | RampsServiceGetProvidersAction | RampsServiceGetPaymentMethodsAction | RampsServiceGetQuotesAction - | RampsServiceGetBuyWidgetUrlAction; + | RampsServiceGetBuyWidgetUrlAction + | RampsServiceGetOrderAction + | RampsServiceGetOrderFromCallbackAction; diff --git a/packages/ramps-controller/src/RampsService.ts b/packages/ramps-controller/src/RampsService.ts index 1f85f03aa89..f0f9dde83be 100644 --- a/packages/ramps-controller/src/RampsService.ts +++ b/packages/ramps-controller/src/RampsService.ts @@ -245,8 +245,14 @@ export type Quote = { /** * Buy URL endpoint that returns the actual provider widget URL. * This is a MetaMask-hosted endpoint that, when fetched, returns JSON with the provider's widget URL. + * @deprecated Use buyWidget instead - it's embedded in the quote response. */ buyURL?: string; + /** + * Widget information embedded in the quote response. + * Contains the widget URL, browser type, and optional pre-order tracking ID. + */ + buyWidget?: BuyWidget; }; /** * Metadata about the quote. @@ -486,6 +492,92 @@ export type TokensResponse = { allTokens: RampsToken[]; }; +// === ORDER TYPES === + +/** + * Possible statuses for a ramps order. + */ +export enum RampsOrderStatus { + Unknown = 'UNKNOWN', + Precreated = 'PRECREATED', + Created = 'CREATED', + Pending = 'PENDING', + Failed = 'FAILED', + Completed = 'COMPLETED', + Cancelled = 'CANCELLED', + IdExpired = 'ID_EXPIRED', +} + +/** + * Network information associated with an order. + */ +export type RampsOrderNetwork = { + name: string; + chainId: string; +}; + +/** + * Crypto currency information associated with an order. + */ +export type RampsOrderCryptoCurrency = { + assetId?: string; + name?: string; + chainId?: string; + decimals?: number; + iconUrl?: string; + symbol: string; +}; + +/** + * Payment method information associated with an order. + */ +export type RampsOrderPaymentMethod = { + id: string; + name?: string; + shortName?: string; + duration?: string; + icon?: string; + isManualBankTransfer?: boolean; +}; + +/** + * A unified order type returned from the V2 API. + * The V2 endpoint normalizes all provider responses into this shape. + */ +export type RampsOrder = { + id?: string; + isOnlyLink: boolean; + provider?: string; + success: boolean; + cryptoAmount: string | number; + fiatAmount: number; + cryptoCurrency?: string | RampsOrderCryptoCurrency; + fiatCurrency?: string; + providerOrderId: string; + providerOrderLink: string; + createdAt: number; + paymentMethod?: string | RampsOrderPaymentMethod; + totalFeesFiat: number; + txHash: string; + walletAddress: string; + status: RampsOrderStatus; + network: string | RampsOrderNetwork; + canBeUpdated: boolean; + idHasExpired: boolean; + idExpirationDate?: number; + excludeFromPurchases: boolean; + timeDescriptionPending: string; + fiatAmountInUsd?: number; + feesInUsd?: number; + region?: string; + orderType: string; + exchangeRate?: number; + pollingSecondsMinimum?: number; + statusDescription?: string; + partnerFees?: number; + networkFees?: number; +}; + /** * The SDK version to send with API requests. (backwards-compatibility) */ @@ -533,6 +625,8 @@ const MESSENGER_EXPOSED_METHODS = [ 'getPaymentMethods', 'getQuotes', 'getBuyWidgetUrl', + 'getOrder', + 'getOrderFromCallback', ] as const; /** @@ -1194,4 +1288,93 @@ export class RampsService { return response; } + + /** + * Fetches an order from the unified V2 API endpoint. + * This endpoint returns a normalized `RampsOrder` (DepositOrder shape) + * for all provider types, including both aggregator and native providers. + * + * @param providerCode - The provider code (e.g., "transak", "transak-native", "moonpay"). + * @param orderCode - The order identifier. + * @param wallet - The wallet address associated with the order. + * @returns The unified order data. + */ + async getOrder( + providerCode: string, + orderCode: string, + wallet: string, + ): Promise { + const url = new URL( + getApiPath(`providers/${providerCode}/orders/${orderCode}`), + this.#getBaseUrl(RampsApiService.Orders), + ); + this.#addCommonParams(url); + url.searchParams.set('wallet', wallet); + + const response = await this.#policy.execute(async () => { + const fetchResponse = await this.#fetch(url); + if (!fetchResponse.ok) { + throw new HttpError( + fetchResponse.status, + `Fetching '${url.toString()}' failed with status '${fetchResponse.status}'`, + ); + } + return fetchResponse.json() as Promise; + }); + + if (!response || typeof response !== 'object') { + throw new Error('Malformed response received from order API'); + } + + return response; + } + + /** + * Extracts an order from a provider callback URL. + * Sends the callback URL to the V2 API backend, which knows how to parse + * each provider's callback format and extract the order ID. Then fetches + * the full order using that ID. + * + * This is the V2 equivalent of the aggregator SDK's `getOrderFromCallback`. + * + * @param providerCode - The provider code (e.g., "transak", "moonpay"). + * @param callbackUrl - The full callback URL the provider redirected to. + * @param wallet - The wallet address associated with the order. + * @returns The unified order data. + */ + async getOrderFromCallback( + providerCode: string, + callbackUrl: string, + wallet: string, + ): Promise { + // Step 1: Send the callback URL to the backend to extract the order ID. + // The backend parses it using provider-specific logic. + const callbackApiUrl = new URL( + getApiPath(`providers/${providerCode}/callback`), + this.#getBaseUrl(RampsApiService.Orders), + ); + this.#addCommonParams(callbackApiUrl); + callbackApiUrl.searchParams.set('url', callbackUrl); + + const callbackResponse = await this.#policy.execute(async () => { + const fetchResponse = await this.#fetch(callbackApiUrl); + if (!fetchResponse.ok) { + throw new HttpError( + fetchResponse.status, + `Fetching '${callbackApiUrl.toString()}' failed with status '${fetchResponse.status}'`, + ); + } + return fetchResponse.json() as Promise<{ id: string }>; + }); + + const orderId = callbackResponse?.id; + if (!orderId) { + throw new Error( + 'Could not extract order ID from callback URL via provider', + ); + } + + // Step 2: Fetch the full order using the extracted order ID. + return this.getOrder(providerCode, orderId, wallet); + } } diff --git a/packages/ramps-controller/src/index.ts b/packages/ramps-controller/src/index.ts index 05ff54ead10..6c266ba3c9a 100644 --- a/packages/ramps-controller/src/index.ts +++ b/packages/ramps-controller/src/index.ts @@ -42,6 +42,11 @@ export type { RampsToken, TokensResponse, BuyWidget, + RampsOrder, + RampsOrderStatus, + RampsOrderNetwork, + RampsOrderCryptoCurrency, + RampsOrderPaymentMethod, } from './RampsService'; export { RampsService, @@ -55,6 +60,8 @@ export type { RampsServiceGetPaymentMethodsAction, RampsServiceGetQuotesAction, RampsServiceGetBuyWidgetUrlAction, + RampsServiceGetOrderAction, + RampsServiceGetOrderFromCallbackAction, } from './RampsService-method-action-types'; export type { RequestCache,