🔄 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:

HTML
<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:

JavaScript
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'
  }, '*');
};
⚠️ Security Note

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:

HTML
<!-- 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>
💡 Why Form POST?

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:

HTML
<!-- 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>
JavaScript
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:

HTML (Form fields)
<!-- 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:

CSS
#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:

HTML
<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: