Skip to main content
MoneyGram Ramps lets users exchange cash and USDC at more than 480,000 MoneyGram locations. Users can cash out in 170+ countries and territories and cash in at locations in 25+ countries. MoneyGram runs identity checks, quotes, compliance, and settlement inside a widget you embed in your app. Para gives the user a wallet and signs the USDC payment when they cash out. This guide covers the flow on Stellar, and the last section shows what to change for Solana.

What you’ll learn

  • Prepare the Stellar wallet to hold USDC
  • Create a MoneyGram session on your server for the user’s Para wallet
  • Sign the cash-out payment with the Para Stellar signer
  • Open the MoneyGram widget for cash-out or cash-in
  • Track each transaction until it settles

How it works

The widget runs in an iframe on your page. Your server creates a short-lived session for the wallet address, and the browser opens the widget with it. On cash-out, the widget asks your app for a USDC payment to MoneyGram, and the Para wallet signs it. On cash-in, the user pays cash at a MoneyGram location and MoneyGram sends USDC to the wallet, so your app signs nothing.
MoneyGram’s crypto services are not available in Alaska, Louisiana, or Hawaii.

Before you start

You need a React app with ParaProvider and a login flow, plus a server that can keep a secret key. Your Para API key must have Stellar enabled in the Developer Portal. These guides cover the Para setup:
You also need MoneyGram Ramps partner access. Register through MoneyGram’s , then get Playground keys and an allowlisted domain from the partner portal. On a domain that MoneyGram has not allowlisted, the widget does not load. A Stellar account does not exist on the network until it receives XLM. Fund the Para wallet’s address before its first transaction, then keep enough XLM for fees and the trustline reserve. Stellar’s shows how to fund a testnet account.

Install the packages

Install the Para React SDK and the Stellar SDK. The versions below are the ones used for this guide.
MoneyGram’s widget SDK is not an npm package. You load it with a script tag when you open the widget.

Connect to Stellar

MoneyGram Playground keys run on Stellar testnet, and production keys run on mainnet. The snippets in this guide use these testnet values for the network passphrase, the Horizon server, and the USDC asset:
Get the signed-in user’s Stellar signer from in a component inside ParaProvider:
stellarSigner.address is the wallet address you send to MoneyGram, and stellarSigner.signTransaction signs Stellar transactions with the Para wallet. Run the steps below after the user logs in and stellarSigner is set.

Add a USDC trustline

A Stellar account can hold USDC only after it adds a trustline to the USDC issuer. Cash-in needs the trustline so MoneyGram can deliver USDC to the wallet. Cash-out needs USDC in the wallet, so the trustline already exists by then. Load the account from Horizon and look for a USDC balance. If there is none, sign and submit a changeTrust operation:
Add the trustline before you open the widget for cash-in. Without it, MoneyGram cannot deliver USDC to the wallet.

Create a session on your server

Each widget opens with a MoneyGram session. Send the wallet address from the browser to your server. Your server then creates the session with the MoneyGram secret key:
Keep the secret key and the API URL in server environment variables. The Playground API URL is https://playground.xramps.moneygram.com/api. Check the response status, then return sessionToken and widgetUrl to the browser, which needs both to open the widget. An error response has neither field. A 401 means the secret key does not match the API URL, such as a Playground key sent to the production API. A session token expires after one hour, so create a new session each time the widget opens. Send the same wallet address for the same user every time. MoneyGram binds each session to that address, which lets the user reopen earlier transactions and lets the session token read their status.
Keep the secret key (ramps_sk_) on your server. Never put it in client code or a public repository.
Before production, confirm the user’s Para session on your server with a , check that the wallet address belongs to that user, and rate-limit session creation.

Sign the cash-out payment

When the user commits a cash-out, the widget calls onSignTransaction with the payment MoneyGram expects. The payload carries the deposit address, the amount, and the network. It also carries the USDC asset and a memo when MoneyGram sets them. The callback checks the network, then builds the USDC payment, signs it with the Para wallet, and submits it. It returns the transaction hash to the widget:
On Stellar, tokenAddress holds the asset code (USDC), not a contract address. The issuer comes from issuer. Widgets deployed before MoneyGram added issuer leave it out, so the callback falls back to the testnet USDC issuer.
Attach the memo with Memo.id. A text memo fails MoneyGram’s deposit check, and so does a payment that leaves out a memo the payload includes.
A submit request that times out can still land on the network, and the widget lets the user retry after a network error. Keep signedTxXdr until the submit succeeds. When the widget calls onSignTransaction again, resubmit that transaction or look up its hash on Horizon before you build a new payment. Stellar applies a signed transaction only once, but a new transaction sends a second payment.

