agentscore-commerce 2.2.0__tar.gz → 2.3.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (227) hide show
  1. {agentscore_commerce-2.2.0 → agentscore_commerce-2.3.0}/CLAUDE.md +1 -1
  2. {agentscore_commerce-2.2.0 → agentscore_commerce-2.3.0}/PKG-INFO +2 -2
  3. {agentscore_commerce-2.2.0 → agentscore_commerce-2.3.0}/README.md +1 -1
  4. {agentscore_commerce-2.2.0 → agentscore_commerce-2.3.0}/agentscore_commerce/payment/rail_spec.py +24 -0
  5. {agentscore_commerce-2.2.0 → agentscore_commerce-2.3.0}/agentscore_commerce/stripe_multichain/__init__.py +4 -0
  6. agentscore_commerce-2.3.0/agentscore_commerce/stripe_multichain/pay_to_address.py +280 -0
  7. {agentscore_commerce-2.2.0 → agentscore_commerce-2.3.0}/examples/README.md +1 -1
  8. {agentscore_commerce-2.2.0 → agentscore_commerce-2.3.0}/examples/multi_rail_merchant.py +26 -13
  9. {agentscore_commerce-2.2.0 → agentscore_commerce-2.3.0}/pyproject.toml +1 -1
  10. {agentscore_commerce-2.2.0 → agentscore_commerce-2.3.0}/tests/test_pay_to_address.py +120 -0
  11. {agentscore_commerce-2.2.0 → agentscore_commerce-2.3.0}/uv.lock +40 -40
  12. agentscore_commerce-2.2.0/agentscore_commerce/stripe_multichain/pay_to_address.py +0 -127
  13. {agentscore_commerce-2.2.0 → agentscore_commerce-2.3.0}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
  14. {agentscore_commerce-2.2.0 → agentscore_commerce-2.3.0}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
  15. {agentscore_commerce-2.2.0 → agentscore_commerce-2.3.0}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
  16. {agentscore_commerce-2.2.0 → agentscore_commerce-2.3.0}/.github/dependabot.yml +0 -0
  17. {agentscore_commerce-2.2.0 → agentscore_commerce-2.3.0}/.github/workflows/ci.yml +0 -0
  18. {agentscore_commerce-2.2.0 → agentscore_commerce-2.3.0}/.github/workflows/publish.yml +0 -0
  19. {agentscore_commerce-2.2.0 → agentscore_commerce-2.3.0}/.github/workflows/security.yml +0 -0
  20. {agentscore_commerce-2.2.0 → agentscore_commerce-2.3.0}/.gitignore +0 -0
  21. {agentscore_commerce-2.2.0 → agentscore_commerce-2.3.0}/CODE_OF_CONDUCT.md +0 -0
  22. {agentscore_commerce-2.2.0 → agentscore_commerce-2.3.0}/CONTRIBUTING.md +0 -0
  23. {agentscore_commerce-2.2.0 → agentscore_commerce-2.3.0}/LICENSE +0 -0
  24. {agentscore_commerce-2.2.0 → agentscore_commerce-2.3.0}/SECURITY.md +0 -0
  25. {agentscore_commerce-2.2.0 → agentscore_commerce-2.3.0}/agentscore_commerce/__init__.py +0 -0
  26. {agentscore_commerce-2.2.0 → agentscore_commerce-2.3.0}/agentscore_commerce/_headers.py +0 -0
  27. {agentscore_commerce-2.2.0 → agentscore_commerce-2.3.0}/agentscore_commerce/_mppx_receipt.py +0 -0
  28. {agentscore_commerce-2.2.0 → agentscore_commerce-2.3.0}/agentscore_commerce/_redis.py +0 -0
  29. {agentscore_commerce-2.2.0 → agentscore_commerce-2.3.0}/agentscore_commerce/_warnings.py +0 -0
  30. {agentscore_commerce-2.2.0 → agentscore_commerce-2.3.0}/agentscore_commerce/api/__init__.py +0 -0
  31. {agentscore_commerce-2.2.0 → agentscore_commerce-2.3.0}/agentscore_commerce/challenge/__init__.py +0 -0
  32. {agentscore_commerce-2.2.0 → agentscore_commerce-2.3.0}/agentscore_commerce/challenge/accepted_methods.py +0 -0
  33. {agentscore_commerce-2.2.0 → agentscore_commerce-2.3.0}/agentscore_commerce/challenge/agent_instructions.py +0 -0
  34. {agentscore_commerce-2.2.0 → agentscore_commerce-2.3.0}/agentscore_commerce/challenge/agent_memory.py +0 -0
  35. {agentscore_commerce-2.2.0 → agentscore_commerce-2.3.0}/agentscore_commerce/challenge/body.py +0 -0
  36. {agentscore_commerce-2.2.0 → agentscore_commerce-2.3.0}/agentscore_commerce/challenge/how_to_pay.py +0 -0
  37. {agentscore_commerce-2.2.0 → agentscore_commerce-2.3.0}/agentscore_commerce/challenge/identity.py +0 -0
  38. {agentscore_commerce-2.2.0 → agentscore_commerce-2.3.0}/agentscore_commerce/challenge/pricing.py +0 -0
  39. {agentscore_commerce-2.2.0 → agentscore_commerce-2.3.0}/agentscore_commerce/challenge/receipt.py +0 -0
  40. {agentscore_commerce-2.2.0 → agentscore_commerce-2.3.0}/agentscore_commerce/challenge/respond_402.py +0 -0
  41. {agentscore_commerce-2.2.0 → agentscore_commerce-2.3.0}/agentscore_commerce/challenge/validation_error.py +0 -0
  42. {agentscore_commerce-2.2.0 → agentscore_commerce-2.3.0}/agentscore_commerce/checkout.py +0 -0
  43. {agentscore_commerce-2.2.0 → agentscore_commerce-2.3.0}/agentscore_commerce/checkout_compute_first.py +0 -0
  44. {agentscore_commerce-2.2.0 → agentscore_commerce-2.3.0}/agentscore_commerce/checkout_hooks.py +0 -0
  45. {agentscore_commerce-2.2.0 → agentscore_commerce-2.3.0}/agentscore_commerce/discovery/__init__.py +0 -0
  46. {agentscore_commerce-2.2.0 → agentscore_commerce-2.3.0}/agentscore_commerce/discovery/agentscore_content.py +0 -0
  47. {agentscore_commerce-2.2.0 → agentscore_commerce-2.3.0}/agentscore_commerce/discovery/bazaar.py +0 -0
  48. {agentscore_commerce-2.2.0 → agentscore_commerce-2.3.0}/agentscore_commerce/discovery/llms_txt.py +0 -0
  49. {agentscore_commerce-2.2.0 → agentscore_commerce-2.3.0}/agentscore_commerce/discovery/openapi.py +0 -0
  50. {agentscore_commerce-2.2.0 → agentscore_commerce-2.3.0}/agentscore_commerce/discovery/probe.py +0 -0
  51. {agentscore_commerce-2.2.0 → agentscore_commerce-2.3.0}/agentscore_commerce/discovery/redemption_md.py +0 -0
  52. {agentscore_commerce-2.2.0 → agentscore_commerce-2.3.0}/agentscore_commerce/discovery/robots_tag.py +0 -0
  53. {agentscore_commerce-2.2.0 → agentscore_commerce-2.3.0}/agentscore_commerce/discovery/skill_md.py +0 -0
  54. {agentscore_commerce-2.2.0 → agentscore_commerce-2.3.0}/agentscore_commerce/discovery/well_known.py +0 -0
  55. {agentscore_commerce-2.2.0 → agentscore_commerce-2.3.0}/agentscore_commerce/discovery/well_known_mpp.py +0 -0
  56. {agentscore_commerce-2.2.0 → agentscore_commerce-2.3.0}/agentscore_commerce/discovery/well_known_x402.py +0 -0
  57. {agentscore_commerce-2.2.0 → agentscore_commerce-2.3.0}/agentscore_commerce/errors.py +0 -0
  58. {agentscore_commerce-2.2.0 → agentscore_commerce-2.3.0}/agentscore_commerce/identity/__init__.py +0 -0
  59. {agentscore_commerce-2.2.0 → agentscore_commerce-2.3.0}/agentscore_commerce/identity/_denial.py +0 -0
  60. {agentscore_commerce-2.2.0 → agentscore_commerce-2.3.0}/agentscore_commerce/identity/_response.py +0 -0
  61. {agentscore_commerce-2.2.0 → agentscore_commerce-2.3.0}/agentscore_commerce/identity/a2a.py +0 -0
  62. {agentscore_commerce-2.2.0 → agentscore_commerce-2.3.0}/agentscore_commerce/identity/address.py +0 -0
  63. {agentscore_commerce-2.2.0 → agentscore_commerce-2.3.0}/agentscore_commerce/identity/aiohttp.py +0 -0
  64. {agentscore_commerce-2.2.0 → agentscore_commerce-2.3.0}/agentscore_commerce/identity/cache.py +0 -0
  65. {agentscore_commerce-2.2.0 → agentscore_commerce-2.3.0}/agentscore_commerce/identity/core.py +0 -0
  66. {agentscore_commerce-2.2.0 → agentscore_commerce-2.3.0}/agentscore_commerce/identity/default_denied.py +0 -0
  67. {agentscore_commerce-2.2.0 → agentscore_commerce-2.3.0}/agentscore_commerce/identity/django.py +0 -0
  68. {agentscore_commerce-2.2.0 → agentscore_commerce-2.3.0}/agentscore_commerce/identity/fastapi.py +0 -0
  69. {agentscore_commerce-2.2.0 → agentscore_commerce-2.3.0}/agentscore_commerce/identity/flask.py +0 -0
  70. {agentscore_commerce-2.2.0 → agentscore_commerce-2.3.0}/agentscore_commerce/identity/middleware.py +0 -0
  71. {agentscore_commerce-2.2.0 → agentscore_commerce-2.3.0}/agentscore_commerce/identity/policy.py +0 -0
  72. {agentscore_commerce-2.2.0 → agentscore_commerce-2.3.0}/agentscore_commerce/identity/py.typed +0 -0
  73. {agentscore_commerce-2.2.0 → agentscore_commerce-2.3.0}/agentscore_commerce/identity/sanic.py +0 -0
  74. {agentscore_commerce-2.2.0 → agentscore_commerce-2.3.0}/agentscore_commerce/identity/sessions.py +0 -0
  75. {agentscore_commerce-2.2.0 → agentscore_commerce-2.3.0}/agentscore_commerce/identity/signer.py +0 -0
  76. {agentscore_commerce-2.2.0 → agentscore_commerce-2.3.0}/agentscore_commerce/identity/tokens.py +0 -0
  77. {agentscore_commerce-2.2.0 → agentscore_commerce-2.3.0}/agentscore_commerce/identity/types.py +0 -0
  78. {agentscore_commerce-2.2.0 → agentscore_commerce-2.3.0}/agentscore_commerce/identity/ucp.py +0 -0
  79. {agentscore_commerce-2.2.0 → agentscore_commerce-2.3.0}/agentscore_commerce/identity/ucp_jwks.py +0 -0
  80. {agentscore_commerce-2.2.0 → agentscore_commerce-2.3.0}/agentscore_commerce/middleware/__init__.py +0 -0
  81. {agentscore_commerce-2.2.0 → agentscore_commerce-2.3.0}/agentscore_commerce/middleware/_core.py +0 -0
  82. {agentscore_commerce-2.2.0 → agentscore_commerce-2.3.0}/agentscore_commerce/middleware/aiohttp.py +0 -0
  83. {agentscore_commerce-2.2.0 → agentscore_commerce-2.3.0}/agentscore_commerce/middleware/asgi.py +0 -0
  84. {agentscore_commerce-2.2.0 → agentscore_commerce-2.3.0}/agentscore_commerce/middleware/django.py +0 -0
  85. {agentscore_commerce-2.2.0 → agentscore_commerce-2.3.0}/agentscore_commerce/middleware/fastapi.py +0 -0
  86. {agentscore_commerce-2.2.0 → agentscore_commerce-2.3.0}/agentscore_commerce/middleware/flask.py +0 -0
  87. {agentscore_commerce-2.2.0 → agentscore_commerce-2.3.0}/agentscore_commerce/middleware/sanic.py +0 -0
  88. {agentscore_commerce-2.2.0 → agentscore_commerce-2.3.0}/agentscore_commerce/payment/__init__.py +0 -0
  89. {agentscore_commerce-2.2.0 → agentscore_commerce-2.3.0}/agentscore_commerce/payment/amounts.py +0 -0
  90. {agentscore_commerce-2.2.0 → agentscore_commerce-2.3.0}/agentscore_commerce/payment/compose_rails.py +0 -0
  91. {agentscore_commerce-2.2.0 → agentscore_commerce-2.3.0}/agentscore_commerce/payment/constants.py +0 -0
  92. {agentscore_commerce-2.2.0 → agentscore_commerce-2.3.0}/agentscore_commerce/payment/default_rails.py +0 -0
  93. {agentscore_commerce-2.2.0 → agentscore_commerce-2.3.0}/agentscore_commerce/payment/directive.py +0 -0
  94. {agentscore_commerce-2.2.0 → agentscore_commerce-2.3.0}/agentscore_commerce/payment/dispatch.py +0 -0
  95. {agentscore_commerce-2.2.0 → agentscore_commerce-2.3.0}/agentscore_commerce/payment/headers.py +0 -0
  96. {agentscore_commerce-2.2.0 → agentscore_commerce-2.3.0}/agentscore_commerce/payment/idempotency.py +0 -0
  97. {agentscore_commerce-2.2.0 → agentscore_commerce-2.3.0}/agentscore_commerce/payment/lazy.py +0 -0
  98. {agentscore_commerce-2.2.0 → agentscore_commerce-2.3.0}/agentscore_commerce/payment/mppx_failures.py +0 -0
  99. {agentscore_commerce-2.2.0 → agentscore_commerce-2.3.0}/agentscore_commerce/payment/mppx_server.py +0 -0
  100. {agentscore_commerce-2.2.0 → agentscore_commerce-2.3.0}/agentscore_commerce/payment/network_kind.py +0 -0
  101. {agentscore_commerce-2.2.0 → agentscore_commerce-2.3.0}/agentscore_commerce/payment/networks.py +0 -0
  102. {agentscore_commerce-2.2.0 → agentscore_commerce-2.3.0}/agentscore_commerce/payment/payment_header.py +0 -0
  103. {agentscore_commerce-2.2.0 → agentscore_commerce-2.3.0}/agentscore_commerce/payment/rails.py +0 -0
  104. {agentscore_commerce-2.2.0 → agentscore_commerce-2.3.0}/agentscore_commerce/payment/settlement_override.py +0 -0
  105. {agentscore_commerce-2.2.0 → agentscore_commerce-2.3.0}/agentscore_commerce/payment/signer.py +0 -0
  106. {agentscore_commerce-2.2.0 → agentscore_commerce-2.3.0}/agentscore_commerce/payment/solana.py +0 -0
  107. {agentscore_commerce-2.2.0 → agentscore_commerce-2.3.0}/agentscore_commerce/payment/usdc.py +0 -0
  108. {agentscore_commerce-2.2.0 → agentscore_commerce-2.3.0}/agentscore_commerce/payment/wwwauthenticate.py +0 -0
  109. {agentscore_commerce-2.2.0 → agentscore_commerce-2.3.0}/agentscore_commerce/payment/x402.py +0 -0
  110. {agentscore_commerce-2.2.0 → agentscore_commerce-2.3.0}/agentscore_commerce/payment/x402_server.py +0 -0
  111. {agentscore_commerce-2.2.0 → agentscore_commerce-2.3.0}/agentscore_commerce/payment/x402_settle.py +0 -0
  112. {agentscore_commerce-2.2.0 → agentscore_commerce-2.3.0}/agentscore_commerce/payment/x402_validation.py +0 -0
  113. {agentscore_commerce-2.2.0 → agentscore_commerce-2.3.0}/agentscore_commerce/payment/zero_settle.py +0 -0
  114. {agentscore_commerce-2.2.0 → agentscore_commerce-2.3.0}/agentscore_commerce/quote_cache.py +0 -0
  115. {agentscore_commerce-2.2.0 → agentscore_commerce-2.3.0}/agentscore_commerce/stripe_multichain/mppx_stripe.py +0 -0
  116. {agentscore_commerce-2.2.0 → agentscore_commerce-2.3.0}/agentscore_commerce/stripe_multichain/payment_intent.py +0 -0
  117. {agentscore_commerce-2.2.0 → agentscore_commerce-2.3.0}/agentscore_commerce/stripe_multichain/pi_cache.py +0 -0
  118. {agentscore_commerce-2.2.0 → agentscore_commerce-2.3.0}/agentscore_commerce/stripe_multichain/simulate_deposit.py +0 -0
  119. {agentscore_commerce-2.2.0 → agentscore_commerce-2.3.0}/agentscore_commerce/stripe_multichain/simulate_dispatch.py +0 -0
  120. {agentscore_commerce-2.2.0 → agentscore_commerce-2.3.0}/examples/api_provider.py +0 -0
  121. {agentscore_commerce-2.2.0 → agentscore_commerce-2.3.0}/examples/compliance_merchant.py +0 -0
  122. {agentscore_commerce-2.2.0 → agentscore_commerce-2.3.0}/examples/compute_first_merchant.py +0 -0
  123. {agentscore_commerce-2.2.0 → agentscore_commerce-2.3.0}/examples/identity_only.py +0 -0
  124. {agentscore_commerce-2.2.0 → agentscore_commerce-2.3.0}/examples/per_product_policy_merchant.py +0 -0
  125. {agentscore_commerce-2.2.0 → agentscore_commerce-2.3.0}/examples/signed_ucp_merchant.py +0 -0
  126. {agentscore_commerce-2.2.0 → agentscore_commerce-2.3.0}/examples/stripe_multichain_merchant.py +0 -0
  127. {agentscore_commerce-2.2.0 → agentscore_commerce-2.3.0}/lefthook.yml +0 -0
  128. {agentscore_commerce-2.2.0 → agentscore_commerce-2.3.0}/osv-scanner.toml +0 -0
  129. {agentscore_commerce-2.2.0 → agentscore_commerce-2.3.0}/ruff.toml +0 -0
  130. {agentscore_commerce-2.2.0 → agentscore_commerce-2.3.0}/scripts/regenerate_cross_lang_fixtures.py +0 -0
  131. {agentscore_commerce-2.2.0 → agentscore_commerce-2.3.0}/tests/__init__.py +0 -0
  132. {agentscore_commerce-2.2.0 → agentscore_commerce-2.3.0}/tests/conftest.py +0 -0
  133. {agentscore_commerce-2.2.0 → agentscore_commerce-2.3.0}/tests/fixtures/cross-lang/node-agentscore-gate-blocked.json +0 -0
  134. {agentscore_commerce-2.2.0 → agentscore_commerce-2.3.0}/tests/fixtures/cross-lang/node-agentscore-gate-full.json +0 -0
  135. {agentscore_commerce-2.2.0 → agentscore_commerce-2.3.0}/tests/fixtures/cross-lang/node-capability.json +0 -0
  136. {agentscore_commerce-2.2.0 → agentscore_commerce-2.3.0}/tests/fixtures/cross-lang/node-emoji-keys.json +0 -0
  137. {agentscore_commerce-2.2.0 → agentscore_commerce-2.3.0}/tests/fixtures/cross-lang/node-es256-rails.json +0 -0
  138. {agentscore_commerce-2.2.0 → agentscore_commerce-2.3.0}/tests/fixtures/cross-lang/node-extras-int.json +0 -0
  139. {agentscore_commerce-2.2.0 → agentscore_commerce-2.3.0}/tests/fixtures/cross-lang/node-int-boundary.json +0 -0
  140. {agentscore_commerce-2.2.0 → agentscore_commerce-2.3.0}/tests/fixtures/cross-lang/node-minimal.json +0 -0
  141. {agentscore_commerce-2.2.0 → agentscore_commerce-2.3.0}/tests/fixtures/cross-lang/node-multikey.json +0 -0
  142. {agentscore_commerce-2.2.0 → agentscore_commerce-2.3.0}/tests/fixtures/cross-lang/node-unicode.json +0 -0
  143. {agentscore_commerce-2.2.0 → agentscore_commerce-2.3.0}/tests/fixtures/cross-lang/py-agentscore-gate-blocked.json +0 -0
  144. {agentscore_commerce-2.2.0 → agentscore_commerce-2.3.0}/tests/fixtures/cross-lang/py-agentscore-gate-full.json +0 -0
  145. {agentscore_commerce-2.2.0 → agentscore_commerce-2.3.0}/tests/fixtures/cross-lang/py-capability.json +0 -0
  146. {agentscore_commerce-2.2.0 → agentscore_commerce-2.3.0}/tests/fixtures/cross-lang/py-emoji-keys.json +0 -0
  147. {agentscore_commerce-2.2.0 → agentscore_commerce-2.3.0}/tests/fixtures/cross-lang/py-es256-rails.json +0 -0
  148. {agentscore_commerce-2.2.0 → agentscore_commerce-2.3.0}/tests/fixtures/cross-lang/py-extras-int.json +0 -0
  149. {agentscore_commerce-2.2.0 → agentscore_commerce-2.3.0}/tests/fixtures/cross-lang/py-int-boundary.json +0 -0
  150. {agentscore_commerce-2.2.0 → agentscore_commerce-2.3.0}/tests/fixtures/cross-lang/py-minimal.json +0 -0
  151. {agentscore_commerce-2.2.0 → agentscore_commerce-2.3.0}/tests/fixtures/cross-lang/py-multikey.json +0 -0
  152. {agentscore_commerce-2.2.0 → agentscore_commerce-2.3.0}/tests/fixtures/cross-lang/py-unicode.json +0 -0
  153. {agentscore_commerce-2.2.0 → agentscore_commerce-2.3.0}/tests/test_a2a.py +0 -0
  154. {agentscore_commerce-2.2.0 → agentscore_commerce-2.3.0}/tests/test_a2a_jws_roundtrip.py +0 -0
  155. {agentscore_commerce-2.2.0 → agentscore_commerce-2.3.0}/tests/test_address.py +0 -0
  156. {agentscore_commerce-2.2.0 → agentscore_commerce-2.3.0}/tests/test_agent_memory_emitter.py +0 -0
  157. {agentscore_commerce-2.2.0 → agentscore_commerce-2.3.0}/tests/test_aiohttp.py +0 -0
  158. {agentscore_commerce-2.2.0 → agentscore_commerce-2.3.0}/tests/test_amounts.py +0 -0
  159. {agentscore_commerce-2.2.0 → agentscore_commerce-2.3.0}/tests/test_api_reexport.py +0 -0
  160. {agentscore_commerce-2.2.0 → agentscore_commerce-2.3.0}/tests/test_cache.py +0 -0
  161. {agentscore_commerce-2.2.0 → agentscore_commerce-2.3.0}/tests/test_challenge.py +0 -0
  162. {agentscore_commerce-2.2.0 → agentscore_commerce-2.3.0}/tests/test_checkout.py +0 -0
  163. {agentscore_commerce-2.2.0 → agentscore_commerce-2.3.0}/tests/test_checkout_compute_first.py +0 -0
  164. {agentscore_commerce-2.2.0 → agentscore_commerce-2.3.0}/tests/test_checkout_compute_first_adapters.py +0 -0
  165. {agentscore_commerce-2.2.0 → agentscore_commerce-2.3.0}/tests/test_checkout_compute_first_ofac.py +0 -0
  166. {agentscore_commerce-2.2.0 → agentscore_commerce-2.3.0}/tests/test_checkout_compute_first_settle.py +0 -0
  167. {agentscore_commerce-2.2.0 → agentscore_commerce-2.3.0}/tests/test_checkout_coverage_gaps.py +0 -0
  168. {agentscore_commerce-2.2.0 → agentscore_commerce-2.3.0}/tests/test_checkout_wallet_ofac_default.py +0 -0
  169. {agentscore_commerce-2.2.0 → agentscore_commerce-2.3.0}/tests/test_classify_orchestration_error.py +0 -0
  170. {agentscore_commerce-2.2.0 → agentscore_commerce-2.3.0}/tests/test_compose_rails.py +0 -0
  171. {agentscore_commerce-2.2.0 → agentscore_commerce-2.3.0}/tests/test_core.py +0 -0
  172. {agentscore_commerce-2.2.0 → agentscore_commerce-2.3.0}/tests/test_coverage_fillers.py +0 -0
  173. {agentscore_commerce-2.2.0 → agentscore_commerce-2.3.0}/tests/test_default_denied.py +0 -0
  174. {agentscore_commerce-2.2.0 → agentscore_commerce-2.3.0}/tests/test_default_rails.py +0 -0
  175. {agentscore_commerce-2.2.0 → agentscore_commerce-2.3.0}/tests/test_default_read_only_on_denied.py +0 -0
  176. {agentscore_commerce-2.2.0 → agentscore_commerce-2.3.0}/tests/test_denial.py +0 -0
  177. {agentscore_commerce-2.2.0 → agentscore_commerce-2.3.0}/tests/test_discovery.py +0 -0
  178. {agentscore_commerce-2.2.0 → agentscore_commerce-2.3.0}/tests/test_dispatch.py +0 -0
  179. {agentscore_commerce-2.2.0 → agentscore_commerce-2.3.0}/tests/test_django.py +0 -0
  180. {agentscore_commerce-2.2.0 → agentscore_commerce-2.3.0}/tests/test_extract_owner_scope.py +0 -0
  181. {agentscore_commerce-2.2.0 → agentscore_commerce-2.3.0}/tests/test_fastapi.py +0 -0
  182. {agentscore_commerce-2.2.0 → agentscore_commerce-2.3.0}/tests/test_flask.py +0 -0
  183. {agentscore_commerce-2.2.0 → agentscore_commerce-2.3.0}/tests/test_gate_quota_info.py +0 -0
  184. {agentscore_commerce-2.2.0 → agentscore_commerce-2.3.0}/tests/test_get_signer_verdict.py +0 -0
  185. {agentscore_commerce-2.2.0 → agentscore_commerce-2.3.0}/tests/test_idempotency_helper.py +0 -0
  186. {agentscore_commerce-2.2.0 → agentscore_commerce-2.3.0}/tests/test_integration.py +0 -0
  187. {agentscore_commerce-2.2.0 → agentscore_commerce-2.3.0}/tests/test_internal_helpers.py +0 -0
  188. {agentscore_commerce-2.2.0 → agentscore_commerce-2.3.0}/tests/test_lifted_helpers.py +0 -0
  189. {agentscore_commerce-2.2.0 → agentscore_commerce-2.3.0}/tests/test_load_ucp_signing_key_from_env.py +0 -0
  190. {agentscore_commerce-2.2.0 → agentscore_commerce-2.3.0}/tests/test_middleware.py +0 -0
  191. {agentscore_commerce-2.2.0 → agentscore_commerce-2.3.0}/tests/test_mppx_failures.py +0 -0
  192. {agentscore_commerce-2.2.0 → agentscore_commerce-2.3.0}/tests/test_mppx_receipt.py +0 -0
  193. {agentscore_commerce-2.2.0 → agentscore_commerce-2.3.0}/tests/test_network_kind.py +0 -0
  194. {agentscore_commerce-2.2.0 → agentscore_commerce-2.3.0}/tests/test_payment_directive.py +0 -0
  195. {agentscore_commerce-2.2.0 → agentscore_commerce-2.3.0}/tests/test_payment_dispatch.py +0 -0
  196. {agentscore_commerce-2.2.0 → agentscore_commerce-2.3.0}/tests/test_payment_handlers.py +0 -0
  197. {agentscore_commerce-2.2.0 → agentscore_commerce-2.3.0}/tests/test_payment_header.py +0 -0
  198. {agentscore_commerce-2.2.0 → agentscore_commerce-2.3.0}/tests/test_payment_headers.py +0 -0
  199. {agentscore_commerce-2.2.0 → agentscore_commerce-2.3.0}/tests/test_payment_misc.py +0 -0
  200. {agentscore_commerce-2.2.0 → agentscore_commerce-2.3.0}/tests/test_payment_servers.py +0 -0
  201. {agentscore_commerce-2.2.0 → agentscore_commerce-2.3.0}/tests/test_payment_signer.py +0 -0
  202. {agentscore_commerce-2.2.0 → agentscore_commerce-2.3.0}/tests/test_policy.py +0 -0
  203. {agentscore_commerce-2.2.0 → agentscore_commerce-2.3.0}/tests/test_pricing.py +0 -0
  204. {agentscore_commerce-2.2.0 → agentscore_commerce-2.3.0}/tests/test_public_surface.py +0 -0
  205. {agentscore_commerce-2.2.0 → agentscore_commerce-2.3.0}/tests/test_quote_cache.py +0 -0
  206. {agentscore_commerce-2.2.0 → agentscore_commerce-2.3.0}/tests/test_quote_cache_redis.py +0 -0
  207. {agentscore_commerce-2.2.0 → agentscore_commerce-2.3.0}/tests/test_rail_spec.py +0 -0
  208. {agentscore_commerce-2.2.0 → agentscore_commerce-2.3.0}/tests/test_rate_limit.py +0 -0
  209. {agentscore_commerce-2.2.0 → agentscore_commerce-2.3.0}/tests/test_redis_internal.py +0 -0
  210. {agentscore_commerce-2.2.0 → agentscore_commerce-2.3.0}/tests/test_response.py +0 -0
  211. {agentscore_commerce-2.2.0 → agentscore_commerce-2.3.0}/tests/test_robots_tag.py +0 -0
  212. {agentscore_commerce-2.2.0 → agentscore_commerce-2.3.0}/tests/test_sanic.py +0 -0
  213. {agentscore_commerce-2.2.0 → agentscore_commerce-2.3.0}/tests/test_seamless_helpers.py +0 -0
  214. {agentscore_commerce-2.2.0 → agentscore_commerce-2.3.0}/tests/test_sessions.py +0 -0
  215. {agentscore_commerce-2.2.0 → agentscore_commerce-2.3.0}/tests/test_signer_match.py +0 -0
  216. {agentscore_commerce-2.2.0 → agentscore_commerce-2.3.0}/tests/test_simulate_dispatch.py +0 -0
  217. {agentscore_commerce-2.2.0 → agentscore_commerce-2.3.0}/tests/test_skill_md.py +0 -0
  218. {agentscore_commerce-2.2.0 → agentscore_commerce-2.3.0}/tests/test_solana.py +0 -0
  219. {agentscore_commerce-2.2.0 → agentscore_commerce-2.3.0}/tests/test_solana_mpp_rail_spec_post_init.py +0 -0
  220. {agentscore_commerce-2.2.0 → agentscore_commerce-2.3.0}/tests/test_stripe_multichain.py +0 -0
  221. {agentscore_commerce-2.2.0 → agentscore_commerce-2.3.0}/tests/test_tokens.py +0 -0
  222. {agentscore_commerce-2.2.0 → agentscore_commerce-2.3.0}/tests/test_ucp.py +0 -0
  223. {agentscore_commerce-2.2.0 → agentscore_commerce-2.3.0}/tests/test_ucp_cross_lang.py +0 -0
  224. {agentscore_commerce-2.2.0 → agentscore_commerce-2.3.0}/tests/test_ucp_jwks.py +0 -0
  225. {agentscore_commerce-2.2.0 → agentscore_commerce-2.3.0}/tests/test_validation_error.py +0 -0
  226. {agentscore_commerce-2.2.0 → agentscore_commerce-2.3.0}/tests/test_x402_settle_extra.py +0 -0
  227. {agentscore_commerce-2.2.0 → agentscore_commerce-2.3.0}/tests/test_zero_settle.py +0 -0
