Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ Two identity types: wallet (`X-Wallet-Address`) and operator-token (`X-Operator-

`create_session_on_missing` auto-mints a verification session when no identity is present AND when `wallet_not_trusted` carries fixable reasons (`kyc_required` / `kyc_pending` / `kyc_failed`) — both paths rewrite the denial to `identity_verification_required` before reaching `on_denied`. When the merchant omits `create_session_on_missing` from `CheckoutGateConfig`, `Checkout` auto-defaults it from `gate.api_key` + `gate.base_url` + `gate.context` + `gate.merchant_name`. Merchants that need `on_before_session` side effects (e.g. pre-minting an order_id) supply their own config to override.

**Getting a verify_url before paying.** On an identity-gated `Checkout`, the gate runs only on a settle leg, so a buyer with no identity reached the session 403 only by sending a payment credential first (an SPT buyer had to mint one). The discovery 402 carries an `identity_bootstrap` block (`build_identity_bootstrap()`) naming `X-Verification-Session: create` whenever the request has no identity header (`has_identity_header`), and a request carrying that header, no identity and no payment credential runs the gate, which answers with the same session-bearing 403. It is opt-in on purpose: scanners replay the Bazaar example body on a schedule, so minting on every identity-less 402 would create a session and (on goods stores) a pending order per probe. Gateless merchants neither advertise nor honor it. Parity with node-commerce.
**Getting a verify_url before paying.** On an identity-gated `Checkout`, the gate runs only on a settle leg, so a buyer with no identity reached the session 403 only by sending a payment credential first (an SPT buyer had to mint one). The discovery 402 carries an `identity_bootstrap` block (`build_identity_bootstrap()`) naming `X-Verification-Session: create` whenever the request has no identity header (`has_identity_header`), and a request carrying that header, no identity and no payment credential runs the gate, which answers with the same session-bearing 403. It is opt-in on purpose: scanners replay the Bazaar example body on a schedule, so minting on every identity-less 402 would create a session and (on goods stores) a pending order per probe. Gateless merchants neither advertise nor honor it.

`build_verification_required_body(reason, message=?, agent_instructions=?, extra=?)` — canonical body builder for the `identity_verification_required` denial. Spreads `verify_url` / `session_id` / `poll_secret` / `poll_url` / `agent_instructions` from the gate-minted reason into a 4xx envelope with merchant-specific message + optional extras. Saves the per-merchant mapping boilerplate.

Expand Down Expand Up @@ -131,7 +131,7 @@ async def gate_on_settle(request: Request) -> None:
async def purchase(...): ...
```

Anonymous POST flows through to the handler unauthenticated and gets a 402 with all rails + per-order pricing. Identity is verified at settle time on the retry leg (when the agent submits `X-Payment` / `Authorization: Payment`); `create_session_on_missing` still auto-mints a verification session there. The shared predicate is `should_run_conditional_gate` (from `agentscore_commerce.payment`): a payment credential OR `requests_verification_session` (`X-Verification-Session: create` with no identity and no payment), so a buyer can get the session 403 before paying. Every conditional adapter variant uses it; a hand-rolled wrap should too, and a merchant building its own 402 advertises the path by passing `build_identity_bootstrap()` into `build_402_body`'s `extra` as `identity_bootstrap`. Parity with node-commerce. The same wrap pattern works identically across all 6 framework adapters (fastapi, flask, django, aiohttp, sanic, middleware/ASGI). See `examples/multi_rail_merchant.py` and `examples/compliance_merchant.py`.
Anonymous POST flows through to the handler unauthenticated and gets a 402 with all rails + per-order pricing. Identity is verified at settle time on the retry leg (when the agent submits `X-Payment` / `Authorization: Payment`); `create_session_on_missing` still auto-mints a verification session there. The shared predicate is `should_run_conditional_gate` (from `agentscore_commerce.payment`): a payment credential OR `requests_verification_session` (`X-Verification-Session: create` with no identity and no payment), so a buyer can get the session 403 before paying. Every conditional adapter variant uses it; a hand-rolled wrap should too, and a merchant building its own 402 advertises the path by passing `build_identity_bootstrap()` into `build_402_body`'s `extra` as `identity_bootstrap`. The same wrap pattern works identically across all 6 framework adapters (fastapi, flask, django, aiohttp, sanic, middleware/ASGI). See `examples/multi_rail_merchant.py` and `examples/compliance_merchant.py`.

### `compatible_clients` field on emitted 402s

Expand Down
Loading