← Overview

MoonPay

The consumer-recognized way to buy, sell, and swap crypto.

What it is

MoonPay is a fiat-to-crypto on/off-ramp that teams embed into their products. Users buy, sell, and swap crypto with cards, bank transfers, PayPal, Venmo, Revolut, and Apple/Google Pay across ~160+ countries. Partners integrate via a hosted widget, web/React/mobile SDKs, or REST API, and MoonPay handles KYC, payment acceptance, fraud, liquidity, and settlement. It has expanded beyond ramps into NFT checkout, a commerce/payments stack (via the 2025 Helio acquisition), and stablecoin infrastructure (via the 2025 Iron acquisition).

How it works

  1. Partners embed MoonPay via a hosted widget (buy.moonpay.com / sell.moonpay.com), web/React/mobile SDKs, or the REST API.
  2. Users buy crypto with card, bank transfer, PayPal, Venmo, Revolut, or Apple/Google Pay; KYC + fraud run inside MoonPay.
  3. MoonPay sources liquidity and pays out the crypto on-chain to the user's wallet (and the reverse for the off-ramp/sell flow).
  4. Swaps let users trade one crypto for another in-widget; NFT checkout lets buyers pay fiat for an NFT.
  5. Order state is pushed to the partner via signed webhooks (Buy: created/updated/failed; Sell: updates + requote events).

Differentiators

  • Consumer brand recognition + a polished, high-converting purchase flow.
  • ~160+ country reach with an unusually broad set of payment methods (PayPal, Venmo, Revolut, Apple/Google Pay, cards, bank transfer).
  • More than a ramp: swaps, NFT checkout, and a commerce/stablecoin stack via the Helio + Iron acquisitions.
  • Large balance sheet (~$645M raised + a $200M Galaxy credit line) funds liquidity for volume spikes and aggressive expansion.

Business model

Fees + spread on each on/off-ramp transaction — roughly ~1% on bank transfer to ~4.5% on card (min ~$3.99), plus a quoted-rate spread (~1–3%) and network fees; commerce/checkout fees on the Helio side.

Depends on

  • Card networks (Visa/Mastercard)
  • Banking partners + acquirers
  • Alt payment rails (PayPal, Venmo, Revolut, Apple/Google Pay)
  • Supported chains
  • Stablecoin / market-maker liquidity

Risks

  • High card fees + embedded spread vs. cheaper rails (e.g. Coinbase zero-fee USDC).
  • Fee compression as ramps commoditize.
  • Card fraud + chargebacks at scale.
  • Regulatory exposure as a money transmitter / VASP across many jurisdictions (MTLs, MiCA/CASP). [verify exact current license list]
Deep dive

Architecture & mechanics

On/off-ramp flow & architecture

MoonPay is the regulated, fraud-bearing layer between traditional payment rails and the chains. The partner only renders a widget; MoonPay does the heavy, regulated work.

  • On-ramp: user pays fiat (card / bank / PayPal / Venmo / Apple-Google Pay) → MoonPay runs KYC + fraud → sources crypto → pays out on-chain to the wallet.
  • Off-ramp (sell): user sends crypto to a MoonPay-controlled address → MoonPay pays out fiat to card/bank.
  • Swaps: in-widget crypto-to-crypto exchange; NFT checkout: fiat payment for an NFT.
  • Async status flows back via signed webhooks; the client redirect is only a UX hint, not settlement truth.

Security model: signed URLs + signed webhooks

MoonPay's distinguishing developer detail is mandatory cryptographic signing in both directions, which keeps the secret key off the client while still allowing rich pre-filled widgets.

  • Outbound: widget URLs are signed HMAC-SHA256(querystring, secretKey) → base64 → URL-encode; required whenever email/walletAddress are passed.
  • SDKs accept just the signature (updateSignature / onUrlSignatureRequested); raw integrations append &signature= to the URL.
  • Inbound: webhooks carry a Moonpay-Signature-V2 header partners must verify; idempotency + de-dupe required.
  • Virtual Accounts API uses RSA-SHA256 request signing for a higher-assurance server-to-server channel.

Fees & economics

Revenue is a blend of an explicit platform fee and an embedded spread, which makes the headline rate understate true cost.

  • Card: up to ~4.5% (min ~$3.99); bank transfer: ~1% (min ~$3.99).
  • Plus a ~1–3% spread baked into the quoted rate (not a separate line item) + on-chain network fees.
  • All-in effective cost can reach ~7–8% on small card buys — the core vulnerability vs. zero-fee USDC rails.
  • Fees vary by order type, volume, fiat, asset, location, and payment method. [verify current numbers against pricing disclosure]

