Checkout Widget
Embed crypto payments in your website or app with a few lines of code
Integrate the MoonPay Commerce Checkout Widget with just a few lines of code to accept USDC, 100+ digital currencies (on Solana, Ethereum, Polygon, Base & Bitcoin), and card payments via a seamless on-ramp flow.
We offer a Vanilla JS & React integration, each with a broad range of config options to customise the checkout for your theme - see here:
IMPORTANT: Phantom requires domain allowlisting for the checkout widget to avoid transaction safety warnings. Fill this form for allowlisting.
Get started
- Log into MoonPay Commerce to setup your paylinkId and generate the code snippet on Step 4 of our payment creation flow. Use https://demo.hel.io/ to customise your theme & config
- Our standard way to embed our checkout is with our JS/HTML embed - simply paste into your code builder as follows
Dynamic Pricing
If you want to set the price in your widget programmatically, create a dynamic pay link in the dashboard and pass the amount as a field in your checkout widget configuration code.

Select "Dynamic" payment to pass the "amount" in your config
MoonPay Commerce Checkout (React)
If you have a React based app, then the easiest way to embed MoonPay Commerce Checkout is with our React component.
Install the dependency:
yarn add @heliofi/checkout-react
# or npm install @heliofi/checkout-react
# or pnpm add @heliofi/checkout-reactThen in your React components import it in (simply grab the example code snippet for our React component on step 4 when creating your Pay Link).
import {HelioCheckout} from '@heliofi/checkout-react'
const helioConfig = {
paylinkId: "6571e7cd4a2bee8095ee84da",
amount: "5.99",
};
function YourCheckoutComponent() {
return <HelioCheckout config={helioConfig} />;
}Note: if you are dynamically generating the config object, we recommend wrapping it in useMemo()
Config Options
The configuration options object is the same for our Vanilla JS and React version.
Common config options:
- One of the following must be set (but not both)
paylinkId- This is the ID for the Pay Link you would like to embed
- **example: **
paylinkId: "6571e7cd4a2bee1095ee84da"
- **example: **
- This is the ID for the Pay Link you would like to embed
chargeToken- A charge token (you cancreate charges via our API).
- example:
chargeToken: "05adc323-0450-4ef8-b127-18949b9ff485"
- example:
- A charge token (you cancreate charges via our API).
network(optional - default ismain)- values:
mainortest - example:
network: "test" - This is only required if you generated a Pay Link on our dev/testnet site
https://app.dev.hel.io/ - See note below how our
networkvalue works.
- values:
paymentType(optional - default ispaylink)- values: paylink or paystream
- example: paymentType: "paystream"
- This is only required if you set up a recurring paystream embed.
- If your Pay Link is recurring, the code snippet on step 4 will already include
paymentType: "paystream"
platformonly if you are embedding from presale.magiceden.io- If you are embedding our checkout for a Pay link created fromhttps://app.hel.io you should not set this value.
- This will automatically be added to the code snippet on step 4 of Pay Link creation if you are on https://presale.magiceden.io
- values:
magic_eden - example: platform:
magic_eden
Customising the look of the embed:
display(optional - default isinline)- values:
buttoninlineornew-tab - example:
display: "button"Use this to display a button that opens up a modal with our checkout flow - example:
display: "new-tab"Use this to display a button that opens up a unique, single use checkout page in a new tab. The "charge pages" are optimal for mobile payment flows, deep-linking, QR codes, embedded pay buttons on simple websites like Wix, or other custom payment flows. Learn more about Charges
- values:
primaryPaymentMethod(optional - default iscrypto)- values:
cryptoorfiat - example:
primaryPaymentMethod: "fiat" - If set to
fiat, then 'pay with card' will be the default button option.
- values:
customTexts(optional)- Use this to override some texts/copy within the embed.
- Current options are as follows:
{
customTexts: {
mainButtonTitle: "Purchase via Helio",
payButtonTitle: "Click to pay",
}
}
theme(optional)
Use this to override theming options within the embed.
themeMode((use this to switch between light or dark appearances):- values:
lightordark
Example usage:
{
paylinkId: "6571e7cd4a2bee1095ee84da",
theme: {
themeMode: "dark", // or "light"
}
}
primaryColor(optional, required whenneutralColoris defined)- Use this to customise main colours within the embed
neutralColor(optional, required whenprimaryColoris defined)- Use this to customise neutral colours such as border and text colours within the embed
Example usage:
{
paylinkId: "6571e7cd4a2bee1095ee84da",
primaryColor: "#E60000",
neutralColor: "#5A6578"
}
backgroundColor(optional, use this to change the background color)- values: Provide a
hex code(e.g., #FFFFFF for white or #000000 for black)
- values: Provide a
Example usage:
{
paylinkId: "6571e7cd4a2bee1095ee84da",
backgroundColor: "#4bbe7c"
}
- showPayWithCard (optional - default is
true)- Use this to remove the 'Pay with card' button from the embed.
- values:
trueorfalse
Example usage:
{
paylinkId: "6571e7cd4a2bee1095ee84da",
showPayWithCard: false
}
Dynamic config options
If you have a dynamic payment (to programatically set the amount a) then you will need to use these options:
- Log in to MoonPay Commerce and select CREATE PAYMENT -> Dynamic pricing -> paylinkId
amount(required for dynamic payments)- values: a string value
- example:
amount: "5.99" - This will be the amount of the payment currency
- Use step 2 of the Pay link creation process on https://app.hel.io to set the currencies you will receive in.
Callback config options
Use our callbacks to run Javascript code when certain actions occur.
You can use these to trigger changes in your application.
onSuccessonErroronPendingonCancelonStartPayment
Example:
{
// ... (your other config options) ...
onSuccess: event => console.log(event),
onError: event => console.log(event),
onPending: event => console.log(event),
onCancel: () => console.log("Cancelled payment"),
onStartPayment: () => console.log("Starting payment"),
}
In addition, we recommend using our webhooks to verify transaction on your backend. You can find an end-to-end developer example here including the Checkout Widget and webhooks here:
Advanced Config Options:
debug(optional - default isfalse)trueorfalse- example:
debug: true - if true then it will output some helpful debug information
AdditionalJSON
additionalJSON(optional)- values can be any valid JSON data.
- example:
additionalJSON: {customerId: "customer1", product: "T-shirt"} - Anything you pass in here will get stored when a customer makes a transaction.
- You can retrieve this on transactions by listening for our webhooks, which include all relevant values
Our Network Options for Testnet
When you generate a paylink on app.dev.hel.io (for test transactions), then you must set network: "test" in your config.
If you generate on app.hel.io (mainnet) you do not need to set
networkconfig options!
(You cannot set network: "test" for a paylink generated on app.hel.io)
The testnets that we use for each blockchain are described below:
- Solana:
main= mainnettest= devnet
- Ethereum
main= mainnettest= Sepolia
- Polygon
main= mainnettest= Mumbai
- Bitcoin
main= mainnettest= testnet3
- Base
main= Mainnettest= Sepolia
Supported Currencies
View our API reference to get a list of all currencies supported.
Embedding the MoonPay Commerce Checkout Widget in Framer (Alpha)
Alpha NoticeThis feature is currently in alpha and intended for testing and early feedback. It may be unstable, incomplete, or change without notice.
You can add the MoonPay Commerce checkout widget to any Framer project using a simple code snippet.
In Framer, open the Assets panel, go to Code → + Code Component, and paste the snippet below into the editor. Name your component (e.g. MoonPay CommerceCheckout), then drag it onto the canvas like any other element. You can enter your pay link ID and other widget configuration options in the right-hand properties panel, the widget will load automatically.
// Framer metadata: defines how this component behaves in layout
// - Width is fixed (manually set in Framer)
// - Height adapts to content
// - Intrinsic size fallback if unset
// @framerSupportedLayoutWidth fixed
// @framerSupportedLayoutHeight auto
// @framerIntrinsicWidth 400
// @framerIntrinsicHeight 600
import { useEffect, useRef, useState } from "react"
import { addPropertyControls, ControlType } from "framer"
// Props accepted from the Framer UI (sidebar)
interface Props {
paylinkId: string // User-defined Paylink ID from MoonPay Commerce
}
// Declare a global `window.moonpayCheckout` interface to avoid TypeScript errors
declare global {
interface Window {
moonpayCheckout: (element: HTMLElement, config: any) => void
}
}
// Main React component exported to Framer
export default function MoonPayCheckout({
paylinkId = "66327978b90031d7202d38cd", // default demo Paylink
}: Props) {
// Used just to differentiate instances for debugging
const [id] = useState(() => Math.random())
console.log({ id })
// Reference to the div where the MoonPay widget will mount
const containerRef = useRef<HTMLDivElement>(null)
// Local loading/error state
const [isLoading, setIsLoading] = useState(true)
const [error, setError] = useState<string | null>(null)
// Effect hook: inject MoonPay’s script and initialize the widget
useEffect(() => {
// Guard against non-browser environments or missing ref
if (!containerRef.current || typeof window === "undefined") return
// Create and configure <script> tag to load MoonPay's embed script
const script = document.createElement("script")
script.type = "module"
script.crossOrigin = "anonymous"
script.src = "https://embed.hel.io/assets/index-v1.js"
// Once script is loaded, initialize the checkout widget
script.onload = () => {
if (window.moonpayCheckout && containerRef.current) {
window.moonpayCheckout(containerRef.current, {
paylinkId, // The configured paylink from Framer
display: "button", // Display mode (can be "button", "new-tab" or "inline")
theme: {
themeMode: "dark", // Force dark theme
},
})
setIsLoading(false) // Done loading
}
}
// Handle error if the script fails to load
script.onerror = () => {
setError("Failed to load MoonPay script")
setIsLoading(false)
}
// Inject the script into the page
document.head.appendChild(script)
// Cleanup on component unmount
return () => {
if (containerRef.current) {
containerRef.current.innerHTML = ""
}
}
}, [paylinkId]) // Re-run if paylinkId changes
// Render error state if script failed
if (error) {
return (
<div className="hel-flex hel-items-center hel-justify-center hel-h-full hel-p-4">
<div className="hel-text-red-500">{error}</div>
</div>
)
}
// Render the widget container and loading state
return (
<div className="hel-w-full hel-h-full hel-relative">
{/* Show loading message until widget loads */}
{isLoading && (
<div className="hel-absolute hel-inset-0 hel-flex hel-items-center hel-justify-center">
<div className="hel-text-gray-500">
Loading Helio Checkout...
</div>
</div>
)}
{/* Mount Helio widget here */}
<div
ref={containerRef}
id="helioCheckoutContainer"
className="hel-w-full hel-h-full"
/>
</div>
)
}
// Expose Paylink ID to the Framer UI sidebar
addPropertyControls(HelioCheckout, {
paylinkId: {
type: ControlType.String,
title: "Paylink ID",
defaultValue: "66327978b90031d7202d38cd", // Default/fallback Paylink
},
})Updated 29 days ago