@@ -14,7 +14,7 @@ Every helper is extracted from a real consumer, not speculated.
14
14
  | `agentscore_commerce.payment` | Networks/USDC/rails registries, paymentauth.org directive builders, `create_x402_server` (wraps `x402[evm]>=2.9` + `cdp-sdk` for `facilitator="coinbase"`; install via the `coinbase` extra), `build_x402_accepts_for_402` (build the 402's `accepts[]` from the registered scheme; derives the right `extra.name` per network), `build_default_checkout_rails(tempo=, x402_base=, solana_mpp=, stripe=)` (canonical 4-rail `rails` dict factory: merchants pass per-rail overrides instead of redeclaring the recipient sentinel + network/chain_id/token boilerplate. When a caller flips `network` without pinning `token` / `chain_id`, the underlying dataclass derives them from the network: Base Sepolia → Sepolia USDC + chain_id 84532, Solana devnet → devnet USDC mint. Explicit overrides always win. Solana's `network` field accepts both CAIP-2 (`solana:5eykt4UsFv8…` / `solana:EtWTRABZaYq6iMfeYKouRu166VU2xqa1`) AND the raw `@solana/mpp` form (`mainnet-beta` / `devnet` / `localnet`)), `build_mppx_compose_rails(amount_usd=, tempo_recipient=, solana_recipient=, ...)` (per-call intent factory replacing the hand-rolled `[("tempo/charge", {...}), ("solana/charge", {...}), ("stripe/charge", {...})]` list; auto-handles USD→atomic conversion for Solana; auto-drops the `stripe/charge` rail with a one-time `logging.warning` when `amount_usd < 0.50` since Stripe's fixed ~$0.30 fee makes sub-50-cent charges unprofitable — many Stripe accounts also reject PI creation below the floor with `amount_too_small`; sub-50-cent APIs pass `include_stripe=False` explicitly to silence the warning), `process_x402_settle` (verify+settle in one call), `create_mppx_server` (wraps `pympp[server,tempo,stripe]>=0.6`), `is_evm_network`/`is_solana_network` (CAIP-2 discriminators that hide the `startswith("eip155:")` / `startswith("solana:")` prefix matching), `has_payment_header` (settle-leg vs discovery-leg discriminator), `parse_did_pkh_address` (parses ``did:pkh:<family>:<chain>:<addr>`` into a `PaymentSigner`), dispatch-by-network, signer extraction, WWW-Authenticate header, Settlement-Overrides header |
15
15
  | `agentscore_commerce.discovery` | Discovery probe (`is_discovery_probe_request`, `build_discovery_probe_response`), Bazaar wrapper, `/.well-known/mpp.json`, `llms.txt` builder, `skill.md` builder (Claude-Skill-compatible agent-discovery manifest), `build_redemption_skill_md` (delivery-neutral; printed/emailed/API-trial codes all covered via `delivery_intro`/`body_shape`/`body_rules`/`extra_recovery_rows` overrides), `build_merchant_index_json` + `standard_endpoint_descriptions(kind=)` (canonical `/` discovery body for goods or API merchants), `build_success_next_steps` (universal Passport-active success block), `build_agentscore_onboarding_steps`, OpenAPI snippets, `NoindexNonDiscoveryMiddleware` ASGI middleware. Plus the UCP/JWKS publish surface: `build_signed_ucp_response`, `build_signed_jwks_response`, `well_known_preflight_response`, `default_a2a_services`, `bootstrap_ucp_signing_key`, framework-neutral `SignedDiscoveryResponse` + per-framework wrappers `signed_response_{fastapi,flask,django,aiohttp,sanic}` |
16
16
  | `agentscore_commerce.challenge` | 402-body builders: accepted_methods, identity_metadata (auto-attached by `Checkout` when wallet header present), how_to_pay, agent_instructions, build_402_body, pricing, agent_memory, `build_validation_error` (4xx body builder), `Receipt`/`ReceiptNextSteps`/`ProductInfo`/`ShippingAddress` (canonical 200-receipt dataclasses) |
17
- | `agentscore_commerce.stripe_multichain` | Multichain PaymentIntent helper (`create_multichain_payment_intent` returns `MultichainPaymentIntentResult`; read `result.deposit_addresses[network]` directly), `create_pay_to_address_from_stripe_pi(authorization_header=, amount_cents=, stripe=, pi_cache=, networks=, metadata=, order_id=, preferred_network=)` — one-call per-order payTo resolver matching `Checkout.mint_recipients`: on the settle leg, reuses the buyer's signed-against payTo from the MPP credential (after `pi_cache.has_address` check); on the discovery leg, mints a fresh PI via `create_multichain_payment_intent` and caches the addresses + PI mapping. Testnet simulator (`simulate_crypto_deposit`, `simulate_deposit_if_test_mode`), `simulate_deposit_for_outcome(outcome=, deposit_address=, get_payment_intent_id=, stripe_secret_key=, stripe_version=)` (dispatches the simulator based on a Checkout / compute_first_checkout settle outcome; replaces the per-merchant rail-switch + thin `simulate_deposit_if_testnet(addr, network)` wrapper), `network_for_outcome` (outcome → simulator network arg, handles both Checkout-shaped `rail_key` and compute-first-shaped `mpp_method`, accepts bare scheme names AND `<scheme>/charge` forms), `create_pi_cache`, `create_mppx_stripe` |
17
+ | `agentscore_commerce.stripe_multichain` | Multichain PaymentIntent helper (`create_multichain_payment_intent` returns `MultichainPaymentIntentResult`; read `result.deposit_addresses[network]` directly), `create_pay_to_address_from_stripe_pi(authorization_header=, amount_cents=, stripe=, pi_cache=, networks=, static_recipients=, metadata=, order_id=, preferred_network=)` — one-call per-order payTo resolver matching `Checkout.mint_recipients`: on the settle leg, reuses the buyer's signed-against payTo from the MPP credential (after `pi_cache.has_address` check OR a `static_recipients` match — the static address is always-accepted because the merchant owns it); on the discovery leg, mints a fresh PI for the rails NOT covered by `static_recipients`, caches the merged map, registers static addresses with `pi_cache.cache_address`. `mint_multichain_recipients(...same kwargs) -> MintMultichainRecipientsResult(recipients, payment_intent_id, reused_from_credential)` — structured variant returning the full per-rail map; prefer this when the merchant's `mint_recipients` hook needs all rail addresses (typical multi-rail merchant), and to avoid the "returned-string-is-ambiguous" trap on the settle leg when `static_recipients` is configured. Use `static_recipients={"solana": "<wallet>"}` for low-margin endpoints where rotating per-PI Solana addresses can't absorb MPP spec §13.6's ~$0.50 ATA rent per call — the SDK skips Stripe minting on that network and reuses the static recipient forever; pair with a one-time external USDC pre-funding of the recipient's ATA and every settle pays only the per-tx fee. `SolanaMppRailSpec.ata_creation_required` defaults to `True` (data-only — solana method registration through `create_mppx_server` is a follow-up; merchants building the solana method directly via `pympp` should pass the flag themselves to the charge factory). Testnet simulator (`simulate_crypto_deposit`, `simulate_deposit_if_test_mode`), `simulate_deposit_for_outcome(outcome=, deposit_address=, get_payment_intent_id=, stripe_secret_key=, stripe_version=)` (dispatches the simulator based on a Checkout / compute_first_checkout settle outcome; replaces the per-merchant rail-switch + thin `simulate_deposit_if_testnet(addr, network)` wrapper), `network_for_outcome` (outcome → simulator network arg, handles both Checkout-shaped `rail_key` and compute-first-shaped `mpp_method`, accepts bare scheme names AND `<scheme>/charge` forms), `create_pi_cache`, `create_mppx_stripe` |
18
18
  | `agentscore_commerce.api` | Re-exports `AgentScore` from `agentscore` SDK |
19
19
  | `agentscore_commerce.middleware.{fastapi,flask,django,aiohttp,sanic,asgi}` | Framework-specific rate-limit middleware. FastAPI exposes `rate_limit_fastapi(...)` (FastAPI dependency) plus the ASGI `RateLimitMiddleware` re-export; Flask exposes `rate_limit_flask(app, ...)` (installs a `before_request` hook); Django exposes a class-based async `RateLimitMiddleware` configured via `settings.AGENTSCORE_RATE_LIMIT`; aiohttp exposes `rate_limit_aiohttp(...)` middleware factory; Sanic exposes `rate_limit_sanic(app, ...)` installer; `asgi.RateLimitMiddleware` is a generic ASGI middleware that works with any starlette-compatible app. Shared options: `window_seconds` (default 60), `max_requests` (default 60), `key_resolver` (default first hop of `x-forwarded-for`), `redis_url` (lazy-imports `redis.asyncio` when set, in-memory `dict` fallback otherwise), `key_prefix`. `redis` is an optional peer dep (install via the `redis` extra). |
20
20
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: agentscore-commerce
3
- Version: 2.2.0
3
+ Version: 2.3.0
4
4
  Summary: Agent commerce SDK for Python — identity middleware (FastAPI, Flask, Django, AIOHTTP, Sanic, ASGI) + payment helpers + 402 builders + discovery + Stripe multichain. The full merchant-side toolkit for AgentScore-powered agent commerce.
5
5
  Project-URL: Homepage, https://agentscore.sh
6
6
  Project-URL: Repository, https://github.com/agentscore/python-commerce
@@ -78,7 +78,7 @@ pip install 'agentscore-commerce[fastapi,x402,coinbase]'
78
78
  | `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 that 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` (accepts positional `x402_payment_header` AND/OR `authorization_header=` kwarg; recovers signer from x402 EIP-3009 `payload.authorization.from` OR MPP `Authorization: Payment <base64>` `did:pkh:eip155:<chain>:<addr>` / `did:pkh:solana:<genesis>:<addr>` source DID), `detect_rail_from_headers` (returns `"x402"` / `"mpp"` / `None` from inbound headers), `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), `classify_x402_settle_result` (maps the tagged settle result to a recommended HTTP status / code / next_steps so merchants get a controlled envelope without coupling to facilitator-specific error text), `classify_orchestration_error` (same `ClassifiedX402Error` shape but for uncaught exceptions thrown elsewhere in the orchestration; returns `None` for unknown errors so merchants rethrow instead of swallowing); `zero_amount_carve_out` (skip CDP / pympp upstream verify+settle for $0 settles where the upstream rejects value=0 payloads; parses the credential, lifts signer + network, returns a `ZeroSettleResult` shaped identically to the success path so callers branch on rail, not on result shape); `usd_to_atomic` (Decimal-based USD → atomic int, ROUND_HALF_UP — for Tempo / Solana / Base USDC amount construction). |
79
79
  | `agentscore_commerce.discovery` | `is_discovery_probe_request`, `build_discovery_probe_response` (with optional `x402_sample` for x402-aware crawlers like `awal x402 details`), `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), `build_skill_md` (Claude-Skill-compatible `/skill.md` agent-discovery manifest; strictly agent-facing data only, no internal posture), `build_redemption_skill_md` (delivery-neutral redemption-code template — printed mailers, emailed codes, API trial credits all covered; `endpoint_path`/`delivery_intro`/`body_shape`/`body_rules`/`extra_recovery_rows` overrides for non-goods shapes), `build_merchant_index_json` (canonical `/` discovery body), `standard_endpoint_descriptions(kind=)` (canonical method+path → description map for goods vs api merchants; optional `include_order_status_route` for goods), `build_success_next_steps` (universal Passport-active success block), `build_agentscore_onboarding_steps` (canonical skill.md onboarding for goods or API merchants), `agentscore_openapi_snippets`, `build_bazaar_discovery_payload`, `NoindexNonDiscoveryMiddleware` (ASGI middleware emitting `X-Robots-Tag: noindex` on every path except the agent-discovery surfaces; pure helpers `is_discovery_path` + `DEFAULT_DISCOVERY_PATHS` for non-ASGI frameworks). Plus the UCP/JWKS publish surface: `build_signed_ucp_response`, `build_signed_jwks_response`, `well_known_preflight_response`, `default_a2a_services`, `bootstrap_ucp_signing_key`, framework-neutral `SignedDiscoveryResponse` + per-framework wrappers `signed_response_{fastapi,flask,django,aiohttp,sanic}`. |
