TypeScript SDK

useFetchWithPayment

A React hook that wraps the native fetch API to automatically handle 402 Payment Required responses using the x402 payment protocol with the currently connected wallet.

This hook enables you to make API calls that require payment without manually handling the payment flow. Responses are automatically parsed as JSON by default (can be customized with parseAs option).

When a 402 response is received, it will automatically:

  • Parse the payment requirements

  • Verify the payment amount is within the allowed maximum

  • Create a payment header using the connected wallet

  • Retry the request with the payment header

If payment fails (e.g. insufficient funds), a modal will be shown to help the user resolve the issue. If no wallet is connected, a sign-in modal will be shown to connect a wallet.

Example

import { useFetchWithPayment } from "thirdweb/react";
import { createThirdwebClient } from "thirdweb";
const client = createThirdwebClient({ clientId: "your-client-id" });
function MyComponent() {
const { fetchWithPayment, isPending } = useFetchWithPayment(client);
const handleApiCall = async () => {
// Response is automatically parsed as JSON
const data = await fetchWithPayment(
"https://api.example.com/paid-endpoint",
);
console.log(data);
};
return (
<button onClick={handleApiCall} disabled={isPending}>
{isPending ? "Loading..." : "Make Paid API Call"}
</button>
);
}

Customize response parsing

const { fetchWithPayment } = useFetchWithPayment(client, {
parseAs: "text", // Get response as text instead of JSON
});
const textData = await fetchWithPayment(
"https://api.example.com/endpoint",
);

Customize payment options

const { fetchWithPayment } = useFetchWithPayment(client, {
maxValue: 5000000n, // 5 USDC in base units
theme: "light",
paymentRequirementsSelector: (requirements) => {
// Custom logic to select preferred payment method
return requirements[0];
},
});

Customize the fund wallet widget

const { fetchWithPayment } = useFetchWithPayment(client, {
fundWalletOptions: {
title: "Add Funds",
description: "You need more tokens to complete this payment",
buttonLabel: "Get Tokens",
},
});

Customize the connect modal

const { fetchWithPayment } = useFetchWithPayment(client, {
connectOptions: {
wallets: [inAppWallet(), createWallet("io.metamask")],
title: "Sign in to continue",
},
});

Disable the UI and handle errors yourself

const { fetchWithPayment, error } = useFetchWithPayment(client, {
uiEnabled: false,
});
// Handle the error manually
if (error) {
console.error("Payment failed:", error);
}
function useFetchWithPayment(
client: ThirdwebClient,
options?: UseFetchWithPaymentConfig,
):
| {
context: unknown;
data: undefined;
error: null;
failureCount: number;
failureReason: null | Error;
fetchWithPayment: (
input: RequestInfo,
init?: RequestInit,
) => Promise<unknown>;
isError: false;
isIdle: true;
isPaused: boolean;
isPending: false;
isSuccess: false;
mutate: UseMutateFunction<
unknown,
Error,
{ init?: RequestInit; input: RequestInfo },
unknown
>;
mutateAsync: UseMutateAsyncFunction<
unknown,
Error,
{ init?: RequestInit; input: RequestInfo },
unknown
>;
reset: () => void;
status: "idle";
submittedAt: number;
variables: undefined;
}
| {
context: unknown;
data: undefined;
error: null;
failureCount: number;
failureReason: null | Error;
fetchWithPayment: (
input: RequestInfo,
init?: RequestInit,
) => Promise<unknown>;
isError: false;
isIdle: false;
isPaused: boolean;
isPending: true;
isSuccess: false;
mutate: UseMutateFunction<
unknown,
Error,
{ init?: RequestInit; input: RequestInfo },
unknown
>;
mutateAsync: UseMutateAsyncFunction<
unknown,
Error,
{ init?: RequestInit; input: RequestInfo },
unknown
>;
reset: () => void;
status: "pending";
submittedAt: number;
variables: { init?: RequestInit; input: RequestInfo };
}
| {
context: unknown;
data: undefined;
error: Error;
failureCount: number;
failureReason: null | Error;
fetchWithPayment: (
input: RequestInfo,
init?: RequestInit,
) => Promise<unknown>;
isError: true;
isIdle: false;
isPaused: boolean;
isPending: false;
isSuccess: false;
mutate: UseMutateFunction<
unknown,
Error,
{ init?: RequestInit; input: RequestInfo },
unknown
>;
mutateAsync: UseMutateAsyncFunction<
unknown,
Error,
{ init?: RequestInit; input: RequestInfo },
unknown
>;
reset: () => void;
status: "error";
submittedAt: number;
variables: { init?: RequestInit; input: RequestInfo };
}
| {
context: unknown;
data: unknown;
error: null;
failureCount: number;
failureReason: null | Error;
fetchWithPayment: (
input: RequestInfo,
init?: RequestInit,
) => Promise<unknown>;
isError: false;
isIdle: false;
isPaused: boolean;
isPending: false;
isSuccess: true;
mutate: UseMutateFunction<
unknown,
Error,
{ init?: RequestInit; input: RequestInfo },
unknown
>;
mutateAsync: UseMutateAsyncFunction<
unknown,
Error,
{ init?: RequestInit; input: RequestInfo },
unknown
>;
reset: () => void;
status: "success";
submittedAt: number;
variables: { init?: RequestInit; input: RequestInfo };
};

