# x402 receipt hardening — V2 preservation and V3 fail-closed integration

## Scope and non-activation

This change adds a **V3-only**, durable receipt-store implementation at
`repos/theloopbreaker-site/app/api/x402/v3/_lib/receipt-store.ts`.  It has no
route handler and no import from the V2 handlers.  It cannot send, sign, pay,
settle, or broadcast a transaction.  Its concrete backend requires existing
Vercel KV / Upstash REST environment pairs:

* `KV_REST_API_URL` + `KV_REST_API_TOKEN`, or
* `UPSTASH_REDIS_REST_URL` + `UPSTASH_REDIS_REST_TOKEN`.

It does not add a dependency or credentials, and it does not fall back to
process memory when those are absent.  A route that tries to use an
unconfigured backend receives an error and must return a fail-closed response.

V3 activation remains blocked.  The checked-in owner evidence template is
intentionally not evidence: placeholders, a zero transaction hash, or a
missing owner attestation must never enable a route.

## Exact V2-to-V3 inheritance proof

The retained V2 verification gates are:

* `repos/theloopbreaker-site/app/api/x402/trust/_lib/verify-payment.ts`:
  `verifyX402Payment` returns `x402ReleaseGateClosed()` before payment-header
  parsing.
* `repos/theloopbreaker-site/app/api/x402/verify/route.ts`: `POST` returns
  `x402ReleaseGateClosed()` before the historical implementation.
* `repos/theloopbreaker-site/app/api/x402/_lib/release-gate.ts`: returns 503,
  `Cache-Control: no-store`, and `X-Robots-Tag: noindex`.

No V2 file is modified by this change.  The V3 store is in an unused `v3`
subdirectory, so it inherits **only** the existing operational policy:
V2 stays the default; V2-to-V3 write redirects are forbidden; V3 is explicit
opt-in and fail-closed.  The authoritative generated statement remains
`pr7/compat/V2_V3_COMPATIBILITY_MATRIX.md`, produced by:

```sh
python3 pr7/compat/build-v2-v3-compatibility-matrix.py --check
```

Integration proof for a future reviewed V3 middleware must include all of:

```sh
git diff -- repos/theloopbreaker-site/app/api/x402/trust/_lib/verify-payment.ts \
  repos/theloopbreaker-site/app/api/x402/verify/route.ts
git grep -n 'receipt-store' -- repos/theloopbreaker-site/app/api/x402
python3 pr7/tools/verify-base-x402-receipt.py \
  --evidence OWNER_APPROVED_EVIDENCE.json --rpc-url "$BASE_RPC_URL"
```

The first command must be empty for V2 preservation.  The second must show an
explicit V3-only import.  The third independently verifies only the on-chain
transfer facts; it cannot prove the route, authorization, nonce, or owner
approval.

## Receipt-store guarantees

`RedisX402ReceiptStore` binds and persists `requestId`, `paymentId`,
`authorizationId`, route, method, EIP-155 network and chain ID, asset, atomic
amount, payee, nonce, authorization window, and required finality.  The
canonical binding is SHA-256 hashed and compared on retry.
The binding also sets a minimum confirmation depth; a higher finality label
cannot compensate for insufficient canonical block depth.

Redis Lua transactions atomically:

1. claim the request and reserve payment, authorization, and nonce identities;
2. conditionally transition by receipt version and expected state;
3. attach an observed transaction hash once, rejecting its reuse by another
   receipt.

Identifiers remain reserved through retention after `expired`, `failed`, or
`reorged`; timeout recovery therefore cannot turn an old authorization into a
new payment opportunity.  Reconciliation records a changed block/transaction
as `reorged`, which is terminal and fail-closed.  No response should be
fulfilled until a separately verified observation reaches the route's
finality requirement.

Audit callbacks expose only receipt ID, receipt state, and short SHA-256
prefixes.  They omit payer, payee, nonce, route, authorization, amount, and
raw transaction hash.