80
80
  | `agentscore_commerce.challenge` | `build_402_body`, `build_accepted_methods`, `build_identity_metadata` (auto-attached by `Checkout` when an inbound `X-Wallet-Address` header is present), `build_how_to_pay`, `build_agent_instructions` (auto-emits per-rail `compatible_clients`: smoke-verified CLIs the agent should use; vendor override supported; pure helper `compatible_clients_by_rails(rails)` returns the same map for vendors building custom 402s), `build_pricing_block` (cents to 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), `Receipt` + `ReceiptNextSteps` + `ProductInfo` + `ShippingAddress` (canonical 200-receipt dataclasses — universal across goods + API merchants); `respond_402`, a 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. |
81
- | `agentscore_commerce.stripe_multichain` | `create_multichain_payment_intent` (returns `MultichainPaymentIntentResult(payment_intent_id, deposit_addresses)`; read `result.deposit_addresses[network]` directly), `create_pay_to_address_from_stripe_pi(authorization_header=, amount_cents=, stripe=, pi_cache=, networks=, metadata=, order_id=, preferred_network=)` — per-order payTo resolver: on the settle leg, reuses the buyer's signed-against payTo from the MPP credential (after `pi_cache.has_address` check); on the discovery leg, mints a fresh PI and caches it. `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`. |
81
+ | `agentscore_commerce.stripe_multichain` | `create_multichain_payment_intent` (returns `MultichainPaymentIntentResult(payment_intent_id, deposit_addresses)`; read `result.deposit_addresses[network]` directly), `create_pay_to_address_from_stripe_pi(authorization_header=, amount_cents=, stripe=, pi_cache=, networks=, static_recipients=, metadata=, order_id=, preferred_network=)` — per-order payTo resolver: on the settle leg, reuses the buyer's signed-against payTo from the MPP credential (after `pi_cache.has_address` check OR a `static_recipients` match — the static address is always-accepted because the merchant owns it); on the discovery leg, mints a fresh PI for the rails NOT covered by `static_recipients`, caches the merged map, registers static addresses with `pi_cache.cache_address` so verify-leg lookups pass. `mint_multichain_recipients(...same kwargs)` — structured variant returning `MintMultichainRecipientsResult(recipients, payment_intent_id, reused_from_credential)` for the full per-rail map (typical multi-rail merchant hook). Use `static_recipients={"solana": "<wallet>"}` for low-margin endpoints where Solana per-call ATA rent (~$0.50 against MPP spec §13.6) dominates revenue — the SDK skips Stripe minting on that network, reuses the static recipient forever, and (with a one-time external pre-funding of the recipient's USDC ATA) lets every settle pay only the ~$0.001 per-tx fee. `SolanaMppRailSpec.ata_creation_required` defaults to `True` (data-only; solana method registration through `create_mppx_server` is a follow-up — merchants building the solana method directly via `pympp` should pass the flag themselves to the charge factory). `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`. |
82
82
  | `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. |
83
83
  | `agentscore_commerce.middleware.{fastapi,flask,django,aiohttp,sanic,asgi}` | Framework-specific rate-limit middleware. FastAPI: `rate_limit_fastapi(...)` (FastAPI dependency) plus the ASGI `RateLimitMiddleware` re-export. Flask: `rate_limit_flask(app, ...)` installer. Django: class-based async `RateLimitMiddleware` configured via `settings.AGENTSCORE_RATE_LIMIT`. aiohttp: `rate_limit_aiohttp(...)` middleware factory. Sanic: `rate_limit_sanic(app, ...)` installer. `asgi.RateLimitMiddleware` works with any starlette-compatible app. Shared options: `window_seconds` (default 60), `max_requests` (default 60), `key_resolver` (default first hop of `x-forwarded-for`), `redis_url` (lazy-imports `redis.asyncio` when set, in-memory `dict` fallback otherwise), `key_prefix`. `redis` is an optional peer dep (install via the `redis` extra). |
84
84
 
@@ -30,7 +30,7 @@ pip install 'agentscore-commerce[fastapi,x402,coinbase]'
30
30
  | `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 that 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` (accepts positional `x402_payment_header` AND/OR `authorization_header=` kwarg; recovers signer from x402 EIP-3009 `payload.authorization.from` OR MPP `Authorization: Payment <base64>` `did:pkh:eip155:<chain>:<addr>` / `did:pkh:solana:<genesis>:<addr>` source DID), `detect_rail_from_headers` (returns `"x402"` / `"mpp"` / `None` from inbound headers), `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), `classify_x402_settle_result` (maps the tagged settle result to a recommended HTTP status / code / next_steps so merchants get a controlled envelope without coupling to facilitator-specific error text), `classify_orchestration_error` (same `ClassifiedX402Error` shape but for uncaught exceptions thrown elsewhere in the orchestration; returns `None` for unknown errors so merchants rethrow instead of swallowing); `zero_amount_carve_out` (skip CDP / pympp upstream verify+settle for $0 settles where the upstream rejects value=0 payloads; parses the credential, lifts signer + network, returns a `ZeroSettleResult` shaped identically to the success path so callers branch on rail, not on result shape); `usd_to_atomic` (Decimal-based USD → atomic int, ROUND_HALF_UP — for Tempo / Solana / Base USDC amount construction). |
