Skip to main content
This guide demonstrates how to consume paid APIs using the x402 protocol with the Faremeter Client SDKs. We’ll walk through creating a simple buyer application that pays for string utility services.

Prerequisites

Ensure you have a funded wallet with AxiosUSD on SKALE Base Sepolia Testnet and Bun installed on your machine. If you do not have a private key, you can create one using MetaMask or generate one with the Cast tool from Foundry.
cast wallet new

Step 1: Create a New Project

Initialize a new project using Bun:
bun init x402-buyer && cd x402-buyer
Install the required dependencies:
bun add @faremeter/fetch @faremeter/payment-evm @faremeter/wallet-evm viem x402-fetch
Create the necessary folders and files:
mkdir src && touch src/custom-chain.ts src/skale-base-sepolia.ts src/base-sepolia.ts .env

Step 2: Set Up Your Wallet

Add your private key to the .env file:
PRIVATE_KEY=0x_your_private_key_here

Step 3: Setup SKALE Base Sepolia Testnet

If you prefer to use Base Sepolia Testnet, you can skip this step and use base in Step 4.
Create src/custom-chain.ts:
src/custom-chain.ts
// Note that this should satisfy the `Chain` type from viem, but due to Typescript type-safety being super strange cross version; the recommended approach is to use the `any` type to avoid issues.
export const skaleBaseSepoliaTestnetV1: any = {
  id: 2140350733,
  name: "SKALE Base Sepolia Testnet",
  rpcUrls: {
    default: {
      http: ["https://base-sepolia-testnet.skalenodes.com/v1/basic-defiant-hadar"]
    }
  },
  blockExplorers: {
    default: {
      name: "Blockscout",
      url: "https://base-sepolia-testnet-explorer.skalenodes.com:10011"
    }
  },
  nativeCurrency: {
    name: "Credits",
    decimals: 18,
    symbol: "CRED"
  }
};

Step 4: Create the Buyer Client

import { createLocalWallet } from "@faremeter/wallet-evm";
import { createPaymentHandler } from "@faremeter/payment-evm/exact";
import { wrap as wrapFetch } from "@faremeter/fetch";
import { skaleBaseSepoliaTestnetV1 } from "./custom-chain";

const PRIVATE_KEY = process.env.PRIVATE_KEY;

if (!PRIVATE_KEY) {
  throw new Error("PRIVATE_KEY environment variable is required");
}

// Create wallet for payments
const wallet = await createLocalWallet(
  skaleBaseSepoliaTestnetV1,
  PRIVATE_KEY
);

// Create payment-enabled fetch
const fetchWithPayment = wrapFetch(fetch, {
  handlers: [createPaymentHandler(wallet, {
    asset: {
      address: "0x61a26022927096f444994dA1e53F0FD9487EAfcf",
      contractName: "Axios USD"
    }
  })]
});

const BASE_URL = "https://string-utils.dirtroad.dev";

async function main() {

    try {
        const endpoint = `${BASE_URL}/tools/paid/snake-to-camel`;
        const payload = { str: "snake_case_string" };
        
        console.log(`\n🧪 Testing ${description}`);
        console.log(`📍 Endpoint: ${endpoint}`);
        console.log(`📤 Payload:`, JSON.stringify(payload, null, 2));
        
        const response = await fetchWithPayer(endpoint, {
            method: "POST",
            headers: {
                "Content-Type": "application/json",
                "Accept": "application/json, text/event-stream",
            },
            body: JSON.stringify(payload),
        });

        if (response.ok) {
        const res = await response.json();
        console.log(`✅ Success:`, res);
        } else {
        console.log(`❌ Error: ${response.status} - ${response.statusText}`);
        }
    } catch (error: any) {
        console.error(`💥 Fetch failed:`, error?.response?.data?.error || error.message);
    }
}

main().catch(console.error);

Step 5: Run the Buyer

Execute your buyer application:

# Run the Buyer on SKALE Base Sepolia Testnet
bun run src/skale-base-sepolia.ts

# Run the Buyer on Base Sepolia Testnet
run run src/base-sepolia.ts
Expected output:
🧪 Testing Snake to Camel Case
📍 Endpoint: /tools/paid/snake-to-camel
📤 Payload: {
  "str": "snake_case_string"
}
✅ Success: { original: 'snake_case_string', result: 'snakeCaseString' }

How It Works

The x402 payment flow follows these steps:
  1. Request Service: Your app makes a normal HTTP request to a paid API
  2. Payment Required: Server responds with 402 Payment Required and payment details
  3. Payment Authorization: Your script automatically creates and signs payment authorization using the Faremeter SDK
  4. Payment Execution: SDK submits payment to the facilitator (e.g., https://facilitator.dirtroad.dev)
  5. Service Access: Once payment is confirmed, the API processes your request
  6. Result Returned: You receive the API response as normal

Configuration Options

Payment Handler

The createPaymentHandler supports various configurations:
// For exact amount payments
const handler = createPaymentHandler(wallet, {
  asset: "USDC" // USDC
});

<Note>
To override the asset to something that is not explicitly available in the @faremeter/info package, you can use the `ContractInfo` type as the asset object which requires `address` and `contractName`.
</Note>

// For specific token contracts (SKALE)
const skaleHandler = createPaymentHandler(wallet, {
  asset: {
    address: "0x61a26022927096f444994dA1e53F0FD9487EAfcf", // Axios USD
    contractName: "Axios USD"
  }
});
Most SDKs including the official ones from Coinbase currently only support USDC which is why the config for the SKALE example is more complex since we are overriding Faremeter’s default behavior.

Error Handling

Common issues and solutions:
  1. “No Applicable Payer”
    • If the config on the server for the custom token is wrong, this is generally going to be the error you will see
  2. “Payment failed”
    • Verify the API endpoint supports x402 payments
    • Verify the accepts response from the endpoint
    • Check if the facilitator is operational
  3. “Invalid signature”
    • Ensure you have a valid private key
    • Verify your config address is correct

Testing Different Services

You can test the buyer against various x402 services including other String Utils.

Next Steps