Guide
React Native · Custodial cash-out · Stellar / USDC
Custodial off-ramp in React Native: WebView, RAMPS_DEPOSIT_ADDRESS, and server-side Stellar USDC transfers.
MoneyGram Ramps: Custodial Cash-Out (React Native / Stellar / USDC)
Preview · Custodial partners · React Native · Cash-out only ·
This guide is for custodial partners building a React Native app. You hold customer USDC in a shared omnibus or hot wallet; end users do not sign on-chain transactions themselves.
For non-custodial React Native (user wallet signing via RAMPS_SIGN_TRANSACTION), use the React Native · Stellar (non-custodial) guide.
For the web iframe SDK (createRamps + onDepositAddress), see Web · Custodial cash-out.
Scope: Custodial cash-out (off-ramp) only. For custodial cash-in, use the Custodial cash-in guide. The WebView message contract is Embed the widget.
How it works:
- Your backend creates a session with
customerIdentifierfor the logged-in user. - Your app opens the MoneyGram widget in a WebView and sends
RAMPS_CONFIGwithwalletType: 'custodial'. - When the user commits, the widget sends
RAMPS_DEPOSIT_ADDRESS(notRAMPS_SIGN_TRANSACTION). - Your app calls your internal transfer API, then replies with
RAMPS_SIGN_SUCCESSand the on-chain txHash. - The widget polls until confirmed and returns a reference number for cash pickup.
The widget and API are the same as web custodial. Only the postMessage bridge differs.
| Topic | Details |
|---|---|
| Custody mode | Set at onboarding (walletCustody: 'custodial'), cannot switch per session |
customerIdentifier | Required in POST /v1/sessions: stable internal user ID (max 255 chars) |
| Omnibus wallet | custodianCashOutWallet: USDC source for off-ramp; set in partner portal |
| Stellar memo | Include depositMemo as Memo.id (numeric). Memo.text fails deposit verification |
See Web · Custodial cash-out for full session API examples and backend transfer orchestration.
async function createCustodialSession(userId: string) { const res = await fetch('https://playground.xramps.moneygram.com/api/v1/sessions', { method: 'POST', headers: { 'Content-Type': 'application/json', 'x-api-key': process.env.MONEYGRAM_SK!, }, body: JSON.stringify({ customerIdentifier: userId, walletAddress: process.env.PARTNER_OMNIBUS_WALLET, chain: 'stellar', }), }) if (!res.ok) throw new Error(`Session failed: ${res.status}`) return res.json()}
Session TTL: 1 hour. Create a fresh session each time the user opens the widget.
| Non-custodial | Custodial |
|---|---|
walletType: 'non-custodial' | walletType: 'custodial' |
wallet.address = user's G-address | wallet.address = your omnibus / hot wallet |
Handle RAMPS_SIGN_TRANSACTION | Handle RAMPS_DEPOSIT_ADDRESS |
| Sign with user wallet | Call internal transfer API → return txHash |
RAMPS_CONFIG (custodial)
On RAMPS_READY:
post('RAMPS_CONFIG', { sessionToken: session.sessionToken, wallet: { address: PARTNER_OMNIBUS_WALLET, chain: 'stellar', asset: 'USDC', walletType: session.walletType, }, devConfig: { mockMode: false, apiBaseUrl: 'https://playground.xramps.moneygram.com/api', }, transaction: { type: 'off-ramp', asset: 'USDC', },})
case 'RAMPS_DEPOSIT_ADDRESS': { try { const res = await fetch(INTERNAL_TRANSFER_URL, { method: 'POST', headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${appAuthToken}`, }, }) if (!res.ok) throw new Error(`Transfer failed (${res.status})`) const { txHash } = await res.json() if (typeof txHash !== 'string' || !txHash) throw new Error('Transfer returned no txHash') post('RAMPS_SIGN_SUCCESS', { txHash, walletAddress: PARTNER_OMNIBUS_WALLET }) } catch (err) { post('RAMPS_SIGN_ERROR', { error: err instanceof Error ? err.message : 'Transfer failed', }) } break}
Do not authorize transfers from WebView-supplied address, amount, or memo alone. Your backend resolves the active Ramps transaction and re-reads authoritative values from GET /v1/transactions/:id/status.
Same idempotent orchestration as the Web custodial guide. Submit a Stellar USDC payment from your hot wallet with the memo from the Ramps API when present.