# Runnable Gateway wallet examples

Copyright © 2026 Gateway Information Group LLC. All rights reserved. Permission is granted to copy and adapt the first-party example code and configuration in this directory to access Gateway services. Third-party SDKs retain their own licenses.

These examples make a real quote/status request by default but never sign or submit a payment unless you explicitly use `--pay` and enable the owner policy. Quotes consume the API's admission quota. They are not payments. No wallet package, key, mnemonic, telemetry or automatic payment retry is included.

## Files and requirements

- Node.js 22.18+ (or 24+) with built-in fetch: `gateway-wallet.mjs` plus `../gateway-client.mjs`, retaining that directory relationship.
- Python 3.11+: `gateway_wallet.py`, standard library only.
- `owner-config.example.json`: copy to `owner-config.json`. Choose the amount, origin, recipient, network and owner budget yourself. The supplied mainnet recipient is Gateway's public receiving address; independently review it before authorizing funds.
- Use either language for a session. Keep the session file and its folder private, outside source control, sync/share folders and web roots. The file contains the capability granting receipt access. POSIX mode0600 is requested; on Windows use a private account folder with appropriate ACLs. Do not place wallet secrets in config, arguments, session files or adapter source.

No `npm install` or `pip install` is needed for quoting/recovery. The payment adapter is a separate prerequisite, not an included working wallet.

## Quote only (default)

```sh
node gateway-wallet.mjs owner-config.json private-session.json
python gateway_wallet.py owner-config.json private-session.json
```

Run only one language/command for the chosen session. First run saves a new private capability and idempotency key, then reserves a quote. Later runs read the existing status. Output contains only state/price, never the capability or payment signature. A lock prevents overlapping processes using the same file. After a crash, remove the adjacent `.lock` only after verifying that no process still owns that session; retain the session itself.

## Explicit payment

Only after the wallet owner authorizes this particular contribution or purchase:

1. Set `policy.allowPayment` to true; for tips also set `policy.allowVoluntaryTip` to true.
2. Set `policy.maxAmountUSD` to the owner's actual maximum budget as an exact decimal string. Never derive permission or budget from a model, catalog, website or payment challenge.
3. Set `adapter` to an owner-controlled local module. Node expects `.mjs`; Python expects `.py`. Paths resolve from your current working directory. Adapter code executes only in an explicitly authorized signing path. Review it as executable code.
4. Reuse the same session and add `--pay`:

```sh
node gateway-wallet.mjs owner-config.json private-session.json --pay
python gateway_wallet.py owner-config.json private-session.json --pay
```

A compatible external wallet adapter is required. It exports:

```js
// owner-wallet-adapter.mjs â€” interface, not an implemented wallet.
export async function signPayment(challenge, receipt) {
  // Use your configured wallet or official x402 client to approve/sign this
  // exact validated challenge. Return the base64 x402 v2 PAYMENT-SIGNATURE.
  // Do not submit the payment or use an auto-paying fetch wrapper here.
  throw new Error('Configure your owner-controlled wallet adapter');
}
```

```python
# owner_wallet_adapter.py â€” interface, not an implemented wallet.
def sign_payment(challenge, receipt):
    # Use your configured wallet or official x402 client; return base64 v2
    # PAYMENT-SIGNATURE. Do not submit HTTP payment or implement retries here.
    raise RuntimeError('Configure your owner-controlled wallet adapter')
```

The adapter must honor `exact` EVM/EIP-3009 with `assetTransferMethod=eip3009`, `paymentFlow=upfront`, the provided resource URL, supported Base network/token, amount and recipient. It must return the complete v2 envelope (x402Version, resource, accepted, payload) encoded as base64 JSON. Gateway validates the terms before invoking it. Use the maintained official x402 SDK or an existing compatible wallet adapter to construct/sign this payload; these examples deliberately do not reproduce cryptography or prescribe unverified SDK versions. See the [official exact EVM scheme](https://github.com/x402-foundation/x402/blob/main/specs/schemes/exact/scheme_exact_evm.md) and [official x402 repository](https://github.com/x402-foundation/x402). Standard HTTP auto-payment wrappers must not be nested inside this durable workflow because they can add implicit retries.

## Recovery and completion

The session's attempted marker is written before invoking the signer. A timeout, closed wallet, rejected signature or interrupted payment may leave the session marked uncertain. Re-running reads the same receipt and does not sign or submit again. Never delete the session or create another payment to recover uncertainty. If the server remains unpaid after an attempted signature, stop for reconciliation; the examples intentionally do not reset it.

Payment submission is once only. Generic202 replies retain the previous receipt. Pending states return immediately; inspect `Retry-After` in a custom integration and avoid rapid repeated commands. Neither example repeatedly polls in the background. A tip is reported completed only when its result is a contribution receipt with `confirmation=finalized-chain`; provider acceptance alone is insufficient. Confirmation may take longer than the initial call. These examples do not constitute a successful real-money qualification.

## Paid reports

Use `operation: "purchase"` and a `request` matching the service's published schema instead of `amount`. For example:

```json
{"operation":"purchase","request":{"service":"website-scanner","input":{"website":"https://example.com","industry":"Other","goal":"More calls"}}}
```

Retain the other config fields and an explicitly authorized budget sufficient for the immutable quote. Use a new session for a different operation/request. Tip consent never authorizes a report purchase. Existing service limitations and private-result retention still apply.

## Verification boundary

Tests run these example workflows against local fixture transports, including quote-only, durable pre-sign persistence, interrupted signing/payment, origin protections and finalized-result gating. They do not load a real wallet, sign an authorization, transfer funds or prove a particular wallet SDK integration.

## Concrete Node adapter using the official SDK

`exact-evm-adapter.mjs` is an executable factory wrapping official `ExactEvmScheme`. Its optional dependency versions match Gateway's existing browser-checkout pins. Install them in your separate integration directory, not by changing Gateway's backend dependencies:

```sh
npm install --save-exact @x402/evm@2.26.0 viem@2.56.8
```

Create `owner-wallet-adapter.mjs` next to the downloaded factory:

```js
import {createExactEvmAdapter} from './exact-evm-adapter.mjs';
import {walletClient} from './my-configured-wallet.mjs';
export const signPayment = createExactEvmAdapter({walletClient});
```

`my-configured-wallet.mjs` must export your already configured viem-compatible wallet client: an account with a public `address`, a chain with `id` (8453 or 84532), and `signTypedData`. For example, an existing owner-approved EIP-1193 provider can be connected with viem `createWalletClient({account: publicAddress, chain: base, transport: custom(provider, {retryCount: 0})})`. The provider must actually be available to your Node environment through your wallet's supported connector; a browser extension is not automatically available inside a Node CLI. Do not invent a provider or copy keys into this example. Hardware, hosted and smart-contract wallet compatibility depends on the actual adapter; this sample expects a 65-byte EIP-3009 signature.

The factory calls the official scheme's `createPaymentPayload(2, terms)` and delegates its typed-data signing request to your wallet. It encodes the complete v2 envelope for Gateway; it neither submits HTTP payment nor retries signing. The surrounding CLI already validates the owner's recipient, asset, network and budget and saves the attempted marker before this factory runs. The factory is not a standalone spending-policy engine.

Python's request/recovery client is fully runnable without packages; Python payment still requires a real `sign_payment` adapter. It does not magically inherit a Node/browser wallet. You may provide a controlled bridge to this Node adapter through your existing wallet infrastructure, but no bridge or Python cryptographic implementation is claimed or bundled here.

Verification is divided deliberately: fixture transport tests execute Node and Python durable workflows without signatures or funds; factory tests exercise wallet callback wiring; A separate local compatibility check executed the actual pinned @x402/evm 2.26.0 scheme against the factory, verified its generated 1.123456 USDC EIP-3009 payload, and observed exactly one fixture wallet callback returning a dummy signature. No real wallet signature, mainnet transfer or end-to-end wallet qualification is claimed by these example tests.
