1

Add the Iframe to Your Page

The simplest way to integrate Merso Modal is to embed it as an iframe. Add this HTML to your page:

HTML
<!-- Modal Overlay Container -->
<div id="modal-overlay" style="
  display: none;
  position: fixed;
  inset: 0;
  background: rgba(0,0,0,0.6);
  align-items: center;
  justify-content: center;
  z-index: 1000;
">
  <iframe
    id="merso-modal"
    src="https://modal.dev.merso.io/modal"
    name="merso-modal-frame"
    width="480"
    height="600"
    style="border: none; border-radius: 1rem; background: white;"
    title="Merso Payment Modal"
    allow="payment"
  ></iframe>
</div>
2

Get Your JWT Token

Before sending data to the modal, authenticate with the Merso API to get a JWT token:

JavaScript
// Server-side: Get JWT token from Merso API
async function getJwtToken() {
  const response = await fetch('https://modal.dev.merso.io/auth', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({
      game_id: 'YOUR_GAME_ID',
      api_key: 'YOUR_API_KEY'
    })
  });
  
  const data = await response.json();
  return data.authResult.token; // Valid for 12 hours
}
⚠️ Security Note

Never expose your API Key in client-side code. Generate the JWT token server-side and pass it securely to your frontend.

3

Send Item/NFT Data

There are two methods to send product data to the modal. Choose the one that best fits your architecture:

3.a via POST (Recommended)

Use a hidden form to POST data directly to the iframe. When a user clicks "Buy" on any product in your store, dynamically fill the form and submit it:

HTML + JavaScript
<!-- Your Game Store -->
<div class="store-items">
  <div class="item-card" onclick="buyItem({id: 101, name: 'Diamond Sword', price: 29.99})">
    <img src="sword.png" alt="Diamond Sword">
    <h3>Diamond Sword</h3>
    <p>$29.99</p>
    <button>Buy Now</button>
  </div>
  
  <div class="item-card" onclick="buyItem({id: 102, name: 'Golden Shield', price: 49.99})">
    <img src="shield.png" alt="Golden Shield">
    <h3>Golden Shield</h3>
    <p>$49.99</p>
    <button>Buy Now</button>
  </div>
  
  <div class="item-card" onclick="buyItem({id: 103, name: 'Health Potion x10', price: 9.99})">
    <img src="potion.png" alt="Health Potion">
    <h3>Health Potion x10</h3>
    <p>$9.99</p>
    <button>Buy Now</button>
  </div>
</div>

<!-- Hidden form (auto-filled when user clicks Buy) -->
<form id="merso-form" method="POST" action="https://modal.dev.merso.io/modal" 
      target="merso-modal-frame" style="display:none;">
  <input type="hidden" name="jwtToken" id="form-jwt">
  <input type="hidden" name="productType" id="form-productType">
  <input type="hidden" name="itemPrice" id="form-itemPrice">
  <input type="hidden" name="itemId" id="form-itemId">
  <input type="hidden" name="itemName" id="form-itemName">
  <input type="hidden" name="playerEmail" id="form-playerEmail">
</form>

<script>
// Called when user clicks any "Buy Now" button
function buyItem(item) {
  // Fill the hidden form with the selected item's data
  document.getElementById('form-jwt').value = JWT_TOKEN;
  document.getElementById('form-productType').value = 'ITEM';
  document.getElementById('form-itemPrice').value = item.price;
  document.getElementById('form-itemId').value = item.id;
  document.getElementById('form-itemName').value = item.name;
  document.getElementById('form-playerEmail').value = currentUser.email;
  
  // Submit form → loads modal in iframe with item data
  document.getElementById('merso-form').submit();
  
  // Show the modal overlay
  document.getElementById('modal-overlay').style.display = 'flex';
}
</script>

3.B via postMessage

Send product data via postMessage after the iframe loads. Best for SPAs and dynamic content:

JavaScript
const iframe = document.getElementById('merso-modal');

iframe.onload = () => {
  // For Item purchases
  iframe.contentWindow.postMessage({
    type: 'api-config',
    jwtToken: 'YOUR_JWT_TOKEN',
    
    // Item data
    productType: 'ITEM',
    itemPrice: 29.99,
    itemId: 123,
    itemName: 'Premium Sword',
    playerEmail: 'player@example.com',
    playerLevel: 42,
    playerCountry: 'US'
  }, '*');
};

// For NFT purchases, use this instead:
iframe.onload = () => {
  iframe.contentWindow.postMessage({
    type: 'api-config',
    jwtToken: 'YOUR_JWT_TOKEN',
    
    // NFT data
    productType: 'NFT',
    walletAddress: '0x1234...abcd',
    collectionAddress: '0xNFT...collection',
    tokenId: '1',
    tokenPrice: '0.05',
    tokenName: 'Epic NFT #1',
    userEmail: 'user@example.com',
    chainId: '1'
  }, '*');
};
4

Listen for Payment Events

The modal communicates payment results via postMessage. Listen for these events:

JavaScript
window.addEventListener('message', (event) => {
  // Ignore Stripe's internal messages
  if (event.origin === 'https://js.stripe.com') return;

  const { data } = event;

  switch (data?.type) {
    case 'payment-success':
      console.log('Payment successful!', {
        paymentIntentId: data.paymentIntentId,
        totalAmount: data.totalAmount
      });
      closeModal();
      showSuccessMessage();
      break;

    case 'payment-cancelled':
      console.log('Payment cancelled');
      closeModal();
      break;

    case 'crypto-payment-success':
      console.log('NFT minted!', {
        transactionHash: data.transactionHash,
        tokenId: data.tokenId
      });
      closeModal();
      break;

    case 'modal-choice':
      console.log('User selected:', data.choice);
      // Track analytics, etc.
      break;
  }
});

