Skip to main content

The Killer Combo

x402 payments are USDC transfers. Session keys on CHIPI wallets authorize transfers without requiring the owner key each time.
ModeUser Experience
Without sessionEach payment requires signing (wallet popup / biometrics / PIN)
With sessionPayments 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

  1. Session creation: Generate a temporary keypair, encrypt private key with user’s PIN
  2. Session registration: Register the session public key on the wallet contract (one owner signature)
  3. Session constraint: allowedEntrypoints: ["transfer"] restricts the session to only USDC transfer calls
  4. 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...
}

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
});
Combined with the client’s maxPaymentAmount, this creates a spending cap:
  • Per-request limit: maxPaymentAmount: "0.10" (max $0.10 per API call)
  • Session limit: maxCalls: 100 (max 100 API calls per session)
  • Total session spend: max $10.00 over 6 hours

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

  1. Set spending limits: Always configure maxPaymentAmount and session maxCalls
  2. Whitelist merchants: Use allowedRecipients when possible
  3. Monitor payments: Use onPaymentComplete callback for logging/analytics
  4. Short sessions: Prefer shorter session durations with renewal over long-lived sessions
  5. Transfer-only: Always set allowedEntrypoints: ["transfer"] for x402 sessions