Strategy, M&A & risk

  • 2025 M&A: acquired Helio (~$175M, crypto commerce/checkout) and Iron (stablecoin infrastructure) — pushing beyond ramps toward a payments/stablecoin platform.
  • Liquidity: $200M revolving credit line from Galaxy (Mar 2025) to absorb transaction spikes.
  • Funding/valuation: ~$645M raised; ~$3.4B (2021); reportedly raising near ~$5B with ICE (NYSE parent) in talks late 2025 [verify if closed].
  • Forward bet: adapting the platform for AI-agent-initiated transactions (2026).
  • Risks: fee compression, card fraud/chargebacks, and multi-jurisdiction regulatory exposure as a money transmitter / VASP.
Builder's track

How it's built

Architecture

MoonPay sits between card acquirers / banks / alt-payment rails and the chains. The partner renders a hosted widget (buy.moonpay.com / sell.moonpay.com) — embedded as an iframe, overlay, or new tab — and MoonPay runs KYC, payment acceptance, fraud, liquidity sourcing, and the on-chain payout (reverse for sell). Unlike a publishable-key-only ramp, MoonPay requires the widget URL to be SIGNED: whenever sensitive params (email, walletAddress) are passed, the partner's backend signs the URL's query string with HMAC-SHA256 using the SECRET key, so the secret never touches the client. Order state is delivered to the partner via signed webhooks (Moonpay-Signature-V2 header).

Integration shape

Two layers: (1) a publishable apiKey (pk_) used on the client to identify the integration, and (2) a secret key (sk_) used server-side only to sign URLs and verify webhooks. Integrate via the hosted widget URL, the Web SDK (script attaches window.MoonPayWebSdk, or as a package), the React SDK (components expose an onUrlSignatureRequested async prop), or React Native / iOS / Android SDKs. For SDKs return just the signature via updateSignature; for raw URLs return the whole signed URL. Sandbox hosts (buy-sandbox.moonpay.com) mirror production by swapping keys.

API surface

buy.moonpay.com (widget URL)
On-ramp widget. Core params: apiKey, currencyCode, baseCurrencyCode, baseCurrencyAmount, walletAddress, paymentMethod, redirectURL, externalCustomerId, signature.
sell.moonpay.com (widget URL)
Off-ramp widget. Params: apiKey, baseCurrencyCode (crypto being sold), quoteCurrencyCode (fiat), refundWalletAddress, externalCustomerId, signature.
signature (query param)
HMAC-SHA256 of the URL query string (incl. leading '?'), keyed by the secret API key, base64-then-URL-encoded. Required when passing email/walletAddress or the widget won't load.
Web SDK (window.MoonPayWebSdk)
moonPay({ flow:'buy'|'sell', environment, variant, params }); .show()/.close(); supports updateSignature() callback for backend-signed URLs.
React SDK <MoonPayBuyWidget />
Component with visible, variant, baseCurrencyCode, walletAddress + onUrlSignatureRequested async prop that returns the backend-generated signature.
Webhooks (Buy / Sell)
POST events: transaction created / updated / failed (buy); sell updates + sell_transaction_requote_required. Verify Moonpay-Signature-V2; respond 200 within 10s; de-dupe (events may repeat / arrive out of order).
REST API + Virtual Accounts API
Server-side currencies, quotes, transactions, and customer data; Virtual Accounts API requests signed with RSA-SHA256 digital signatures.

Minimal integration

Minimal on-ramp: build a buy-widget URL on the server and HMAC-sign it before handing it to the client.

import crypto from 'crypto';

// SECRET key stays on the server — never ship it to the client.
const SECRET = process.env.MOONPAY_SECRET_KEY!;   // sk_live_...
const PK = process.env.MOONPAY_PUBLISHABLE_KEY!;  // pk_live_...

export function buildSignedBuyUrl() {
  const base = 'https://buy.moonpay.com';
  const qs = new URLSearchParams({
    apiKey: PK,
    currencyCode: 'usdc',          // crypto to receive
    baseCurrencyCode: 'usd',       // fiat to pay with
    baseCurrencyAmount: '50',
    walletAddress: '0xabc...def',  // sensitive -> signature now required
    redirectURL: 'https://rails.app/done',
  });

  const search = `?${qs.toString()}`;
  const signature = crypto
    .createHmac('sha256', SECRET)
    .update(search)                // sign the query string incl. '?'
    .digest('base64');

  return `${base}${search}&signature=${encodeURIComponent(signature)}`;
}

Build notes

  • Two keys: publishable (pk_) on the client, secret (sk_) server-side only for URL signing + webhook verification. The widget refuses to load if a URL carrying email/walletAddress isn't signed.
  • Treat the SIGNED WEBHOOK as the source of truth for order status — not the client redirect. Verify the Moonpay-Signature-V2 header, respond 200 within ~10s, and de-dupe (events may repeat or arrive out of order).
  • Start on the sandbox hosts (buy-sandbox.moonpay.com) with test keys; flip to production by swapping keys + host.
  • [verify against current docs — exact param names (e.g. quoteCurrencyCode vs currencyCode on sell) and the full webhook event list evolve]