31
31
  | `agentscore_commerce.discovery` | `is_discovery_probe_request`, `build_discovery_probe_response` (with optional `x402_sample` for x402-aware crawlers like `awal x402 details`), `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), `build_skill_md` (Claude-Skill-compatible `/skill.md` agent-discovery manifest; strictly agent-facing data only, no internal posture), `build_redemption_skill_md` (delivery-neutral redemption-code template — printed mailers, emailed codes, API trial credits all covered; `endpoint_path`/`delivery_intro`/`body_shape`/`body_rules`/`extra_recovery_rows` overrides for non-goods shapes), `build_merchant_index_json` (canonical `/` discovery body), `standard_endpoint_descriptions(kind=)` (canonical method+path → description map for goods vs api merchants; optional `include_order_status_route` for goods), `build_success_next_steps` (universal Passport-active success block), `build_agentscore_onboarding_steps` (canonical skill.md onboarding for goods or API merchants), `agentscore_openapi_snippets`, `build_bazaar_discovery_payload`, `NoindexNonDiscoveryMiddleware` (ASGI middleware emitting `X-Robots-Tag: noindex` on every path except the agent-discovery surfaces; pure helpers `is_discovery_path` + `DEFAULT_DISCOVERY_PATHS` for non-ASGI frameworks). Plus the UCP/JWKS publish surface: `build_signed_ucp_response`, `build_signed_jwks_response`, `well_known_preflight_response`, `default_a2a_services`, `bootstrap_ucp_signing_key`, framework-neutral `SignedDiscoveryResponse` + per-framework wrappers `signed_response_{fastapi,flask,django,aiohttp,sanic}`. |
32
32
  | `agentscore_commerce.challenge` | `build_402_body`, `build_accepted_methods`, `build_identity_metadata` (auto-attached by `Checkout` when an inbound `X-Wallet-Address` header is present), `build_how_to_pay`, `build_agent_instructions` (auto-emits per-rail `compatible_clients`: smoke-verified CLIs the agent should use; vendor override supported; pure helper `compatible_clients_by_rails(rails)` returns the same map for vendors building custom 402s), `build_pricing_block` (cents to 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), `Receipt` + `ReceiptNextSteps` + `ProductInfo` + `ShippingAddress` (canonical 200-receipt dataclasses — universal across goods + API merchants); `respond_402`, a 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. |
33
- | `agentscore_commerce.stripe_multichain` | `create_multichain_payment_intent` (returns `MultichainPaymentIntentResult(payment_intent_id, deposit_addresses)`; read `result.deposit_addresses[network]` directly), `create_pay_to_address_from_stripe_pi(authorization_header=, amount_cents=, stripe=, pi_cache=, networks=, metadata=, order_id=, preferred_network=)` — per-order payTo resolver: on the settle leg, reuses the buyer's signed-against payTo from the MPP credential (after `pi_cache.has_address` check); on the discovery leg, mints a fresh PI and caches it. `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`. |
33
+ | `agentscore_commerce.stripe_multichain` | `create_multichain_payment_intent` (returns `MultichainPaymentIntentResult(payment_intent_id, deposit_addresses)`; read `result.deposit_addresses[network]` directly), `create_pay_to_address_from_stripe_pi(authorization_header=, amount_cents=, stripe=, pi_cache=, networks=, static_recipients=, metadata=, order_id=, preferred_network=)` — per-order payTo resolver: on the settle leg, reuses the buyer's signed-against payTo from the MPP credential (after `pi_cache.has_address` check OR a `static_recipients` match — the static address is always-accepted because the merchant owns it); on the discovery leg, mints a fresh PI for the rails NOT covered by `static_recipients`, caches the merged map, registers static addresses with `pi_cache.cache_address` so verify-leg lookups pass. `mint_multichain_recipients(...same kwargs)` — structured variant returning `MintMultichainRecipientsResult(recipients, payment_intent_id, reused_from_credential)` for the full per-rail map (typical multi-rail merchant hook). Use `static_recipients={"solana": "<wallet>"}` for low-margin endpoints where Solana per-call ATA rent (~$0.50 against MPP spec §13.6) dominates revenue — the SDK skips Stripe minting on that network, reuses the static recipient forever, and (with a one-time external pre-funding of the recipient's USDC ATA) lets every settle pay only the ~$0.001 per-tx fee. `SolanaMppRailSpec.ata_creation_required` defaults to `True` (data-only; solana method registration through `create_mppx_server` is a follow-up — merchants building the solana method directly via `pympp` should pass the flag themselves to the charge factory). `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`. |
34
34
  | `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. |
35
35
  | `agentscore_commerce.middleware.{fastapi,flask,django,aiohttp,sanic,asgi}` | Framework-specific rate-limit middleware. FastAPI: `rate_limit_fastapi(...)` (FastAPI dependency) plus the ASGI `RateLimitMiddleware` re-export. Flask: `rate_limit_flask(app, ...)` installer. Django: class-based async `RateLimitMiddleware` configured via `settings.AGENTSCORE_RATE_LIMIT`. aiohttp: `rate_limit_aiohttp(...)` middleware factory. Sanic: `rate_limit_sanic(app, ...)` installer. `asgi.RateLimitMiddleware` works with any starlette-compatible app. Shared options: `window_seconds` (default 60), `max_requests` (default 60), `key_resolver` (default first hop of `x-forwarded-for`), `redis_url` (lazy-imports `redis.asyncio` when set, in-memory `dict` fallback otherwise), `key_prefix`. `redis` is an optional peer dep (install via the `redis` extra). |
36
36
 
@@ -124,6 +124,30 @@ class SolanaMppRailSpec:
124
124
  rpc_url: str | None = None
125
125
  signer: Any | None = None
126
126
  token_program: str | None = None
127
+ # Whether the recipient's ATA may be auto-created on first payment. Default True.
128
+ # When True (default), the SDK passes
129
+ # ``splits=[{"recipient": recipient, "amount": "0", "ataCreationRequired": True}]``
130
+ # to ``solana.charge``, putting the recipient in the MPP spec §13.6
131
+ # ``allowedAtaOwners`` allow-list. Required on ``@solana/mpp >= 0.6.0`` /
132
+ # ``pympp[solana] >= 0.6`` with a sponsored (fee-payer) setup — without it,
133
+ # every settle that emits a ``CreateIdempotent`` ATA instruction is rejected.
134
+ # On older runtimes the field is unknown and silently ignored, so the
135
+ # default is safe across versions.
136
+ #
137
+ # Opt out (``False``) only when every recipient's ATA is guaranteed to
138
+ # exist out-of-band (typically when the merchant pre-creates the ATA from
139
+ # an external wallet and refuses to let the fee-payer fund creation).
140
+ #
141
+ # Economic note: with rotating recipients (Stripe-multichain per-PI deposit
142
+ # addresses), the sponsor pays ~0.002 SOL (~$0.50) of rent per call into
143
+ # accounts the merchant can't close. Acceptable when settle amounts
144
+ # dominate ($50+); not viable for sub-dollar merchants.
145
+ #
146
+ # NOTE: python-commerce doesn't yet wire SolanaMppRailSpec through
147
+ # ``create_mppx_server`` (node parity gap); this field is data-only here
148
+ # today. Merchants building the solana method directly via ``pympp``
149
+ # should pass ``ata_creation_required`` themselves to the charge factory.
150
+ ata_creation_required: bool = True
127
151
 