Parameters

The thirdweb client used to access RPC infrastructure

Type

let client: {
readonly clientId: string;
readonly secretKey: string | undefined;
} & Readonly<ClientOptions>;

Optional configuration for payment handling

Type

let options: UseFetchWithPaymentConfig;

Returns

let returnType:
| {
context: unknown;
data: undefined;
error: null;
failureCount: number;
failureReason: null | Error;
fetchWithPayment: (
input: RequestInfo,
init?: RequestInit,
) => Promise<unknown>;
isError: false;
isIdle: true;
isPaused: boolean;
isPending: false;
isSuccess: false;
mutate: UseMutateFunction<
unknown,
Error,
{ init?: RequestInit; input: RequestInfo },
unknown
>;
mutateAsync: UseMutateAsyncFunction<
unknown,
Error,
{ init?: RequestInit; input: RequestInfo },
unknown
>;
reset: () => void;
status: "idle";
submittedAt: number;
variables: undefined;
}
| {
context: unknown;
data: undefined;
error: null;
failureCount: number;
failureReason: null | Error;
fetchWithPayment: (
input: RequestInfo,
init?: RequestInit,
) => Promise<unknown>;
isError: false;
isIdle: false;
isPaused: boolean;
isPending: true;
isSuccess: false;
mutate: UseMutateFunction<
unknown,
Error,
{ init?: RequestInit; input: RequestInfo },
unknown
>;
mutateAsync: UseMutateAsyncFunction<
unknown,
Error,
{ init?: RequestInit; input: RequestInfo },
unknown
>;
reset: () => void;
status: "pending";
submittedAt: number;
variables: { init?: RequestInit; input: RequestInfo };
}
| {
context: unknown;
data: undefined;
error: Error;
failureCount: number;
failureReason: null | Error;
fetchWithPayment: (
input: RequestInfo,
init?: RequestInit,
) => Promise<unknown>;
isError: true;
isIdle: false;
isPaused: boolean;
isPending: false;
isSuccess: false;
mutate: UseMutateFunction<
unknown,
Error,
{ init?: RequestInit; input: RequestInfo },
unknown
>;
mutateAsync: UseMutateAsyncFunction<
unknown,
Error,
{ init?: RequestInit; input: RequestInfo },
unknown
>;
reset: () => void;
status: "error";
submittedAt: number;
variables: { init?: RequestInit; input: RequestInfo };
}
| {
context: unknown;
data: unknown;
error: null;
failureCount: number;
failureReason: null | Error;
fetchWithPayment: (
input: RequestInfo,
init?: RequestInit,
) => Promise<unknown>;
isError: false;
isIdle: false;
isPaused: boolean;
isPending: false;
isSuccess: true;
mutate: UseMutateFunction<
unknown,
Error,
{ init?: RequestInit; input: RequestInfo },
unknown
>;
mutateAsync: UseMutateAsyncFunction<
unknown,
Error,
{ init?: RequestInit; input: RequestInfo },
unknown
>;
reset: () => void;
status: "success";
submittedAt: number;
variables: { init?: RequestInit; input: RequestInfo };
};

An object containing:

  • fetchWithPayment: Function to make fetch requests with automatic payment handling (returns parsed data)

  • isPending: Boolean indicating if a request is in progress

  • error: Any error that occurred during the request

  • data: The parsed response data (JSON by default, or based on parseAs option)

  • Other mutation properties from React Query