Custom Client Implementation
Manual implementation using only @x402/core
exampletypescriptclientadvanced
Files
2 files in this example
Custom x402 Client Implementation
Demonstrates how to implement x402 payment handling manually using only the core packages, without convenience wrappers like @x402/fetch or @x402/axios.
import { x402Client } from "@x402/core/client";
import { decodePaymentRequiredHeader, encodePaymentSignatureHeader } from "@x402/core/http";
import { ExactEvmScheme } from "@x402/evm/exact/client";
import { privateKeyToAccount } from "viem/accounts";
const client = new x402Client().register(
"eip155:*",
new ExactEvmScheme(privateKeyToAccount(evmPrivateKey)),
);
// 1. Make initial request
let response = await fetch(url);
// 2. Handle 402 Payment Required
if (response.status === 402) {
const paymentRequired = decodePaymentRequiredHeader(response.headers.get("PAYMENT-REQUIRED"));
const paymentPayload = await client.createPaymentPayload(paymentRequired);
// 3. Retry with payment
response = await fetch(url, {
headers: { "PAYMENT-SIGNATURE": encodePaymentSignatureHeader(paymentPayload) },
});
}
console.log(await response.json());
Prerequisites
- Node.js v20+ (install via nvm)
- pnpm v10 (install via pnpm.io/installation)
- Valid EVM and SVM private keys for making payments
- A running x402 server (see server examples)
Setup
- Copy
.env-localto.env:
cp .env-local .env
and fill required environment variables:
EVM_PRIVATE_KEY- Ethereum private key for EVM paymentsSVM_PRIVATE_KEY- Solana private key for SVM payments
- Install and build all packages from the typescript examples root:
cd ../../
pnpm install && pnpm build
cd clients/custom
- Run the example
pnpm dev
Testing the Example
Start a server first:
cd ../../servers/express
pnpm dev
Then run the custom client:
cd ../../clients/custom
pnpm dev
HTTP Headers (v2 Protocol)
| Header | Direction | Description |
|---|---|---|
PAYMENT-REQUIRED | Server → Client | 402 response with payment requirements |
PAYMENT-SIGNATURE | Client → Server | Retry request with payment payload |
PAYMENT-RESPONSE | Server → Client | 200 response with settlement details |
Payment Flow
- Initial Request — Make HTTP request to protected endpoint
- 402 Response — Server responds with requirements in
PAYMENT-REQUIREDheader - Parse Requirements — Decode requirements using
decodePaymentRequiredHeader() - Create Payment — Use
x402Client.createPaymentPayload()to generate payload - Encode Payment — Use
encodePaymentSignatureHeader()for the header value - Retry with Payment — Make new request with
PAYMENT-SIGNATUREheader - Success — Receive 200 with settlement in
PAYMENT-RESPONSEheader
Key Implementation Details
1. Setting Up the Client
import { x402Client } from "@x402/core/client";
import { ExactEvmScheme } from "@x402/evm/exact/client";
import { ExactSvmScheme } from "@x402/svm/exact/client";
import { privateKeyToAccount } from "viem/accounts";
const evmSigner = privateKeyToAccount(evmPrivateKey);
const svmSigner = await createKeyPairSignerFromBytes(base58.decode(svmPrivateKey));
// Optional: custom selector to pick which payment option to use
const selectPayment = (_version: number, requirements: PaymentRequirements[]) => {
return requirements[1]; // Select second option (e.g., Solana)
};
const client = new x402Client(selectPayment)
.register("eip155:*", new ExactEvmScheme(evmSigner))
.register("solana:*", new ExactSvmScheme(svmSigner));
2. Detecting Payment Required
import { decodePaymentRequiredHeader } from "@x402/core/http";
if (response.status === 402) {
const paymentRequiredHeader = response.headers.get("PAYMENT-REQUIRED");
const paymentRequired = decodePaymentRequiredHeader(paymentRequiredHeader);
// paymentRequired.accepts contains the payment options
}
3. Creating Payment Payload
import { encodePaymentSignatureHeader } from "@x402/core/http";
const paymentPayload = await client.createPaymentPayload(paymentRequired);
const paymentHeader = encodePaymentSignatureHeader(paymentPayload);
4. Retrying with Payment
const response = await fetch(url, {
headers: {
"PAYMENT-SIGNATURE": paymentHeader,
},
});
5. Extracting Settlement
import { decodePaymentResponseHeader } from "@x402/core/http";
const settlementHeader = response.headers.get("PAYMENT-RESPONSE");
const settlement = decodePaymentResponseHeader(settlementHeader);
// settlement.transaction, settlement.network, settlement.payer
Wrapper vs Custom Comparison
| Aspect | With Wrapper (@x402/fetch) | Custom Implementation |
|---|---|---|
| Code Complexity | ~10 lines | ~100 lines |
| Automatic Retry | ✅ Yes | ❌ Manual |
| Error Handling | ✅ Built-in | ❌ You implement |
| Header Management | ✅ Automatic | ❌ Manual |
| Flexibility | Limited | ✅ Complete control |
When to Use Custom Implementation
- Need complete control over every step of the payment flow
- Integrating with non-standard HTTP libraries
- Implementing custom retry/error logic
- Learning how x402 works under the hood
Adapting to Other HTTP Clients
To use this pattern with other HTTP clients (axios, got, etc.):
- Detect 402 status code
- Extract requirements from
PAYMENT-REQUIREDheader - Use
decodePaymentRequiredHeader()to parse - Use
x402Client.createPaymentPayload()to create payload - Use
encodePaymentSignatureHeader()to encode - Add
PAYMENT-SIGNATUREheader to retry request - Extract settlement from
PAYMENT-RESPONSEheader
Related Content
Looking for more? Check out our other typescript examples or browse by client content.