Open the widget

Load MoneyGram’s SDK on the page that shows the widget:
The script adds window.RampsSDK and has no TypeScript types, so declare the fields you use in a TypeScript project. To open the widget, pass the session, the Para wallet, and your callbacks to createRamps. container is the page element that holds the widget’s iframe:
Set mode to off-ramp for cash-out or on-ramp for cash-in. The session’s widgetUrl always includes mode=off-ramp, so replace that query parameter instead of adding a second one. Pass the same mode in mode and transaction.type. The widget calls onSignTransaction for cash-out. Standard cash-in flows do not call it. The next section covers onTransactionCreated and onComplete. Call widget.destroy() to remove the iframe when the user leaves the page.
The script URL above is MoneyGram’s Playground build. Confirm the production URL with MoneyGram before you go live. Your Content Security Policy must also allow iframes from MoneyGram’s domains.

Track settlement

onTransactionCreated fires at most once, after MoneyGram validates the transaction and before it commits. MoneyGram does not retry it, so store the record as soon as it arrives. Keep id to look up the status later and mgiTransactionId to match webhook events. onComplete fires when the widget shows its final screen. For a cash-out, the record includes the referenceNumber the user gives at pickup. The transaction can still be in progress after onComplete. A cash-in, for example, waits for the user to pay at the counter. Ask MoneyGram for the current status from your server:
Poll until the transaction reaches a final status for its flow:
Do not stop polling a cash-out at funds_received. That status means MoneyGram has received the USDC. The cash is not ready to collect yet.
For server-to-server updates, add a webhook URL in the MoneyGram partner portal under Settings > Integration. You can save a production webhook URL only after MoneyGram approves your business verification (KYB). Webhook events use their own status names and can arrive out of order. Find the stored record by mgiTransactionId, then confirm the status with the request above before you act on it. MoneyGram’s covers the event format and signature verification.

Use Solana instead

The Solana flow keeps the session, widget, and settlement steps from Stellar. You replace the signer and the cash-out callback, and you set two chain values to solana. Your Para API key must have Solana enabled, and the wallet needs SOL to pay the cash-out transaction fee. Solana has no trustlines, so skip that step. Install the Solana packages. The versions below are the ones used for this guide.
Connect to Solana devnet, which MoneyGram Playground keys use. Create rpc once, outside your components, because the signer hook rebuilds the signer each time it gets a new rpc object:
Get the Solana signer from in a component inside ParaProvider:
The Solana payload carries the USDC mint in tokenAddress and its decimals in tokenDecimals. MoneyGram reports devnet as testnet in requiredNetwork. The callback converts the decimal amount to base units. It then sends a USDC transferChecked from the user’s token account to MoneyGram’s token account and returns the transaction signature:
The retry risk from the Stellar step applies here too. Keep the signed transaction until it confirms. On a retry, resend it or check its signature with getSignatureStatuses before you build a new transfer. The old transaction can no longer land once its blockhash expires.
Then change these values from the Stellar steps:
  1. Create the session with chain: "solana".
  2. In createRamps, set wallet.address to solanaSigner.address and wallet.chain to "solana".
  3. Pass the Solana onSignTransaction to createRamps.
MoneyGram’s covers the rest of the Solana payload.

Go live

Switch every network value to mainnet at the same time. MoneyGram picks the network from your key, and a key sent to the other environment’s API returns 401. To get production keys, complete these steps with MoneyGram:
  1. Complete KYB in the partner portal.
  2. Submit verified Playground transaction IDs for MoneyGram’s integration tests.
  3. Configure your product fees.
  4. Request production activation.
MoneyGram’s covers the rest, such as CORS limited to your production domain and session tokens kept out of logs.

Keep building with Para

Use the same signed-in Para wallets for other actions on each chain: