@@ -76,6 +76,7 @@ _gate = AgentScoreGate(
7676# proving identity. Identity is verified at settle time on the retry leg.
7777from agentscore_commerce.payment import has_payment_header
7878
79+
7980async def gate_on_settle (request : Request) -> None :
8081 if not has_payment_header(request):
8182 return None
@@ -97,14 +98,22 @@ async def purchase(request: Request, assess=Depends(get_agentscore_data)):
9798``` python
9899from fastapi import FastAPI, Request
99100from agentscore_commerce import (
100- Checkout, CheckoutGateConfig, DiscoveryProbeConfig, PricingResult, pricing_result,
101- SolanaMppRailSpec, StripeRailSpec, TempoRailSpec, X402BaseRailSpec,
101+ Checkout,
102+ CheckoutGateConfig,
103+ DiscoveryProbeConfig,
104+ PricingResult,
105+ pricing_result,
106+ SolanaMppRailSpec,
107+ StripeRailSpec,
108+ TempoRailSpec,
109+ X402BaseRailSpec,
102110 validate_shipping_against_policy,
103111)
104112from agentscore_commerce.discovery import default_a2a_services
105113
106114app = FastAPI()
107115
116+
108117async def _pre_validate (ctx ):
109118 body = ctx.request.body or {}
110119 product = await lookup_product(body.get(" product_slug" ))
@@ -116,6 +125,7 @@ async def _pre_validate(ctx):
116125 )
117126 return {" product" : product}
118127
128+
119129async def _compute_pricing (ctx ) -> PricingResult:
120130 return pricing_result(
121131 subtotal_cents = ctx.state[" product" ][" price_cents" ],
@@ -124,15 +134,17 @@ async def _compute_pricing(ctx) -> PricingResult:
124134 tax_state = ctx.state[" product" ][" tax_state" ],
125135 )
126136
137+
127138async def _on_settled (ctx , outcome ):
128139 return {" ok" : True , " order_id" : ctx.reference_id, " tx_hash" : outcome.tx_hash}
129140
141+
130142checkout = Checkout(
131143 rails = {
132- " tempo" : TempoRailSpec(recipient = os.environ[" TEMPO_RECIPIENT" ]),
144+ " tempo" : TempoRailSpec(recipient = os.environ[" TEMPO_RECIPIENT" ]),
133145 " x402_base" : X402BaseRailSpec(recipient = os.environ[" X402_BASE_RECIPIENT" ], network = " eip155:8453" ),
134- " solana_mpp" :SolanaMppRailSpec(recipient = os.environ[" SOLANA_RECIPIENT" ], network = " solana:mainnet" ),
135- " stripe" : StripeRailSpec(profile_id = os.environ[" STRIPE_PROFILE_ID" ]),
146+ " solana_mpp" : SolanaMppRailSpec(recipient = os.environ[" SOLANA_RECIPIENT" ], network = " solana:mainnet" ),
147+ " stripe" : StripeRailSpec(profile_id = os.environ[" STRIPE_PROFILE_ID" ]),
136148 },
137149 url = " https://merchant.example/purchase" ,
138150 pre_validate = _pre_validate,
@@ -144,7 +156,10 @@ checkout = Checkout(
144156 gate = CheckoutGateConfig(
145157 api_key = os.environ[" AGENTSCORE_API_KEY" ],
146158 merchant_name = " Merchant" ,
147- require_kyc = True , require_sanctions_clear = True , min_age = 21 , allowed_jurisdictions = [" US" ],
159+ require_kyc = True ,
160+ require_sanctions_clear = True ,
161+ min_age = 21 ,
162+ allowed_jurisdictions = [" US" ],
148163 ),
149164 # Optional: empty-body POSTs without a payment header auto-route to a sample 402
150165 # so x402 crawlers (awal x402 details, x402-proxy, ...) can discover the surface.
@@ -165,6 +180,7 @@ checkout.mount_ucp_routes_fastapi(
165180 signing_kid = " merchant-2026-05" ,
166181)
167182
183+
168184@app.post (" /purchase" )
169185async def purchase (request : Request):
170186 return await checkout.handle_fastapi(request)
@@ -192,10 +208,18 @@ from agentscore_commerce.payment import (
192208# Build paymentauth.org directives by symbolic rail name (decimals + currency from registry)
193209directives = [
194210 build_payment_directive(
195- rail = " tempo-mainnet" , id = " chg_t" , realm = " ex.com" , recipient = TEMPO_ADDR , amount_usd = 0.01 ,
211+ rail = " tempo-mainnet" ,
212+ id = " chg_t" ,
213+ realm = " ex.com" ,
214+ recipient = TEMPO_ADDR ,
215+ amount_usd = 0.01 ,
196216 ),
197217 build_payment_directive(
198- rail = " x402-base-mainnet" , id = " chg_b" , realm = " ex.com" , recipient = BASE_ADDR , amount_usd = 0.01 ,
218+ rail = " x402-base-mainnet" ,
219+ id = " chg_b" ,
220+ realm = " ex.com" ,
221+ recipient = BASE_ADDR ,
222+ amount_usd = 0.01 ,
199223 ),
200224]
201225www_auth = www_authenticate_header(directives)
@@ -229,7 +253,9 @@ from agentscore_commerce.challenge import (
229253# build_accepted_methods + build_how_to_pay are async (they resolve per-order recipients).
230254accepted = await build_accepted_methods(tempo = TempoRailSpec(recipient = TEMPO_ADDR ))
231255how_to_pay = await build_how_to_pay(
232- url = " https://my.merchant/buy" , retry_body_json = " {} " , total_usd = " 10.00" ,
256+ url = " https://my.merchant/buy" ,
257+ retry_body_json = " {} " ,
258+ total_usd = " 10.00" ,
233259 rails = {" tempo" : TempoRailSpec(recipient = TEMPO_ADDR )},
234260)
235261body = build_402_body(
@@ -313,7 +339,7 @@ card = build_a2a_agent_card(
313339# Google Universal Commerce Protocol. Publish at /.well-known/ucp.
314340# Output shape: {"ucp": {"version", "services", "capabilities",
315341# "payment_handlers", "name?", "supported_versions?"}, "signing_keys": [...]}
316- # , services / capabilities / payment_handlers are MAPS keyed by reverse-DNS
342+ # , services / capabilities / payment_handlers are MAPS keyed by reverse-DNS
317343# service / capability / handler name (UCP spec §3 + §6).
318344profile = build_ucp_profile(
319345 name = " My Service" ,
@@ -338,7 +364,9 @@ profile = build_ucp_profile(
338364 # binding inside the public profile. Static policy declaration only, no per-operator
339365 # claims. Per-operator identity attestation flows through the AP2 risk-signal endpoint.
340366 agentscore_gate = AgentScoreGatePolicy(
341- require_kyc = True , min_age = 21 , allowed_jurisdictions = [" US" ],
367+ require_kyc = True ,
368+ min_age = 21 ,
369+ allowed_jurisdictions = [" US" ],
342370 ),
343371)
344372```
@@ -452,6 +480,7 @@ from agentscore_commerce.payment import (
452480# Boot-time guard. Raises if a configured network isn't supported.
453481validate_x402_network_config(base_network = X402_BASE )
454482
483+
455484@app.post (" /purchase" )
456485async def purchase (request : Request):
457486 # Path A: agent presented an x402 X-Payment header
@@ -467,15 +496,24 @@ async def purchase(request: Request):
467496 settle = await process_x402_settle(
468497 x402_server = x402_server,
469498 payload = verified.payload,
470- resource_config = {" scheme" : " exact" , " network" : verified.signed_network, " price" : f " $ { total} " , " payTo" : verified.signed_pay_to, " maxTimeoutSeconds" : 300 },
499+ resource_config = {
500+ " scheme" : " exact" ,
501+ " network" : verified.signed_network,
502+ " price" : f " $ { total} " ,
503+ " payTo" : verified.signed_pay_to,
504+ " maxTimeoutSeconds" : 300 ,
505+ },
471506 resource_meta = {" url" : str (request.url), " mimeType" : " application/json" },
472507 )
473508 classified = classify_x402_settle_result(settle)
474509 if classified is not None :
475510 # Log raw `settle` server-side; return controlled phase-based response to the agent.
476511 logger.error(" x402-settle failed phase=%s raw=%r " , settle.phase, settle)
477512 return JSONResponse(
478- {" error" : {" code" : classified.code, " message" : classified.message}, " next_steps" : classified.next_steps},
513+ {
514+ " error" : {" code" : classified.code, " message" : classified.message},
515+ " next_steps" : classified.next_steps,
516+ },
479517 status_code = classified.status,
480518 )
481519
@@ -487,8 +525,18 @@ async def purchase(request: Request):
487525 # `body` is the dict from build_402_body; `x402` carries the payment_required_header kwargs.
488526 result = respond_402(
489527 mppx_challenge_headers = pympp_challenge_headers,
490- body = build_402_body(accepted_methods = accepted, agent_instructions = instructions, pricing = pricing, amount_usd = total, retry_body = body),
491- x402 = {" x402_version" : 2 , " accepts" : x402_accepts, " resource" : {" url" : str (request.url), " mimeType" : " application/json" }},
528+ body = build_402_body(
529+ accepted_methods = accepted,
530+ agent_instructions = instructions,
531+ pricing = pricing,
532+ amount_usd = total,
533+ retry_body = body,
534+ ),
535+ x402 = {
536+ " x402_version" : 2 ,
537+ " accepts" : x402_accepts,
538+ " resource" : {" url" : str (request.url), " mimeType" : " application/json" },
539+ },
492540 )
493541 return JSONResponse(result.body, status_code = result.status, headers = result.headers)
494542```
@@ -504,6 +552,7 @@ from agentscore_commerce.identity.fastapi import AgentScoreGate, get_gate_degrad
504552app = FastAPI()
505553gate = AgentScoreGate(api_key = os.environ[" AGENTSCORE_API_KEY" ], fail_open = True )
506554
555+
507556@app.post (" /purchase" , dependencies = [Depends(gate)])
508557async def purchase (request : Request):
509558 state = get_gate_degraded_state(request)
0 commit comments