Deposits

Let users fund your app with any crypto, ideal for gaming deposits, embedded wallets & trading apps.

⚠️

IMPORTANT: Login here: mainnet or devnet to generate your API key (Developer -> API)

Before going live, you're required to complete KYB verification via the dashboard. Alternatively, get a link from your MP Account Manager. Note: KYB is not required to start the integration process.

1. Create your Deposit

Create a deposit via the Create Deposit endpoint or the dashboard. Upon creation, a depositId will be returned. Configure the following options:

  • Deposit Name: Assign a unique name to the deposit. Within this section, you can:
    • Recipient Currency: Specify the blockchain/currency in which you wish to receive fund
    • Payment Options: Enable Transfer Manually, Connect Wallet and Pay with Cash (you can use your existing MP API key)
      • Customisation: Update badges and CTA (Call to Action) copy
    • Notifications: Enable email notifications to receive updates when a deposit is successfully completed

2. Create a Deposit Customer

Generate a Deposit Customer via the API and their associated deposit addresses across SVM, EVM, and BTC networks.

  • Required Parameters: To create a customer, you must specify a unique customerId, a recipientWallet, and any optional metadata via the additionalJson payload.
  • Save the token: Store the returned Deposit Customer token. This token allows you to reuse the same deposit address for subsequent transactions from the same customer.
  • Related endpoints: use the Update Deposit Customer and Get Deposit Customer endpoints as needed.
  • Test Customer Deposit Session: In this step you can simulate a deposit widget. You must define a customerId and a recipientWallet to generate the test environment.

3. Build Your Own or Serve our Deposit UI in Your App

Headless support

  • Serve deposit addresses in your own UI. Create and store the depositCustomerToken -> use the Get Deposit Customer endpoint to get the depositWallets array
  • Use the Record Wallet Activity endpoint whenever you display a deposit wallet to a customer. This increases balance polling and detect incoming payments more quickly
  • Use Get Deposit Currencies to check available currencies

Deposit Widget Setup

Embed the deposit widget in your app using the following NPM package . The below configuration options are available for customising your deposit widget via config:

display

Controls how the deposit flow is rendered on your page.

  • Type: 'inline' | 'button' | 'new-tab'
  • Default: 'inline'
  • Required: No
📘

Use our demo environment to test display and other configurations. Simply toggle to the Deposit view and paste the Deposit Customer token.

network

Specifies the environment for the deposit flow.

  • Type: 'test' | 'main'
  • Default: 'main'
  • Required: No

Values:

  • 'test' - Use the test/sandbox environment for development and testing
  • 'main' - Use the production environment for live transactions

themeMode

Specifies the default light/dark mode. Alternatively you can also set a fixed Company Theme (Settings -> Merchant settings) that overrides this config.

  • Type: 'dark' | 'light'
  • Default: 'undefined'
  • Required: No

Values:

  • 'dark' - The widget uses dark mode
  • 'light' -The widget uses light mode
ℹ️

If themeMode is undefined and no Company Theme is set, the deposit flow will automatically match the user’s system light/dark mode.


Values:

  • 'inline' - Embeds the deposit flow directly in the container element
  • 'button' - Renders a button that opens the deposit flow in a modal
  • 'new-tab' - Renders a button that opens the deposit flow in a new browser tab
ℹ️

If you enable Pay with Cash and use the deposit widget in inline or button mode, your domain(s) must be whitelisted by MoonPay Ramps. In some cases, this may require additional KYB. You can always use new-tab mode without domain whitelisting, or disable the Pay with Cash option.


variant

Minimal removes padding and background for inline embeds

  • Type: 'default' | 'minimal'
  • Default: 'default'
  • Required: No

Values:

  • 'default' – Standard embed with padding and background
  • 'minimal' – Removes padding and background for inline embeds

openWalletConnectInNewTab

Controls whether the WalletConnect flow opens in a new browser tab.

  • Type: boolean
  • Default: false
  • Required: No

Values:

  • true – WalletConnect opens in a new browser tab
  • false – WalletConnect opens in the current tab or modal
🧠

Note: For better UX, consider setting this conditionally (e.g. openWalletConnectInNewTab={wallet.isConnected}) so a new tab is only opened when needed.


forcedStep

Forces the deposit flow to begin at a specific step, skipping any preceding steps in the default deposit journey.

  • Type: 'connect-wallet' | 'wallet-display' | 'moonpay-onramp'
  • Default: undefined
  • Required: No

