API Integration

This guide covers the technical implementation of Merso BNPL API integration for game companies.

⚠️ Before Starting

Prior to integrating the Merso BNPL API into your game, follow these preliminary steps:

When a company expresses the desire to integrate BNPL into their game, we generate an API Key and a Game ID for them. These are unique identifiers for your project and should be kept confidential to prevent unauthorized access. The JWT is generated when you first authenticate with the /auth endpoint and will expire every 12 hours, requiring you to re-authenticate to continue making requests.

To call the /auth endpoint, the client needs to send the following parameters in the request body:

  • gameId

  • apiKey

We provide both parameters to the companies once they decide to implement BNPL in their games.

0. Auth

Endpoint: POST /auth

Purpose: Verify API connectivity and status

Request:

curl -X POST https://api3.dev.merso.io/auth \
  -H "Content-Type: application/json" \
  -d '{
    "game_id": "YOUR_GAME_ID",
    "api_key": "YOUR_API_KEY"
  }'

Response:

{
    "authResult": {
        "token": "YOUR_NEW_JWT_TOKEN",
        "expires_at": "2025-08-05T21:21:13.000Z"
    }
}

Example Request Body:

const axios = require('axios');

async function authenticateGame() {
  try {
    const response = await axios.post('/auth', {
      gameid: 'exampleGameId',
      apikey: 'exampleApiKey'
    });
    console.log('Authentication successful:', response.data.authResult);
  } catch (error) {
    if (error.response) {
      console.error('Error:', error.response.data.error);
    } else {
      console.error('Failed to authenticate game. Error Message:', error.message);
    }
  }
}

🎯 Integration Overview

The Merso BNPL API provides three core endpoints for game integration:

  • /health - API health check

  • /user-approval - Approve ERC20 token spending

  • /buy-token-with-merso - Purchase NFT with BNPL

🔐 Authentication

JWT Token Setup

During onboarding, you'll receive a custom Game ID token for API access:

// Your custom GameID (provided during onboarding)
const GAME_ID = 'YOUR_GAME_ID';

// API base URL for your game
const API_BASE_URL = 'https://api.merso.io/game/YOUR_GAME_ID';

Request Headers

All API requests require these headers:

const headers = {
  'Content-Type': 'application/json',
  'Authorization': `Bearer ${JWT_TOKEN}`
};

📋 API Endpoints

1. Health Check

Endpoint: GET /health

Purpose: Verify API connectivity and status

Request:

curl -X GET "https://api3.dev.merso.io/health"

Response:

{
  "success": true,
  "message": "Hello World, Merso BNPL Backend",
}

JavaScript Example:

async function checkAPIHealth() {
  try {
    const response = await fetch(`https://api3.dev.merso.io/health`, {
      method: 'GET',
      headers: headers
    });
    
    const data = await response.json();
    console.log('API Status:', data.status);
    return data;
  } catch (error) {
    console.error('Health check failed:', error);
  }
}

2. User Approval

Endpoint: POST /user-approval

Purpose: Approve Merso BNPL to spend user's ERC20 tokens

Request :

curl -X POST "https://api3.dev.merso.io/user-approval" \
  -H "Authorization: Bearer <your_token>" \
  -H "Content-Type: application/json" \
  -d '{
    "userAddress": "VALID_USER_ADDRESS",
    "userEmail": "user@email.com",
    "gameTokenAddress": "YOUR_GAME_ERC20_ADDRESS",
    "tokenPrice": "PRICE_IN_WEI",
    "collectionAddress": "YOUR_GAME_ERC721_ADDRESS"
}'

Parameters:

  • userAddress (string): User's wallet address

  • userEmail (string): User's in-game email

  • tokenAddress (string): ERC20 token contract address

  • amount (string): Amount to approve (in wei)

Response:

{
  "success": true,
  "txData": {
    "to": "0x1234567890123456789012345678901234567890",
    "from": "0x742d35Cc6634C0532925a3b8D4C9db96C4b4d8b6",
    "data": "0xa9059cbb000000000000000000000000...",
    "gasPrice": "20000000000",
    "nonce": 5,
    "value": "0"
  },
}

JavaScript Example:

