> ## 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.

# x402 Buyers — SHHH Wallets

> Pay for x402-protected APIs from a SHHH V8.4 wallet. Uses X402ShhhClient, which handles the V8.4 envelope shape the existing X402Client can't produce.

If your wallet is a **SHHH V8.4** wallet — the default since v14.5.0 — use `X402ShhhClient`. The existing [`X402Client`](/sdk/guides/x402-client) only works for CHIPI v29 and Argent X wallets; it signs payments with a raw `{r, s}` pair that V8.4 rejects on chain.

`X402ShhhClient` is in `@chipi-stack/backend` (and re-exported from `@chipi-stack/x402` for convenience). Same `fetch(url, init)` surface as the regular client.

## Which buyer client do I need?

| Your wallet                                     | Use                                     |
| ----------------------------------------------- | --------------------------------------- |
| SHHH V8.4 (default for new wallets in v14.5.0+) | `X402ShhhClient` (this page)            |
| CHIPI v29 (legacy default)                      | [`X402Client`](/sdk/guides/x402-client) |
| READY (Argent X v0.4.0)                         | [`X402Client`](/sdk/guides/x402-client) |

Not sure which? Read `wallet.walletType` off any wallet returned from `getWallet` / `createWallet` / `useChipiWallet`.

## Install

```bash theme={null}
npm install @chipi-stack/backend @chipi-stack/x402
# or pnpm / yarn
```

`@chipi-stack/x402` re-exports `X402ShhhClient` so a single import covers facilitator + client surfaces.

## Construct the client

```ts theme={null}
import { X402ShhhClient, ChipiClient } from "@chipi-stack/backend";

const client = new X402ShhhClient({
  wallet: {
    publicKey: wallet.publicKey,
    encryptedPrivateKey: wallet.encryptedPrivateKey,
    signerKind: "STARK", // defaults to "STARK" if omitted
  },
  encryptKey,
  chipiClient: new ChipiClient({ apiPublicKey }),
  bearerToken,
});
```

Required:

* **`wallet`** — the wallet's `publicKey` (deployed account address) and `encryptedPrivateKey` (ciphertext). Same `WalletData`-derived shape every other SDK call uses.
* **`encryptKey`** — passkey-derived key or PIN that decrypts the wallet's STARK key client-side at sign time.
* **`chipiClient`** — Chipi HTTP client (the same one your server uses elsewhere).
* **`bearerToken`** — your chipi-back auth token. Server-side only; never crosses the wire to the x402 seller.

Optional `config`:

```ts theme={null}
new X402ShhhClient({
  wallet,
  encryptKey,
  chipiClient,
  bearerToken,
  config: {
    maxPaymentAmount: "1.00",                      // hard cap per request in USDC
    allowedRecipients: ["0xtrusted-merchant…"],   // whitelist of payTo addresses
    headerName: "X-PAYMENT",                       // default — override only if the seller wants a different name
    chainId: undefined,                            // defaults to Starknet mainnet
  },
});
```

<Warning>
  **PIN is weak — not recommended for production.**

  A user-typed PIN is a short, low-entropy string. Anyone who shoulder-surfs the PIN, observes a phishing form, or compromises the browser at typing time can decrypt the wallet's private key. PIN remains in the SDK only as a fallback recovery surface for users who lose access to their platform authenticator.

  **Production embedded-wallet apps should default to a platform passkey** (Touch ID, Face ID, Windows Hello, Android biometrics) via the `@chipi-stack/chipi-passkey` package. For SHHH V8.4 wallets, `signerKind: "WEBAUTHN_P256"` keeps the private key inside the platform authenticator — it never leaves the device, never reaches Chipi servers, and is never derived from a user-typed secret.

  Only prompt for a PIN as the encryption key when:

  * The user explicitly opted into a PIN-only flow (e.g. cold-storage / paper-backup recovery), or
  * The platform genuinely has no WebAuthn / biometric support available.

  If you are migrating an existing PIN-based wallet to a passkey, look up `useMigrateWalletToPasskey` in your framework's hook docs.
