<!-- Generated raw Markdown for /quickstarts/payer-typescript; product pin 638fa1e9ca7face19a442cef43754f0d627c7705. -->

This quickstart wires the SubEtha payment scheme into the **official x402 v2
client** so that a plain `fetch` call pays for a 402-gated resource
automatically. Everything on this page targets the **local, non-production**
stack: loopback URLs, chain 31337, and dev placeholder keys. The code is
adapted from the pinned protocol spec and in-repo payer apps; the flow
expectations at the end describe what those sources define — run the
[local end-to-end tutorial](/tutorial/local-flow/) to see them for real.

<VersionBadge name="@subetha/x402-scheme" version="0.1.0" registry="npm" verified="2026-07-27" />

## 1. Install (published packages only)

```bash
npm install @subetha/x402-scheme @x402/core @x402/fetch
```

- `@subetha/x402-scheme` is the payer-side scheme plugin (Apache-2.0, ESM,
  Node.js >= 20). `viem` arrives with it as a regular dependency.
- `@x402/core` and `@x402/fetch` are the **official x402 packages**; the
  pinned product commit builds its payer apps against `^2.17.0` of both.
- No other SubEtha package is needed to pay. In particular there is no
  facilitator package to install — the facilitator is a
  [source-only external service](/quickstarts/facilitator/).

## 2. Configure the environment (local placeholders)

Set the paying account and RPC endpoint in the environment, never in code.
These are the same `SUBETHA_*` names the product's agent tools and the Python
client read — the full table is in the
[configuration reference](/reference/configuration/):

```bash
# Local anvil dev account only — placeholder; never a real key, never committed.
export SUBETHA_PAYER_PK=0x…
# Local anvil chain (31337).
export SUBETHA_RPC_URL=http://127.0.0.1:8545
```

Quoting an offer needs no key at all; only paying does.

## 3. Configure `SubethaSchemeClient` and wrap `fetch`

The scheme client structurally implements the official `SchemeNetworkClient`,
so it registers on the official `x402Client` like any other scheme, and the
official `wrapFetchWithPayment` does the rest (this is the payer wiring the
pinned
[`docs/PROTOCOL.md` §6](https://github.com/peaceandwhisky/SubEtha/blob/638fa1e9ca7face19a442cef43754f0d627c7705/docs/PROTOCOL.md)
defines):

```ts

const account = privateKeyToAccount(process.env.SUBETHA_PAYER_PK as `0x${string}`);

const scheme = new SubethaSchemeClient({
  account,
  mode: "permit", // gasless default; "self-transfer" broadcasts the burn itself
  rpcUrl: process.env.SUBETHA_RPC_URL ?? "http://127.0.0.1:8545",
});

const client = new x402Client().register("eip155:31337", scheme);
const fetchWithPay = wrapFetchWithPayment(fetch, client);

// A locally running provider (the in-repo demo provider listens on :4031).
const res = await fetchWithPay("http://127.0.0.1:4031/api/complete", {
  method: "POST",
  headers: { "content-type": "application/json" },
  body: JSON.stringify({ prompt: "hi" }),
});
console.log(res.status, await res.json());
```

Two constructor behaviors worth knowing before the first run
([`client.ts`](https://github.com/peaceandwhisky/SubEtha/blob/638fa1e9ca7face19a442cef43754f0d627c7705/packages/x402-scheme/src/client.ts)):

- Construction requires `rpcUrl` or an injected `publicClient`.
- In the default `"permit"` mode the client **fail-fasts when an offer lacks
  `extra.helper`** — it never silently degrades to a gas-paying transfer. The
  gasless path therefore needs the facilitator configured with a deployed
  `PermitBurner` (`settlement.permit_burner`); with a bare facilitator config,
  use `mode: "self-transfer"` and the payer broadcasts the burn itself,
  paying gas.

Offers can carry a facilitator fee. `parseFee(requirements)` exposes
`{ fee, bps, quoted, listPrice }`, and `feeAwarePolicy()` is a ready-made
`PaymentPolicy` that ranks offers by the **quoted** total — the payer's true
spend — never by list price. Details in the
[TypeScript API reference](/reference/typescript/).

## 4. What to expect: 402 → payment → accepted → finalized

With a local [provider](/quickstarts/provider/) and
[facilitator](/quickstarts/facilitator/) running, the pinned sources define
this sequence for the call above:

1. **402** — the first request is unpaid; the provider answers
   `402 Payment Required` with a `PAYMENT-REQUIRED` header carrying the offer,
   whose `payTo` is a fresh one-time burn address.
2. **Payment** — `wrapFetchWithPayment` intercepts the 402, has
   `SubethaSchemeClient` build the `subetha-zerc20` payload (permit mode: two
   signatures, no transaction), and retries with `PAYMENT-SIGNATURE`.
3. **Accepted** — the facilitator's settle succeeds, the response arrives
   with your resource body and a `PAYMENT-RESPONSE` header whose
   `SettleResponse` has `success: true` and `extra.phase: "accepted"`. This
   is the terminal state of the HTTP request.
4. **Finalized** — later and asynchronously, the facilitator's finalize loop
   drives the proof-gated mint. No HTTP response ever says `finalized`;
   [accepted vs finalized](/concepts/accepted-vs-finalized/) explains why the
   distinction is load-bearing.

A client without the SubEtha scheme handler simply skips `subetha-zerc20`
offers — standard x402 scheme filtering.

## Next steps

- [Local end-to-end tutorial](/tutorial/local-flow/) — bring up the whole
  stack and watch all four states.
- [Payer quickstart — Python](/quickstarts/payer-python/) — the same role
  with spending-policy guardrails built in.
- [TypeScript API reference](/reference/typescript/) — every export of the
  two published packages.