Values:

'connect-wallet' — Forces the flow to start at the Connect Wallet step.

'wallet-display' — Forces the flow to start at the Wallet Display step.

'moonpay-onramp' — Forces the flow to start directly at the MoonPay Onramp step.


currentlyConnectedWallet

Allows the host application to pass an already connected user wallet into the deposit flow, surfacing it as an additional selectable option.

  • Type: ConnectWalletSelection
  • Default: undefined
  • Required: No

Fields:

  • wallet — The connected wallet provider.
  • blockchain — The chain the wallet address belongs to.
  • address — The connected wallet address.

wallet values (DepositConnectWallet)

  • 'METAMASK', 'PHANTOM', 'TRUST_WALLET', 'WALLET_CONNECT'

blockchain values

  • 'SOL', 'ETH', 'BASE', 'POLYGON', 'ARBITRUM', 'BSC', 'ABSTRACT', 'HYPERLIQUID'
// Optional: Pre-provides a wallet that is already connected in the host application.
  currentlyConnectedWallet?: {
    wallet: string;
    blockchain: string;
    address: string;
	};

Embedding the widget inside a React app

Embed the deposit widget in your React application using the Deposit Widget on npm.

import { MoonpayCommerceDeposit } from '@heliofi/deposit-react';

interface MoonpayCommerceDepositConfig {
  // Required: Your unique deposit customer token
  // Create it from https://commerce.moonpay.com
  depositCustomerToken: string;

  // Optional: Network to use
  // 'main' = Mainnet
  // 'test' = Testnet
  network?: 'test' | 'main';

  // Optional: Controls how the deposit flow is rendered on your page.
  // 'inline' = Embeds the deposit flow directly in the container element
  // 'button' = Renders a button that opens the deposit flow in a modal
  // 'new-tab' = Renders a button that opens the deposit flow in a new browser tab
  display?: 'inline' | 'button' | 'new-tab'
  
	// Optional: Theme to use
  // 'dark' = Dark mode
  // 'light' = Light mode
  themeMode?: 'dark' | 'light';
  
  // Optional: Opens WalletConnect in a new browser tab when enabled	
  // Tip: Best used conditionally (e.g. openWalletConnectInNewTab={wallet.isConnected})
	openWalletConnectInNewTab?: boolean;
  
	// Optional: Forces the deposit flow to start at a specific step
	// 'connect-wallet' = start at wallet connection
	// 'wallet-display' = start at wallet display/selection
	// 'moonpay-onramp' = start directly at MoonPay onramp
  forcedStep?: 'connect-wallet' | 'wallet-display' | 'moonpay-onramp';

  // Optional: Called when deposit completes successfully
  // Note: this might not be working in the current beta version
  onSuccess?: (data: {
    transaction?: string;
    depositCustomer?: unknown;
  }) => void;

  // Optional: Called if an error occurs
  // Note: this might not be working in the current beta version
  onError?: (error: { errorMessage?: string; transaction?: string }) => void;
}

function App() {
  return (
    <MoonpayCommerceDeposit
        config={{
    			depositCustomerToken: 'your-deposit-customer-token',
        	onSuccess: (data) => {
          	console.log('Deposit completed:', data);
        	},
        	onError: (error) => {
          	console.error('Deposit failed:', error);
        	},
      }}
    />
  );
}

Optional: Preload Assets with the Provider

To improve load performance, you can optionally wrap your application with the MoonpayCommerceDepositProvider.

The provider preloads the required JavaScript assets ahead of time, so when <MoonpayCommerceDeposit /> renders, it does not need to wait for the scripts to load.

Import

import { MoonpayCommerceDepositProvider } from "@heliofi/deposit-react";

Usage (Next.js example)

The provider takes no props and should be placed near the root of your application (e.g. layout.tsx or _app.tsx):

// layout.tsx or _app.tsx
export default function RootLayout({ children }) {
  return (
    <YourOtherProviders>
      <MoonpayCommerceDepositProvider>
        {children}
      </MoonpayCommerceDepositProvider>
    </YourOtherProviders>
  );
}

Notes:

  • children should include the rest of your application, including any usage of <MoonpayCommerceDeposit />.
  • This setup is optional. If you do not use the provider, assets will still load automatically when <MoonpayCommerceDeposit /> is rendered.