## Owner V2 evidence and independent Base verification

`pr7/schemas/x402-v2-live-evidence.schema.json` is the machine-readable
owner-evidence schema.  Copy
`pr7/evidence/x402-v2-live-evidence.template.json` outside the repository,
replace every placeholder with owner-supplied evidence, and preserve the
authorization record.  Do not commit a fabricated receipt.

`pr7/tools/verify-base-x402-receipt.py` is read-only.  It validates every
submitted hash as untrusted input, checks `eth_chainId`, transaction/receipt
identity, success status, confirmation depth, and an exact ERC-20 `Transfer`
log to the evidence asset/payee/amount.  Its output explicitly names
off-chain facts it cannot prove.  It makes no wallet, payment, signing,
deployment, or broadcast call.

The verifier rejects this checked-in template's placeholder owner attestation
and all-zero transaction hash before it attempts an RPC request.  Therefore a
template cannot accidentally become an activation artifact merely because an
RPC URL is present.

## Received Base USDC log scan: bounded on-chain-only evidence

`pr7/evidence/v2-onchain-usdc-payee-scan-20260509_13.json` is a reproducible
summary of the owner-provided raw input
`v2_x402_payee_usdc_logs_20260509_13.jsonl` (SHA-256
`5458542ffb760d86ca1c0d2dc899324943531e27ec308b960093e5108dee5193`).
The builder validates the raw log shape and the exact Base USDC `Transfer`
topic, asset, and payee, then applies the two owner-supplied external-payer
filters.  It records 173 supplied incoming logs and 19 filtered transfers:
18 totaling `360000` atomic units from
`0x7e571e959cc7c75ccdd2eac24f8775ea2eaa2f09`, and one `10000`-unit transfer
from `0x7d188b4981bb4c8add7b89a1e8ee888cb8f29ae2`.

This is deliberately **not** V2 live-route, authorization, endpoint,
request/response, or receipt evidence. The builder makes no RPC call, so its
output alone is not a current-chain assertion. The separate read-only batch
verifier checks every selected transaction against Base:

```sh
python3 pr7/tools/verify-v2-onchain-payee-scan.py \
  --input pr7/evidence/v2-onchain-usdc-payee-scan-20260509_13.json \
  --output pr7/evidence/v2-onchain-independent-verification.json \
  --rpc-url "$BASE_RPC_URL" \
  --required-confirmations 64
```

That output proves only the Base chain, successful canonical receipts,
confirmation depth, and exact USDC Transfer asset, payer, payee, and amount.
It still cannot prove which HTTP request caused a transfer, whether a
facilitator accepted an authorization, whether a paid response was delivered,
or whether Coinbase Bazaar or an external wallet invoked the route.

The screenshot review in
`pr7/evidence/x402-owner-screenshot-review-20260823.json` records the same
boundary. The reviewed playground screenshots show one route pattern and
method but no transaction hash, receipt, payment response, facilitator result,
external-wallet invocation, or Coinbase Bazaar discovery result.

The bounded scan artifact's machine-readable
`activation` field is
`fail-closed-no-route-or-request-receipt-correlation`, and its `notProven`
array must remain non-empty.  A future route-specific claim needs the owner
to supply a complete live-evidence record and an independently verified
transaction under the tools above.

Rebuild the bounded summary only from the received raw input:

```sh
python3 pr7/tools/build-v2-onchain-payee-scan.py \
  --input /path/to/v2_x402_payee_usdc_logs_20260509_13.jsonl \
  --output pr7/evidence/v2-onchain-usdc-payee-scan-20260509_13.json \
  --payee 0xfA15Ee28939B222B0448261A22156070f0A7813C \
  --asset 0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913 \
  --external-payer 0x7e571e959cc7c75ccdd2eac24f8775ea2eaa2f09 \
  --external-payer 0x7d188b4981Bb4c8ADd7B89a1e8EE888CB8f29ae2
```