async function approveTokens(userAddress, tokenAddress, amount) {
  try {
	const approvalResponse = await fetch(
        `https://api3.dev.merso.io/user-approval`,
        {
          method: "POST",
          headers: {
            "Content-Type": "application/json",
          },
          body: JSON.stringify({
            userAddress: walletAddress,
            userEmail: "test@test.com",
            tokenPrice: tokenPrice,
            gameTokenAddress: MOCK_ERC20_ADDRESS,
          }),
        }
      );
    
      if (!approvalResponse.ok) {
        throw new Error("Approval API request failed");
      }
      const approvalData: ApiResponse = await approvalResponse.json();
      
      // Execute approval transaction
      const approvalTx = await signer.sendTransaction({
        to: approvalData.txData.to,
        from: approvalData.txData.from,
        data: approvalData.txData.data,
        gasPrice: approvalData.txData.gasPrice,
        nonce: approvalData.txData.nonce,
        value: approvalData.txData.value,
      });

      // Wait for approval transaction to be mined
      await approvalTx.wait();

  } catch (error) {
    console.error('Approval failed:', error);
    throw error;
  }
}

3. Buy Token with Merso

Endpoint: POST /buy-token-with-merso

Purpose: Purchase NFT using BNPL functionality

Request:

curl -X POST "https://api3.dev.merso.io/buy-token-with-merso" \
  -H "Authorization: Bearer <your_token>" \
  -H "Content-Type: application/json" \
  -d '{
    "userAddress": "VALID_USER_ADDRESS",
    "tokenId": "1",
    "tokenPrice": "PRICE_IN_WEI",
    "nftCollectionAddress": "YOUR_GAME_ERC721_ADDRESS"
}'

Parameters:

  • userAddress (string): User's wallet address

  • tokenId (string): NFT token ID to purchase

  • tokenPrice (string): Total price in wei

  • nftCollectionAddress (string): NFT collection contract address

Response:

{
  "success": true,
  "txData": {
    "to": "0x1234567890123456789012345678901234567890",
    "from": "0x742d35Cc6634C0532925a3b8D4C9db96C4b4d8b6",
    "data": "0x...",
    "gasPrice": "20000000000",
    "nonce": 6,
    "value": "0"
  },
}

JavaScript Example:

async function buyNFTWithMerso(userAddress, tokenId, price, collectionAddress) {
  try {
    const response = await fetch(`https://api3.dev.merso.io/buy-token-with-merso`, {
      method: 'POST',
      headers: headers,
      body: JSON.stringify({
        userAddress,
        tokenId,
        price,
        collectionAddress
      })
    });
    const buyResponse = await fetch(
        `${API_BASE_URL}/buy-token-with-merso`,
        {
          method: "POST",
          headers: {
            "Content-Type": "application/json",
          },
          body: JSON.stringify({
            nftCollectionAddress: MOCK_ERC721_ADDRESS,
            tokenId: tokenId,
            tokenPrice: tokenPrice,
            userAddress: walletAddress,
          }),
        }
      );
    
      if (!buyResponse.ok) {
        throw new Error("Buy API request failed");
      }

      const buyData: ApiResponse = await buyResponse.json();
      
      // Execute buy transaction
      const buyTx = await signer.sendTransaction({
        to: buyData.txData.to,
        from: buyData.txData.from,
        data: buyData.txData.data,
        gasPrice: buyData.txData.gasPrice,
        nonce: buyData.txData.nonce,
        value: buyData.txData.value,
      });

	  // Wait for buy transaction to be mined
      await buyTx.wait();
      
  } catch (error) {
    console.error('NFT purchase failed:', error);
    throw error;
  }
}

🔄 Integration Flow

Complete Purchase Flow

  const handleBuy = async (tokenId: number) => {

	if (!window.ethereum || !walletAddress) return;
    setIsLoading(true);
    setError("");

	try {
      const provider = new ethers.BrowserProvider(window.ethereum as any);
      const signer = await provider.getSigner();
      const tokenPrice = ethers.parseEther("100").toString();

      // Step 1: Call your API for approval transaction
      const approvalResponse = await fetch(
        `https://api3.dev.merso.io/user-approval`,
        {
          method: "POST",
          headers: {
            "Content-Type": "application/json",
          },
          body: JSON.stringify({
            userAddress: walletAddress,
            userEmail: "test@test.com",
            tokenPrice: tokenPrice,
            gameTokenAddress: MOCK_ERC20_ADDRESS,
          }),
        }
      );

      if (!approvalResponse.ok) {
        throw new Error("Approval API request failed");
      }

      const approvalData: ApiResponse = await approvalResponse.json();

      if (!approvalData.success) {
        throw new Error("Approval API returned error");
      }

      // Execute approval transaction
      const approvalTx = await signer.sendTransaction({
        to: approvalData.txData.to,
        from: approvalData.txData.from,
        data: approvalData.txData.data,
        gasPrice: approvalData.txData.gasPrice,
        nonce: approvalData.txData.nonce,
        value: approvalData.txData.value,
      });

      // Wait for approval transaction to be mined
      await approvalTx.wait();
      console.log("Approval transaction confirmed:", approvalTx.hash);

	  // Step 2: Call your API for buy transaction
      const buyResponse = await fetch(
        `https://api3.dev.merso.io/buy-token-with-merso`,
        {
          method: "POST",
          headers: {
            "Content-Type": "application/json",
          },

		  body: JSON.stringify({
            nftCollectionAddress: MOCK_ERC721_ADDRESS,
            tokenId: tokenId,
            tokenPrice: tokenPrice,
            userAddress: walletAddress,
          }),
        }
      );

      if (!buyResponse.ok) {
        throw new Error("Buy API request failed");
      }

      const buyData: ApiResponse = await buyResponse.json();

      if (!buyData.success) {
        throw new Error("Buy API returned error");
      }

      // Execute buy transaction
      const buyTx = await signer.sendTransaction({
        to: buyData.txData.to,
        from: buyData.txData.from,
        data: buyData.txData.data,
        gasPrice: buyData.txData.gasPrice,
        nonce: buyData.txData.nonce,
        value: buyData.txData.value,
      });

      // Wait for buy transaction to be mined
      await buyTx.wait();
      console.log("Buy transaction confirmed:", buyTx.hash);

      // Optionally reload NFTs
      await loadNFTs();
    } catch (err: any) {
      setError(
        "Transaction failed: " +
          (err?.reason || err?.message || "Unknown error")
      );
      console.error(err);
    }
    setIsLoading(false);
  };```

🪄 Optional endpoints

Endpoint: GET /user-loans

Purpose: Retrieves loan information for a specific user

Request:

curl -X GET "https://api3.dev.merso.io/user-loans?userEmail=USER_EMAIL" \
  -H "Authorization: Bearer <your_token>"

Parameters:

  • userId (string): User's ID

Response:

{
    "loans": [
        {
            "id": "67829bea-00ef-45eb-bc23-0d6798e048f5",
            "user_id": "USER_ID",
            "collection_id": "19d210ae-5836-4cf0-ad7a-8d8a6b0f08b3",
            "nft_id": "nftCromo2",
            "initial_payment_usd": 250,
            "splits_left_no": 4,
            "price_usd": 500,
            "paid_status": 1,
            "game_id": "e6d8a143-3d70-45e2-9e4c-75ff7805e0fd",
            "initial_splits_payments": 2,
            "initial_payment_token": 2500000000000000000,
            "price_token": 5000000000000000000,
            "wallet_address": "0x1234567890ABCDEF1234567890ABCDEF12345678",
            "created_at": "2025-07-28T08:02:57.744Z",
            "paid_date": "2025-07-29T05:00:54.838Z"
        }
    ]
}

Example Usage

Here's an example of how you can call the GET /user-loans endpoint using JavaScript:

const fetchUserLoans = async (userId, jwtToken) => {
  try {
    const response = await fetch(`https://api3.dev.merso.io/user-loans?userEmail=${userEmail}`, {
      method: 'GET',
      headers: {
        'Content-Type': 'application/json',
        'Authorization': `Bearer ${jwtToken}`
      }
    });

    if (!response.ok) {
      throw new Error(`Error: ${response.statusText}`);
    }

    const data = await response.json();
    console.log('User Loans:', data);
    return data;
  } catch (error) {
    console.error('Failed to fetch user loans:', error);
    throw error;
  }
};

// Example usage
const userEmail = 'USER_EMAIL';
const jwtToken = 'YOUR_JWT_TOKEN';

fetchUserLoans(userId, jwtToken)
  .then(loans => {
    console.log('Loans data:', loans);
  })
  .catch(error => {
    console.error('Failed to get loans:', error);
  });

Last updated