Vanilla JS

If you are not using React.js, you can alternatively embed using the pure JS approach like below:

<script type="module" src="https://embed.deposits.hel.io/assets/index-v1.js" id="deposit-script"></script>

<div id="deposit-container"></div>

<script>
  document.getElementById('deposit-script').addEventListener('load', () => {
    window.moonpayCommerceDeposit(
      document.getElementById('deposit-container'),
      {
        depositCustomerToken: 'your-token-here',
        network: 'test',
        display: 'inline',
        onSuccess: () => {
          console.log('Deposit completed!');
        },
        onError: (error) => {
          console.error('Deposit error:', error.message);
        },
      }
    );
  });
</script>

Iframe

Alternatively you can embed an iframe using the HTML snippet below. It lets users deposit crypto or pay by card directly on your site without leaving your app.

<iframe
  allow="clipboard-write"
  style="width:420px; height:260px;"
  src="https://moonpay.hel.io/embed/deposit/acac3ec9-f8f6-4128-8159-a91e418e2581"
/>

Replace the token at the end of the src URL with your own token returned when creating the customer deposit via the API (e.g. acac3ec9-f8f6-4128-8159-a91e418e2581) to render the correct deposit widget.

Iframe Post Message Events

Our deposit widget emits structured postMessage events that let your application react instantly to lifecycle changes such as completion, errors, or height updates, keeping your interface responsive and in sync without requiring a page reload.

EventPayloadWhen it fires
onSuccess{} (empty object)Deposit flow completed and the merchant has been successfully paid.
onError{ message: string }Something went wrong
onHeightChanged{ height: number } (pixels)The widget needs a new height (e.g. after a step change)

The snippet below illustrates how to integrate and respond to our postMessage events within a React component.

import { useEffect, useState } from "react";

export const Deposit = () => {
  // Dynamic height set by the deposit widget via postMessage
  const [iframeHeight, setIframeHeight] = useState(0);

  useEffect(() => {
    // Handle events sent from the deposit widget iframe
    const handleMessage = (event: MessageEvent) => {
      // Only process messages coming from the Helio/MoonPay widget
      if (event.origin !== "https://moonpay.hel.io") return;

      const data = event.data;

      switch (data.type) {
        // Widget requests the parent to resize the iframe
        case "onHeightChanged":
          setIframeHeight(data.height || 0);
          break;

        // Deposit flow completed successfully
        case "onSuccess":
          alert("Deposit completed");
          break;

        // An error occurred inside the widget
        case "onError":
          alert(`Deposit error: ${data.message}`);
          break;

        default:
          break;
      }
    };

    // Subscribe to postMessage events
    window.addEventListener("message", handleMessage);

    // Clean up on unmount
    return () => window.removeEventListener("message", handleMessage);
  }, []);

  return (
    <iframe
      allow="clipboard-write"
      className="rounded-3xl shadow-2xl"
      // Apply dynamic height returned by the widget
      style={{ width: 420, minHeight: 260, height: iframeHeight }}
      src="https://moonpay.hel.io/embed/deposit/ad1ecf2d-f535-41f8-96df-fd6a45a27cdd"
    />
  );
};
🔒

Always validate the event.origin before handling messages from the widget. This ensures you only process events coming from our domain and ignore anything unexpected.

4. Use Webhooks to Confirm Deposits on Your Backend

Create a webhook to listen for customer deposit events either in the dashboard under Developer Settings (see here for more information) or via the Create a Deposit Webhook endpoint. You can find payload examples here.

Supported Chains and Currencies

Use Get Deposit Currencies to check ALL available currencies including the key ones listed below. Users can choose their preferred network and currency when making a deposit. Deposited funds will be bridged and swapped into your specified recipient currency.

ChainSupported Currencies
SolanaSOL, USDC, USDT
Ethereum (EVM)ETH, USDC, USDT
Polygon (EVM)POL, USDC, USDCe., USDT
Base (EVM)ETH, USDC, USDT
Binance Smart Chain (EVM)BNB, USDT, USDT1
BitcoinBTC
Arbitrum (EVM)ETH, USDC, USDT
AbstractETH
PlasmaUSDT0, XPL
HyperEVMHYPE, USDC, ETH
TronUSDT

You can request support for additional currencies which can be enabled for your depositId

Demo