feat(buy-x402): go auto-dispatch front door and buyer UX hardening#686
Closed
OisinKyne wants to merge 1 commit into
Closed
feat(buy-x402): go auto-dispatch front door and buyer UX hardening#686OisinKyne wants to merge 1 commit into
OisinKyne wants to merge 1 commit into
Conversation
Contributor
|
Superseded by #690. The buyer-funnel stack (#686 → #687 → #688) and the x402 v2 Leaving this open for the author to close in favor of #690. |
This was referenced Jul 2, 2026
OisinKyne
added a commit
that referenced
this pull request
Jul 2, 2026
…des #686–689) (#690) * feat(buy-x402): go auto-dispatch front door and buyer UX hardening * feat(catalog): single-source buyer prompts, docs deep links, signing metadata - new internal/buyprompts package is the one authoring point for how-to-buy copy; the 402 page, /api/services.json ('buy' block with callShape + prompts per buyer-software kind), the storefront, and 'obol sell info' all render it, so instructions can no longer drift between surfaces (the storefront was still advertising the removed --no-verify-identity flag) - catalog entries gain openapiPath + docsPath (Scalar deep-link anchor); storefront shows API-docs links for every offer type, skill.md service details link the anchored docs - x-payment-info always emits accepts[] with payTo, CAIP-2 network, atomic amount, and the asset's EIP-712 signing domain, so an OpenAPI-only client can construct a valid X-PAYMENT without a second fetch - @scalar/api-reference bumped 1.34.0 -> 1.62.1; renovate custom manager + postUpgradeTasks now keep it current, with scripts/update-scalar-sri.sh refreshing the SRI hash inside the same renovate PR Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * feat(x402): tolerant chat-path gateway, structured payment errors, funnel metrics - HandleProxy rewrites bare POST /services/<name> (and /chat/completions, /v1) to /v1/chat/completions for agent/inference offers, so the most common wrong-path mistake from external buyers succeeds instead of paying into a 404; the 402 page's agent copy taught exactly that bare form until this change - terminal payment failures return structured JSON {error, reason, hint, retriable}; the facilitator's invalidReason (previously discarded) now rides the re-issued 402 challenge in error + extensions.paymentFailure, and signature rejections state the expected EIP-712 domain - legacy error phrases kept verbatim (flows/lib.sh greps for them) - new funnel metrics: payment_failure_reasons_total{reason} (bounded 6-value set) and upstream_failed_after_verify_total, so first-try buyer success is measurable per stage; docs/observability.md updated Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(x402): accept x402 v2 PAYMENT-SIGNATURE header in verifier The ForwardAuth verifier advertises x402Version 2 in its 402 challenge but only read the payment from the legacy v1 X-PAYMENT header. Spec-compliant x402 v2 buyers (agentcash, poncho, coinbase SDK >= v2) attach the payment under PAYMENT-SIGNATURE, so their valid payment was silently ignored and the caller re-challenged — no verify, no settle, no log. This blocked every third-party v2 client from paying obol endpoints; only the in-tree buyer (which sends X-PAYMENT) worked. - Read the payment from X-PAYMENT (v1) OR PAYMENT-SIGNATURE (v2). - Decode via the canonical x402types.ToPaymentPayload helper instead of a local json.Unmarshal, keeping the envelope in lockstep with the SDK. - Mirror the settlement receipt onto both X-PAYMENT-RESPONSE and PAYMENT-RESPONSE. - Tests for the v2 header accept + settle + dual response header. Claude-Session: https://claude.ai/code/session_01VquWN9UMaSHH7MHGcG8bw1 * fix(x402): meter v2 (PAYMENT-SIGNATURE) payments in the funnel Post-#689 a spec-compliant x402 v2 payment arrives under PAYMENT-SIGNATURE, but the funnel gate hadPayment only checked X-PAYMENT — so every successful v2 payment (exactly the cohort #689 unblocked) incremented none of the success/charge/upstream counters. Gate on both headers. Emergent defect of integrating #688 (X-PAYMENT-gated metrics) with #689 (v2 via PAYMENT-SIGNATURE). --------- Co-authored-by: Oisín Kyne <oisin@obol.tech> Co-authored-by: Claude Fable 5 <noreply@anthropic.com> Co-authored-by: bussyjd <145845+bussyjd@users.noreply.github.com> Co-authored-by: Oisín Kyne <4981644+OisinKyne@users.noreply.github.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Why
Our first-try payment success rate for agents buying x402 services sits around 20%. A big share of failures happen before the first paid byte: a buying model must correctly choose
payvspay-agentvsbuy, the right--type, the right endpoint shape, and a payment option on multi-currency offers — four coin-flips a weak model reliably fumbles. Every wrong pre-flight decision is derivable from the seller's 402 response, so the tooling should make those decisions itself.What
buy.py go <url> [--message …]front door. Probes the endpoint, classifies the offer from the 402 (accepts[].extra.agentModel→ agent; bazaar messages-shaped body → chat inference; otherwise plain HTTP) and dispatches to the right flow with the correct path, method, streaming mode, and timeout. Never creates persistent state; points atbuyfor pre-authorized pools.--token/--network/--payment-optionstill override. TTY prompt behavior unchanged.--budgetand--cost-capaccept atomic units (1500000), token units (1.5), and symbol/$forms ('1.5 USDC','$1.50'). The parsed interpretation is echoed before signing; a symbol contradicting the settlement asset aborts. Weak buyers passing dollars where micro-units were expected was a silent 1,000,000× budgeting error.status. Pools expired silently before; the first symptom was a failed spend weeks later.paydefaults to 600 s (was 100 s — a client-side kill risks paying for a response still in flight, and a blind retry double-pays). Streaming (go/pay-agent) remains the recommended shape.refill/removestubs and deprecatedmaintaindropped fromusage(); SKILL.md restructured to lead withgo.Testing
python3 -m py_compile+ logic sanity checks for_parse_money_amount,_offer_shape,_pool_expiry_horizon(all forms verified, including wrong-symbol and garbage rejection)go test ./internal/embed/... ./internal/buy/...(embedded-skill guards) green🤖 Generated with Claude Code