Documentation Index
Fetch the complete documentation index at: https://docs.chipipay.com/llms.txt
Use this file to discover all available pages before exploring further.
The Killer Combo
x402 payments are USDC transfers. Session keys on CHIPI wallets authorize transfers without requiring the owner key each time.
| Mode | User Experience |
|---|
| Without session | Each payment requires signing (wallet popup / biometrics / PIN) |
| With session | Payments happen automatically — zero interaction per request |
This makes x402 + sessions ideal for:
- AI agent autonomous API consumption
- Streaming data feeds (pay-per-query)
- Backend automation (server-to-server payments)
- Mobile apps with “subscribe for X hours” UX
React Implementation
import { useChipiSession, useX402Payment } from "@chipi-stack/nextjs";
function AutoPayContent() {
// Step 1: Set up session
const {
session,
hasActiveSession,
createSession,
registerSession,
} = useChipiSession({
wallet,
encryptKey: pin,
getBearerToken: getToken,
});
// Step 2: Pass session to x402
const { x402Fetch, isPaying, paymentCount } = useX402Payment({
wallet,
encryptKey: pin,
getBearerToken: getToken,
session, // Session key signs payments automatically
maxPaymentAmount: "0.50",
});
// Step 3: Set up session (one-time)
const setupSession = async () => {
await createSession();
await registerSession({
allowedEntrypoints: ["transfer"], // Only USDC transfers
});
};
// Step 4: Use — payments are fully automatic
const fetchData = async () => {
// No popup, no PIN, no interaction
const response = await x402Fetch("https://api.example.com/ai-query?q=...");
return response.json();
};
return (
<div>
{!hasActiveSession ? (
<button onClick={setupSession}>Enable Auto-Pay (6 hours)</button>
) : (
<div>
<button onClick={fetchData} disabled={isPaying}>
Query AI API
</button>
<p>{paymentCount} queries paid</p>
</div>
)}
</div>
);
}
How It Works Under the Hood
- Session creation: Generate a temporary keypair, encrypt private key with user’s PIN
- Session registration: Register the session public key on the wallet contract (one owner signature)
- Session constraint:
allowedEntrypoints: ["transfer"] restricts the session to only USDC transfer calls
- x402 payment: When a 402 is received, the hook uses
executeTransactionWithSession() instead of executeTransaction() — the session key signs automatically
User sets up session (one-time PIN entry)
|
v
[Session key registered on wallet contract]
|
v
x402Fetch("https://api.example.com/data")
|
v
Server returns 402 → parse requirement → sign with session key → retry
| ^
| |
+-- No user interaction! Session key signs automatically
Node.js Backend Automation
For server-side automation where you want to consume paid APIs:
import { X402Client, DirectSigner, createAccount } from "@chipi-stack/core";
import { ChipiServerSDK } from "@chipi-stack/backend";
const sdk = new ChipiServerSDK({
apiPublicKey: process.env.CHIPI_PUBLIC_KEY!,
apiSecretKey: process.env.CHIPI_SECRET_KEY!,
});
// Create session for automated payments
const session = sdk.sessions.createSessionKey({
encryptKey: process.env.ENCRYPT_KEY!,
durationSeconds: 3600, // 1 hour
});
// Register session with transfer-only permissions
await sdk.sessions.addSessionKeyToContract({
encryptKey: process.env.ENCRYPT_KEY!,
wallet: myWallet,
sessionConfig: {
sessionPublicKey: session.publicKey,
validUntil: session.validUntil,
maxCalls: 100,
allowedEntrypoints: ["transfer"],
},
}, bearerToken);
// Now use session for automated x402 payments
const signer = new DirectSigner(decryptedSessionKey);
const account = await createAccount({ signer, provider });
const client = new X402Client(account, { maxPaymentAmount: "0.10" });
// Automated — no human interaction needed
for (const query of queries) {
const response = await client.fetch(`https://api.example.com/ai?q=${query}`);
const result = await response.json();
// Process result...
}
On-Chain Spending Limits
Session keys alone don’t enforce dollar limits — maxCalls caps the number of transactions, not the amount. For real budget control, use Spending Policies:
// After registering the session, set on-chain limits
await setSpendingPolicy({
token: USDC_ADDRESS,
maxPerCall: 100_000n, // Max $0.10 per x402 payment (6 decimals)
maxPerWindow: 50_000_000n, // Max $50 per day
windowSeconds: 86400, // 24h rolling window
});
The contract enforces this automatically — if a payment exceeds the per-call limit or the rolling window total, the transaction reverts on-chain. No middleware or backend enforcement needed.
Three Layers of Protection
| Layer | Scope | Enforced By |
|---|
maxPaymentAmount (X402Client) | Per-request, client-side | SDK (can be bypassed) |
maxCalls (Session) | Total transaction count | Smart contract |
| Spending Policy | Per-call amount + rolling window total | Smart contract (cannot be bypassed) |
For production x402 deployments, use all three.
Security Considerations
Session Scope
Always restrict session keys to the minimum required permissions:
await registerSession({
allowedEntrypoints: ["transfer"], // Only allow transfers
maxCalls: 100, // Limit total calls
});
// Then add on-chain spending limits
await setSpendingPolicy({
token: USDC_ADDRESS,
maxPerCall: 100_000n, // $0.10 per call
maxPerWindow: 10_000_000n, // $10 total per session window
windowSeconds: 21600, // 6h (match session duration)
});
This creates a comprehensive spending cap:
- Per-request limit:
maxPerCall enforced on-chain ($0.10 per API call)
- Session window limit:
maxPerWindow enforced on-chain ($10 per 6 hours)
- Transaction count limit:
maxCalls: 100 enforced on-chain
- Client-side guard:
maxPaymentAmount: "0.10" (early rejection, saves gas)
Session Expiry Fallback
If the session expires during a payment, the hook falls back to requiring the owner’s signature (wallet popup / biometrics / PIN):
const { x402Fetch } = useX402Payment({
wallet,
encryptKey: pin,
getBearerToken: getToken,
session, // May be expired
// If session is expired, falls back to standard wallet signing
});
Best Practices
- Set spending limits: Always configure
maxPaymentAmount and session maxCalls
- Whitelist merchants: Use
allowedRecipients when possible
- Monitor payments: Use
onPaymentComplete callback for logging/analytics
- Short sessions: Prefer shorter session durations with renewal over long-lived sessions
- Transfer-only: Always set
allowedEntrypoints: ["transfer"] for x402 sessions