Iframe Embed
Learn how to embed the Merso Modal in your website using an iframe with postMessage communication.
Integration Methods
There are two ways to send data to the Merso Modal:
postMessage (Client-Side)
Load the iframe and send data via postMessage after it loads. Best for SPAs and dynamic content.
Form POST (Recommended)
Use a hidden form to POST data to the iframe. JWT token stays secure in body, not URL.
Basic Iframe Structure
The modal is designed to be embedded in an iframe within your application:
<iframe
id="merso-modal"
name="merso-modal"
src="https://modal.dev.merso.io/modal"
width="480"
height="600"
style="border: none; border-radius: 1rem;"
title="Merso Payment Modal"
allow="payment"
></iframe>
Iframe Attributes
| Attribute | Value | Description |
|---|---|---|
src |
https://modal.dev.merso.io/modal |
The modal endpoint URL |
name |
Unique identifier | Required for form POST method targeting |
width |
480 (recommended) | Iframe width in pixels |
height |
600 (recommended) | Iframe height - 600px for full payment flow |
allow |
"payment" | Required for Stripe payment processing |
title |
Descriptive text | Accessibility - describes the iframe content |
Method 1: postMessage
Send data to the modal after it loads using the postMessage API:
const iframe = document.getElementById('merso-modal');
iframe.onload = () => {
// Send configuration and product data via postMessage
iframe.contentWindow.postMessage({
type: 'api-config',
jwtToken: 'YOUR_JWT_TOKEN',
// Product data
productType: 'ITEM',
itemPrice: 29.99,
itemId: 123,
itemName: 'Premium Sword',
playerEmail: 'player@example.com',
playerLevel: 42,
playerCountry: 'US',
// Optional: Pre-select payment mode
paymentMode: 'BNPL' // or 'UPFRONT'
}, '*');
};
With postMessage, the JWT token is sent client-side. Make sure to fetch the token from your server, never hardcode it.
Method 2: Form POST (Recommended)
Use a hidden form to POST data directly to the iframe. This keeps the JWT token in the request body:
<!-- Hidden form that POSTs to the iframe -->
<form id="modal-form" action="https://modal.dev.merso.io/modal" method="POST" target="merso-modal" style="display: none;">
<input type="hidden" name="jwt_token" value="YOUR_JWT_TOKEN" />
<input type="hidden" name="product_type" value="ITEM" />
<input type="hidden" name="item_price" value="29.99" />
<input type="hidden" name="item_id" value="123" />
<input type="hidden" name="item_name" value="Premium Sword" />
<input type="hidden" name="player_email" value="player@example.com" />
<input type="hidden" name="player_level" value="42" />
<input type="hidden" name="player_country" value="US" />
</form>
<!-- Iframe receives the POST data -->
<iframe
id="merso-modal"
name="merso-modal"
width="480"
height="600"
style="border: none; border-radius: 1rem;"
allow="payment"
></iframe>
<script>
// Submit form on page load (or when ready)
document.getElementById('modal-form').submit();
</script>
The form's target attribute matches the iframe's name. When submitted, the POST response loads directly into the iframe with all data securely in the body.
Modal Overlay Pattern
Show the iframe in a modal overlay that can be opened/closed:
<!-- Trigger Button -->
<button id="open-modal">Buy Now - $29.99</button>
<!-- Hidden Form -->
<form id="modal-form" action="https://modal.dev.merso.io/modal" method="POST" target="merso-modal" style="display: none;">
<input type="hidden" name="jwt_token" id="jwt-token" />
<input type="hidden" name="product_type" value="ITEM" />
<input type="hidden" name="item_price" value="29.99" />
<input type="hidden" name="item_id" value="123" />
<input type="hidden" name="item_name" value="Premium Sword" />
<input type="hidden" name="player_email" value="player@example.com" />
</form>
<!-- Modal Overlay -->
<div id="modal-overlay" style="
display: none;
position: fixed;
inset: 0;
background: rgba(0, 0, 0, 0.6);
backdrop-filter: blur(4px);
align-items: center;
justify-content: center;
z-index: 1000;
">
<div style="position: relative;">
<button id="close-modal" style="
position: absolute;
top: -40px;
right: 0;
background: white;
border: none;
border-radius: 50%;
width: 32px;
height: 32px;
cursor: pointer;
font-size: 18px;
">×</button>
<iframe
id="merso-modal"
name="merso-modal"
width="480"
height="600"
style="border: none; border-radius: 16px; background: white;"
allow="payment"
></iframe>
</div>
</div>
const overlay = document.getElementById('modal-overlay');
const form = document.getElementById('modal-form');
// Get JWT token from your server
async function getJwtToken() {
const response = await fetch('/api/get-merso-token');
const data = await response.json();
return data.token;
}
// Open modal
document.getElementById('open-modal').addEventListener('click', async () => {
// Set JWT token
const token = await getJwtToken();
document.getElementById('jwt-token').value = token;
// Show overlay and submit form
overlay.style.display = 'flex';
form.submit();
});
// Close modal
document.getElementById('close-modal').addEventListener('click', () => {
overlay.style.display = 'none';
});
// Close on overlay click
overlay.addEventListener('click', (e) => {
if (e.target === overlay) {
overlay.style.display = 'none';
}
});
// Listen for payment events
window.addEventListener('message', (event) => {
if (event.origin === 'https://js.stripe.com') return;
if (event.data?.type === 'payment-success') {
console.log('Payment successful!', event.data);
overlay.style.display = 'none';
// Handle success (e.g., show confirmation, update inventory)
}
if (event.data?.type === 'payment-cancelled') {
overlay.style.display = 'none';
}
});
Skip Selection Steps
Pre-select product type and payment mode to streamline the checkout flow:
<!-- Skip directly to payment method selection -->
<form action="https://modal.dev.merso.io/modal" method="POST" target="merso-modal">
<input type="hidden" name="jwt_token" value="..." />
<!-- Pre-select product type (skips Item/NFT selection) -->
<input type="hidden" name="product_type" value="ITEM" />
<!-- Pre-select payment mode (skips BNPL/Upfront selection) -->
<input type="hidden" name="payment_mode" value="BNPL" />
<!-- Item data -->
<input type="hidden" name="item_price" value="99.99" />
<input type="hidden" name="item_id" value="456" />
<input type="hidden" name="item_name" value="Legendary Sword" />
...
</form>
| Pre-selection | Fields to Include | User Sees |
|---|---|---|
| None | - |
Item/NFT → BNPL/Upfront → Card/Crypto |
| Product only | product_type=ITEM |
BNPL/Upfront → Card/Crypto |
| Product + Mode | product_type + payment_mode |
Card/Crypto selection directly |
Responsive Sizing
Make the modal responsive for different screen sizes:
#modal-overlay {
padding: 1rem;
}
#merso-modal {
width: 100%;
max-width: 480px;
height: 600px;
max-height: 90vh;
}
@media (max-width: 520px) {
#merso-modal {
width: 100%;
height: 100%;
max-height: none;
border-radius: 0;
}
#modal-overlay {
padding: 0;
}
}
NFT Purchase Example
For NFT purchases, use different form fields:
<form action="https://modal.dev.merso.io/modal" method="POST" target="merso-modal">
<input type="hidden" name="jwt_token" value="YOUR_JWT_TOKEN" />
<input type="hidden" name="product_type" value="NFT" />
<!-- NFT-specific fields -->
<input type="hidden" name="wallet_address" value="0x1234...abcd" />
<input type="hidden" name="collection_address" value="0xNFT...collection" />
<input type="hidden" name="token_id" value="1" />
<input type="hidden" name="token_price" value="0.05" />
<input type="hidden" name="token_name" value="Epic NFT #1" />
<input type="hidden" name="user_email" value="user@example.com" />
<input type="hidden" name="chain_id" value="1" />
</form>
React / Next.js
For React applications, we provide a ready-to-use component that handles all the iframe communication: