Prerequisites
Before starting, ensure you have:- A React/Next.js application
 - Wallet connection functionality (MetaMask, WalletConnect, etc.)
 - Access to a Meridian facilitator service
 - Required dependencies installed
 
Installation
Install the necessary packages:Copy
npm install viem axios x402/client x402/types
# or
yarn add viem axios x402/client x402/types
# or
pnpm add viem axios x402/client x402/types
Environment Setup
Configure your environment variables:Copy
# .env.local
NEXT_PUBLIC_MERIDIAN_PK=pk_...
NEXT_PUBLIC_FACILITATOR_URL=https://api.mrdn.finance/v1
Core Implementation
1. Wallet Connection Setup
First, set up wallet connection functionality:Copy
import { createWalletClient, custom, WalletClient } from "viem";
import { baseSepolia } from "viem/chains";
// Extend Window interface for wallet access
interface ExtendedWindow extends Window {
  ethereum?: {
    request: (args: { method: string; params?: unknown[] }) => Promise<unknown>;
    on: (event: string, callback: (...args: unknown[]) => void) => void;
    removeListener: (
      event: string,
      callback: (...args: unknown[]) => void
    ) => void;
  };
}
const getExtendedWindow = (): ExtendedWindow | null => {
  if (typeof window !== "undefined") {
    return window as ExtendedWindow;
  }
  return null;
};
const connectWallet = async () => {
  const extendedWindow = getExtendedWindow();
  if (extendedWindow?.ethereum) {
    try {
      // Request account access
      const accounts = (await extendedWindow.ethereum.request({
        method: "eth_requestAccounts",
      })) as string[];
      if (accounts.length > 0) {
        const account = accounts[0] as `0x${string}`;
        // Create wallet client with browser wallet
        const walletClient = createWalletClient({
          account,
          transport: custom(extendedWindow.ethereum),
          chain: baseSepolia,
        });
        return { account, walletClient };
      }
    } catch (error) {
      console.error("Error connecting to wallet:", error);
      throw error;
    }
  } else {
    throw new Error("Please install MetaMask or another wallet extension");
  }
};
2. Authentication Headers
Create functions to generate authentication headers:Copy
// Function to create Bearer Auth header with API key
const createAuthHeader = () => {
  const apiKey = process.env.NEXT_PUBLIC_MERIDIAN_PK;
  if (!apiKey) {
    console.warn("API key not configured. Requests may fail authentication.");
    return {};
  }
  return {
    Authorization: `Bearer ${apiKey}`,
  };
};
// Function to create headers for forwarding to x402 middleware
const createForwardingHeaders = () => {
  const apiKey = process.env.NEXT_PUBLIC_MERIDIAN_PK;
  if (!apiKey) {
    console.warn("API key not configured for forwarding");
    return {};
  }
  return {
    Authorization: `Bearer ${apiKey}`,
  };
};
// Function to create Meridian-specific forwarding headers
const createMeridianForwardingHeaders = () => {
  const meridianPk = process.env.NEXT_PUBLIC_MERIDIAN_PK;
  if (!meridianPk) {
    console.warn("Meridian public key not configured, using default API key");
    return createForwardingHeaders();
  }
  return {
    Authorization: `Bearer ${meridianPk}`,
  };
};
3. Making API Requests
Implement the core API request logic with payment handling:Copy
import axios from "axios";
import { createPaymentHeader, selectPaymentRequirements } from "x402/client";
import { ChainIdToNetwork, PaymentRequirementsSchema } from "x402/types";
interface PaymentRequirements {
  amount: string;
  recipient: string;
  network: string;
  scheme: string;
  maxAmountRequired: string;
  resource: string;
  mimeType: string;
  payTo: string;
  maxTimeoutSeconds: number;
  asset: string;
}
const requestSample = async (client: WalletClient, account: string) => {
  try {
    // Make a regular API request to the facilitator with auth headers
    const response = await axios.get("http://localhost:4021/v1/samples", {
      headers: {
        Authorization: `Bearer ${process.env.NEXT_PUBLIC_MERIDIAN_PK}`,
      },
    });
    console.log("Sample response:", response.data);
    // Check if payment is required
    if (response.status === 402) {
      // Handle payment manually
      console.log("Payment required:", response.data.accepts[0]);
      await handlePayment(
        response.data.accepts[0],
        response.data.x402Version,
        response.data.accepts,
        client,
        account
      );
    }
    return response.data;
  } catch (error) {
    console.error("Error requesting sample:", error);
    if (axios.isAxiosError(error) && error.response?.status === 402) {
      // Handle payment requirement
      console.log("Payment required:", error.response.data.accepts[0]);
      await handlePayment(
        error.response.data.accepts[0],
        error.response.data.x402Version,
        error.response.data.accepts,
        client,
        account
      );
    } else {
      throw error;
    }
  }
};
4. Payment Handling
Implement the payment processing logic:Copy
const handlePayment = async (
  paymentDetails: PaymentRequirements,
  x402Version: number,
  accepts: PaymentRequirements[],
  client: WalletClient,
  account: string
) => {
  try {
    const parsed = accepts.map((x) => PaymentRequirementsSchema.parse(x));
    // Get chain ID from the client's chain property
    const chainId = client.chain?.id;
    const selectedPaymentRequirements = selectPaymentRequirements(
      parsed,
      chainId ? ChainIdToNetwork[chainId] : undefined,
      "exact"
    );
    console.log("selectedPaymentRequirements", selectedPaymentRequirements);
    const paymentHeader = await createPaymentHeader(
      client as unknown as Parameters<typeof createPaymentHeader>[0],
      x402Version,
      selectedPaymentRequirements
    );
    console.log("Payment header:", paymentHeader);
    // Send the payment header to our facilitator
    const facilitatorResponse = await axios.get(
      "http://localhost:4021/v1/settle",
      {
        headers: {
          Authorization: `Bearer ${process.env.NEXT_PUBLIC_MERIDIAN_PK}`,
          "X-PAYMENT": paymentHeader,
          "Access-Control-Expose-Headers": "X-PAYMENT-RESPONSE",
        },
      }
    );
    console.log("Facilitator response:", facilitatorResponse.data);
    // Request the sample again with the payment header
    const response = await axios.get("http://localhost:4021/v1/samples", {
      headers: {
        Authorization: `Bearer ${process.env.NEXT_PUBLIC_MERIDIAN_PK}`,
        "X-PAYMENT": paymentHeader,
        "Access-Control-Expose-Headers": "X-PAYMENT-RESPONSE",
      },
    });
    console.log("Sample response:", response.data);
    return response.data;
  } catch (error: unknown) {
    console.error("Payment error:", error);
    const errorMessage =
      error instanceof Error ? error.message : "Unknown error";
    throw new Error("Payment failed: " + errorMessage);
  }
};