This section explains how sellers (API providers, resource servers) can integrate x402 payment verification and settlement into their services. For the buyer-side implementation, see Quick Start for Buyers.
- Cronos-compatible wallet for receiving payments
- Detect Payment Requirement: Check if the requested resource requires payment
- Respond with 402: Return payment requirements to the buyer
- Extract Payment Header: Buyer retries with signed authorization in
X-PAYMENT - Verify Payment: Forward to facilitator's Verify Endpoint
- Settle Payment: If valid, call the Settle Endpoint
- Deliver Resource: Return protected content with 200 OK
const express = require('express');
const axios = require('axios');
const app = express();
app.use(express.json());
// Configuration
const FACILITATOR_URL = 'https://facilitator.cronoslabs.org/v2/x402';
const SELLER_WALLET = process.env.SELLER_WALLET;
const USDCE_CONTRACT = '0xc01efAaF7C5C61bEbFAeb358E1161b537b8bC0e0'; // Cronos testnet - see Network Constants
// Protected API endpoint
app.get('/api/premium-data', async (req, res) => {
const paymentHeader = req.headers['X-PAYMENT'] || req.body?.paymentHeader;
// Step 1: Check if payment is provided
if (!paymentHeader) {
return res.status(402).json({
error: 'Payment Required',
x402Version: 1,
paymentRequirements: {
scheme: 'exact',
network: 'cronos-testnet', // Switch to 'cronos' for Cronos mainnet
payTo: SELLER_WALLET,
asset: USDCE_CONTRACT,
description: 'Premium API data access',
mimeType: 'application/json',
maxAmountRequired: '1000000', // 1 USDC.e (6 decimals)
maxTimeoutSeconds: 300
}
});
}
try {
const requestBody = {
x402Version: 1,
paymentHeader: paymentHeader,
paymentRequirements: {
scheme: 'exact',
network: 'cronos-testnet', // Same network as in 402 response
payTo: SELLER_WALLET,
asset: USDCE_CONTRACT,
description: 'Premium API data access',
mimeType: 'application/json',
maxAmountRequired: '1000000',
maxTimeoutSeconds: 300
}
};
// Step 2: Verify payment
const verifyRes = await axios.post(`${FACILITATOR_URL}/verify`, requestBody, {
headers: { 'Content-Type': 'application/json', 'X402-Version': '1' }
});
if (!verifyRes.data.isValid) {
return res.status(402).json({
error: 'Invalid payment',
reason: verifyRes.data.invalidReason
});
}
// Step 3: Settle payment
const settleRes = await axios.post(`${FACILITATOR_URL}/settle`, requestBody, {
headers: { 'Content-Type': 'application/json', 'X402-Version': '1' }
});
// Step 4: Check settlement and return content
if (settleRes.data.event === 'payment.settled') {
return res.status(200).json({
data: {
premiumContent: 'This is your premium data',
},
payment: {
txHash: settleRes.data.txHash,
from: settleRes.data.from,
to: settleRes.data.to,
value: settleRes.data.value,
blockNumber: settleRes.data.blockNumber,
timestamp: settleRes.data.timestamp
}
});
} else {
return res.status(402).json({
error: 'Payment settlement failed',
reason: settleRes.data.error
});
}
} catch (error) {
return res.status(500).json({
error: 'Server error processing payment',
details: error.response?.data || error.message
});
}
});
app.listen(3000);{% hint style="info" %}
Note: For Python, Go, or other language examples, the integration pattern is identical: make HTTP POST requests to /verify and /settle with the same JSON structure.
{% endhint %}
The facilitator uses a single base URL for both networks:
- Facilitator URL:
https://facilitator.cronoslabs.org/v2/x402 - Network Selection: Specify via
"network"field in payment requirements- Testnet:
"network": "cronos-testnet"(Chain ID:338) - Mainnet:
"network": "cronos"(Chain ID:25)
- Testnet:
See the Health Check Endpoint for details.
curl -X GET https://facilitator.cronoslabs.org/healthcheckSee the Supported Endpoint for details.
curl -X GET https://facilitator.cronoslabs.org/v2/x402/supported# Request without payment (should return 402)
curl -X GET http://localhost:3000/api/premium-data
# Request with payment header (after signing)
curl -X GET http://localhost:3000/api/premium-data \
-H "X-PAYMENT: eyJ4NDAyVmVyc2lvbiI6MS4uLn0="See Verify Endpoint and Settle Endpoint for complete error documentation.
| Scenario | Expected Response |
|---|---|
| Invalid signature | {"isValid": false, "invalidReason": "Invalid EIP-3009 signature"} |
| Wrong network | {"isValid": false, "invalidReason": "Unsupported network: ethereum-mainnet"} |
| Duplicate nonce | {"event": "payment.failed", "error": "Authorization already used"} |
| Expired auth | {"event": "payment.failed", "error": "Authorization expired"} |
- Check facilitator response for
txHashandblockNumber - View on Cronos Block Explorer:
https://explorer.cronos.org/testnet/tx/[txHash]