128
152
  def __post_init__(self) -> None:
129
153
  # Mirror X402BaseRailSpec: when ``network`` flips to the devnet CAIP-2 (or the
@@ -5,7 +5,9 @@ from agentscore_commerce.stripe_multichain.mppx_stripe import (
5
5
  create_mppx_stripe,
6
6
  )
7
7
  from agentscore_commerce.stripe_multichain.pay_to_address import (
8
+ MintMultichainRecipientsResult,
8
9
  create_pay_to_address_from_stripe_pi,
10
+ mint_multichain_recipients,
9
11
  )
10
12
  from agentscore_commerce.stripe_multichain.payment_intent import (
11
13
  MultichainPaymentIntentResult,
@@ -31,6 +33,7 @@ __all__ = [
31
33
  "DEFAULT_PAYMENT_METHOD_TYPES",
32
34
  "STRIPE_TEST_TX_HASH_FAILED",
33
35
  "STRIPE_TEST_TX_HASH_SUCCESS",
36
+ "MintMultichainRecipientsResult",
34
37
  "MultichainPaymentIntentResult",
35
38
  "PiCache",
36
39
  "SimulateNetwork",
@@ -39,6 +42,7 @@ __all__ = [
39
42
  "create_multichain_payment_intent",
40
43
  "create_pay_to_address_from_stripe_pi",
41
44
  "create_pi_cache",
45
+ "mint_multichain_recipients",
42
46
  "network_for_outcome",
43
47
  "simulate_crypto_deposit",
44
48
  "simulate_deposit_for_outcome",
@@ -0,0 +1,280 @@
1
+ """Per-order Stripe-multichain ``pay_to`` resolver.
2
+
3
+ Stripe-multichain merchants need ONE function for their ``mint_recipients``
4
+ (or per-request payTo) hook that does the right thing on both legs:
5
+
6
+ - **Discovery leg** (no payment header): mint a fresh PaymentIntent so the 402
7
+ advertises a stable per-order deposit address.
8
+ - **Settle leg** (MPP credential attached): reuse the buyer's signed-against
9
+ payTo from the credential (after verifying it's in the local cache OR matches
10
+ a configured ``static_recipients`` entry) — otherwise the verify leg would
11
+ compare against a freshly-rotated address and reject the credential.
12
+
13
+ Stripe SPT and card methods don't carry an on-chain recipient, so the settle
14
+ leg still mints a fresh PaymentIntent for them.
15
+
16
+ Mirrors node-commerce ``src/stripe-multichain/pay_to_address.ts``.
17
+ """
18
+
19
+ from __future__ import annotations
20
+
21
+ import asyncio
22
+ from dataclasses import dataclass
23
+ from typing import TYPE_CHECKING, Any
24
+
25
+ from agentscore_commerce.errors import CheckoutValidationError
26
+ from agentscore_commerce.stripe_multichain.payment_intent import (
27
+ create_multichain_payment_intent,
28
+ )
29
+
30
+ if TYPE_CHECKING:
31
+ from agentscore_commerce.stripe_multichain.pi_cache import PiCache
32
+
33
+
34
+ async def _maybe_await(value: Any) -> Any:
35
+ if asyncio.iscoroutine(value) or asyncio.isfuture(value):
36
+ return await value
37
+ return value
38
+
39
+
40
+ async def create_pay_to_address_from_stripe_pi(
41
+ *,
42
+ authorization_header: str | None,
43
+ amount_cents: int,
44
+ stripe: Any,
45
+ pi_cache: PiCache,
46
+ networks: list[str] | None = None,
47
+ static_recipients: dict[str, str] | None = None,
48
+ metadata: dict[str, str] | None = None,
49
+ order_id: str | None = None,
50
+ preferred_network: str = "tempo",
51
+ ) -> str:
52
+ """Resolve the on-chain ``pay_to`` for a Stripe-multichain order.
53
+
54
+ On the settle leg, when ``authorization_header`` carries an MPP credential
55
+ binding a ``tempo`` or ``solana`` recipient, returns THAT address (after
56
+ verifying it's in ``pi_cache`` OR matches a configured ``static_recipients``
57
+ entry — static addresses are always-accepted because the merchant owns
58
+ them). Otherwise mints a fresh :func:`create_multichain_payment_intent`
59
+ for the rails NOT covered by ``static_recipients``, caches the merged
60
+ address map, and registers static recipients with ``pi_cache.cache_address``
61
+ so future verify-leg lookups pass. Returns the address on the
62
+ ``preferred_network`` (default ``"tempo"``, falling back to ``base`` then
63
+ ``tempo``).
64
+
65
+ ``static_recipients`` (optional, keyed by network) lets the merchant pin a
66
+ fixed receive wallet on chains where per-call rotation is expensive — Solana
67
+ in particular, since MPP spec §13.6 charges ~0.002 SOL of ATA rent per
68
+ new recipient into accounts the merchant can't close. Example:
69
+ ``static_recipients={"solana": "FR96wd96urH..."}``. The SDK skips Stripe
70
+ minting for that network so the static address is reused forever; pair
71
+ with a one-time external USDC transfer to pre-create the recipient's USDC
72
+ ATA and every settle pays only the per-tx fee.
73
+ """
74
+ if authorization_header:
75
+ recipient = await _try_resolve_from_credential(
76
+ authorization_header=authorization_header,
77
+ pi_cache=pi_cache,
78
+ static_recipients=static_recipients or {},
79
+ )
80
+ if recipient is not None:
81
+ return recipient
82
+
83
+ result_payto, _ = await _mint_and_cache(
84
+ amount_cents=amount_cents,
85
+ stripe=stripe,
86
+ pi_cache=pi_cache,
87
+ networks=networks or ["tempo", "base", "solana"],
88
+ static_recipients=static_recipients or {},
89
+ metadata=metadata,
90
+ order_id=order_id,
91
+ preferred_network=preferred_network,
92
+ )
93
+ return result_payto
94
+
95
+
96
+ @dataclass
97
+ class MintMultichainRecipientsResult:
98
+ """Structured result for :func:`mint_multichain_recipients`.
99
+
100
+ Exposes the full per-network deposit map plus the PI id, so merchants don't
101
+ have to guess "is the returned string the tempo address or the solana
102
+ static?" when ``static_recipients`` is configured.
103
+ """
104
+
105
+ recipients: dict[str, str]
106
+ """Per-network deposit address map (merges Stripe-minted + static_recipients)."""
107
+
108
+ payment_intent_id: str | None
109
+ """Stripe PI id, or None if all networks were covered by ``static_recipients``."""
110
+
111
+ reused_from_credential: bool
112
+ """True when the settle leg short-circuited to the credential-bound recipient."""
113
+
114
+
115
+ async def mint_multichain_recipients(
116
+ *,
117
+ authorization_header: str | None,
118
+ amount_cents: int,
119
+ stripe: Any,
120
+ pi_cache: PiCache,
121
+ networks: list[str] | None = None,
122
+ static_recipients: dict[str, str] | None = None,
123
+ metadata: dict[str, str] | None = None,
124
+ order_id: str | None = None,
125
+ preferred_network: str = "tempo",
126
+ ) -> MintMultichainRecipientsResult:
127
+ """Structured variant of :func:`create_pay_to_address_from_stripe_pi`.
128
+
129
+ Returns the full per-rail deposit map. Prefer this when the merchant's
130
+ ``mint_recipients`` hook needs every rail's address (typical multi-rail
131
+ merchant) — avoids the "returned string is ambiguous" trap when
132
+ ``static_recipients`` is configured (settle leg's bound recipient may be
133
+ the solana static rather than the tempo per-PI address).
134
+ """
135
+ static = static_recipients or {}
136
+ if authorization_header:
137
+ from agentscore_commerce.stripe_multichain.pi_cache import PiCache # noqa: F401
138
+
139
+ recipient = await _try_resolve_from_credential(
140
+ authorization_header=authorization_header,
141
+ pi_cache=pi_cache,
142
+ static_recipients=static,
143
+ )
144
+ if recipient is not None:
145
+ pi_id = pi_cache.get_payment_intent_id(recipient)
146
+ network_map: dict[str, str] = {}
147
+ if pi_id:
148
+ for net in ("tempo", "base", "solana"):
149
+ addr = pi_cache.get_network_deposit_address(pi_id, net)
150
+ if addr:
151
+ network_map[net] = addr
152
+ merged = {**network_map, **static}
153
+ return MintMultichainRecipientsResult(
154
+ recipients=merged,
155
+ payment_intent_id=pi_id,
156
+ reused_from_credential=True,
157
+ )
158
+
159
+ _, merged = await _mint_and_cache(
160
+ amount_cents=amount_cents,
161
+ stripe=stripe,
162
+ pi_cache=pi_cache,
163
+ networks=networks or ["tempo", "base", "solana"],
164
+ static_recipients=static,
165
+ metadata=metadata,
166
+ order_id=order_id,
167
+ preferred_network=preferred_network,
168
+ )
169
+ pi_id = pi_cache.get_payment_intent_id(next(iter(merged.values()), ""))
170
+ return MintMultichainRecipientsResult(
171
+ recipients=merged,
172
+ payment_intent_id=pi_id,
173
+ reused_from_credential=False,
174
+ )
175
+
176
+
177
+ async def _try_resolve_from_credential(
178
+ *,
179
+ authorization_header: str,
180
+ pi_cache: PiCache,
181
+ static_recipients: dict[str, str],
182
+ ) -> str | None:
183
+ """Parse the MPP credential and return the bound recipient when valid.
184
+
185
+ Returns the address when it's cached OR matches a configured
186
+ ``static_recipients`` entry, or None to fall through to the mint path.
187
+ Raises CheckoutValidationError on malformed credentials.
188
+ """
189
+ if not authorization_header.startswith("Payment "):
190
+ return None
191
+ from mpp import Credential # type: ignore[import-untyped]
192
+
193
+ try:
194
+ credential = Credential.from_authorization(authorization_header)
195
+ except Exception as err:
196
+ raise CheckoutValidationError(
197
+ code="invalid_credential",
198
+ message="The Authorization: Payment header is not a valid MPP credential.",
199
+ action="retry_without_credential",
200
+ status=401,
201
+ ) from err
202
+ method = getattr(credential.challenge, "method", None)
203
+ if method not in ("tempo", "solana"):
204
+ return None
205
+ recipient = getattr(credential.challenge.request, "recipient", None)
206
+ if not isinstance(recipient, str) or not recipient:
207
+ raise CheckoutValidationError(
208
+ code="invalid_credential",
209
+ message="The MPP credential is missing its recipient field.",
210
+ action="retry_without_credential",
211
+ status=401,
212
+ )
213
+ static_for_method = static_recipients.get(method)
214
+ if static_for_method and static_for_method == recipient:
215
+ return recipient
216
+ if not await _maybe_await(pi_cache.has_address(recipient)):
217
+ raise CheckoutValidationError(
218
+ code="invalid_credential",
219
+ message=(
220
+ "The signed-against payTo recipient is not in this merchant's cache "
221
+ "(unknown or expired). Retry without the Authorization: Payment header "
222
+ "to receive a fresh 402 challenge."
223
+ ),
224
+ action="retry_without_credential",
225
+ status=401,
226
+ )
227
+ return recipient
228
+
229
+
230
+ async def _mint_and_cache(
231
+ *,
232
+ amount_cents: int,
233
+ stripe: Any,
234
+ pi_cache: PiCache,
235
+ networks: list[str],
236
+ static_recipients: dict[str, str],
237
+ metadata: dict[str, str] | None,
238
+ order_id: str | None,
239
+ preferred_network: str,
240
+ ) -> tuple[str, dict[str, str]]:
241
+ """Mint a fresh PI for rails not covered by static_recipients.
242
+
243
+ Registers everything in the cache and returns (preferred_address, merged_map).
244
+ """
245
+ stripe_networks = [n for n in networks if n not in static_recipients]
246
+ idempotency_key = f"pi-{order_id}-{amount_cents}" if order_id else None
247
+ result = create_multichain_payment_intent(
248
+ stripe=stripe,
249
+ amount=amount_cents,
250
+ networks=stripe_networks,
251
+ metadata=metadata,
252
+ idempotency_key=idempotency_key,
253
+ )
254
+ for address in result.deposit_addresses.values():
255
+ await _maybe_await(pi_cache.cache_address(address))
256
+ pi_cache.cache_payment_intent(address, result.payment_intent_id)
257
+ for address in static_recipients.values():
258
+ await _maybe_await(pi_cache.cache_address(address))
259
+ merged: dict[str, str] = {**result.deposit_addresses, **static_recipients}
260
+ pi_cache.cache_network_addresses(result.payment_intent_id, merged)
261
+
262
+ pay_to = merged.get(preferred_network) or merged.get("base") or merged.get("tempo")
263
+ if not pay_to:
264
+ raise CheckoutValidationError(
265
+ code="payment_provider_unavailable",
266
+ message=(
267
+ "Stripe returned deposit addresses but none matched the requested network (tempo / base / solana). "
268
+ "The account may have only a subset of multichain networks enabled."
269
+ ),
270
+ action="retry_later",
271
+ status=503,
272
+ )
273
+ return pay_to, merged
274
+
275
+
276
+ __all__ = [
277
+ "MintMultichainRecipientsResult",
278
+ "create_pay_to_address_from_stripe_pi",
279
+ "mint_multichain_recipients",
280
+ ]
@@ -7,7 +7,7 @@ Runnable, copy-pasteable example integrations covering the most common merchant
7
7
  | [`identity_only.py`](./identity_only.py) | Compliance gate without payment | Minimal: wraps any endpoint with KYC + age + jurisdiction checks. Vendor handles their own payment. |
8
8
  | [`api_provider.py`](./api_provider.py) | API provider (Exa-style) | Per-call billing on multiple rails: Tempo MPP + x402 (Base + Solana), all driven by `Checkout`. No identity gate. Demos `Checkout(discovery_probe=...)` for x402-crawler auto-routing, `build_merchant_index_json` + `standard_endpoint_descriptions(kind="api")` for `GET /` discovery, and `build_redemption_skill_md` with the trial-credit body shape on `GET /redemption.md`. |
9
9
  | [`multi_rail_merchant.py`](./multi_rail_merchant.py) | Full agent-commerce merchant | Identity gate + Tempo MPP + x402 (Base + Solana) + Stripe SPT via `Checkout`. Demos `pricing_result` (cents → typed PricingResult), `Receipt` + `ReceiptNextSteps` + `build_success_next_steps` in `on_settled`, per-order Stripe-multichain deposit minting via `mint_recipients`, and `simulate_deposit_if_test_mode`. |
10
- | [`stripe_multichain_merchant.py`](./stripe_multichain_merchant.py) | Stripe-anchored multi-chain | Stripe PaymentIntent with deposit_options for tempo/base/solana; crypto deposits flow through Stripe. Read `result.deposit_addresses[network]` directly. Includes testnet `simulate_crypto_deposit` helper. |
10
+ | [`stripe_multichain_merchant.py`](./stripe_multichain_merchant.py) | Stripe-anchored multi-chain | Stripe PaymentIntent with deposit_options for tempo/base/solana; crypto deposits flow through Stripe. Read `result.deposit_addresses[network]` directly. Includes testnet `simulate_crypto_deposit` helper. For low-margin endpoints (sub-dollar APIs), use `create_pay_to_address_from_stripe_pi` / `mint_multichain_recipients` with `static_recipients={"solana": "<wallet>"}` — see the `stripe_multichain` row in the main `README.md` for the full pattern and economics. |
11
11
  | [`variable_cost_merchant.py`](./variable_cost_merchant.py) | Pay-per-actual-usage (LLM, transcode, etc.) | Same use case on **two protocols**: x402 upto (Permit2 authorize-max → `Settlement-Overrides` settle-actual) AND MPP tempo session (channel + SSE + mid-stream vouchers). Stays on lower-level helpers (`payment_directive`, `www_authenticate_header`, `settlement_override_header`) because variable-cost flows don't fit the one-shot `Checkout` model. |
12
12
  | [`compliance_merchant.py`](./compliance_merchant.py) | Regulated-goods merchant (wine, cannabis, etc.) | Full compliance gate via `Checkout(gate=CheckoutGateConfig(...))` + custom `on_denied` composing commerce helpers: `verification_agent_instructions`, `is_fixable_denial`, `build_contact_support_next_steps`, `denial_reason_to_body`/`denial_reason_status`. Shows how vendors write only the business-specific denial branches and let commerce handle the rest. |
13
13
  | [`per_product_policy_merchant.py`](./per_product_policy_merchant.py) | Multi-product merchant with mixed compliance needs | One product carries a hard gate (wine: KYC + 21 + US-state allowlist), another has no gate at all (anonymous merch, ships anywhere), a third uses `enforcement="soft"` (request KYC as a fraud signal but accept anonymous sales, stamping `identity_status="unverified"` on the order). Uses `PolicyBlock`, the one-call `validate_shipping_against_policy`, and `Checkout(gate=CheckoutGateConfig(per_request_policy=...))`. |
@@ -60,8 +60,8 @@ from agentscore_commerce.discovery import build_success_next_steps
60
60
  from agentscore_commerce.middleware.fastapi import RateLimitMiddleware
61
61
  from agentscore_commerce.payment import networks, validate_x402_network_config
62
62
  from agentscore_commerce.stripe_multichain import (
63
- create_multichain_payment_intent,
64
63
  create_pi_cache,
64
+ mint_multichain_recipients,
65
65
  simulate_deposit_for_outcome,
66
66
  )
67
67
 
@@ -104,22 +104,35 @@ async def _compute_pricing(ctx: Any) -> PricingResult:
104
104
 
105
105
 
106
106
  async def _mint_recipients(ctx: Any) -> dict[str, str]:
107
- """Per-order recipient mint: Stripe multichain PI → per-network deposit addresses."""
107
+ """Per-order recipient mint: Stripe multichain PI → per-network deposit addresses.
108
+
109
+ ``mint_multichain_recipients`` returns the full per-rail map and registers
110
+ everything in the PI cache in one call. On the settle leg it short-circuits
111
+ to the buyer's signed-against payTo from the MPP credential; on the discovery
112
+ leg it mints a fresh multichain PI.
113
+
114
+ For low-margin endpoints (sub-dollar per call), pass
115
+ ``static_recipients={"solana": os.environ["MERCHANT_SOLANA_RECIPIENT"]}`` to
116
+ skip Stripe minting on Solana — at $0.01/call MPP spec §13.6's ~$0.50 per-PI
117
+ ATA rent dominates revenue. With a stable merchant-owned recipient + one-time
118
+ external pre-funding of its USDC ATA, every settle pays only the per-tx fee.
119
+ """
108
120
  total_cents = round(ctx.pricing.amount_usd * 100)
109
- result = create_multichain_payment_intent(
121
+ result = await mint_multichain_recipients(
122
+ authorization_header=ctx.request.headers.get("authorization"),
123
+ amount_cents=total_cents,
110
124
  stripe=stripe_client,
111
- amount=total_cents,
125
+ pi_cache=pi_cache,
112
126
  networks=["tempo", "base", "solana"],
113
127
  )
114
- for addr in result.deposit_addresses.values():
115
- await pi_cache.cache_address(addr)
116
- pi_cache.cache_payment_intent(addr, result.payment_intent_id)
117
- pi_cache.cache_network_addresses(result.payment_intent_id, result.deposit_addresses)
118
- return {
119
- "tempo": result.deposit_addresses["tempo"],
120
- "x402_base": result.deposit_addresses["base"],
121
- "solana_mpp": result.deposit_addresses["solana"],
122
- }
128
+ out: dict[str, str] = {}
129
+ if "tempo" in result.recipients:
130
+ out["tempo"] = result.recipients["tempo"]
131
+ if "base" in result.recipients:
132
+ out["x402_base"] = result.recipients["base"]
133
+ if "solana" in result.recipients:
134
+ out["solana_mpp"] = result.recipients["solana"]
135
+ return out
123
136
 
124
137
 
125
138
  async def _on_settled(ctx: Any, outcome: SettleOutcome) -> dict[str, Any]:
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "agentscore-commerce"
7
- version = "2.2.0"
7
+ version = "2.3.0"
8
8
  description = "Agent commerce SDK for Python — identity middleware (FastAPI, Flask, Django, AIOHTTP, Sanic, ASGI) + payment helpers + 402 builders + discovery + Stripe multichain. The full merchant-side toolkit for AgentScore-powered agent commerce."
9
9
  readme = "README.md"
10
10
  license = "MIT"
@@ -9,7 +9,9 @@ from unittest.mock import patch
9
9
  import pytest
10
10
 
11
11
  from agentscore_commerce.stripe_multichain.pay_to_address import (
12
+ MintMultichainRecipientsResult,
12
13
  create_pay_to_address_from_stripe_pi,
14
+ mint_multichain_recipients,
13
15
  )
14
16
 
15
17
 
@@ -196,3 +198,121 @@ async def test_mints_fresh_when_credential_method_is_stripe() -> None:
196
198
  pi_cache=cache, # type: ignore[arg-type]
197
199
  )
198
200
  assert result == "0xFRESH"
201
+
202
+
203
+ # ---------------------------------------------------------------------------
204
+ # static_recipients + mint_multichain_recipients
205
+ # ---------------------------------------------------------------------------
206
+
207
+
208
+ class _StripeRecordingNetworks:
209
+ """Stripe stub that records which networks were requested on PI creation.
210
+
211
+ Lets us assert that ``static_recipients``-covered networks are excluded
212
+ from the underlying ``create_multichain_payment_intent`` call.
213
+ """
214
+
215
+ def __init__(self, addresses: dict[str, str]) -> None:
216
+ self.payment_intents = self
217
+ self._addresses = addresses
218
+ self.last_networks: list[str] | None = None
219
+ self.last_idempotency_key: str | None = None
220
+
221
+ def create(self, params: dict[str, Any], idempotency_key: str | None = None) -> Any:
222
+ self.last_networks = list(params["payment_method_options"]["crypto"]["deposit_options"]["networks"])
223
+ self.last_idempotency_key = idempotency_key
224
+ deposits = {n: {"address": a} for n, a in self._addresses.items()}
225
+
226
+ class _PI:
227
+ id = "pi_test_456"
228
+ next_action = {"crypto_display_details": {"deposit_addresses": deposits}}
229
+
230
+ return _PI()
231
+
232
+
233
+ @pytest.mark.asyncio
234
+ async def test_static_recipients_excluded_from_stripe_pi_networks() -> None:
235
+ cache = FakePiCache()
236
+ stripe = _StripeRecordingNetworks({"tempo": "0xTEMPO", "base": "0xBASE"})
237
+ await create_pay_to_address_from_stripe_pi(
238
+ authorization_header=None,
239
+ amount_cents=100,
240
+ stripe=stripe, # type: ignore[arg-type]
241
+ pi_cache=cache, # type: ignore[arg-type]
242
+ static_recipients={"solana": "STATIC123"},
243
+ )
244
+ assert stripe.last_networks == ["tempo", "base"]
245
+
246
+
247
+ @pytest.mark.asyncio
248
+ async def test_static_recipients_registered_in_cache_and_merged_map() -> None:
249
+ cache = FakePiCache()
250
+ stripe = _fake_stripe({"tempo": "0xTEMPO", "base": "0xBASE"})
251
+ await create_pay_to_address_from_stripe_pi(
252
+ authorization_header=None,
253
+ amount_cents=100,
254
+ stripe=stripe,
255
+ pi_cache=cache, # type: ignore[arg-type]
256
+ static_recipients={"solana": "STATIC123"},
257
+ )
258
+ assert "STATIC123" in cache.cached_addresses
259
+ assert cache.cached_network_addresses == [
260
+ ("pi_test_123", {"tempo": "0xTEMPO", "base": "0xBASE", "solana": "STATIC123"}),
261
+ ]
262
+
263
+
264
+ @pytest.mark.asyncio
265
+ async def test_settle_leg_accepts_static_recipient_without_cache_check() -> None:
266
+ """A bound recipient that matches static_recipients is always accepted,
267
+ even if the local PI cache wouldn't know about it (e.g. cold start).
268
+ """
269
+ cache = FakePiCache(has_address_result=False)
270
+ with patch("mpp.Credential", FakeCredential):
271
+ result = await create_pay_to_address_from_stripe_pi(
272
+ authorization_header="Payment solana:STATIC123",
273
+ amount_cents=100,
274
+ stripe=_fake_stripe({}),
275
+ pi_cache=cache, # type: ignore[arg-type]
276
+ static_recipients={"solana": "STATIC123"},
277
+ )
278
+ assert result == "STATIC123"
279
+ assert cache.cached_addresses == []
280
+
281
+
282
+ @pytest.mark.asyncio
283
+ async def test_settle_leg_rejects_attacker_recipient_when_static_configured() -> None:
284
+ """Attacker forges a credential bound to a different solana address;
285
+ static_recipients match check fails, then the cache check fails, then
286
+ the SDK raises invalid_credential.
287
+ """
288
+ from agentscore_commerce.errors import CheckoutValidationError
289
+
290
+ cache = FakePiCache(has_address_result=False)
291
+ with (
292
+ patch("mpp.Credential", FakeCredential),
293
+ pytest.raises(CheckoutValidationError) as exc,
294
+ ):
295
+ await create_pay_to_address_from_stripe_pi(
296
+ authorization_header="Payment solana:ATTACKER456",
297
+ amount_cents=100,
298
+ stripe=_fake_stripe({}),
299
+ pi_cache=cache, # type: ignore[arg-type]
300
+ static_recipients={"solana": "STATIC123"},
301
+ )
302
+ assert exc.value.code == "invalid_credential"
303
+
304
+
305
+ @pytest.mark.asyncio
306
+ async def test_mint_multichain_recipients_returns_full_merged_map() -> None:
307
+ cache = FakePiCache()
308
+ stripe = _fake_stripe({"tempo": "0xTEMPO", "base": "0xBASE"})
309
+ out = await mint_multichain_recipients(
310
+ authorization_header=None,
311
+ amount_cents=100,
312
+ stripe=stripe,
313
+ pi_cache=cache, # type: ignore[arg-type]
314
+ static_recipients={"solana": "STATIC123"},
315
+ )
316
+ assert isinstance(out, MintMultichainRecipientsResult)
317
+ assert out.recipients == {"tempo": "0xTEMPO", "base": "0xBASE", "solana": "STATIC123"}
318
+ assert out.reused_from_credential is False