</Warning>

## Pay for a 402-protected request

`fetch` is a drop-in replacement for the global `fetch`. If the seller returns `200`, it passes through. If they return `402 Payment Required`, the client signs the payment locally and retries:

```ts theme={null}
const response = await client.fetch("https://api.example.com/premium-data");

if (response.status === 200) {
  const data = await response.json();
  // …use the paid response
} else {
  // 402 still after retry, 4xx/5xx, etc.
}
```

The client adds the signed `X-PAYMENT` header to the retry. No paymaster call from your side — the facilitator (the seller's side) settles by forwarding the signed calldata to chipi-back's `/transactions/execute-sponsored-raw`.

## What gets signed

1. Build a `USDC.transfer(payTo, amount)` call.
2. Build a SHHH V8.4 `execute_from_outside_v2` outside-execution wrapping that call, with `caller = ANY_CALLER` (paymaster sponsorship).
3. Compute the V8.4 SNIP-12 hash against the wallet's address + chain id.
4. Decrypt the STARK key client-side, sign the hash, wrap into a `V2_SNIP12` envelope.
5. Serialize the full `execute_from_outside_v2` calldata into `payload.shhh.oeCalldata`.
6. Ship that as the `X-PAYMENT` header.

The cleartext STARK key never crosses the wire. Chipi-back receives only the pre-signed calldata span; the wallet's owner is the only party that could have produced it.

## Signer kind support

| `signerKind`                              | Status                                                                                                              |
| ----------------------------------------- | ------------------------------------------------------------------------------------------------------------------- |
| `"STARK"`                                 | Shipped — used by 100% of SHHH wallets created today                                                                |
| `"ED25519"`                               | Coming — Phantom (Solana) buyers paying x402 sellers                                                                |
| `WEBAUTHN_P256`, `EIP191_SECP256K1`, etc. | Not in v14.5.0 — the SHHH client throws a clear error for non-STARK kinds so silent on-chain reverts are impossible |

For browser-rooted kinds (`WEBAUTHN_P256`, `EIP191_SECP256K1`), the right pattern is to call your wallet adapter's `signMessage` directly and build the envelope via `buildWebAuthnEnvelopeFromAssertion` / `buildEip191EnvelopeFromSignature` from `@chipi-stack/backend`, then submit through the paymaster yourself. The drop-in `fetch(url)` flow is STARK-only today.

## Safety: caps + allowlists

Both the [original `X402Client`](/sdk/guides/x402-client#safety-features) and `X402ShhhClient` enforce the same two safety nets at sign time:

* `maxPaymentAmount` — hard cap per request in human USDC ("1.00" = 1 USDC). Throws before signing if a 402 asks for more.
* `allowedRecipients` — whitelist of seller addresses. Throws before signing if a 402's `payTo` isn't on the list.

Both errors surface before any signature is produced, so the user is never asked to sign a payment that would have been rejected anyway.

## Facilitator side (sellers)

You don't need a different facilitator for SHHH buyers — `X402Facilitator` from PR #298 onward auto-detects `payload.shhh` on the inbound payment and routes through the SHHH dispatch path. See [x402 server — monetizing APIs](/sdk/guides/x402-server) for the seller-side setup; that page works as-is for SHHH buyers.

## Related

* [x402 introduction](/sdk/guides/x402-introduction) — protocol overview
* [x402 client (CHIPI v29 / Argent X)](/sdk/guides/x402-client) — the matching page for legacy wallet types
* [x402 server](/sdk/guides/x402-server) — accept x402 payments from any buyer type
* [x402 + sessions](/sdk/guides/x402-sessions) — automated payments via session keys
* [Spending policies](/sdk/guides/spending-policies) — per-token caps on session keys
