Integration Overview
CheckPoint enables pay-per-request micropayments using the x402 protocol. There are two integration paths depending on your role:
🏪 Merchants
Accept USDC payments for your API endpoints
- Return
402 Payment Requiredwith payment details - Receive payment in
X-Paymentheader - Verify/settle with CheckPoint
- Serve protected content
🤖 Agents
Pay for APIs automatically with USDC
- Request protected resource
- Parse
402response - Sign EIP-3009 authorization
- Retry with
X-Paymentheader
Payment Flow
Merchant: Express.js Integration
Complete example for accepting payments in an Express.js server.
Install Dependencies
npm install express
Configure Your Wallet
Set your wallet address that will receive USDC payments.
Protect Your Endpoint
Return 402 when no payment, verify with CheckPoint when payment received.
Serve Premium Content
After successful verification, return your protected resource.
// server.js - Express.js example
import express from 'express';
const app = express();
app.use(express.json());
// Your wallet address (receives USDC payments)
const MERCHANT_WALLET = '0xYourWalletAddress';
const CHECKPOINT_URL = 'https://checkpoint-pied.vercel.app';
// Protected endpoint that requires payment
app.get('/api/premium-data', async (req, res) => {
const paymentHeader = req.headers['x-payment'];
// Step 1: No payment? Return 402 with payment requirements
if (!paymentHeader) {
return res.status(402).json({
x402Version: 2,
accepts: [{
scheme: 'exact',
network: 'eip155:8453', // Base mainnet
maxAmountRequired: '10000', // 0.01 USDC (6 decimals)
resource: `${req.protocol}://${req.get('host')}${req.originalUrl}`,
payTo: MERCHANT_WALLET,
maxTimeoutSeconds: 300,
asset: '0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913', // USDC on Base
extra: {
name: 'Premium Data API',
description: 'Access to premium market data'
}
}]
});
}
// Step 2: Verify the payment with CheckPoint
try {
const verification = await fetch(`${CHECKPOINT_URL}/api/v2/x402/verify`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
payment: paymentHeader,
paymentPayload: {
scheme: 'exact',
network: 'eip155:8453',
maxAmountRequired: '10000',
resource: `${req.protocol}://${req.get('host')}${req.originalUrl}`,
payTo: MERCHANT_WALLET,
maxTimeoutSeconds: 300,
asset: '0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913'
}
})
});
const result = await verification.json();
// Step 3: Check if payment is valid
if (!result.valid) {
return res.status(402).json({
error: 'Payment verification failed',
reason: result.invalidReason
});
}
// Step 4: Optionally settle the payment (execute USDC transfer)
const settlement = await fetch(`${CHECKPOINT_URL}/api/v2/x402/settle`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
payment: paymentHeader,
paymentPayload: {
scheme: 'exact',
network: 'eip155:8453',
maxAmountRequired: '10000',
resource: `${req.protocol}://${req.get('host')}${req.originalUrl}`,
payTo: MERCHANT_WALLET,
maxTimeoutSeconds: 300,
asset: '0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913'
}
})
});
const settlementResult = await settlement.json();
// Step 5: Payment verified! Serve the premium content
res.json({
data: {
premium: true,
message: 'Welcome to premium data!',
marketData: { btc: 97500, eth: 3450 }
},
payment: {
txHash: settlementResult.txHash,
payer: result.payer,
amount: '0.01 USDC'
}
});
} catch (error) {
console.error('Payment processing error:', error);
res.status(500).json({ error: 'Payment processing failed' });
}
});
app.listen(3001, () => console.log('Merchant server on port 3001'));Merchant: Next.js App Router
Integration for Next.js 13+ App Router API routes.
// app/api/premium/route.ts - Next.js App Router example
import { NextRequest, NextResponse } from 'next/server';
const MERCHANT_WALLET = process.env.MERCHANT_WALLET!;
const CHECKPOINT_URL = 'https://checkpoint-pied.vercel.app';
export async function GET(req: NextRequest) {
const paymentHeader = req.headers.get('x-payment');
const resource = req.url;
// No payment? Return 402
if (!paymentHeader) {
return NextResponse.json({
x402Version: 2,
accepts: [{
scheme: 'exact',
network: 'eip155:8453',
maxAmountRequired: '10000',
resource,
payTo: MERCHANT_WALLET,
maxTimeoutSeconds: 300,
asset: '0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913',
extra: { name: 'My API' }
}]
}, { status: 402 });
}
// Verify with CheckPoint
const verification = await fetch(`${CHECKPOINT_URL}/api/v2/x402/verify`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
payment: paymentHeader,
paymentPayload: {
scheme: 'exact',
network: 'eip155:8453',
maxAmountRequired: '10000',
resource,
payTo: MERCHANT_WALLET,
maxTimeoutSeconds: 300,
asset: '0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913'
}
})
});
const result = await verification.json();
if (!result.valid) {
return NextResponse.json({ error: result.invalidReason }, { status: 402 });
}
// Success! Return premium content
return NextResponse.json({
data: 'Your premium content here',
payer: result.payer
});
}Agent: TypeScript with Viem
AI agent implementation using viem for wallet operations.
Install Dependencies
npm install viem
Configure Agent Wallet
Your agent needs a wallet with USDC on Base to pay for services.
Handle 402 Responses
Parse the payment requirements from the 402 response.
Sign EIP-3009
Create a gasless authorization signature for USDC transfer.
Retry with Payment
Include the signed payment in the X-Payment header.
// agent.ts - AI Agent payment with viem
import { createWalletClient, http, encodeFunctionData } from 'viem';
import { base } from 'viem/chains';
import { privateKeyToAccount } from 'viem/accounts';
import { randomBytes } from 'crypto';
const AGENT_PRIVATE_KEY = process.env.AGENT_PRIVATE_KEY as `0x${string}`;
const SERVICE_URL = 'https://api.merchant.com/premium-data';
// USDC contract details for Base
const USDC_ADDRESS = '0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913';
const USDC_DOMAIN = {
name: 'USD Coin',
version: '2',
chainId: 8453,
verifyingContract: USDC_ADDRESS as `0x${string}`
};
const TRANSFER_TYPES = {
TransferWithAuthorization: [
{ name: 'from', type: 'address' },
{ name: 'to', type: 'address' },
{ name: 'value', type: 'uint256' },
{ name: 'validAfter', type: 'uint256' },
{ name: 'validBefore', type: 'uint256' },
{ name: 'nonce', type: 'bytes32' }
]
};
async function payForService() {
// Step 1: Request the protected resource
console.log('Requesting protected resource...');
const response = await fetch(SERVICE_URL);
// Step 2: Handle 402 Payment Required
if (response.status === 402) {
const paymentDetails = await response.json();
const accepted = paymentDetails.accepts[0];
console.log('Payment required:', {
amount: accepted.maxAmountRequired,
payTo: accepted.payTo,
network: accepted.network
});
// Step 3: Create wallet client
const account = privateKeyToAccount(AGENT_PRIVATE_KEY);
const client = createWalletClient({
account,
chain: base,
transport: http()
});
// Step 4: Create EIP-3009 signature
const nonce = `0x${randomBytes(32).toString('hex')}` as `0x${string}`;
const validBefore = BigInt(Math.floor(Date.now() / 1000) + 300);
const signature = await client.signTypedData({
domain: USDC_DOMAIN,
types: TRANSFER_TYPES,
primaryType: 'TransferWithAuthorization',
message: {
from: account.address,
to: accepted.payTo as `0x${string}`,
value: BigInt(accepted.maxAmountRequired),
validAfter: 0n,
validBefore,
nonce
}
});
// Step 5: Create payment payload
const payment = {
x402Version: 2,
scheme: 'exact',
network: accepted.network,
payload: {
signature,
authorization: {
from: account.address,
to: accepted.payTo,
value: accepted.maxAmountRequired,
validAfter: '0',
validBefore: validBefore.toString(),
nonce
}
}
};
// Step 6: Retry request with payment header
console.log('Retrying with payment...');
const paidResponse = await fetch(SERVICE_URL, {
headers: {
'X-Payment': Buffer.from(JSON.stringify(payment)).toString('base64')
}
});
if (paidResponse.ok) {
const data = await paidResponse.json();
console.log('Success! Got premium data:', data);
return data;
} else {
const error = await paidResponse.json();
console.error('Payment failed:', error);
throw new Error(error.error || 'Payment failed');
}
}
// No payment needed
return response.json();
}
// Run the agent
payForService().catch(console.error);Agent: Using @x402-checkpoint/sdk SDK
Simplified integration using our TypeScript SDK.
Install SDK
npm install @x402-checkpoint/sdk viem
// agent-sdk.ts - Using @x402-checkpoint/sdk SDK
import { CheckPointClient } from '@x402-checkpoint/sdk';
import { createWalletClient, http } from 'viem';
import { base } from 'viem/chains';
import { privateKeyToAccount } from 'viem/accounts';
import { randomBytes } from 'crypto';
const checkpoint = new CheckPointClient({
baseUrl: 'https://checkpoint-pied.vercel.app'
});
async function payWithSDK() {
const account = privateKeyToAccount(process.env.AGENT_KEY as `0x${string}`);
// 1. Try to access resource
const response = await fetch('https://api.service.com/data');
if (response.status !== 402) {
return response.json();
}
const { accepts } = await response.json();
const accepted = accepts[0];
// 2. Create payment signature (simplified - use full code above for production)
const client = createWalletClient({ account, chain: base, transport: http() });
const nonce = `0x${randomBytes(32).toString('hex')}` as `0x${string}`;
const validBefore = BigInt(Math.floor(Date.now() / 1000) + 300);
const signature = await client.signTypedData({
domain: { name: 'USD Coin', version: '2', chainId: 8453, verifyingContract: '0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913' },
types: { TransferWithAuthorization: [
{ name: 'from', type: 'address' }, { name: 'to', type: 'address' },
{ name: 'value', type: 'uint256' }, { name: 'validAfter', type: 'uint256' },
{ name: 'validBefore', type: 'uint256' }, { name: 'nonce', type: 'bytes32' }
]},
primaryType: 'TransferWithAuthorization',
message: {
from: account.address, to: accepted.payTo as `0x${string}`,
value: BigInt(accepted.maxAmountRequired),
validAfter: 0n, validBefore, nonce
}
});
// 3. Verify with CheckPoint first (optional but recommended)
const payment = Buffer.from(JSON.stringify({
x402Version: 2, scheme: 'exact', network: accepted.network,
payload: { signature, authorization: {
from: account.address, to: accepted.payTo,
value: accepted.maxAmountRequired,
validAfter: '0', validBefore: validBefore.toString(), nonce
}}
})).toString('base64');
const verification = await checkpoint.verify({
payment,
paymentPayload: accepted
});
if (!verification.valid) {
throw new Error(`Payment invalid: ${verification.invalidReason}`);
}
// 4. Make payment request
const paidResponse = await fetch('https://api.service.com/data', {
headers: { 'X-Payment': payment }
});
return paidResponse.json();
}Agent: Python Implementation
Python agent for AI/ML applications.
Install Dependencies
pip install requests eth-account
# agent.py - Python AI Agent example
import requests
import json
import base64
from eth_account import Account
from eth_account.messages import encode_typed_data
from secrets import token_bytes
import time
AGENT_PRIVATE_KEY = "0x..." # Your private key
SERVICE_URL = "https://api.merchant.com/premium-data"
# USDC contract on Base
USDC_ADDRESS = "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913"
def pay_for_service():
# Step 1: Request resource
response = requests.get(SERVICE_URL)
if response.status_code != 402:
return response.json()
# Step 2: Parse payment requirements
payment_details = response.json()
accepted = payment_details["accepts"][0]
print(f"Payment required: {accepted['maxAmountRequired']} units")
# Step 3: Create EIP-3009 signature
account = Account.from_key(AGENT_PRIVATE_KEY)
nonce = "0x" + token_bytes(32).hex()
valid_before = int(time.time()) + 300
typed_data = {
"types": {
"EIP712Domain": [
{"name": "name", "type": "string"},
{"name": "version", "type": "string"},
{"name": "chainId", "type": "uint256"},
{"name": "verifyingContract", "type": "address"}
],
"TransferWithAuthorization": [
{"name": "from", "type": "address"},
{"name": "to", "type": "address"},
{"name": "value", "type": "uint256"},
{"name": "validAfter", "type": "uint256"},
{"name": "validBefore", "type": "uint256"},
{"name": "nonce", "type": "bytes32"}
]
},
"primaryType": "TransferWithAuthorization",
"domain": {
"name": "USD Coin",
"version": "2",
"chainId": 8453,
"verifyingContract": USDC_ADDRESS
},
"message": {
"from": account.address,
"to": accepted["payTo"],
"value": int(accepted["maxAmountRequired"]),
"validAfter": 0,
"validBefore": valid_before,
"nonce": nonce
}
}
signature = account.sign_typed_data(typed_data)
# Step 4: Create payment header
payment = {
"x402Version": 2,
"scheme": "exact",
"network": accepted["network"],
"payload": {
"signature": signature.signature.hex(),
"authorization": {
"from": account.address,
"to": accepted["payTo"],
"value": accepted["maxAmountRequired"],
"validAfter": "0",
"validBefore": str(valid_before),
"nonce": nonce
}
}
}
payment_header = base64.b64encode(json.dumps(payment).encode()).decode()
# Step 5: Retry with payment
paid_response = requests.get(
SERVICE_URL,
headers={"X-Payment": payment_header}
)
return paid_response.json()
if __name__ == "__main__":
result = pay_for_service()
print("Got data:", result)cURL Examples
Quick API testing from the command line.
# ============================================
# CHECKPOINT API - cURL Examples
# ============================================
# 1. Check health
curl https://checkpoint-pied.vercel.app/api/health
# 2. List registered services
curl https://checkpoint-pied.vercel.app/api/discovery
# 3. Verify a payment
curl -X POST https://checkpoint-pied.vercel.app/api/v2/x402/verify \
-H "Content-Type: application/json" \
-d '{
"payment": "BASE64_ENCODED_PAYMENT_HEADER",
"paymentPayload": {
"scheme": "exact",
"network": "eip155:8453",
"maxAmountRequired": "10000",
"resource": "https://api.example.com/data",
"payTo": "0xMerchantWallet",
"maxTimeoutSeconds": 300,
"asset": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913"
}
}'
# 4. Settle a payment (execute USDC transfer)
curl -X POST https://checkpoint-pied.vercel.app/api/v2/x402/settle \
-H "Content-Type: application/json" \
-d '{
"payment": "BASE64_ENCODED_PAYMENT_HEADER",
"paymentPayload": { ... }
}'
# 5. Batch settle (multiple payments)
curl -X POST https://checkpoint-pied.vercel.app/api/v2/x402/batch-settle \
-H "Content-Type: application/json" \
-d '{
"payments": [
{ "payment": "...", "paymentPayload": { ... } },
{ "payment": "...", "paymentPayload": { ... } }
]
}'