function closeModal() {
  document.getElementById('modal-overlay').style.display = 'none';
}

📝 Complete Examples

Here are complete working examples for both integration methods:

A Complete Example (POST)

HTML
<!DOCTYPE html>
<html>
<head>
  <title>My Game Shop - POST Method</title>
</head>
<body>
  <button id="buy-item-btn">Buy Premium Sword - $29.99</button>

  <!-- Modal Overlay -->
  <div id="modal-overlay" style="display:none; position:fixed; inset:0; 
       background:rgba(0,0,0,0.6); align-items:center; justify-content:center; z-index:1000;">
    <iframe id="merso-modal" name="merso-modal-frame" width="480" height="600"
            style="border:none; border-radius:1rem;"
            allow="payment"></iframe>
  </div>

  <!-- Hidden Form for POST -->
  <form id="merso-form" method="POST" action="https://modal.dev.merso.io/modal" 
        target="merso-modal-frame" style="display:none;">
    <input type="hidden" name="jwtToken" id="form-jwt">
    <input type="hidden" name="productType" id="form-productType">
    <input type="hidden" name="itemPrice" id="form-itemPrice">
    <input type="hidden" name="itemId" id="form-itemId">
    <input type="hidden" name="itemName" id="form-itemName">
    <input type="hidden" name="playerEmail" id="form-playerEmail">
    <input type="hidden" name="playerLevel" id="form-playerLevel">
    <input type="hidden" name="playerCountry" id="form-playerCountry">
  </form>

  <script>
    let JWT_TOKEN = null;
    
    // Get JWT token from your backend
    async function initAuth() {
      const response = await fetch('/api/get-merso-token');
      const data = await response.json();
      JWT_TOKEN = data.token;
    }
    
    initAuth();
    
    document.getElementById('buy-item-btn').addEventListener('click', () => {
      // Set form values
      document.getElementById('form-jwt').value = JWT_TOKEN;
      document.getElementById('form-productType').value = 'ITEM';
      document.getElementById('form-itemPrice').value = '29.99';
      document.getElementById('form-itemId').value = '123';
      document.getElementById('form-itemName').value = 'Premium Sword';
      document.getElementById('form-playerEmail').value = 'player@example.com';
      document.getElementById('form-playerLevel').value = '42';
      document.getElementById('form-playerCountry').value = 'US';
      
      // Submit form (POSTs to iframe)
      document.getElementById('merso-form').submit();
      
      // Show modal overlay
      document.getElementById('modal-overlay').style.display = 'flex';
    });

    window.addEventListener('message', (event) => {
      if (event.origin === 'https://js.stripe.com') return;
      
      if (event.data?.type === 'payment-success') {
        alert('Payment successful! Thank you for your purchase.');
        document.getElementById('modal-overlay').style.display = 'none';
      }
      
      if (event.data?.type === 'payment-cancelled') {
        document.getElementById('modal-overlay').style.display = 'none';
      }
    });

    document.getElementById('modal-overlay').addEventListener('click', (e) => {
      if (e.target.id === 'modal-overlay') {
        e.target.style.display = 'none';
      }
    });
  </script>
</body>
</html>

B Complete Example (postMessage)

HTML
<!DOCTYPE html>
<html>
<head>
  <title>My Game Shop - postMessage Method</title>
</head>
<body>
  <button id="buy-item-btn">Buy Premium Sword - $29.99</button>

  <div id="modal-overlay" style="display:none; position:fixed; inset:0; 
       background:rgba(0,0,0,0.6); align-items:center; justify-content:center; z-index:1000;">
    <iframe id="merso-modal" width="480" height="600"
            style="border:none; border-radius:1rem;"
            allow="payment"></iframe>
  </div>

  <script>
    const MODAL_URL = 'https://modal.dev.merso.io/modal';
    let JWT_TOKEN = null;
    
    // Get JWT token from your backend
    async function initAuth() {
      const response = await fetch('/api/get-merso-token');
      const data = await response.json();
      JWT_TOKEN = data.token;
    }
    
    initAuth();
    
    document.getElementById('buy-item-btn').addEventListener('click', () => {
      const iframe = document.getElementById('merso-modal');
      iframe.src = MODAL_URL;
      
      iframe.onload = () => {
        // Send all data via postMessage
        iframe.contentWindow.postMessage({
          type: 'api-config',
          jwtToken: JWT_TOKEN,
          
          // Item data
          productType: 'ITEM',
          itemPrice: 29.99,
          itemId: 123,
          itemName: 'Premium Sword',
          playerEmail: 'player@example.com',
          playerLevel: 42,
          playerCountry: 'US'
        }, '*');
      };

      document.getElementById('modal-overlay').style.display = 'flex';
    });

    window.addEventListener('message', (event) => {
      if (event.origin === 'https://js.stripe.com') return;
      
      if (event.data?.type === 'payment-success') {
        alert('Payment successful! Thank you for your purchase.');
        document.getElementById('modal-overlay').style.display = 'none';
      }
      
      if (event.data?.type === 'payment-cancelled') {
        document.getElementById('modal-overlay').style.display = 'none';
      }
    });

    document.getElementById('modal-overlay').addEventListener('click', (e) => {
      if (e.target.id === 'modal-overlay') {
        e.target.style.display = 'none';
      }
    });
  </script>
</body>
</html>

➡️ Next Steps