Usage
const {
x402Fetch,
payFetch,
signPayment,
isPaying,
lastTxHash,
lastAmount,
lastPayment,
totalSpent,
error,
paymentCount,
} = useX402Payment({
wallet,
encryptKey: pin,
getBearerToken: getToken,
maxPaymentAmount: "1.00",
});
Configuration
| Parameter | Type | Required | Description |
|---|
wallet | WalletData | null | Yes | Wallet data with publicKey and encryptedPrivateKey |
encryptKey | string | Yes | PIN or passkey-derived key for signing |
getBearerToken | () => Promise<string> | No | Function returning auth token (for session key path) |
bearerToken | string | No | Static bearer token (alternative to getBearerToken) |
maxPaymentAmount | string | No | Max USDC per payment in human-readable format (e.g. "1.00") |
maxAmount | string | No | Alias for maxPaymentAmount |
allowedRecipients | string[] | No | Whitelist of recipient addresses. Empty = allow all |
session | SessionKeyData | null | No | Active session key for automatic payments without owner signature |
onPaymentComplete | (txHash: string, amount: string) => void | No | Callback fired after successful payment |
Return Value
| Property | Type | Description |
|---|
x402Fetch | (url: string, init?: RequestInit) => Promise<Response> | Drop-in fetch replacement — auto-handles 402 |
payFetch | Same as x402Fetch | Alias |
signPayment | (requirement: PaymentRequirement) => Promise<PaymentPayload> | Manual payment signing for custom flows |
isPaying | boolean | Whether a payment is in progress |
lastTxHash | string | null | Last payment transaction hash (session payments only — standard wallet returns null) |
lastAmount | string | null | Last payment amount in human-readable USDC |
lastPayment | LastPaymentInfo | null | Last payment details: { txHash, amount, timestamp } |
totalSpent | string | Total USDC spent this session (e.g. "0.030000") |
error | Error | null | Error from last payment attempt |
paymentCount | number | Total payments made this session |
Example Implementation
import { useX402Payment, ChainToken } from "@chipi-stack/chipi-react";
import { useAuth } from "@clerk/nextjs";
export function PremiumApiClient({ wallet, pin }: { wallet: WalletData; pin: string }) {
const { getToken } = useAuth();
const {
x402Fetch,
isPaying,
lastAmount,
paymentCount,
totalSpent,
error,
} = useX402Payment({
wallet,
encryptKey: pin,
getBearerToken: getToken,
maxPaymentAmount: "0.10", // Max 0.10 USDC per request
onPaymentComplete: (txHash, amount) => {
console.log(`Paid ${amount} USDC`);
},
});
const handleQuery = async () => {
// x402Fetch works like fetch — if the server returns 402,
// the hook automatically signs and retries with X-PAYMENT header
const response = await x402Fetch("https://api.example.com/premium/data");
const data = await response.json();
console.log("Response:", data);
};
return (
<div>
<button onClick={handleQuery} disabled={isPaying}>
{isPaying ? "Paying..." : "Query Premium API"}
</button>
{lastAmount && <p>Last payment: {lastAmount} USDC</p>}
<p>Total spent: {totalSpent} USDC ({paymentCount} payments)</p>
{error && <p>Error: {error.message}</p>}
</div>
);
}
Payment Flows
Standard Wallet (no session)
x402Fetch makes initial request
- Server returns
402 with PAYMENT-REQUIRED header
- Hook parses requirement, validates against
maxPaymentAmount and allowedRecipients
- Signs SNIP-12 typed data locally via
signTypedData() (no on-chain transaction)
- Retries with
X-PAYMENT header containing the signed payload
- Facilitator settles the payment on-chain
Session Key (automatic payments)
- Same flow, but signs using session key via
executeTransactionWithSession()
- Payment executes on-chain immediately (pre-signed)
lastTxHash contains the actual transaction hash
- No owner signature needed — session key handles it
Security
maxPaymentAmount enforced before signing — prevents overpayment
allowedRecipients whitelist — prevents payment to unauthorized addresses
- Only
"exact" scheme and "starknet-mainnet" network accepted
- Only USDC asset accepted (native USDC contract address validated)
- Amount validation uses BigInt math (no float precision issues)
Standard wallet payments return lastTxHash: null because the signature is SNIP-12 off-chain — the facilitator settles on-chain later. Session payments return the actual transaction hash.