Skip to content

docs(examples): migrate 6 examples to Checkout + canonical 2.0 surface - #48

Merged
vvillait88 merged 1 commit into
mainfrom
chore/migrate-examples
May 15, 2026
Merged

docs(examples): migrate 6 examples to Checkout + canonical 2.0 surface#48
vvillait88 merged 1 commit into
mainfrom
chore/migrate-examples

Conversation

@vvillait88

Copy link
Copy Markdown
Contributor

Summary

Migrates the bulk of the example set from hand-rolled gate + 402 envelope + header parsing to Checkout(...) orchestration. Each example uses the canonical 2.0 SDK helpers end-to-end.

Migrated (6):

  • api_provider — Checkout per-call billing on 3 rails; discovery probe stays inline (sample rails != real rails). Fail-fast on missing recipient env.
  • multi_rail_merchant — Checkout + CheckoutGateConfig + mint_recipients hook for Stripe-anchored deposit addresses. simulate_deposit_if_test_mode gates on tx_hash (skips $0 zero-settle carve-outs which have no PI).
  • compliance_merchant — CheckoutGateConfig.on_denied composes the canonical denial helpers. Signer-match enforcement is automatic in the gate (no handler-side recheck).
  • per_product_policy_merchant — CheckoutGateConfig.per_request_policy returns the matching PolicyBlock from ctx.state; SDK handles hard/soft enforcement.
  • signed_ucp_merchantbuild_signed_ucp_response + build_signed_jwks_response + bootstrap_ucp_signing_key + well_known_preflight_response. payment_handlers compose from the Checkout's rails automatically.
  • variable_cost_merchant — kept custom 402 emit (variable-cost flows don't fit Checkout); added missing PAYMENT-REQUIRED header + payment-signature alt-header check.

Untouched (2):

  • identity_only — already on canonical surface (AgentScoreGate + get_agentscore_data)
  • stripe_multichain_merchant — Stripe-specific demo, doesn't fit Checkout

Imports tightened across all 6 to use the top-level agentscore_commerce re-exports (CheckoutValidationError, DenialReason, PolicyBlock, AgentScoreGatePolicy, denial helpers).

Review

Each migrated example was hand-reviewed twice for bugs / gaps / edge cases. Findings + fixes:

Pass 1 (file-by-file functional review):

  • api_provider: static rails with .get(..., \"\") silently produces empty-recipient rails. Switched to os.environ[...] (fail-fast at import).
  • multi_rail_merchant: stripe_secret_key was read inside _on_settled hot path → lifted to module constant.
  • compliance_merchant: rails={} would break the 402 emit. Added a minimal tempo rail; _on_denied return type widened to dict | None.
  • per_product_policy_merchant: same empty-rails issue → minimal tempo rail. _per_request_policy simplified.
  • variable_cost_merchant: missing PAYMENT-REQUIRED header; _build_402_body(_url) param unused → wired through.

Pass 2 (fresh-eye edge-case review):

  • multi_rail_merchant: simulate_deposit_if_test_mode was firing on $0 zero-settle carve-outs (which have signer but no tx). Re-gated on tx_hash.
  • variable_cost_merchant: x402 credential can arrive as x-payment OR payment-signature (purl uses the latter); added both checks.

SDK-lift candidates surfaced during the review

Not fixed in this PR; flagged for follow-up:

  1. Discovery probe routing into Checkout — every API merchant ends up writing the same if is_discovery_probe_request: build_discovery_probe_response branch before handle_fastapi. Could be Checkout(discovery_probe=DiscoveryProbeConfig(...)).
  2. Shipping allowlist validation in the gate — the pre_validate wrapper that calls shipping_country_allowed + shipping_state_allowed is pure boilerplate. Could auto-fire when policy.allowed_shipping_* is set.
  3. Stripe deposit-sim auto-fire on x402-base settles — every Stripe-anchored merchant has to remember to call simulate_deposit_if_test_mode from on_settled. Checkout could detect a Stripe-PI-derived recipient + auto-fire.
  4. Pricing-block composition in PricingResult — every tax-aware merchant calls build_pricing_block + stuffs into body_extras. Could be PricingResult(subtotal_cents=..., tax_cents=..., tax_rate=..., tax_state=...).
  5. Checkout.mount_ucp(app, path=...) — every UCP-publishing merchant wires GET ucp + GET jwks + OPTIONS preflight identically.

Verification

  • uv run ruff check clean
  • uv run ty check agentscore_commerce examples clean
  • uv run pytest 1156 passed, 4 skipped, coverage 95%
  • Each migrated example imports cleanly under mock env vars.

🤖 Generated with Claude Code

- api_provider — Checkout per-call billing on 3 rails; discovery probe stays
  inline (sample rails != real rails). Fail-fast on missing recipient env.
- multi_rail_merchant — Checkout + CheckoutGateConfig + mint_recipients hook
  for Stripe-anchored deposit addresses. simulate_deposit_if_test_mode now
  gates on tx_hash (skips $0 zero-settle carve-outs which have no PI).
- compliance_merchant — CheckoutGateConfig.on_denied composes the canonical
  denial helpers. Signer-match enforcement is now automatic in the gate.
- per_product_policy_merchant — CheckoutGateConfig.per_request_policy returns
  the matching PolicyBlock from ctx.state; SDK handles hard/soft enforcement.
- signed_ucp_merchant — build_signed_ucp_response + build_signed_jwks_response
  + bootstrap_ucp_signing_key + well_known_preflight_response. payment_handlers
  compose from the Checkout's rails automatically.
- variable_cost_merchant — kept custom 402 emit (variable-cost flows don't fit
  Checkout); added missing PAYMENT-REQUIRED header + payment-signature alt.

Imports tightened across all 6 to use the top-level agentscore_commerce
re-exports (CheckoutValidationError, DenialReason, PolicyBlock,
AgentScoreGatePolicy, denial helpers).

identity_only + stripe_multichain_merchant unchanged (already canonical /
Stripe-specific).

Verification: ruff check, ty check, pytest all green (1156 passed, 95 percent
coverage). Each migrated example imports cleanly under mock env.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@vvillait88
vvillait88 merged commit 63358bf into main May 15, 2026
7 checks passed
@vvillait88
vvillait88 deleted the chore/migrate-examples branch May 15, 2026 13:59
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant