
Research
/Security News
Mini Shai-Hulud Campaign Hits Red Hat Cloud Services npm Packages
A mini Shai-Hulud campaign compromised Red Hat Cloud Services npm packages to steal developer and CI/CD secrets during installation.
@primersystems/x402
Advanced tools
TypeScript SDK for x402 payments - pay and charge for APIs with stablecoins. OpenClaw compatible.
TypeScript SDK for x402 HTTP payments by Primer. OpenClaw compatible 🦞
Easily add pay-per-request monetization to your JavaScript/TypeScript APIs using the x402 protocol. Accept stablecoin payments (USDC, EURC) or any ERC-20 token with gasless transactions—payers never pay gas fees.
# Create a new wallet
npx @primersystems/x402 wallet create
# Check balance
npx @primersystems/x402 wallet balance 0xYourAddress
# Probe a URL for x402 support
npx @primersystems/x402 probe https://api.example.com/paid
# Set up for OpenClaw
npx @primersystems/x402 openclaw init
transferWithAuthorizationnpm install @primersystems/x402
Wrap fetch or axios to automatically handle 402 responses:
const { createSigner, x402Fetch, x402Axios } = require('@primersystems/x402');
// Create a signer (use CAIP-2 network format)
const signer = await createSigner('eip155:8453', process.env.PRIVATE_KEY);
// Wrap fetch
const fetch402 = x402Fetch(fetch, signer, { maxAmount: '1.00' });
const response = await fetch402('https://example.com/api/paywall');
// Or wrap axios
const axios402 = x402Axios(axios.create(), signer, { maxAmount: '1.00' });
const response = await axios402.get('https://example.com/api/paywall');
| Option | Required | Description |
|---|---|---|
maxAmount | Yes | Maximum payment per request (e.g., '1.00') |
facilitator | No | Custom facilitator URL |
verify | No | Verify payment before sending (default: true) |
Middleware for Express, Hono, and Next.js:
const { x402Express } = require('@primersystems/x402');
app.use(x402Express('0xYourAddress', {
'/api/paywall': {
amount: '0.01',
asset: '0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913',
network: 'eip155:8453' // CAIP-2 format
},
'/api/data/*': {
amount: '0.001',
asset: '0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913',
network: 'eip155:8453' // CAIP-2 format
}
}));
const { x402Hono } = require('@primersystems/x402');
app.use('*', x402Hono('0xYourAddress', {
'/api/paywall': { amount: '0.01', asset: '0x...', network: 'eip155:8453' }
}));
// App Router (Next.js 13+)
import { x402Next } from '@primersystems/x402';
async function handler(req) {
return Response.json({ data: 'paywall' });
}
export const GET = x402Next(handler, {
payTo: '0xYourAddress',
amount: '0.01',
asset: '0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913',
network: 'eip155:8453' // CAIP-2 format
});
These tokens support gasless transfers natively via transferWithAuthorization. The payer signs an authorization, and the facilitator executes the transfer—payer pays zero gas.
For other ERC-20 tokens, Primer's Prism contract enables gasless payments:
const { createSigner, approveToken } = require('@primersystems/x402');
const signer = await createSigner('eip155:8453', process.env.PRIVATE_KEY);
// One-time approval (this transaction requires gas)
await approveToken(signer, '0xTokenAddress');
// Now you can make gasless payments with this token
Networks use CAIP-2 identifiers (e.g., eip155:8453 for Base).
| Network (CAIP-2) | Chain ID | Legacy Name | Default Facilitator |
|---|---|---|---|
| eip155:8453 | 8453 | base | ✓ Primer |
| eip155:84532 | 84532 | base-sepolia | ✓ Primer |
| eip155:1 | 1 | ethereum | Custom required |
| eip155:11155111 | 11155111 | sepolia | Custom required |
| eip155:42161 | 42161 | arbitrum | Custom required |
| eip155:421614 | 421614 | arbitrum-sepolia | Custom required |
| eip155:10 | 10 | optimism | Custom required |
| eip155:11155420 | 11155420 | optimism-sepolia | Custom required |
| eip155:137 | 137 | polygon | Custom required |
| eip155:80002 | 80002 | polygon-amoy | Custom required |
| eip155:1187947933 | 1187947933 | skale-base | ✓ Primer |
| eip155:324705682 | 324705682 | skale-base-sepolia | ✓ Primer |
Note: Legacy network names (e.g.,
'base') are still accepted for backward compatibility but CAIP-2 format is recommended.
For non-Base networks, you must provide your own facilitator:
// Payee
app.use(x402Express('0xYourAddress', {
'/api/paywall': { amount: '0.01', asset: '0x...', network: 'eip155:1' }
}, { facilitator: 'https://your-facilitator.com' }));
// Payer
const fetch402 = x402Fetch(fetch, signer, {
maxAmount: '1.00',
facilitator: 'https://your-facilitator.com'
});
DEBUG=x402:* node app.js
DEBUG=x402:payer,x402:payee node app.js
The SDK provides testing utilities to help you test your x402 integration without making real payments. Import from @primersystems/x402/testing:
const {
createMockFacilitator,
createTestPayment,
createTest402Response,
fixtures
} = require('@primersystems/x402/testing');
| Utility | Purpose |
|---|---|
createMockFacilitator() | Fake payment server that approves/rejects without blockchain |
createTestPayment() | Generate valid X-PAYMENT headers without a real wallet |
createTest402Response() | Generate 402 responses for testing client code |
fixtures | Pre-built test addresses, route configs, and sample payloads |
If you've built an API with x402 payments, test it like this:
// my-api.test.js
const request = require('supertest');
const app = require('./my-app');
const { createMockFacilitator, createTestPayment } = require('@primersystems/x402/testing');
describe('My Paid API', () => {
let mockFac;
beforeAll(async () => {
// Start a fake facilitator on port 3001
mockFac = await createMockFacilitator({ port: 3001 });
});
afterAll(async () => {
await mockFac.close();
});
test('returns 402 when no payment provided', async () => {
const res = await request(app).get('/api/premium');
expect(res.status).toBe(402);
expect(res.headers['payment-required']).toBeDefined();
});
test('returns 200 when valid payment provided', async () => {
const payment = createTestPayment({ amount: '10000' }); // 0.01 USDC
const res = await request(app)
.get('/api/premium')
.set('X-PAYMENT', payment);
expect(res.status).toBe(200);
});
test('rejects insufficient payment', async () => {
const payment = createTestPayment({ amount: '1' }); // way too little
const res = await request(app)
.get('/api/premium')
.set('X-PAYMENT', payment);
expect(res.status).toBe(402);
});
});
Important: Point your middleware at the mock facilitator during tests:
// In your app setup for tests
const middleware = x402Express(payTo, routes, {
facilitator: 'http://127.0.0.1:3001' // Mock facilitator URL
});
If you've built a client that pays for APIs, test the 402 handling:
const { createTest402Response, fixtures } = require('@primersystems/x402/testing');
test('client handles 402 response', async () => {
// Create a mock server that returns 402
const server = setupMockServer((req, res) => {
const body = createTest402Response({
amount: '10000',
payTo: fixtures.TEST_ADDRESSES.payee,
resource: '/api/data'
});
res.status(402)
.set('PAYMENT-REQUIRED', Buffer.from(JSON.stringify(body)).toString('base64'))
.end();
});
// Test your client handles it correctly
const result = await myClient.fetchWithPayment('/api/data');
expect(result).toBeDefined();
});
// Auto-approve all payments (default)
const mock = await createMockFacilitator({ mode: 'approve' });
// Reject all payments
const mock = await createMockFacilitator({ mode: 'reject' });
// Custom logic
const mock = await createMockFacilitator({
mode: 'custom',
handler: (payload) => {
const amount = payload.paymentRequirements?.maxAmountRequired;
if (parseInt(amount) > 1000000) {
return { success: false, error: 'Amount too high for test' };
}
return { success: true, transaction: '0x' + 'f'.repeat(64) };
}
});
// Add artificial latency (for timeout testing)
const mock = await createMockFacilitator({ latencyMs: 5000 });
The mock facilitator records all requests for assertions:
const mock = await createMockFacilitator();
// ... run your test ...
// Check what was sent to the facilitator
expect(mock.requests.length).toBe(1);
expect(mock.lastRequest().payload.paymentRequirements.network).toBe('eip155:8453');
// Clear between tests
mock.clearRequests();
createTestPayment({
amount: '10000', // Amount in smallest units (default: '10000')
from: '0x...', // Payer address (default: test address)
to: '0x...', // Payee address (default: test address)
network: 'eip155:84532', // Network in CAIP-2 format (default: 'eip155:8453')
validForSeconds: 7200 // Validity window (default: 3600)
});
const { fixtures } = require('@primersystems/x402/testing');
fixtures.TEST_ADDRESSES.payer // '0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266'
fixtures.TEST_ADDRESSES.payee // '0x70997970C51812dc3A010C7d01b50e0d17dc79C8'
fixtures.USDC_ADDRESSES['eip155:8453'] // USDC on Base mainnet
fixtures.USDC_ADDRESSES['eip155:84532'] // USDC on Base Sepolia
fixtures.sampleRouteConfig // Example route config for middleware
fixtures.sample402ResponseBody // Example 402 response structure
fixtures.samplePaymentPayload // Example payment payload structure
The SDK includes a command-line interface for wallet management, probing, and OpenClaw integration.
npx @primersystems/x402 <command> [options]
| Command | Description |
|---|---|
wallet create | Create a new wallet (address, private key, mnemonic) |
wallet balance <address> | Check USDC/ETH balance |
wallet from-mnemonic | Restore wallet from mnemonic phrase |
probe <url> | Check if URL supports x402 payments |
pay <url> | Make a payment to a 402 endpoint |
pay <url> --dry-run | Preview payment without paying |
create <template> [dir] | Scaffold a project from template |
networks | List supported networks |
facilitator | Show facilitator info |
openclaw init | Set up x402 for OpenClaw agents |
openclaw status | Check OpenClaw x402 status |
| Variable | Description |
|---|---|
X402_PRIVATE_KEY | Wallet private key |
X402_NETWORK | Default network (default: base) |
X402_MAX_AMOUNT | Default max payment amount |
X402_FACILITATOR | Facilitator URL override |
# Create wallet and save output
npx @primersystems/x402 wallet create --json > wallet.json
# Check balance on Arbitrum
npx @primersystems/x402 wallet balance 0x... --network arbitrum
# Preview payment (dry run)
npx @primersystems/x402 pay https://api.example.com/data --dry-run
# Pay for an API
X402_PRIVATE_KEY=0x... npx @primersystems/x402 pay https://api.example.com/data --max-amount 0.10
New in v0.5.0 - programmatic wallet management:
const {
createWallet,
walletFromMnemonic,
getBalance,
x402Probe
} = require('@primersystems/x402');
// Create a new wallet
const wallet = createWallet();
console.log(wallet.address); // 0x...
console.log(wallet.privateKey); // 0x...
console.log(wallet.mnemonic); // "word1 word2 ..."
// Restore from mnemonic
const restored = walletFromMnemonic('word1 word2 ...');
// Check balance
const balance = await getBalance('0x...', 'base', 'USDC');
console.log(balance.balance); // "100.50"
// Probe a URL for x402 support
const probe = await x402Probe('https://api.example.com/paid');
if (probe.supports402) {
console.log('Payment required:', probe.requirements);
}
The SDK provides structured errors for programmatic handling:
const { X402Error, ErrorCodes } = require('@primersystems/x402');
try {
const response = await x402Fetch(url, signer, { maxAmount: '0.10' });
} catch (error) {
if (error instanceof X402Error) {
switch (error.code) {
case ErrorCodes.INSUFFICIENT_FUNDS:
console.log('Need more funds:', error.details);
break;
case ErrorCodes.AMOUNT_EXCEEDS_MAX:
console.log('Payment too expensive:', error.details);
break;
case ErrorCodes.SETTLEMENT_FAILED:
console.log('Settlement failed:', error.details);
break;
}
}
}
| Code | Description |
|---|---|
INVALID_CONFIG | Missing or invalid configuration |
MISSING_PRIVATE_KEY | Private key not provided |
UNSUPPORTED_NETWORK | Network not supported |
INSUFFICIENT_FUNDS | Wallet balance too low |
AMOUNT_EXCEEDS_MAX | Payment exceeds maxAmount |
PAYMENT_FAILED | Payment transaction failed |
SETTLEMENT_FAILED | Facilitator settlement failed |
INVALID_RESPONSE | Malformed 402 response |
MISSING_PAYMENT_HEADER | No payment requirements header |
This SDK is compatible with OpenClaw (formerly Moltbot/Clawdbot) AI agents.
npx @primersystems/x402 openclaw init
This will:
~/.openclaw/skills/primer-x402/Install the skill from ClawHub:
clawhub install primer/x402
Or via npm and then init:
npm install -g @primersystems/x402
x402 openclaw init
The SDK includes templates for integrating with Bittensor subnets, enabling pay-per-inference access to decentralized AI.
Scaffold a pay-per-inference proxy for Chutes AI (Bittensor Subnet 64):
npx @primersystems/x402 create chutes-proxy my-ai-proxy
cd my-ai-proxy
npm install
This creates a ready-to-deploy proxy that:
Configure and run:
cp .env.example .env
# Edit .env with your Chutes API key and wallet address
npm run dev
Deploy to Cloudflare Workers:
wrangler login
wrangler secret put CHUTES_API_KEY
wrangler secret put WALLET_ADDRESS
npm run deploy
See the generated README.md for full documentation.
x402 create chutes-proxy command to scaffold a pay-per-inference proxy for Bittensor Chutes AIx402 create <template>eip155:1187947933) and SKALE Base Sepolia (eip155:324705682) networksnpx @primersystems/x402 ...)createWallet(), walletFromMnemonic(), getBalance(), x402Probe()X402Error class with error codes for programmatic handlingopenclaw init and openclaw status commandsX402_PRIVATE_KEY, X402_NETWORK, X402_MAX_AMOUNT, X402_FACILITATOR{}) instead of empty bodyx402Version: 2'eip155:8453' instead of 'base')toCaipNetwork(), fromCaipNetwork(), chainIdToCaip(), caipToChainId()@primersystems/x402/testing module with mock facilitator, test payment generator, and fixturescreateSigner, x402Fetch, x402Axiosx402Express, x402Hono, x402Next middlewareMIT - Primer Systems
FAQs
TypeScript SDK for x402 payments - pay and charge for APIs with stablecoins. OpenClaw compatible.
We found that @primersystems/x402 demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Research
/Security News
A mini Shai-Hulud campaign compromised Red Hat Cloud Services npm packages to steal developer and CI/CD secrets during installation.

Research
/Security News
The North Korean malware loader hides in a Packagist-listed package and its GitHub branch to fetch and execute remote code in a likely Contagious Interview-style lure.

Security News
The Rust project is moving toward formal rules on LLM use in contributions after months of internal debate over maintainer burden, code quality, and contributor experience.