@x402relay/sdk
v0.1.0
Published
TypeScript SDK for the x402 payment protocol — automatic HTTP 402 handling with EIP-3009 transferWithAuthorization
Maintainers
Readme
@x402relay/sdk
TypeScript SDK for the x402 payment protocol — automatic HTTP 402 handling with EIP-3009 transferWithAuthorization.
Features
- Fetch-compatible HTTP client — drop-in replacement with
.get()/.post()convenience methods - Automatic 402 handling — detects HTTP 402 responses, signs EIP-712 payment authorizations, and retries
- Zero external dependencies — uses native
fetchAPI (Node.js 18+) - x402 protocol v2 — full support for the x402 v2 payment flow
- EIP-3009 helpers — build and sign
transferWithAuthorizationpayloads - Event hooks —
onPaymentRequired,onPaymentComplete,onError - Configurable — retry count, timeout, custom payment selection strategy
Installation
npm install @x402relay/sdkQuick Start
import { x402Client } from "@x402relay/sdk";
// Implement the WalletProvider interface
const walletProvider = {
async getAddress() {
return "0xYourWalletAddress";
},
async signTypedData(data) {
// Use your preferred signing library (ethers, viem, etc.)
return await signer.signTypedData(data.domain, data.types, data.message);
},
};
// Create the client
const client = x402Client({ walletProvider });
// Make requests — 402 payments are handled automatically
const response = await client.get("https://api.example.com/translate?text=hello&target=ja");
const data = await response.json();API
x402Client(config)
Creates an HTTP client with automatic x402 payment handling.
interface X402ClientConfig {
walletProvider: WalletProvider;
maxRetries?: number; // default: 1
timeout?: number; // default: 30000 (ms)
paymentSelector?: (accepts: PaymentRequirements[]) => PaymentRequirements;
onPaymentRequired?: (response: PaymentRequiredResponse, url: string) => void;
onPaymentComplete?: (payload: PaymentPayload, url: string) => void;
onError?: (error: X402Error, url: string) => void;
}Client Methods
// Fetch-compatible (any HTTP method)
client.fetch(url: string | URL, init?: RequestInit): Promise<Response>
// Convenience methods
client.get(url: string, init?: RequestInit): Promise<Response>
client.post(url: string, body?: object | BodyInit, init?: RequestInit): Promise<Response>WalletProvider Interface
interface WalletProvider {
getAddress(): Promise<string>;
signTypedData(data: EIP712TypedData): Promise<string>;
}EIP-3009 Helpers
import {
buildEIP712Domain,
buildAuthorization,
buildTypedData,
signTransferAuthorization,
} from "@x402relay/sdk";
// Build EIP-712 domain from payment requirements
const domain = buildEIP712Domain(requirements);
// Build authorization with timestamps and nonce
const authorization = buildAuthorization(fromAddress, requirements);
// Build complete typed data for signing
const typedData = buildTypedData(domain, authorization);
// Or do it all at once
const { authorization, signature } = await signTransferAuthorization(
walletProvider,
requirements,
);Utilities
import {
generateNonce,
encodePaymentHeader,
decodePaymentHeader,
} from "@x402relay/sdk";
const nonce = generateNonce(); // 0x-prefixed 32-byte hex
const encoded = encodePaymentHeader(payload); // base64 for X-PAYMENT header
const decoded = decodePaymentHeader(encoded); // parse backPayment Flow
- Client makes an HTTP request
- Server responds with
402 Payment Requiredcontaining payment requirements - SDK automatically:
- Parses the 402 response body (x402 v2 format)
- Selects a payment option from
accepts[] - Builds an EIP-3009
transferWithAuthorizationpayload - Signs it with EIP-712 via the wallet provider
- Retries the request with the
X-PAYMENTheader
Error Handling
All SDK errors are instances of X402Error with a code property:
| Code | Description |
|------|-------------|
| PAYMENT_FAILED | Payment was not accepted by the server |
| SIGNING_FAILED | Wallet provider failed to sign |
| INVALID_402_RESPONSE | 402 body is not valid x402 v2 |
| NO_ACCEPTABLE_PAYMENT | No payment option matched selector |
| TIMEOUT | Request timed out |
| ABORTED | Request was aborted by the caller |
| MAX_RETRIES_EXCEEDED | All retry attempts failed |
import { X402Error } from "@x402relay/sdk";
try {
await client.get("https://api.example.com/paid-endpoint");
} catch (err) {
if (err instanceof X402Error) {
console.error(`x402 error [${err.code}]: ${err.message}`);
}
}License
MIT
