đ x402 Pay-Per-Use File Upload
Introductionâ
x402 is a payment protocol that enables pay-per-use APIs, allowing developers to charge users for each API request using on-chain payments. This tutorial demonstrates how to upload files to IPFS via Lighthouse using the x402 payment system, where you pay only for what you use instead of maintaining a subscription or API key balance.
Traditional file upload APIs typically require users to maintain API key balances or subscriptions, which can be cumbersome for occasional users or applications with variable usage patterns. The x402 protocol eliminates these barriers by enabling micro-payments for each upload, making it ideal for pay-as-you-go scenarios.
Why Use x402 for File Uploads?â
The x402 payment protocol offers several advantages over traditional API key-based systems:
- Pay-as-you-go: Only pay for actual file uploads, no subscription fees
- No balance management: No need to pre-fund API key balances
- Transparent pricing: Dynamic pricing based on file size
- Blockchain-native: Uses USDC on Base Sepolia (testnet) or Base (mainnet)
- Automatic payment handling: The
x402-fetchlibrary handles the entire payment flow
What Will You Learn?â
This tutorial will guide you step-by-step on how to:
- Set up your environment for x402 payments
- Configure a wallet client for making payments
- Upload files to IPFS using x402 payment protocol
- Handle retry scenarios for failed uploads
Prerequisitesâ
Before starting this tutorial, ensure you have:
- Node.js (v18 or higher) installed on your system
- A wallet with USDC on Base Sepolia testnet (for testing)
- Basic knowledge of JavaScript/TypeScript and Node.js
- Lighthouse API Key
Getting Testnet USDCâ
For testing on Base Sepolia, you can obtain testnet USDC from:
- QuickNode Base Sepolia Faucet
- Base Sepolia testnet faucets
Step 1: Install Required Packagesâ
Begin by installing the necessary packages for x402 payment integration:
npm install x402-fetch viem dotenv
Step 2: Set Up Project Environmentâ
Create a .env file in your project root:
API_URL=http://91.98.152.92
TEST_PRIVATE_KEY=0xYourPrivateKeyHere
LIGHTHOUSE_API_KEY=your_lighthouse_api_key_here
Step 3: Upload File Functionâ
Create an upload function that reads a file and uploads it with x402 payment:
import { wrapFetchWithPayment } from 'x402-fetch'
import { createWalletClient, http } from 'viem'
import { privateKeyToAccount } from 'viem/accounts'
import { baseSepolia } from 'viem/chains'
import dotenv from 'dotenv'
import { readFileSync } from 'fs'
dotenv.config()
const API_URL = process.env.API_URL || 'http://91.98.152.92'
const UPLOAD_ENDPOINT = `${API_URL}/api/x402/upload`
async function uploadFile(filePath) {
try {
// Set up wallet client
const account = privateKeyToAccount(process.env.TEST_PRIVATE_KEY)
const walletClient = createWalletClient({
account,
chain: baseSepolia, // Use 'base' for mainnet
transport: http(),
})
// Read file from filesystem
const fileBuffer = readFileSync(filePath)
const fileName = filePath.split('/').pop() || `upload-${Date.now()}.txt`
// Wrap fetch with payment handler
const fetchWithPayment = wrapFetchWithPayment(fetch, walletClient)
// Upload file with automatic payment
const response = await fetchWithPayment(UPLOAD_ENDPOINT, {
method: 'POST',
headers: {
'Content-Type': 'application/octet-stream',
'x-file-name': fileName,
'lighthouse-api-key': process.env.LIGHTHOUSE_API_KEY,
},
body: fileBuffer,
})
if (response.ok) {
const result = await response.json()
console.log('â
Upload Successful!')
console.log(`CID: ${result.cid}`)
console.log(`File Size: ${result.fileSize} bytes`)
console.log(`Amount Paid: ${result.amount}`)
return result
} else {
const errorText = await response.text()
throw new Error(`Upload failed: ${errorText}`)
}
} catch (error) {
console.error('Error:', error.message)
throw error
}
}
Step 4: Retry Upload Functionâ
If an upload fails after payment, use the retry function with the previous payment transaction hash:
const RETRY_ENDPOINT = `${API_URL}/api/x402/retry-upload`
async function retryUpload(filePath, previousPaymentTxHash) {
try {
// Set up wallet client
const account = privateKeyToAccount(process.env.TEST_PRIVATE_KEY)
const walletClient = createWalletClient({
account,
chain: baseSepolia,
transport: http(),
})
// Read file from filesystem
const fileBuffer = readFileSync(filePath)
const fileName = filePath.split('/').pop() || `upload-${Date.now()}.txt`
// Wrap fetch with payment handler
const fetchWithPayment = wrapFetchWithPayment(fetch, walletClient)
// Retry upload with payment proof
const response = await fetchWithPayment(RETRY_ENDPOINT, {
method: 'POST',
headers: {
'Content-Type': 'application/octet-stream',
'x-file-name': fileName,
'x-payment-tx-hash': previousPaymentTxHash,
'lighthouse-api-key': process.env.LIGHTHOUSE_API_KEY,
},
body: fileBuffer,
})
if (response.ok) {
const result = await response.json()
console.log('â
Retry Successful!')
console.log(`CID: ${result.cid}`)
return result
} else {
const errorText = await response.text()
throw new Error(`Retry failed: ${errorText}`)
}
} catch (error) {
console.error('Error:', error.message)
throw error
}
}
Understanding the Payment Flowâ
The x402 payment flow works as follows:
- Initial Request: Client makes POST request to
/api/x402/upload - 402 Response: Server responds with 402 Payment Required and payment details
- Payment Transaction: Client submits USDC payment transaction on-chain
- Confirmation: Wait for transaction confirmation on the blockchain
- Retry with Proof: Client retries request with payment proof in
X-PAYMENTheader - Upload: Server verifies payment and uploads file to IPFS via Lighthouse
- Response: Server returns CID and file details
The x402-fetch library automatically handles steps 2-5, making the process seamless.
API Endpointsâ
POST /api/x402/uploadâ
Upload a file to IPFS with x402 payment.
Headers:
Content-Type: application/octet-stream(required)lighthouse-api-key: <your-api-key>(required)x-file-name: <filename>(optional, defaults to timestamp)
Request Body:
- Raw file buffer (binary data)
Response (200 OK):
{
"name": "filename.txt",
"cid": "QmHash...",
"fileSize": 1024,
"amount": "1000"
}
POST /api/x402/retry-uploadâ
Retry a failed upload using a previous payment transaction.
Headers:
Content-Type: application/octet-stream(required)x-file-name: <filename>(optional)x-payment-tx-hash:(required)lighthouse-api-key: <your-api-key>(optional)
Request Body:
- Raw file buffer (binary data)
Response: Same as /upload endpoint
Pricingâ
The x402 API uses dynamic pricing based on file size:
- Minimum price: $0.0001 per request
- Price per MB: Configurable (default: $0.01 per MB)
- Payment: USDC on Base Sepolia (testnet) or Base (mainnet)
The price is calculated automatically based on your file size before payment is requested.
Summaryâ
In this tutorial, you learned how to:
- Set up your environment for x402 payments
- Configure a wallet client for making payments
- Upload files to IPFS using x402 payment protocol
- Handle retry scenarios for failed uploads
The x402 protocol enables pay-per-use file uploads to IPFS via Lighthouse, eliminating the need for API key balance management and subscriptions. With automatic payment handling through the x402-fetch library, you can seamlessly integrate pay-as-you-go file storage into your applications.