ap2-algorand 0.1.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.
@@ -0,0 +1,102 @@
1
+ Metadata-Version: 2.4
2
+ Name: ap2-algorand
3
+ Version: 0.1.0
4
+ Summary: Google AP2 × Algorand adapter — settle x402 payments from AP2 CartMandate/PaymentMandate
5
+ License-Expression: MIT
6
+ Keywords: algorand,x402,usdc,ap2,agent-payments,ai-agent
7
+ Requires-Python: >=3.10
8
+ Description-Content-Type: text/markdown
9
+ Requires-Dist: py-algorand-sdk<3.0,>=2.0
10
+ Requires-Dist: pydantic>=2.0
11
+ Provides-Extra: ap2
12
+ Provides-Extra: dev
13
+ Requires-Dist: pytest>=8.0; extra == "dev"
14
+
15
+ # ap2-algorand
16
+
17
+ **Google AP2 × Algorand x402 adapter** — settle AI agent payments on Algorand from an AP2 `PaymentMandate`.
18
+
19
+ Bridges the [Google Agent Payments Protocol (AP2)](https://github.com/google-agentic-commerce/AP2) with non-custodial USDC payments on Algorand, enforced by AVM MandateContracts.
20
+
21
+ ## Install
22
+
23
+ ```bash
24
+ pip install ap2-algorand
25
+ ```
26
+
27
+ ## Quick start
28
+
29
+ ### Buyer: advertise x402 Algorand in a CartMandate
30
+
31
+ ```python
32
+ from ap2_algorand import AlgorandUsdcMethodData, X402_METHOD_NAME
33
+ from ap2.types.payment_request import PaymentMethodData
34
+
35
+ method = PaymentMethodData(
36
+ supported_methods=X402_METHOD_NAME, # "https://www.x402.org/"
37
+ data=AlgorandUsdcMethodData(
38
+ mandate_app_id=3_498_113_854, # agent's MandateContract app ID
39
+ asset_id=31_566_704, # mainnet USDC ASA
40
+ network="mainnet",
41
+ x402_endpoint="https://api.example.com/weather",
42
+ ).model_dump(),
43
+ )
44
+ ```
45
+
46
+ ### Buyer: parse payment args from a CartMandate
47
+
48
+ ```python
49
+ from ap2_algorand import cart_to_payment_args
50
+
51
+ args = cart_to_payment_args(cart_mandate)
52
+ # args.mandate_app_id → int
53
+ # args.amount_micro_usdc → int (e.g. 10_000 for $0.01 USDC)
54
+ # args.asset_id → int
55
+ # args.x402_endpoint → str
56
+ ```
57
+
58
+ ### Merchant: submit payment and get a PaymentReceipt
59
+
60
+ ```python
61
+ import os
62
+ from ap2_algorand import settle
63
+
64
+ receipt = settle(
65
+ payment_mandate=payment_mandate,
66
+ algod_url="https://mainnet-api.algonode.cloud",
67
+ )
68
+ txid = receipt.payment_status.network_confirmation_id
69
+ # txid == Algorand transaction ID (network IS the ledger)
70
+ ```
71
+
72
+ ### Lower-level: submit without AP2 types
73
+
74
+ ```python
75
+ from ap2_algorand import settle_raw
76
+
77
+ txid = settle_raw(
78
+ x_payment="<base64-msgpack-signed-tx>",
79
+ algod_url="https://mainnet-api.algonode.cloud",
80
+ )
81
+ ```
82
+
83
+ ## How it maps to AP2
84
+
85
+ | AP2 field | Algorand value |
86
+ |---|---|
87
+ | `payment_response.method_name` | `https://www.x402.org/` |
88
+ | `payment_response.details["value"]` | `base64(msgpack(SignedTransaction))` |
89
+ | `merchant_confirmation_id` | Algorand txid |
90
+ | `psp_confirmation_id` | Algorand txid (AVM is the PSP) |
91
+ | `network_confirmation_id` | Algorand txid (Algorand is the network) |
92
+ | OTP challenge | skipped — AVM MandateContract enforces limits |
93
+
94
+ ## Dependencies
95
+
96
+ - `py-algorand-sdk >= 2.0`
97
+ - `pydantic >= 2.0`
98
+ - `ap2` — optional, only needed for `settle()` return type (`PaymentReceipt`)
99
+
100
+ ## License
101
+
102
+ MIT © Algo Wallet
@@ -0,0 +1,88 @@
1
+ # ap2-algorand
2
+
3
+ **Google AP2 × Algorand x402 adapter** — settle AI agent payments on Algorand from an AP2 `PaymentMandate`.
4
+
5
+ Bridges the [Google Agent Payments Protocol (AP2)](https://github.com/google-agentic-commerce/AP2) with non-custodial USDC payments on Algorand, enforced by AVM MandateContracts.
6
+
7
+ ## Install
8
+
9
+ ```bash
10
+ pip install ap2-algorand
11
+ ```
12
+
13
+ ## Quick start
14
+
15
+ ### Buyer: advertise x402 Algorand in a CartMandate
16
+
17
+ ```python
18
+ from ap2_algorand import AlgorandUsdcMethodData, X402_METHOD_NAME
19
+ from ap2.types.payment_request import PaymentMethodData
20
+
21
+ method = PaymentMethodData(
22
+ supported_methods=X402_METHOD_NAME, # "https://www.x402.org/"
23
+ data=AlgorandUsdcMethodData(
24
+ mandate_app_id=3_498_113_854, # agent's MandateContract app ID
25
+ asset_id=31_566_704, # mainnet USDC ASA
26
+ network="mainnet",
27
+ x402_endpoint="https://api.example.com/weather",
28
+ ).model_dump(),
29
+ )
30
+ ```
31
+
32
+ ### Buyer: parse payment args from a CartMandate
33
+
34
+ ```python
35
+ from ap2_algorand import cart_to_payment_args
36
+
37
+ args = cart_to_payment_args(cart_mandate)
38
+ # args.mandate_app_id → int
39
+ # args.amount_micro_usdc → int (e.g. 10_000 for $0.01 USDC)
40
+ # args.asset_id → int
41
+ # args.x402_endpoint → str
42
+ ```
43
+
44
+ ### Merchant: submit payment and get a PaymentReceipt
45
+
46
+ ```python
47
+ import os
48
+ from ap2_algorand import settle
49
+
50
+ receipt = settle(
51
+ payment_mandate=payment_mandate,
52
+ algod_url="https://mainnet-api.algonode.cloud",
53
+ )
54
+ txid = receipt.payment_status.network_confirmation_id
55
+ # txid == Algorand transaction ID (network IS the ledger)
56
+ ```
57
+
58
+ ### Lower-level: submit without AP2 types
59
+
60
+ ```python
61
+ from ap2_algorand import settle_raw
62
+
63
+ txid = settle_raw(
64
+ x_payment="<base64-msgpack-signed-tx>",
65
+ algod_url="https://mainnet-api.algonode.cloud",
66
+ )
67
+ ```
68
+
69
+ ## How it maps to AP2
70
+
71
+ | AP2 field | Algorand value |
72
+ |---|---|
73
+ | `payment_response.method_name` | `https://www.x402.org/` |
74
+ | `payment_response.details["value"]` | `base64(msgpack(SignedTransaction))` |
75
+ | `merchant_confirmation_id` | Algorand txid |
76
+ | `psp_confirmation_id` | Algorand txid (AVM is the PSP) |
77
+ | `network_confirmation_id` | Algorand txid (Algorand is the network) |
78
+ | OTP challenge | skipped — AVM MandateContract enforces limits |
79
+
80
+ ## Dependencies
81
+
82
+ - `py-algorand-sdk >= 2.0`
83
+ - `pydantic >= 2.0`
84
+ - `ap2` — optional, only needed for `settle()` return type (`PaymentReceipt`)
85
+
86
+ ## License
87
+
88
+ MIT © Algo Wallet
@@ -0,0 +1,26 @@
1
+ [build-system]
2
+ requires = ["setuptools>=68", "wheel"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "ap2-algorand"
7
+ version = "0.1.0"
8
+ description = "Google AP2 × Algorand adapter — settle x402 payments from AP2 CartMandate/PaymentMandate"
9
+ requires-python = ">=3.10"
10
+ license = "MIT"
11
+ readme = "README.md"
12
+ keywords = ["algorand", "x402", "usdc", "ap2", "agent-payments", "ai-agent"]
13
+ dependencies = [
14
+ "py-algorand-sdk>=2.0,<3.0",
15
+ "pydantic>=2.0",
16
+ ]
17
+
18
+ [project.optional-dependencies]
19
+ ap2 = [] # pip install ap2 (from google-agentic-commerce/AP2 when published)
20
+ dev = ["pytest>=8.0"]
21
+
22
+ [tool.setuptools.packages.find]
23
+ where = ["src"]
24
+
25
+ [tool.setuptools.package-dir]
26
+ "" = "src"
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,63 @@
1
+ """ap2-algorand — Google AP2 × Algorand x402 adapter.
2
+
3
+ Bridges the `Google Agent Payments Protocol (AP2)
4
+ <https://github.com/google-agentic-commerce/AP2>`_ with Algorand x402
5
+ payments.
6
+
7
+ **Buyer side** — build a ``PaymentMethodData`` for your ``CartMandate``::
8
+
9
+ from ap2_algorand import AlgorandUsdcMethodData, X402_METHOD_NAME
10
+ from ap2.types.payment_request import PaymentMethodData
11
+
12
+ method = PaymentMethodData(
13
+ supported_methods=X402_METHOD_NAME,
14
+ data=AlgorandUsdcMethodData(
15
+ mandate_app_id=3_498_113_854,
16
+ asset_id=31_566_704, # mainnet USDC
17
+ network="mainnet",
18
+ x402_endpoint="https://api.example.com/premium",
19
+ ).model_dump(),
20
+ )
21
+
22
+ **Buyer side** — extract payment args from a ``CartMandate``::
23
+
24
+ from ap2_algorand import cart_to_payment_args
25
+
26
+ args = cart_to_payment_args(cart_mandate)
27
+ # args.mandate_app_id, args.amount_micro_usdc, args.asset_id, args.x402_endpoint
28
+
29
+ **Merchant side** — submit payment and get ``PaymentReceipt``::
30
+
31
+ from ap2_algorand import settle
32
+
33
+ receipt = settle(payment_mandate, algod_url="https://mainnet-api.algonode.cloud")
34
+ # receipt.payment_status.network_confirmation_id == Algorand txid
35
+ """
36
+
37
+ __version__ = "0.1.0"
38
+
39
+ from ap2_algorand.payment_method import (
40
+ AlgorandUsdcMethodData,
41
+ X402_METHOD_NAME,
42
+ USDC_MAINNET_ASA_ID,
43
+ USDC_TESTNET_ASA_ID,
44
+ )
45
+ from ap2_algorand.cart_adapter import (
46
+ AlgorandPaymentArgs,
47
+ cart_to_payment_args,
48
+ )
49
+ from ap2_algorand.settle import settle, settle_raw
50
+
51
+ __all__ = [
52
+ # Payment method
53
+ "AlgorandUsdcMethodData",
54
+ "X402_METHOD_NAME",
55
+ "USDC_MAINNET_ASA_ID",
56
+ "USDC_TESTNET_ASA_ID",
57
+ # Cart adapter
58
+ "AlgorandPaymentArgs",
59
+ "cart_to_payment_args",
60
+ # Settlement
61
+ "settle",
62
+ "settle_raw",
63
+ ]
@@ -0,0 +1,89 @@
1
+ """cart_to_payment_args — extract Algorand payment parameters from an AP2 CartMandate.
2
+
3
+ Converts the merchant-signed AP2 ``CartMandate`` into the concrete arguments
4
+ needed to build and sign an Algorand MandateContract.pay() transaction.
5
+
6
+ Usage::
7
+
8
+ from ap2_algorand import cart_to_payment_args
9
+
10
+ args = cart_to_payment_args(cart_mandate)
11
+ # args.mandate_app_id, args.amount_micro_usdc, args.asset_id, args.x402_endpoint
12
+ """
13
+
14
+ from __future__ import annotations
15
+
16
+ from dataclasses import dataclass
17
+ from typing import Any
18
+
19
+ from ap2_algorand.payment_method import AlgorandUsdcMethodData, X402_METHOD_NAME
20
+
21
+
22
+ @dataclass
23
+ class AlgorandPaymentArgs:
24
+ """Parameters extracted from an AP2 CartMandate for Algorand payment."""
25
+
26
+ mandate_app_id: int
27
+ """Algorand application ID of the buyer's MandateContract."""
28
+
29
+ asset_id: int
30
+ """Algorand ASA ID for the payment token (USDC)."""
31
+
32
+ amount_micro_usdc: int
33
+ """Payment amount in micro-USDC (value × 1_000_000)."""
34
+
35
+ x402_endpoint: str
36
+ """The x402-gated endpoint being paid for."""
37
+
38
+ network: str
39
+ """Algorand network ('mainnet' or 'testnet')."""
40
+
41
+
42
+ def cart_to_payment_args(cart_mandate: Any) -> AlgorandPaymentArgs:
43
+ """Extract Algorand payment parameters from an AP2 CartMandate.
44
+
45
+ Locates the ``https://www.x402.org/`` entry in the cart's
46
+ ``payment_request.method_data``, parses it as
47
+ :class:`~ap2_algorand.AlgorandUsdcMethodData`, and converts the cart
48
+ total to micro-USDC.
49
+
50
+ Args:
51
+ cart_mandate: An AP2 ``CartMandate`` instance (or any object with a
52
+ compatible ``.contents.payment_request`` attribute).
53
+
54
+ Returns:
55
+ :class:`AlgorandPaymentArgs` ready to pass to the x402 buyer client.
56
+
57
+ Raises:
58
+ ValueError: If no x402 Algorand payment method is found in the cart.
59
+ """
60
+ payment_request = cart_mandate.contents.payment_request
61
+
62
+ # Locate the x402 payment method entry
63
+ x402_method_data: AlgorandUsdcMethodData | None = None
64
+ for method in payment_request.method_data:
65
+ if method.supported_methods == X402_METHOD_NAME:
66
+ data = method.data or {}
67
+ x402_method_data = AlgorandUsdcMethodData(**data)
68
+ break
69
+
70
+ if x402_method_data is None:
71
+ available = [m.supported_methods for m in payment_request.method_data]
72
+ raise ValueError(
73
+ f"No x402 Algorand payment method found in CartMandate. "
74
+ f"Available methods: {available}"
75
+ )
76
+
77
+ # Convert total to micro-USDC.
78
+ # PaymentCurrencyAmount.value is a float USD amount; 1 USDC = 1_000_000 µUSDC.
79
+ total = payment_request.details.total
80
+ amount_float: float = float(total.amount.value)
81
+ amount_micro_usdc = round(amount_float * 1_000_000)
82
+
83
+ return AlgorandPaymentArgs(
84
+ mandate_app_id=x402_method_data.mandate_app_id,
85
+ asset_id=x402_method_data.asset_id,
86
+ amount_micro_usdc=amount_micro_usdc,
87
+ x402_endpoint=x402_method_data.x402_endpoint,
88
+ network=x402_method_data.network,
89
+ )
@@ -0,0 +1,71 @@
1
+ """AlgorandUsdcMethodData — PaymentMethodData.data for x402 on Algorand.
2
+
3
+ Use this as the ``data`` dict inside a W3C ``PaymentMethodData`` object when
4
+ constructing an AP2 ``CartMandate`` or ``PaymentRequest`` that accepts x402
5
+ payments settled on Algorand.
6
+
7
+ Example::
8
+
9
+ from ap2.types.payment_request import PaymentMethodData
10
+ from ap2_algorand import AlgorandUsdcMethodData
11
+
12
+ method = PaymentMethodData(
13
+ supported_methods="https://www.x402.org/",
14
+ data=AlgorandUsdcMethodData(
15
+ mandate_app_id=3498113854,
16
+ asset_id=31_566_704,
17
+ network="mainnet",
18
+ x402_endpoint="https://api.example.com/premium",
19
+ ).model_dump(),
20
+ )
21
+ """
22
+
23
+ from typing import Literal
24
+
25
+ from pydantic import BaseModel, Field
26
+
27
+
28
+ X402_METHOD_NAME = "https://www.x402.org/"
29
+
30
+ USDC_MAINNET_ASA_ID = 31_566_704
31
+ USDC_TESTNET_ASA_ID = 10_458_941
32
+
33
+
34
+ class AlgorandUsdcMethodData(BaseModel):
35
+ """Configuration data for an Algorand / x402 payment method.
36
+
37
+ Carried in ``PaymentMethodData.data`` with
38
+ ``supported_methods="https://www.x402.org/"``.
39
+ """
40
+
41
+ mandate_app_id: int = Field(
42
+ ...,
43
+ description=(
44
+ "Algorand application ID of the agent's MandateContract. "
45
+ "The AVM enforces per-tx and velocity caps for this agent."
46
+ ),
47
+ )
48
+ asset_id: int = Field(
49
+ ...,
50
+ description=(
51
+ "Algorand ASA ID for the payment token. "
52
+ f"Mainnet USDC = {USDC_MAINNET_ASA_ID}, "
53
+ f"testnet USDC = {USDC_TESTNET_ASA_ID}."
54
+ ),
55
+ )
56
+ network: Literal["mainnet", "testnet"] = Field(
57
+ "mainnet",
58
+ description="Algorand network.",
59
+ )
60
+ x402_endpoint: str = Field(
61
+ ...,
62
+ description=(
63
+ "The x402-gated API endpoint URL that will receive the payment. "
64
+ "The merchant verifies the signed transaction references this endpoint."
65
+ ),
66
+ )
67
+
68
+ @property
69
+ def usdc_asset_id(self) -> int:
70
+ """Convenience: returns the canonical USDC ASA ID for the network."""
71
+ return USDC_MAINNET_ASA_ID if self.network == "mainnet" else USDC_TESTNET_ASA_ID
@@ -0,0 +1,154 @@
1
+ """settle — submit an x402 Algorand payment and return an AP2 PaymentReceipt.
2
+
3
+ This is the merchant-side counterpart: given an AP2 ``PaymentMandate`` whose
4
+ ``payment_response.details["value"]`` carries a base64-encoded msgpack
5
+ ``SignedTransaction`` (the x402 payment header), submit it to Algorand and
6
+ return a fully-formed AP2 ``PaymentReceipt``.
7
+
8
+ The Algorand transaction ID serves as all three confirmation IDs:
9
+
10
+ * ``merchant_confirmation_id`` — the merchant records the txid
11
+ * ``psp_confirmation_id`` — Algorand IS the PSP (self-custodial mandate)
12
+ * ``network_confirmation_id`` — Algorand IS the network
13
+
14
+ Usage (merchant payment processor agent)::
15
+
16
+ import os
17
+ from ap2_algorand import settle
18
+
19
+ receipt = settle(
20
+ payment_mandate=payment_mandate,
21
+ algod_url=os.environ["ALGOD_URL"], # e.g. https://mainnet-api.algonode.cloud
22
+ algod_token=os.environ.get("ALGOD_TOKEN", ""),
23
+ wait_rounds=4,
24
+ )
25
+ """
26
+
27
+ from __future__ import annotations
28
+
29
+ import base64
30
+ import uuid
31
+ from datetime import datetime, timezone
32
+ from typing import Any
33
+
34
+ import algosdk
35
+ from algosdk.v2client import algod
36
+
37
+ from ap2_algorand.payment_method import X402_METHOD_NAME
38
+
39
+
40
+ def settle(
41
+ payment_mandate: Any,
42
+ algod_url: str,
43
+ algod_token: str = "",
44
+ wait_rounds: int = 4,
45
+ ) -> Any:
46
+ """Submit an x402 Algorand payment and return an AP2 PaymentReceipt.
47
+
48
+ Reads the signed transaction from
49
+ ``payment_mandate.payment_mandate_contents.payment_response.details["value"]``,
50
+ submits it to the Algorand network, waits for on-chain confirmation, and
51
+ returns a ``PaymentReceipt`` with the transaction ID as the confirmation ID.
52
+
53
+ Args:
54
+ payment_mandate: An AP2 ``PaymentMandate`` instance.
55
+ algod_url: Algod REST endpoint (e.g. ``https://mainnet-api.algonode.cloud``).
56
+ algod_token: Algod API token. Empty string for public nodes.
57
+ wait_rounds: Maximum rounds to wait for confirmation (default 4).
58
+
59
+ Returns:
60
+ An AP2 ``PaymentReceipt`` (``payment_status`` is ``Success``).
61
+
62
+ Raises:
63
+ ValueError: If the payment_response is missing or has no x402 value.
64
+ algosdk.error.AlgodHTTPError: On algod submission failure.
65
+ Exception: If confirmation times out.
66
+ """
67
+ # ── 1. Extract signed transaction bytes ───────────────────────────────────
68
+ contents = payment_mandate.payment_mandate_contents
69
+ response = contents.payment_response
70
+
71
+ if response.method_name != X402_METHOD_NAME:
72
+ raise ValueError(
73
+ f"Expected payment method '{X402_METHOD_NAME}', "
74
+ f"got '{response.method_name}'"
75
+ )
76
+
77
+ x_payment: str | None = (response.details or {}).get("value")
78
+ if not x_payment:
79
+ raise ValueError(
80
+ "PaymentMandate.payment_response.details['value'] is missing. "
81
+ "The buyer agent must set this to the base64-encoded x402 SignedTransaction."
82
+ )
83
+
84
+ stxn_bytes = base64.b64decode(x_payment)
85
+
86
+ # ── 2. Submit to Algorand ─────────────────────────────────────────────────
87
+ algod_client = algod.AlgodClient(algod_token, algod_url)
88
+ txid: str = algod_client.send_raw_transaction(stxn_bytes)
89
+
90
+ # ── 3. Wait for on-chain confirmation ─────────────────────────────────────
91
+ algosdk.util.wait_for_confirmation(algod_client, txid, wait_rounds)
92
+
93
+ # ── 4. Build PaymentReceipt ───────────────────────────────────────────────
94
+ # Import AP2 types at call time so the package works without ap2 installed
95
+ # (e.g. for testing or non-AP2 use of the settle logic alone).
96
+ try:
97
+ from ap2.types.payment_receipt import PaymentReceipt, Success # type: ignore[import]
98
+ from ap2.types.payment_request import PaymentCurrencyAmount # type: ignore[import]
99
+ except ImportError as exc:
100
+ raise ImportError(
101
+ "ap2 package is required to build a PaymentReceipt. "
102
+ "Install it with: pip install ap2"
103
+ ) from exc
104
+
105
+ payment_id = uuid.uuid4().hex
106
+ payment_mandate_id = contents.payment_mandate_id
107
+ total_item = contents.payment_details_total
108
+
109
+ return PaymentReceipt(
110
+ payment_mandate_id=payment_mandate_id,
111
+ timestamp=datetime.now(timezone.utc).isoformat(),
112
+ payment_id=payment_id,
113
+ amount=PaymentCurrencyAmount(
114
+ currency=total_item.amount.currency,
115
+ value=total_item.amount.value,
116
+ ),
117
+ payment_status=Success(
118
+ merchant_confirmation_id=txid,
119
+ psp_confirmation_id=txid,
120
+ network_confirmation_id=txid,
121
+ ),
122
+ payment_method_details={
123
+ "method_name": X402_METHOD_NAME,
124
+ "network": "algorand",
125
+ "txid": txid,
126
+ },
127
+ )
128
+
129
+
130
+ def settle_raw(
131
+ x_payment: str,
132
+ algod_url: str,
133
+ algod_token: str = "",
134
+ wait_rounds: int = 4,
135
+ ) -> str:
136
+ """Submit a raw x402 payment header to Algorand and return the txid.
137
+
138
+ Lower-level alternative to :func:`settle` that works without AP2 types.
139
+ Useful for testing or non-AP2 contexts.
140
+
141
+ Args:
142
+ x_payment: Base64-encoded msgpack SignedTransaction (the X-PAYMENT header value).
143
+ algod_url: Algod REST endpoint.
144
+ algod_token: Algod API token. Empty string for public nodes.
145
+ wait_rounds: Maximum rounds to wait for confirmation.
146
+
147
+ Returns:
148
+ Algorand transaction ID string.
149
+ """
150
+ stxn_bytes = base64.b64decode(x_payment)
151
+ algod_client = algod.AlgodClient(algod_token, algod_url)
152
+ txid: str = algod_client.send_raw_transaction(stxn_bytes)
153
+ algosdk.util.wait_for_confirmation(algod_client, txid, wait_rounds)
154
+ return txid
@@ -0,0 +1,102 @@
1
+ Metadata-Version: 2.4
2
+ Name: ap2-algorand
3
+ Version: 0.1.0
4
+ Summary: Google AP2 × Algorand adapter — settle x402 payments from AP2 CartMandate/PaymentMandate
5
+ License-Expression: MIT
6
+ Keywords: algorand,x402,usdc,ap2,agent-payments,ai-agent
7
+ Requires-Python: >=3.10
8
+ Description-Content-Type: text/markdown
9
+ Requires-Dist: py-algorand-sdk<3.0,>=2.0
10
+ Requires-Dist: pydantic>=2.0
11
+ Provides-Extra: ap2
12
+ Provides-Extra: dev
13
+ Requires-Dist: pytest>=8.0; extra == "dev"
14
+
15
+ # ap2-algorand
16
+
17
+ **Google AP2 × Algorand x402 adapter** — settle AI agent payments on Algorand from an AP2 `PaymentMandate`.
18
+
19
+ Bridges the [Google Agent Payments Protocol (AP2)](https://github.com/google-agentic-commerce/AP2) with non-custodial USDC payments on Algorand, enforced by AVM MandateContracts.
20
+
21
+ ## Install
22
+
23
+ ```bash
24
+ pip install ap2-algorand
25
+ ```
26
+
27
+ ## Quick start
28
+
29
+ ### Buyer: advertise x402 Algorand in a CartMandate
30
+
31
+ ```python
32
+ from ap2_algorand import AlgorandUsdcMethodData, X402_METHOD_NAME
33
+ from ap2.types.payment_request import PaymentMethodData
34
+
35
+ method = PaymentMethodData(
36
+ supported_methods=X402_METHOD_NAME, # "https://www.x402.org/"
37
+ data=AlgorandUsdcMethodData(
38
+ mandate_app_id=3_498_113_854, # agent's MandateContract app ID
39
+ asset_id=31_566_704, # mainnet USDC ASA
40
+ network="mainnet",
41
+ x402_endpoint="https://api.example.com/weather",
42
+ ).model_dump(),
43
+ )
44
+ ```
45
+
46
+ ### Buyer: parse payment args from a CartMandate
47
+
48
+ ```python
49
+ from ap2_algorand import cart_to_payment_args
50
+
51
+ args = cart_to_payment_args(cart_mandate)
52
+ # args.mandate_app_id → int
53
+ # args.amount_micro_usdc → int (e.g. 10_000 for $0.01 USDC)
54
+ # args.asset_id → int
55
+ # args.x402_endpoint → str
56
+ ```
57
+
58
+ ### Merchant: submit payment and get a PaymentReceipt
59
+
60
+ ```python
61
+ import os
62
+ from ap2_algorand import settle
63
+
64
+ receipt = settle(
65
+ payment_mandate=payment_mandate,
66
+ algod_url="https://mainnet-api.algonode.cloud",
67
+ )
68
+ txid = receipt.payment_status.network_confirmation_id
69
+ # txid == Algorand transaction ID (network IS the ledger)
70
+ ```
71
+
72
+ ### Lower-level: submit without AP2 types
73
+
74
+ ```python
75
+ from ap2_algorand import settle_raw
76
+
77
+ txid = settle_raw(
78
+ x_payment="<base64-msgpack-signed-tx>",
79
+ algod_url="https://mainnet-api.algonode.cloud",
80
+ )
81
+ ```
82
+
83
+ ## How it maps to AP2
84
+
85
+ | AP2 field | Algorand value |
86
+ |---|---|
87
+ | `payment_response.method_name` | `https://www.x402.org/` |
88
+ | `payment_response.details["value"]` | `base64(msgpack(SignedTransaction))` |
89
+ | `merchant_confirmation_id` | Algorand txid |
90
+ | `psp_confirmation_id` | Algorand txid (AVM is the PSP) |
91
+ | `network_confirmation_id` | Algorand txid (Algorand is the network) |
92
+ | OTP challenge | skipped — AVM MandateContract enforces limits |
93
+
94
+ ## Dependencies
95
+
96
+ - `py-algorand-sdk >= 2.0`
97
+ - `pydantic >= 2.0`
98
+ - `ap2` — optional, only needed for `settle()` return type (`PaymentReceipt`)
99
+
100
+ ## License
101
+
102
+ MIT © Algo Wallet
@@ -0,0 +1,11 @@
1
+ README.md
2
+ pyproject.toml
3
+ src/ap2_algorand/__init__.py
4
+ src/ap2_algorand/cart_adapter.py
5
+ src/ap2_algorand/payment_method.py
6
+ src/ap2_algorand/settle.py
7
+ src/ap2_algorand.egg-info/PKG-INFO
8
+ src/ap2_algorand.egg-info/SOURCES.txt
9
+ src/ap2_algorand.egg-info/dependency_links.txt
10
+ src/ap2_algorand.egg-info/requires.txt
11
+ src/ap2_algorand.egg-info/top_level.txt
@@ -0,0 +1,7 @@
1
+ py-algorand-sdk<3.0,>=2.0
2
+ pydantic>=2.0
3
+
4
+ [ap2]
5
+
6
+ [dev]
7
+ pytest>=8.0
@@ -0,0 +1 @@
1
+ ap2_algorand