{"id":49530478,"url":"https://github.com/agentscore/python-commerce","last_synced_at":"2026-05-10T22:25:10.089Z","repository":{"id":354786860,"uuid":"1222372615","full_name":"agentscore/python-commerce","owner":"agentscore","description":"Python merchant SDK for agent commerce: identity, payment, discovery, Stripe multichain","archived":false,"fork":false,"pushed_at":"2026-04-30T03:44:25.000Z","size":907,"stargazers_count":0,"open_issues_count":1,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-04-30T04:24:03.571Z","etag":null,"topics":["agent-commerce","agentscore","ai-agent","merchant-sdk","mpp","payments","python","stripe","x402"],"latest_commit_sha":null,"homepage":"https://agentscore.sh","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/agentscore.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":"SECURITY.md","support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-04-27T09:52:50.000Z","updated_at":"2026-04-29T16:38:10.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/agentscore/python-commerce","commit_stats":null,"previous_names":["agentscore/python-commerce"],"tags_count":1,"template":false,"template_full_name":null,"purl":"pkg:github/agentscore/python-commerce","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/agentscore%2Fpython-commerce","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/agentscore%2Fpython-commerce/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/agentscore%2Fpython-commerce/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/agentscore%2Fpython-commerce/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/agentscore","download_url":"https://codeload.github.com/agentscore/python-commerce/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/agentscore%2Fpython-commerce/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":32525898,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-02T01:12:54.858Z","status":"online","status_checked_at":"2026-05-02T02:00:05.923Z","response_time":132,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["agent-commerce","agentscore","ai-agent","merchant-sdk","mpp","payments","python","stripe","x402"],"created_at":"2026-05-02T07:04:02.465Z","updated_at":"2026-05-02T07:04:04.122Z","avatar_url":"https://github.com/agentscore.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# agentscore-commerce\n\n[![PyPI version](https://img.shields.io/pypi/v/agentscore-commerce.svg)](https://pypi.org/project/agentscore-commerce/)\n[![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE)\n\nThe full merchant-side SDK for [AgentScore](https://agentscore.sh) in Python — agent commerce in one install. Identity middleware (FastAPI, Flask, Django, AIOHTTP, Sanic, ASGI), payment helpers, 402 challenge builders, MPP discovery, and Stripe multichain support.\n\n## Install\n\n```bash\npip install agentscore-commerce[fastapi]   # or [flask], [django], [aiohttp], [sanic], [stripe]\n```\n\n## What's in the package\n\n| Submodule | What it provides |\n|---|---|\n| `agentscore_commerce.identity.{fastapi,flask,django,aiohttp,sanic,middleware}` | Trust gate middleware: KYC, sanctions, age, jurisdiction. `AgentScoreGate(...)` (or `agentscore_gate(app, ...)` on Flask/Sanic), `get_assess_data(...)`, `capture_wallet(...)`, `verify_wallet_signer_match(...)`. |\n| `agentscore_commerce.identity` (package level) | Re-exports the denial helpers: `denial_reason_status`, `denial_reason_to_body`, `build_signer_mismatch_body`, `build_contact_support_next_steps`, `verification_agent_instructions`, `is_fixable_denial`, `FIXABLE_DENIAL_REASONS`. Also re-exports the per-product policy helpers: `PolicyBlock`, `GateResult`, `EnforcementMode`, `IdentityStatus`, `build_gate_from_policy`, `run_gate_with_enforcement`, `shipping_country_allowed`, `shipping_state_allowed` — for multi-product merchants where each product carries its own compliance config (hard gate vs soft vs none, per-product shipping allowlists). |\n| `agentscore_commerce.payment` | `networks`, `USDC`, `rails` registries; `payment_directive`, `build_payment_directive`, `www_authenticate_header`, `payment_required_header`, `alias_amount_fields` (v1↔v2 amount field shim — emits both `amount` and `maxAmountRequired` so v1-only x402 parsers like Coinbase awal can read v2 bodies), `settlement_override_header`, `dispatch_settlement_by_network`, `extract_payment_signer` (returns `PaymentSigner({address, network})`), `register_x402_schemes_v1_v2`; drop-in x402 helpers: `validate_x402_network_config` (boot-time guard), `verify_x402_request` (parse + validate inbound X-Payment), `process_x402_settle` (verify-then-settle with one call). |\n| `agentscore_commerce.discovery` | `is_discovery_probe_request`, `build_discovery_probe_response` (with optional `x402_sample` for x402-aware crawlers — `awal x402 details` etc.), `sample_x402_accept_for_network` (USDC sample-accept builder for known CAIP-2 networks), `build_well_known_mpp`, `build_llms_txt` + `llms_txt_identity_section` + `llms_txt_payment_section` (compact + verbose modes), `agentscore_openapi_snippets`, `build_bazaar_discovery_payload`, `NoindexNonDiscoveryMiddleware` (ASGI middleware that emits `X-Robots-Tag: noindex` on every path except the agent-discovery surfaces — defaults cover `/openapi.json`, `/llms.txt`, `/.well-known/{mpp.json,agent-card.json,ucp}`, `/favicon.{png,ico}`; pure helpers `is_discovery_path` + `DEFAULT_DISCOVERY_PATHS` for non-ASGI frameworks). |\n| `agentscore_commerce.challenge` | `build_402_body`, `build_accepted_methods`, `build_identity_metadata`, `build_how_to_pay`, `build_agent_instructions` (auto-emits per-rail `compatible_clients` — smoke-verified CLIs the agent should use; vendor override supported), `build_pricing_block` (cents → dollar-string with optional shipping/tax), `first_encounter_agent_memory` (cross-merchant hint, returns the canonical block or `None` based on a per-merchant first-seen flag), `OrderReceipt` (dataclass for the post-settlement 200 response shape); `respond_402` — drop-in 402 emit that preserves pympp's `WWW-Authenticate` and layers x402's `PAYMENT-REQUIRED`. `build_validation_error` — structured 4xx body builder (`{error: {code, message}, required_fields?, example_body?, next_steps?, ...extra}`) so vendors compose body shapes by name instead of inlining at every validation site. |\n| `agentscore_commerce.stripe_multichain` | `create_multichain_payment_intent`, `get_deposit_address`, `simulate_crypto_deposit`; `create_pi_cache` (TTL'd PI / deposit-address cache, Redis-backed when `redis_url` set, in-memory otherwise), `simulate_deposit_if_test_mode` (gates on `sk_test_` and looks up the PI for you), `STRIPE_TEST_TX_HASH_SUCCESS` / `STRIPE_TEST_TX_HASH_FAILED` constants. Peer dep on `stripe`. |\n| `agentscore_commerce.api` | Everything from `agentscore-py` re-exported in one place: `AgentScore` + `AgentScoreError`, `AGENTSCORE_TEST_ADDRESSES` + `is_agentscore_test_address`. **Don't add `agentscore-py` as a separate dep** — the two can drift versions and cause subtle type mismatches. |\n\n## Quick start (FastAPI)\n\n```python\nfrom fastapi import Depends, FastAPI, Request\nfrom agentscore_commerce.identity.fastapi import (\n    AgentScoreGate,\n    capture_wallet,\n    get_assess_data,\n    verify_wallet_signer_match,\n)\n\napp = FastAPI()\n_gate = AgentScoreGate(\n    api_key=\"as_live_...\",\n    require_kyc=True,\n    min_age=21,\n    allowed_jurisdictions=[\"US\"],\n)\n\n\n# Run the gate CONDITIONALLY — only when a payment credential is already attached.\n# Anonymous discovery (no payment header) flows through to the handler so any spec-\n# compliant x402 wallet can read the 402 challenge with rails + pricing without first\n# proving identity. Identity is verified at settle time on the retry leg.\nasync def gate_on_settle(request: Request) -\u003e None:\n    has_payment_header = bool(\n        request.headers.get(\"payment-signature\")\n        or request.headers.get(\"x-payment\")\n        or (request.headers.get(\"authorization\") or \"\").startswith(\"Payment \")\n    )\n    if not has_payment_header:\n        return None\n    return await _gate(request)\n\n\n@app.post(\"/purchase\", dependencies=[Depends(gate_on_settle)])\nasync def purchase(request: Request, assess=Depends(get_assess_data)):\n    # ... settle payment ...\n    # After payment, capture the signer wallet for cross-merchant attribution\n    await capture_wallet(request, signer, \"evm\", idempotency_key=payment_intent_id)\n    return {\"ok\": True}\n```\n\n## Payment helpers\n\n```python\nfrom agentscore_commerce.payment import (\n    BuildPaymentDirectiveInput,\n    PaymentDirectiveInput,\n    build_payment_directive,\n    extract_payment_signer,\n    networks,\n    payment_directive,\n    www_authenticate_header,\n)\n\n# Build paymentauth.org directives by symbolic rail name (decimals + currency from registry)\ndirectives = [\n    build_payment_directive(BuildPaymentDirectiveInput(\n        rail=\"tempo-mainnet\", id=\"chg_t\", realm=\"ex.com\", recipient=TEMPO_ADDR, amount_usd=0.01,\n    )),\n    build_payment_directive(BuildPaymentDirectiveInput(\n        rail=\"x402-base-mainnet\", id=\"chg_b\", realm=\"ex.com\", recipient=BASE_ADDR, amount_usd=0.01,\n    )),\n]\nwww_auth = www_authenticate_header(directives)\n\n# Recover the on-chain signer (EVM) from an x402 header — returns PaymentSigner | None\nsigner = extract_payment_signer(request.headers.get(\"x-payment\"))\nif signer:\n    print(signer.address, signer.network)  # ('0x...', 'evm')\n```\n\n## Discovery + 402 builders\n\n```python\nfrom agentscore_commerce.discovery import (\n    BuildLlmsTxtInput,\n    LlmsTxtIdentitySectionInput,\n    LlmsTxtPaymentSectionInput,\n    LlmsTxtSection,\n    PaymentMethodConfig,\n    WellKnownMppInput,\n    build_llms_txt,\n    build_well_known_mpp,\n)\nfrom agentscore_commerce.challenge import (\n    Build402BodyInput,\n    BuildAcceptedMethodsInput,\n    BuildAgentInstructionsInput,\n    BuildHowToPayInput,\n    HowToPayRails,\n    PricingBlock,\n    TempoConfig,\n    TempoRailConfig,\n    build_402_body,\n    build_accepted_methods,\n    build_agent_instructions,\n    build_how_to_pay,\n    build_pricing_block,\n    first_encounter_agent_memory,\n)\n\naccepted = build_accepted_methods(BuildAcceptedMethodsInput(tempo=TempoConfig(recipient=TEMPO_ADDR)))\nhow_to_pay = build_how_to_pay(BuildHowToPayInput(\n    url=\"https://my.merchant/buy\", retry_body_json=\"{}\", total_usd=\"10.00\",\n    rails=HowToPayRails(tempo=TempoRailConfig(recipient=TEMPO_ADDR)),\n))\nbody = build_402_body(Build402BodyInput(\n    accepted_methods=accepted,\n    agent_instructions=build_agent_instructions(BuildAgentInstructionsInput(how_to_pay=how_to_pay)),\n    pricing=build_pricing_block(subtotal_cents=1000, tax_cents=80, shipping_cents=999, tax_rate=0.08, tax_state=\"CA\"),\n    amount_usd=\"10.80\",\n    # First-encounter merchants attach the cross-merchant agent_memory hint.\n    agent_memory=first_encounter_agent_memory(first_encounter=not merchant.has_seen_operator(op_token)),\n))\n```\n\n`build_pricing_block` handles cents → dollar-string (with optional shipping). `first_encounter_agent_memory` returns the canonical hint or `None` based on a per-merchant first-seen flag. `OrderReceipt` is a dataclass for the post-settlement 200 response shape.\n\n### Idempotency-key + multi-rail header bundle\n\n```python\nfrom agentscore_commerce.payment import (\n    BuildPaymentHeadersInput,\n    PaymentHeadersRail,\n    build_idempotency_key,\n    build_payment_headers,\n)\n\nidempotency_key = build_idempotency_key(payment_intent_id=pi_id, order_id=order_id, amount_cents=amount)\n\nheaders = build_payment_headers(BuildPaymentHeadersInput(\n    order_id=order_id,\n    realm=\"agents.merchant.example\",\n    rails=[\n        PaymentHeadersRail(rail=\"tempo-mainnet\", amount_usd=\"10.00\", recipient=TEMPO_ADDR),\n        PaymentHeadersRail(rail=\"x402-base-mainnet\", amount_usd=\"10.00\", recipient=BASE_ADDR),\n        PaymentHeadersRail(rail=\"stripe\", amount_usd=\"10.00\", network_id=STRIPE_PROFILE_ID),\n    ],\n))\n# headers[\"www_authenticate\"] → set as Authorization-style WWW-Authenticate header\n# headers[\"payment_required\"] → set as PAYMENT-REQUIRED header (when x402 is present)\n```\n\n### Identity publishing (cross-vendor standards)\n\n```python\nfrom agentscore_commerce.identity import (\n    UCPService,\n    UCPSigningKey,\n    UCPPaymentHandler,\n    A2AAgentCardCapabilities,\n    build_a2a_agent_card,\n    build_ucp_profile,\n)\n\n# Google A2A v1.0 Signed Agent Card — publish at /.well-known/agent-card.json\ncard = build_a2a_agent_card(name=\"My Service\", url=base_url, capabilities=A2AAgentCardCapabilities(...), data=assess_result)\n\n# Google Universal Commerce Protocol — publish at /.well-known/ucp\nprofile = build_ucp_profile(\n    name=\"My Service\",\n    services=[UCPService(type=\"rest\", url=base_url)],\n    payment_handlers=[UCPPaymentHandler(name=\"tempo\", config={\"recipient\": TEMPO_ADDR})],\n    signing_keys=[UCPSigningKey(kid=\"me\", kty=\"EC\", alg=\"ES256\")],\n    data=assess_result,\n)\n```\n\nACP (Stripe + OpenAI Agentic Commerce Protocol) is a transactional checkout protocol with no identity-publishing surface — ACP merchants integrate via the existing `build_402_body` + `build_payment_headers` + Stripe SPT rail.\n\n## Stripe multichain\n\n```python\nimport os\nimport stripe\nfrom agentscore_commerce.stripe_multichain import (\n    CreateMultichainPaymentIntentInput,\n    PiCacheOptions,\n    SimulateDepositIfTestModeInput,\n    create_multichain_payment_intent,\n    create_pi_cache,\n    get_deposit_address,\n    simulate_deposit_if_test_mode,\n)\n\nstripe_client = stripe.StripeClient(os.environ[\"STRIPE_SECRET_KEY\"])\nresult = create_multichain_payment_intent(CreateMultichainPaymentIntentInput(\n    stripe=stripe_client,\n    amount=1000,\n    networks=[\"tempo\", \"base\", \"solana\"],\n    metadata={\"order_id\": order_id},\n    idempotency_key=order_id,\n))\nbase_address = get_deposit_address(result, \"base\")\nsolana_address = get_deposit_address(result, \"solana\")\n\n# PI / deposit-address cache. Redis-backed when REDIS_URL is set, in-memory otherwise —\n# multi-instance deployments need Redis so a deposit lands on whichever instance settles it.\npi_cache = create_pi_cache(PiCacheOptions(redis_url=os.environ.get(\"REDIS_URL\")))\nfor addr in result.deposit_addresses.values():\n    await pi_cache.cache_address(addr)\n    pi_cache.cache_payment_intent(addr, result.payment_intent_id)\npi_cache.cache_network_addresses(result.payment_intent_id, result.deposit_addresses)\n\n# Testnet helper — gates on sk_test_ and looks up the PI for you. No-op on live keys.\nawait simulate_deposit_if_test_mode(SimulateDepositIfTestModeInput(\n    get_payment_intent_id=pi_cache.get_payment_intent_id,\n    deposit_address=base_address,\n    network=\"base\",\n    stripe_secret_key=os.environ[\"STRIPE_SECRET_KEY\"],\n))\n```\n\n## Drop-in 402 + settle (x402)\n\n```python\nfrom agentscore_commerce.challenge import Build402BodyInput, Respond402Input, respond_402\nfrom agentscore_commerce.payment import (\n    PaymentRequiredHeaderInput,\n    ProcessX402SettleInput,\n    ValidateX402NetworkConfigInput,\n    VerifyX402RequestInput,\n    process_x402_settle,\n    validate_x402_network_config,\n    verify_x402_request,\n)\n\n# Boot-time guard — raises if a configured network isn't supported.\nvalidate_x402_network_config(\n    ValidateX402NetworkConfigInput(base_network=X402_BASE, svm_network=X402_SVM)\n)\n\n@app.post(\"/purchase\")\nasync def purchase(request: Request):\n    # Path A — agent presented an x402 X-Payment header\n    if request.headers.get(\"payment-signature\") or request.headers.get(\"x-payment\"):\n        verified = await verify_x402_request(VerifyX402RequestInput(\n            headers=dict(request.headers),\n            is_cached_address=pi_cache.has_address,\n            accepted_base_network=X402_BASE,\n            accepted_svm_network=X402_SVM,\n        ))\n        if not verified.ok:\n            return JSONResponse(verified.body, status_code=verified.status)\n\n        settle = await process_x402_settle(ProcessX402SettleInput(\n            x402_server=x402_server,\n            payload=verified.payload,\n            resource_config={\"scheme\": \"exact\", \"network\": verified.signed_network, \"price\": f\"${total}\", \"payTo\": verified.signed_pay_to, \"maxTimeoutSeconds\": 300},\n            resource_meta={\"url\": str(request.url), \"mimeType\": \"application/json\"},\n        ))\n        if not settle.success:\n            return JSONResponse({\"error\": {\"code\": \"payment_proof_invalid\", \"phase\": settle.phase}}, status_code=400)\n\n        headers = {\"payment-response\": settle.payment_response_header} if settle.payment_response_header else {}\n        return JSONResponse({\"ok\": True}, headers=headers)\n\n    # Path B — cold call (or Authorization: Payment for pympp). After pympp.compose() returns 402,\n    # respond_402 PRESERVES pympp's WWW-Authenticate and ADDS x402's PAYMENT-REQUIRED.\n    result = respond_402(Respond402Input(\n        mppx_challenge_headers=pympp_challenge_headers,\n        body=Build402BodyInput(accepted_methods=accepted, agent_instructions=instructions, pricing=pricing, amount_usd=total, retry_body=body),\n        x402=PaymentRequiredHeaderInput(x402_version=2, accepts=x402_accepts, resource={\"url\": str(request.url), \"mimeType\": \"application/json\"}),\n    ))\n    return JSONResponse(result.body, status_code=result.status, headers=result.headers)\n```\n\n## Fail-open behavior\n\nBy default AgentScore Gate fails closed: any AgentScore-side infrastructure failure (HTTP 429, 5xx, network timeout) returns 503 to the buyer. Set `fail_open=True` on `AgentScoreGate(...)` to opt in to graceful degradation:\n\n```python\nfrom fastapi import Depends, FastAPI, Request\nfrom agentscore_commerce.identity.fastapi import AgentScoreGate, get_gate_degraded_state\n\napp = FastAPI()\ngate = AgentScoreGate(api_key=os.environ[\"AGENTSCORE_API_KEY\"], fail_open=True)\n\n@app.post(\"/purchase\", dependencies=[Depends(gate)])\nasync def purchase(request: Request):\n    state = get_gate_degraded_state(request)\n    if state[\"degraded\"]:\n        # Compliance was NOT enforced this request — log/alert/refund-async/etc.\n        logger.warning(\"gate degraded: %s\", state[\"infra_reason\"])\n    # ...rest of handler\n```\n\nWhen `fail_open=True` AND the failure is infra-shape, the gate state carries `degraded=True` + `infra_reason=\"quota_exceeded\" | \"api_error\" | \"network_timeout\"` so merchants can log/alert without parsing console output. **Compliance denials (sanctions, age, jurisdiction, signer-mismatch) still deny regardless of `fail_open`** — `fail_open` only covers \"AgentScore couldn't tell us,\" never \"AgentScore said no.\"\n\nFor regulated commerce (alcohol, age-gated, sanctioned-jurisdiction-relevant) keep the default `fail_open=False` — outage is the correct posture; bypassing compliance on infra failure is a compliance gap. For low-stakes commerce or high-uptime SLAs, opt in and use the `degraded` flag as the audit trail.\n\nThe `get_gate_degraded_state` helper is exported by every framework adapter (FastAPI, Flask, Django, AIOHTTP, Sanic, ASGI middleware) and reads from the framework-appropriate request state. The signature takes a request argument everywhere except Flask, which reads from `g` and takes no arguments.\n\n## Examples\n\nThe [examples/](./examples) directory has 7 runnable single-file FastAPI apps covering common merchant scenarios. See [examples/README.md](./examples/README.md) for the full table.\n\n## Stability\n\n`agentscore-commerce@1.0.0` ships with the full merchant SDK surface stable. Helpers are protocol translations + configurable opinions — most evolution is additive (new optional params, new helpers, new networks/rails). Major bumps are reserved for genuine protocol-mapping bugs.\n\n## Documentation\n\nFull integration docs at [docs.agentscore.sh/integrations/python-commerce](https://docs.agentscore.sh/integrations/python-commerce).\n\n## License\n\n[MIT](LICENSE)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fagentscore%2Fpython-commerce","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fagentscore%2Fpython-commerce","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fagentscore%2Fpython-commerce/lists"}