@stablecoin.xyz/x402 SDK
npm install @stablecoin.xyz/x402:::tip Prerequisites
- Node.js 18+
- An SBC wallet address to receive payments (server) or SBC token balance (client)
- Testnet tokens: faucet.stablecoin.xyz (Base Sepolia) or radiustech.xyz (Radius) — free, no API key needed
- Mainnet API key: dashboard.stablecoin.xyz :::
I'm building a server (gate my API)
import express from 'express'
import { x402Middleware } from '@stablecoin.xyz/x402/middleware/express'
const app = express()
app.get('/premium',
x402Middleware({
payTo: '0xYourAddress',
amount: '1000000', // 1 SBC (6 decimals on Base Sepolia)
network: 'base-sepolia', // testnet — no API key required
}),
(req, res) => {
res.json({ data: 'you paid for this' })
}
)For mainnet, add apiKey:
x402Middleware({
payTo: '0xYourAddress',
amount: '1000000000000000', // 0.001 SBC (18 decimals on Base mainnet)
network: 'base',
apiKey: process.env.API_KEY,
})Accept multiple networks
Let clients pay in any network you support:
x402Middleware([
{ payTo: '0xYourEVMAddress', amount: '1000000000000000', network: 'base', apiKey: process.env.API_KEY },
{ payTo: 'YourSolanaAddress', amount: '1000000000', network: 'solana', apiKey: process.env.API_KEY },
])The client picks whichever network it supports. Your server accepts either.
I'm building a client (pay for APIs)
EVM
import { createWalletClient, http } from 'viem'
import { privateKeyToAccount } from 'viem/accounts'
import { base } from 'viem/chains'
import { createX402Client, viemSignerAdapter } from '@stablecoin.xyz/x402/evm'
const walletClient = createWalletClient({
account: privateKeyToAccount(process.env.PRIVATE_KEY as `0x${string}`),
chain: base,
transport: http(),
})
const client = createX402Client({
signer: viemSignerAdapter(walletClient),
network: 'base',
apiKey: process.env.API_KEY,
})
// drop-in for fetch — handles 402, signs off-chain, retries automatically
const response = await client.fetch('https://api.example.com/premium')
const data = await response.json()
if (response.paymentResult) {
console.log('tx:', response.paymentResult.txHash)
console.log('paid:', response.paymentResult.amountPaid)
}Solana
npm install @stablecoin.xyz/x402 @solana/web3.jsimport { createSolanaX402Client, keypairSignerAdapter } from '@stablecoin.xyz/x402/solana'
import { Keypair } from '@solana/web3.js'
const keypair = Keypair.fromSecretKey(/* your key bytes */)
const client = createSolanaX402Client({
signer: keypairSignerAdapter(keypair),
network: 'solana-devnet', // testnet — no API key required
})
const response = await client.fetch('https://api.example.com/premium')React hook
npm install @stablecoin.xyz/x402 viem reactimport { useX402, viemSignerAdapter } from '@stablecoin.xyz/x402/react'
function PremiumButton({ walletClient }) {
const { fetch, paying } = useX402({
signer: walletClient ? viemSignerAdapter(walletClient) : null,
network: 'base-sepolia',
})
async function loadData() {
const res = await fetch('https://api.example.com/premium')
console.log(await res.json())
}
return (
<button onClick={loadData} disabled={paying}>
{paying ? 'Paying...' : 'Get Premium Data'}
</button>
)
}The useX402 hook integrates directly with SBC AppKit — walletClient from useSbcApp() is a standard viem WalletClient and works as-is.
API Reference
createX402Client(options)
| Option | Type | Default | Description |
|---|---|---|---|
signer | EvmSigner | required | Adapter wrapping your wallet (see below) |
network | NetworkKey | 'base-sepolia' | Network to pay on |
apiKey | string | — | Required for mainnet |
facilitatorUrl | string | https://x402.stablecoin.xyz | Self-hosted override |
rpcUrl | string | — | RPC for balance pre-check |
skipBalanceCheck | boolean | false | Skip pre-flight balance check |
createSolanaX402Client(options)
| Option | Type | Default | Description |
|---|---|---|---|
signer | SolanaSigner | required | Adapter wrapping your keypair |
network | NetworkKey | 'solana-devnet' | 'solana' or 'solana-devnet' |
apiKey | string | — | Required for mainnet |
x402Middleware(options) / withX402(options, handler)
| Option | Type | Default | Description |
|---|---|---|---|
payTo | string | required | Recipient address |
amount | string | required | Amount in atomic units (see Networks for decimals) |
network | NetworkKey | required | e.g. 'base', 'solana' |
apiKey | string | — | Required for mainnet |
settle | boolean | true | Set false to verify only (no on-chain settlement) |
maxTimeoutSeconds | number | 300 | Seconds the client has to submit payment |
facilitatorUrl | string | https://x402.stablecoin.xyz | Self-hosted override |
EVM signer adapters
import { viemSignerAdapter, ethersSignerAdapter } from '@stablecoin.xyz/x402/evm'
viemSignerAdapter(walletClient) // viem WalletClient (EOA, smart account, embedded wallet)
ethersSignerAdapter(signer) // ethers.js v5 or v6 SignerSolana signer adapters
import { keypairSignerAdapter, walletAdapterSignerAdapter } from '@stablecoin.xyz/x402/solana'
keypairSignerAdapter(keypair) // @solana/web3.js Keypair (server-side)
walletAdapterSignerAdapter(walletAdapter) // @solana/wallet-adapter-base (browser)Troubleshooting
My endpoint returned 402 but the client didn't payYou're using native fetch. Replace with client.fetch from createX402Client or createSolanaX402Client — it handles the 402 response, signs the payment, and retries automatically.
401 Unauthorized from the facilitator
apiKey is missing. Mainnet networks (base, solana) require an API key. Get one at dashboard.stablecoin.xyz and pass it as apiKey in your options.
403 Forbidden from the facilitator
Your API key is invalid, inactive, or revoked. Verify it in the dashboard.
Payment rejected: insufficient balance
Your wallet doesn't have enough SBC. For testnet, get free tokens at faucet.stablecoin.xyz (Base Sepolia) or radiustech.xyz (Radius). For mainnet, acquire SBC on the respective network.
Solana: payment rejected immediately
The payer wallet hasn't approved the facilitator as an SPL token delegate. This is a one-time step per wallet. See Facilitator → Solana setup.
Payment rejected: amount mismatch
Check decimals. Base mainnet uses 18 decimals — 1000000000000000000 = 1 SBC. All other networks use 6 or 9 decimals. See Networks.
Testnet quickstart
No API key, no real funds. Start here:
- Get testnet SBC from faucet.stablecoin.xyz (Base Sepolia) or radiustech.xyz (Radius)
- Use
network: 'base-sepolia',network: 'radius-testnet', ornetwork: 'solana-devnet' - Run the examples in
examples/