agentspay 0.1.0 → 0.2.0
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.
- package/.env.example +40 -0
- package/README.md +35 -4
- package/VERIFICATION_SUMMARY.txt +151 -0
- package/dist/api/server.d.ts.map +1 -1
- package/dist/api/server.js +530 -74
- package/dist/api/server.js.map +1 -1
- package/dist/bsv/crypto.d.ts +65 -0
- package/dist/bsv/crypto.d.ts.map +1 -0
- package/dist/bsv/crypto.js +158 -0
- package/dist/bsv/crypto.js.map +1 -0
- package/dist/bsv/mnee.d.ts +88 -0
- package/dist/bsv/mnee.d.ts.map +1 -0
- package/dist/bsv/mnee.js +173 -0
- package/dist/bsv/mnee.js.map +1 -0
- package/dist/bsv/opreturn.d.ts +22 -0
- package/dist/bsv/opreturn.d.ts.map +1 -0
- package/dist/bsv/opreturn.js +117 -0
- package/dist/bsv/opreturn.js.map +1 -0
- package/dist/bsv/whatsonchain.d.ts +46 -0
- package/dist/bsv/whatsonchain.d.ts.map +1 -0
- package/dist/bsv/whatsonchain.js +98 -0
- package/dist/bsv/whatsonchain.js.map +1 -0
- package/dist/config.d.ts +38 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +85 -0
- package/dist/config.js.map +1 -0
- package/dist/currency/currency.d.ts +70 -0
- package/dist/currency/currency.d.ts.map +1 -0
- package/dist/currency/currency.js +137 -0
- package/dist/currency/currency.js.map +1 -0
- package/dist/disputes/dispute.d.ts +50 -0
- package/dist/disputes/dispute.d.ts.map +1 -0
- package/dist/disputes/dispute.js +162 -0
- package/dist/disputes/dispute.js.map +1 -0
- package/dist/docs/openapi.yaml +1079 -0
- package/dist/docs/swagger.d.ts +12 -0
- package/dist/docs/swagger.d.ts.map +1 -0
- package/dist/docs/swagger.js +104 -0
- package/dist/docs/swagger.js.map +1 -0
- package/dist/middleware/auth.d.ts +20 -0
- package/dist/middleware/auth.d.ts.map +1 -0
- package/dist/middleware/auth.js +32 -0
- package/dist/middleware/auth.js.map +1 -0
- package/dist/middleware/rateLimit.d.ts +25 -0
- package/dist/middleware/rateLimit.d.ts.map +1 -0
- package/dist/middleware/rateLimit.js +61 -0
- package/dist/middleware/rateLimit.js.map +1 -0
- package/dist/payment/payment.d.ts +79 -9
- package/dist/payment/payment.d.ts.map +1 -1
- package/dist/payment/payment.js +387 -47
- package/dist/payment/payment.js.map +1 -1
- package/dist/registry/db.d.ts.map +1 -1
- package/dist/registry/db.js +110 -3
- package/dist/registry/db.js.map +1 -1
- package/dist/registry/registry.d.ts +1 -1
- package/dist/registry/registry.d.ts.map +1 -1
- package/dist/registry/registry.js +12 -4
- package/dist/registry/registry.js.map +1 -1
- package/dist/types/index.d.ts +34 -0
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/index.js.map +1 -1
- package/dist/utils/validation.d.ts +27 -0
- package/dist/utils/validation.d.ts.map +1 -0
- package/dist/utils/validation.js +164 -0
- package/dist/utils/validation.js.map +1 -0
- package/dist/verification/receipt.d.ts +42 -0
- package/dist/verification/receipt.d.ts.map +1 -0
- package/dist/verification/receipt.js +10 -0
- package/dist/verification/receipt.js.map +1 -0
- package/dist/verification/verification.d.ts +41 -0
- package/dist/verification/verification.d.ts.map +1 -0
- package/dist/verification/verification.js +217 -0
- package/dist/verification/verification.js.map +1 -0
- package/dist/wallet/providerManager.d.ts +32 -0
- package/dist/wallet/providerManager.d.ts.map +1 -0
- package/dist/wallet/providerManager.js +118 -0
- package/dist/wallet/providerManager.js.map +1 -0
- package/dist/wallet/providers/handcash.d.ts +22 -0
- package/dist/wallet/providers/handcash.d.ts.map +1 -0
- package/dist/wallet/providers/handcash.js +214 -0
- package/dist/wallet/providers/handcash.js.map +1 -0
- package/dist/wallet/providers/internal.d.ts +15 -0
- package/dist/wallet/providers/internal.d.ts.map +1 -0
- package/dist/wallet/providers/internal.js +208 -0
- package/dist/wallet/providers/internal.js.map +1 -0
- package/dist/wallet/providers/types.d.ts +50 -0
- package/dist/wallet/providers/types.d.ts.map +1 -0
- package/dist/wallet/providers/types.js +6 -0
- package/dist/wallet/providers/types.js.map +1 -0
- package/dist/wallet/providers/yours.d.ts +18 -0
- package/dist/wallet/providers/yours.d.ts.map +1 -0
- package/dist/wallet/providers/yours.js +122 -0
- package/dist/wallet/providers/yours.js.map +1 -0
- package/dist/wallet/wallet.d.ts +52 -5
- package/dist/wallet/wallet.d.ts.map +1 -1
- package/dist/wallet/wallet.js +223 -34
- package/dist/wallet/wallet.js.map +1 -1
- package/dist/webhooks/delivery.d.ts +37 -0
- package/dist/webhooks/delivery.d.ts.map +1 -0
- package/dist/webhooks/delivery.js +182 -0
- package/dist/webhooks/delivery.js.map +1 -0
- package/dist/webhooks/webhook.d.ts +85 -0
- package/dist/webhooks/webhook.d.ts.map +1 -0
- package/dist/webhooks/webhook.js +271 -0
- package/dist/webhooks/webhook.js.map +1 -0
- package/package.json +74 -54
- package/sdk-python/LICENSE +21 -0
- package/sdk-python/MANIFEST.in +9 -0
- package/sdk-python/README.md +372 -0
- package/sdk-python/agentspay/__init__.py +97 -0
- package/sdk-python/agentspay/client.py +256 -0
- package/sdk-python/agentspay/disputes.py +174 -0
- package/sdk-python/agentspay/exceptions.py +53 -0
- package/sdk-python/agentspay/payments.py +169 -0
- package/sdk-python/agentspay/services.py +198 -0
- package/sdk-python/agentspay/types.py +154 -0
- package/sdk-python/agentspay/wallet.py +113 -0
- package/sdk-python/agentspay/webhooks.py +195 -0
- package/sdk-python/examples/consumer.py +147 -0
- package/sdk-python/examples/provider.py +116 -0
- package/sdk-python/pyproject.toml +61 -0
- package/sdk-python/setup.py +53 -0
- package/sdk-python/tests/test_client.py +221 -0
- package/test-addr.js +29 -0
- package/test-mnee-simple.js +51 -0
- package/test-mnee.js +47 -0
|
@@ -0,0 +1,256 @@
|
|
|
1
|
+
"""AgentPay SDK Main Client"""
|
|
2
|
+
|
|
3
|
+
from typing import Optional, List, Dict, Any
|
|
4
|
+
from .wallet import WalletOperations
|
|
5
|
+
from .services import ServiceOperations
|
|
6
|
+
from .payments import PaymentOperations
|
|
7
|
+
from .disputes import DisputeOperations
|
|
8
|
+
from .webhooks import WebhookOperations
|
|
9
|
+
from .types import (
|
|
10
|
+
AgentWallet,
|
|
11
|
+
Service,
|
|
12
|
+
ExecutionResult,
|
|
13
|
+
Payment,
|
|
14
|
+
Dispute,
|
|
15
|
+
Webhook,
|
|
16
|
+
ReputationScore
|
|
17
|
+
)
|
|
18
|
+
from .exceptions import AgentPayError
|
|
19
|
+
import requests
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class AgentPayClient:
|
|
23
|
+
"""
|
|
24
|
+
AgentPay SDK Client
|
|
25
|
+
|
|
26
|
+
Main interface for interacting with AgentPay platform.
|
|
27
|
+
|
|
28
|
+
Example:
|
|
29
|
+
>>> client = AgentPayClient(base_url="http://localhost:3100", api_key="your-key")
|
|
30
|
+
>>> wallet = client.create_wallet()
|
|
31
|
+
>>> service = client.register_service(
|
|
32
|
+
... agent_id=wallet.id,
|
|
33
|
+
... name="TextAnalyzer",
|
|
34
|
+
... description="NLP analysis",
|
|
35
|
+
... price=1000,
|
|
36
|
+
... currency="BSV",
|
|
37
|
+
... endpoint="https://my-agent.com/analyze",
|
|
38
|
+
... category="nlp"
|
|
39
|
+
... )
|
|
40
|
+
"""
|
|
41
|
+
|
|
42
|
+
def __init__(
|
|
43
|
+
self,
|
|
44
|
+
base_url: str = "http://localhost:3100",
|
|
45
|
+
api_key: Optional[str] = None
|
|
46
|
+
):
|
|
47
|
+
"""
|
|
48
|
+
Initialize AgentPay client
|
|
49
|
+
|
|
50
|
+
Args:
|
|
51
|
+
base_url: AgentPay API base URL
|
|
52
|
+
api_key: Optional API key for authentication
|
|
53
|
+
"""
|
|
54
|
+
self.base_url = base_url.rstrip("/")
|
|
55
|
+
self.api_key = api_key
|
|
56
|
+
|
|
57
|
+
# Initialize operation modules
|
|
58
|
+
self._wallet = WalletOperations(self.base_url, self.api_key)
|
|
59
|
+
self._services = ServiceOperations(self.base_url, self.api_key)
|
|
60
|
+
self._payments = PaymentOperations(self.base_url, self.api_key)
|
|
61
|
+
self._disputes = DisputeOperations(self.base_url, self.api_key)
|
|
62
|
+
self._webhooks = WebhookOperations(self.base_url, self.api_key)
|
|
63
|
+
|
|
64
|
+
# Wallet Operations
|
|
65
|
+
|
|
66
|
+
def create_wallet(self) -> AgentWallet:
|
|
67
|
+
"""Create a new agent wallet"""
|
|
68
|
+
return self._wallet.create_wallet()
|
|
69
|
+
|
|
70
|
+
def get_wallet(self, wallet_id: str) -> AgentWallet:
|
|
71
|
+
"""Get wallet by ID"""
|
|
72
|
+
return self._wallet.get_wallet(wallet_id)
|
|
73
|
+
|
|
74
|
+
def get_balance(self, wallet_id: str, currency: str = "BSV") -> int:
|
|
75
|
+
"""Get wallet balance for a specific currency"""
|
|
76
|
+
return self._wallet.get_balance(wallet_id, currency)
|
|
77
|
+
|
|
78
|
+
# Service Operations
|
|
79
|
+
|
|
80
|
+
def register_service(
|
|
81
|
+
self,
|
|
82
|
+
agent_id: str,
|
|
83
|
+
name: str,
|
|
84
|
+
description: str,
|
|
85
|
+
price: int,
|
|
86
|
+
endpoint: str,
|
|
87
|
+
category: str = "general",
|
|
88
|
+
currency: str = "BSV",
|
|
89
|
+
method: str = "POST",
|
|
90
|
+
timeout: int = 30,
|
|
91
|
+
dispute_window: int = 30,
|
|
92
|
+
input_schema: Optional[dict] = None,
|
|
93
|
+
output_schema: Optional[dict] = None
|
|
94
|
+
) -> Service:
|
|
95
|
+
"""Register a new service"""
|
|
96
|
+
return self._services.register_service(
|
|
97
|
+
agent_id=agent_id,
|
|
98
|
+
name=name,
|
|
99
|
+
description=description,
|
|
100
|
+
price=price,
|
|
101
|
+
endpoint=endpoint,
|
|
102
|
+
category=category,
|
|
103
|
+
currency=currency,
|
|
104
|
+
method=method,
|
|
105
|
+
timeout=timeout,
|
|
106
|
+
dispute_window=dispute_window,
|
|
107
|
+
input_schema=input_schema,
|
|
108
|
+
output_schema=output_schema
|
|
109
|
+
)
|
|
110
|
+
|
|
111
|
+
def search_services(
|
|
112
|
+
self,
|
|
113
|
+
keyword: Optional[str] = None,
|
|
114
|
+
category: Optional[str] = None,
|
|
115
|
+
max_price: Optional[int] = None,
|
|
116
|
+
min_rating: Optional[float] = None,
|
|
117
|
+
limit: int = 20,
|
|
118
|
+
offset: int = 0
|
|
119
|
+
) -> List[Service]:
|
|
120
|
+
"""Search for services"""
|
|
121
|
+
return self._services.search_services(
|
|
122
|
+
keyword=keyword,
|
|
123
|
+
category=category,
|
|
124
|
+
max_price=max_price,
|
|
125
|
+
min_rating=min_rating,
|
|
126
|
+
limit=limit,
|
|
127
|
+
offset=offset
|
|
128
|
+
)
|
|
129
|
+
|
|
130
|
+
def get_service(self, service_id: str) -> Service:
|
|
131
|
+
"""Get service by ID"""
|
|
132
|
+
return self._services.get_service(service_id)
|
|
133
|
+
|
|
134
|
+
# Payment & Execution Operations
|
|
135
|
+
|
|
136
|
+
def execute(
|
|
137
|
+
self,
|
|
138
|
+
service_id: str,
|
|
139
|
+
buyer_wallet_id: str,
|
|
140
|
+
input_data: Dict[str, Any]
|
|
141
|
+
) -> ExecutionResult:
|
|
142
|
+
"""
|
|
143
|
+
Execute a service and handle payment
|
|
144
|
+
|
|
145
|
+
Returns execution result with payment details and cryptographic receipt
|
|
146
|
+
"""
|
|
147
|
+
return self._payments.execute(service_id, buyer_wallet_id, input_data)
|
|
148
|
+
|
|
149
|
+
def get_payment(self, payment_id: str) -> Payment:
|
|
150
|
+
"""Get payment by ID"""
|
|
151
|
+
return self._payments.get_payment(payment_id)
|
|
152
|
+
|
|
153
|
+
def get_receipt(self, receipt_id: str):
|
|
154
|
+
"""Get execution receipt by ID"""
|
|
155
|
+
return self._payments.get_receipt(receipt_id)
|
|
156
|
+
|
|
157
|
+
# Dispute Operations
|
|
158
|
+
|
|
159
|
+
def open_dispute(
|
|
160
|
+
self,
|
|
161
|
+
payment_id: str,
|
|
162
|
+
reason: str,
|
|
163
|
+
evidence: Optional[str] = None
|
|
164
|
+
) -> Dispute:
|
|
165
|
+
"""Open a dispute for a payment"""
|
|
166
|
+
return self._disputes.open_dispute(payment_id, reason, evidence)
|
|
167
|
+
|
|
168
|
+
def get_dispute(self, dispute_id: str) -> Dispute:
|
|
169
|
+
"""Get dispute by ID"""
|
|
170
|
+
return self._disputes.get_dispute(dispute_id)
|
|
171
|
+
|
|
172
|
+
def get_payment_disputes(self, payment_id: str) -> List[Dispute]:
|
|
173
|
+
"""Get all disputes for a payment"""
|
|
174
|
+
return self._disputes.get_payment_disputes(payment_id)
|
|
175
|
+
|
|
176
|
+
def add_dispute_evidence(self, dispute_id: str, evidence: str) -> Dispute:
|
|
177
|
+
"""Add evidence to a dispute"""
|
|
178
|
+
return self._disputes.add_evidence(dispute_id, evidence)
|
|
179
|
+
|
|
180
|
+
# Webhook Operations
|
|
181
|
+
|
|
182
|
+
def register_webhook(
|
|
183
|
+
self,
|
|
184
|
+
url: str,
|
|
185
|
+
events: List[str]
|
|
186
|
+
) -> Webhook:
|
|
187
|
+
"""
|
|
188
|
+
Register a webhook
|
|
189
|
+
|
|
190
|
+
Args:
|
|
191
|
+
url: Webhook endpoint URL
|
|
192
|
+
events: Events to subscribe to (e.g., ["payment.completed", "payment.failed"])
|
|
193
|
+
"""
|
|
194
|
+
return self._webhooks.register_webhook(url, events)
|
|
195
|
+
|
|
196
|
+
def get_webhook(self, webhook_id: str) -> Webhook:
|
|
197
|
+
"""Get webhook by ID"""
|
|
198
|
+
return self._webhooks.get_webhook(webhook_id)
|
|
199
|
+
|
|
200
|
+
def list_webhooks(self) -> List[Webhook]:
|
|
201
|
+
"""List all registered webhooks"""
|
|
202
|
+
return self._webhooks.list_webhooks()
|
|
203
|
+
|
|
204
|
+
def delete_webhook(self, webhook_id: str) -> bool:
|
|
205
|
+
"""Delete a webhook"""
|
|
206
|
+
return self._webhooks.delete_webhook(webhook_id)
|
|
207
|
+
|
|
208
|
+
def update_webhook(
|
|
209
|
+
self,
|
|
210
|
+
webhook_id: str,
|
|
211
|
+
url: Optional[str] = None,
|
|
212
|
+
events: Optional[List[str]] = None,
|
|
213
|
+
active: Optional[bool] = None
|
|
214
|
+
) -> Webhook:
|
|
215
|
+
"""Update a webhook"""
|
|
216
|
+
return self._webhooks.update_webhook(webhook_id, url, events, active)
|
|
217
|
+
|
|
218
|
+
# Reputation Operations
|
|
219
|
+
|
|
220
|
+
def get_reputation(self, agent_id: str) -> ReputationScore:
|
|
221
|
+
"""
|
|
222
|
+
Get reputation score for an agent
|
|
223
|
+
|
|
224
|
+
Args:
|
|
225
|
+
agent_id: Agent wallet ID
|
|
226
|
+
|
|
227
|
+
Returns:
|
|
228
|
+
ReputationScore: Agent's reputation metrics
|
|
229
|
+
"""
|
|
230
|
+
try:
|
|
231
|
+
headers = {"Content-Type": "application/json"}
|
|
232
|
+
if self.api_key:
|
|
233
|
+
headers["Authorization"] = f"Bearer {self.api_key}"
|
|
234
|
+
|
|
235
|
+
response = requests.get(
|
|
236
|
+
f"{self.base_url}/api/agents/{agent_id}/reputation",
|
|
237
|
+
headers=headers
|
|
238
|
+
)
|
|
239
|
+
response.raise_for_status()
|
|
240
|
+
data = response.json()
|
|
241
|
+
|
|
242
|
+
rep_data = data.get("reputation")
|
|
243
|
+
if not rep_data:
|
|
244
|
+
raise AgentPayError(f"Reputation for agent {agent_id} not found")
|
|
245
|
+
|
|
246
|
+
return ReputationScore(
|
|
247
|
+
agent_id=rep_data["agentId"],
|
|
248
|
+
total_jobs=rep_data["totalJobs"],
|
|
249
|
+
success_rate=rep_data["successRate"],
|
|
250
|
+
avg_response_time_ms=rep_data["avgResponseTimeMs"],
|
|
251
|
+
total_earned=rep_data["totalEarned"],
|
|
252
|
+
total_spent=rep_data["totalSpent"],
|
|
253
|
+
rating=rep_data["rating"]
|
|
254
|
+
)
|
|
255
|
+
except requests.RequestException as e:
|
|
256
|
+
raise AgentPayError(f"Failed to get reputation: {str(e)}") from e
|
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
"""Dispute management for AgentPay SDK"""
|
|
2
|
+
|
|
3
|
+
from typing import Optional, List
|
|
4
|
+
import requests
|
|
5
|
+
from .types import Dispute
|
|
6
|
+
from .exceptions import DisputeError
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class DisputeOperations:
|
|
10
|
+
"""Handles dispute operations"""
|
|
11
|
+
|
|
12
|
+
def __init__(self, base_url: str, api_key: Optional[str] = None):
|
|
13
|
+
self.base_url = base_url
|
|
14
|
+
self.api_key = api_key
|
|
15
|
+
|
|
16
|
+
def _get_headers(self) -> dict:
|
|
17
|
+
"""Get request headers with API key if available"""
|
|
18
|
+
headers = {"Content-Type": "application/json"}
|
|
19
|
+
if self.api_key:
|
|
20
|
+
headers["Authorization"] = f"Bearer {self.api_key}"
|
|
21
|
+
return headers
|
|
22
|
+
|
|
23
|
+
def open_dispute(
|
|
24
|
+
self,
|
|
25
|
+
payment_id: str,
|
|
26
|
+
reason: str,
|
|
27
|
+
evidence: Optional[str] = None
|
|
28
|
+
) -> Dispute:
|
|
29
|
+
"""
|
|
30
|
+
Open a dispute for a payment
|
|
31
|
+
|
|
32
|
+
Args:
|
|
33
|
+
payment_id: Payment ID to dispute
|
|
34
|
+
reason: Reason for the dispute
|
|
35
|
+
evidence: Optional evidence (URL, description, etc.)
|
|
36
|
+
|
|
37
|
+
Returns:
|
|
38
|
+
Dispute: The created dispute
|
|
39
|
+
|
|
40
|
+
Raises:
|
|
41
|
+
DisputeError: If dispute creation fails
|
|
42
|
+
"""
|
|
43
|
+
payload = {
|
|
44
|
+
"paymentId": payment_id,
|
|
45
|
+
"reason": reason
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
if evidence:
|
|
49
|
+
payload["evidence"] = evidence
|
|
50
|
+
|
|
51
|
+
try:
|
|
52
|
+
response = requests.post(
|
|
53
|
+
f"{self.base_url}/api/disputes",
|
|
54
|
+
json=payload,
|
|
55
|
+
headers=self._get_headers()
|
|
56
|
+
)
|
|
57
|
+
response.raise_for_status()
|
|
58
|
+
data = response.json()
|
|
59
|
+
|
|
60
|
+
dispute_data = data.get("dispute")
|
|
61
|
+
if not dispute_data:
|
|
62
|
+
raise DisputeError("Invalid response: missing dispute data")
|
|
63
|
+
|
|
64
|
+
return self._parse_dispute(dispute_data)
|
|
65
|
+
except requests.RequestException as e:
|
|
66
|
+
raise DisputeError(f"Failed to open dispute: {str(e)}") from e
|
|
67
|
+
|
|
68
|
+
def get_dispute(self, dispute_id: str) -> Dispute:
|
|
69
|
+
"""
|
|
70
|
+
Get dispute by ID
|
|
71
|
+
|
|
72
|
+
Args:
|
|
73
|
+
dispute_id: Dispute ID
|
|
74
|
+
|
|
75
|
+
Returns:
|
|
76
|
+
Dispute: The dispute
|
|
77
|
+
|
|
78
|
+
Raises:
|
|
79
|
+
DisputeError: If dispute not found or request fails
|
|
80
|
+
"""
|
|
81
|
+
try:
|
|
82
|
+
response = requests.get(
|
|
83
|
+
f"{self.base_url}/api/disputes/{dispute_id}",
|
|
84
|
+
headers=self._get_headers()
|
|
85
|
+
)
|
|
86
|
+
response.raise_for_status()
|
|
87
|
+
data = response.json()
|
|
88
|
+
|
|
89
|
+
dispute_data = data.get("dispute")
|
|
90
|
+
if not dispute_data:
|
|
91
|
+
raise DisputeError(f"Dispute {dispute_id} not found")
|
|
92
|
+
|
|
93
|
+
return self._parse_dispute(dispute_data)
|
|
94
|
+
except requests.RequestException as e:
|
|
95
|
+
raise DisputeError(f"Failed to get dispute: {str(e)}") from e
|
|
96
|
+
|
|
97
|
+
def get_payment_disputes(self, payment_id: str) -> List[Dispute]:
|
|
98
|
+
"""
|
|
99
|
+
Get all disputes for a payment
|
|
100
|
+
|
|
101
|
+
Args:
|
|
102
|
+
payment_id: Payment ID
|
|
103
|
+
|
|
104
|
+
Returns:
|
|
105
|
+
List[Dispute]: List of disputes for the payment
|
|
106
|
+
|
|
107
|
+
Raises:
|
|
108
|
+
DisputeError: If request fails
|
|
109
|
+
"""
|
|
110
|
+
try:
|
|
111
|
+
response = requests.get(
|
|
112
|
+
f"{self.base_url}/api/payments/{payment_id}/disputes",
|
|
113
|
+
headers=self._get_headers()
|
|
114
|
+
)
|
|
115
|
+
response.raise_for_status()
|
|
116
|
+
data = response.json()
|
|
117
|
+
|
|
118
|
+
disputes_data = data.get("disputes", [])
|
|
119
|
+
return [self._parse_dispute(d) for d in disputes_data]
|
|
120
|
+
except requests.RequestException as e:
|
|
121
|
+
raise DisputeError(f"Failed to get payment disputes: {str(e)}") from e
|
|
122
|
+
|
|
123
|
+
def add_evidence(
|
|
124
|
+
self,
|
|
125
|
+
dispute_id: str,
|
|
126
|
+
evidence: str
|
|
127
|
+
) -> Dispute:
|
|
128
|
+
"""
|
|
129
|
+
Add evidence to a dispute
|
|
130
|
+
|
|
131
|
+
Args:
|
|
132
|
+
dispute_id: Dispute ID
|
|
133
|
+
evidence: Evidence to add
|
|
134
|
+
|
|
135
|
+
Returns:
|
|
136
|
+
Dispute: Updated dispute
|
|
137
|
+
|
|
138
|
+
Raises:
|
|
139
|
+
DisputeError: If evidence submission fails
|
|
140
|
+
"""
|
|
141
|
+
payload = {"evidence": evidence}
|
|
142
|
+
|
|
143
|
+
try:
|
|
144
|
+
response = requests.post(
|
|
145
|
+
f"{self.base_url}/api/disputes/{dispute_id}/evidence",
|
|
146
|
+
json=payload,
|
|
147
|
+
headers=self._get_headers()
|
|
148
|
+
)
|
|
149
|
+
response.raise_for_status()
|
|
150
|
+
data = response.json()
|
|
151
|
+
|
|
152
|
+
dispute_data = data.get("dispute")
|
|
153
|
+
if not dispute_data:
|
|
154
|
+
raise DisputeError("Invalid response: missing dispute data")
|
|
155
|
+
|
|
156
|
+
return self._parse_dispute(dispute_data)
|
|
157
|
+
except requests.RequestException as e:
|
|
158
|
+
raise DisputeError(f"Failed to add evidence: {str(e)}") from e
|
|
159
|
+
|
|
160
|
+
def _parse_dispute(self, data: dict) -> Dispute:
|
|
161
|
+
"""Parse dispute data from API response"""
|
|
162
|
+
return Dispute(
|
|
163
|
+
id=data["id"],
|
|
164
|
+
payment_id=data["paymentId"],
|
|
165
|
+
buyer_wallet_id=data["buyerWalletId"],
|
|
166
|
+
provider_wallet_id=data["providerWalletId"],
|
|
167
|
+
reason=data["reason"],
|
|
168
|
+
status=data["status"],
|
|
169
|
+
created_at=data["createdAt"],
|
|
170
|
+
evidence=data.get("evidence"),
|
|
171
|
+
resolution=data.get("resolution"),
|
|
172
|
+
split_percent=data.get("splitPercent"),
|
|
173
|
+
resolved_at=data.get("resolvedAt")
|
|
174
|
+
)
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
"""AgentPay SDK Exceptions"""
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class AgentPayError(Exception):
|
|
5
|
+
"""Base exception for all AgentPay errors"""
|
|
6
|
+
pass
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class APIError(AgentPayError):
|
|
10
|
+
"""Raised when API returns an error response"""
|
|
11
|
+
|
|
12
|
+
def __init__(self, message: str, status_code: int = None, response: dict = None):
|
|
13
|
+
super().__init__(message)
|
|
14
|
+
self.status_code = status_code
|
|
15
|
+
self.response = response
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class ValidationError(AgentPayError):
|
|
19
|
+
"""Raised when request validation fails"""
|
|
20
|
+
pass
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
class WalletError(AgentPayError):
|
|
24
|
+
"""Raised for wallet-related errors"""
|
|
25
|
+
pass
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
class ServiceError(AgentPayError):
|
|
29
|
+
"""Raised for service-related errors"""
|
|
30
|
+
pass
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
class PaymentError(AgentPayError):
|
|
34
|
+
"""Raised for payment-related errors"""
|
|
35
|
+
pass
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
class DisputeError(AgentPayError):
|
|
39
|
+
"""Raised for dispute-related errors"""
|
|
40
|
+
pass
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
class WebhookError(AgentPayError):
|
|
44
|
+
"""Raised for webhook-related errors"""
|
|
45
|
+
pass
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
class ExecutionError(AgentPayError):
|
|
49
|
+
"""Raised when service execution fails"""
|
|
50
|
+
|
|
51
|
+
def __init__(self, message: str, execution_result: dict = None):
|
|
52
|
+
super().__init__(message)
|
|
53
|
+
self.execution_result = execution_result
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
"""Payment and execution operations for AgentPay SDK"""
|
|
2
|
+
|
|
3
|
+
from typing import Optional, Dict, Any
|
|
4
|
+
import requests
|
|
5
|
+
from .types import ExecutionResult, ExecutionReceipt, Payment
|
|
6
|
+
from .exceptions import PaymentError, ExecutionError
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class PaymentOperations:
|
|
10
|
+
"""Handles payment and service execution"""
|
|
11
|
+
|
|
12
|
+
def __init__(self, base_url: str, api_key: Optional[str] = None):
|
|
13
|
+
self.base_url = base_url
|
|
14
|
+
self.api_key = api_key
|
|
15
|
+
|
|
16
|
+
def _get_headers(self) -> dict:
|
|
17
|
+
"""Get request headers with API key if available"""
|
|
18
|
+
headers = {"Content-Type": "application/json"}
|
|
19
|
+
if self.api_key:
|
|
20
|
+
headers["Authorization"] = f"Bearer {self.api_key}"
|
|
21
|
+
return headers
|
|
22
|
+
|
|
23
|
+
def execute(
|
|
24
|
+
self,
|
|
25
|
+
service_id: str,
|
|
26
|
+
buyer_wallet_id: str,
|
|
27
|
+
input_data: Dict[str, Any]
|
|
28
|
+
) -> ExecutionResult:
|
|
29
|
+
"""
|
|
30
|
+
Execute a service and handle payment
|
|
31
|
+
|
|
32
|
+
Args:
|
|
33
|
+
service_id: Service ID to execute
|
|
34
|
+
buyer_wallet_id: Buyer's wallet ID
|
|
35
|
+
input_data: Input data for the service
|
|
36
|
+
|
|
37
|
+
Returns:
|
|
38
|
+
ExecutionResult: Execution result with payment and receipt
|
|
39
|
+
|
|
40
|
+
Raises:
|
|
41
|
+
ExecutionError: If execution fails
|
|
42
|
+
"""
|
|
43
|
+
payload = {
|
|
44
|
+
"buyerWalletId": buyer_wallet_id,
|
|
45
|
+
"input": input_data
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
try:
|
|
49
|
+
response = requests.post(
|
|
50
|
+
f"{self.base_url}/api/execute/{service_id}",
|
|
51
|
+
json=payload,
|
|
52
|
+
headers=self._get_headers()
|
|
53
|
+
)
|
|
54
|
+
response.raise_for_status()
|
|
55
|
+
data = response.json()
|
|
56
|
+
|
|
57
|
+
# Parse receipt if available
|
|
58
|
+
receipt = None
|
|
59
|
+
if "receipt" in data and data["receipt"]:
|
|
60
|
+
receipt = self._parse_receipt(data["receipt"])
|
|
61
|
+
|
|
62
|
+
# Parse payment if available
|
|
63
|
+
payment = None
|
|
64
|
+
if "payment" in data and data["payment"]:
|
|
65
|
+
payment = self._parse_payment(data["payment"])
|
|
66
|
+
|
|
67
|
+
return ExecutionResult(
|
|
68
|
+
payment_id=data["paymentId"],
|
|
69
|
+
service_id=data["serviceId"],
|
|
70
|
+
output=data.get("output", {}),
|
|
71
|
+
execution_time_ms=data.get("executionTimeMs", 0),
|
|
72
|
+
status=data.get("status", "success"),
|
|
73
|
+
receipt=receipt,
|
|
74
|
+
payment=payment
|
|
75
|
+
)
|
|
76
|
+
except requests.RequestException as e:
|
|
77
|
+
raise ExecutionError(f"Service execution failed: {str(e)}") from e
|
|
78
|
+
|
|
79
|
+
def get_payment(self, payment_id: str) -> Payment:
|
|
80
|
+
"""
|
|
81
|
+
Get payment by ID
|
|
82
|
+
|
|
83
|
+
Args:
|
|
84
|
+
payment_id: Payment ID
|
|
85
|
+
|
|
86
|
+
Returns:
|
|
87
|
+
Payment: The payment record
|
|
88
|
+
|
|
89
|
+
Raises:
|
|
90
|
+
PaymentError: If payment not found or request fails
|
|
91
|
+
"""
|
|
92
|
+
try:
|
|
93
|
+
response = requests.get(
|
|
94
|
+
f"{self.base_url}/api/payments/{payment_id}",
|
|
95
|
+
headers=self._get_headers()
|
|
96
|
+
)
|
|
97
|
+
response.raise_for_status()
|
|
98
|
+
data = response.json()
|
|
99
|
+
|
|
100
|
+
payment_data = data.get("payment")
|
|
101
|
+
if not payment_data:
|
|
102
|
+
raise PaymentError(f"Payment {payment_id} not found")
|
|
103
|
+
|
|
104
|
+
return self._parse_payment(payment_data)
|
|
105
|
+
except requests.RequestException as e:
|
|
106
|
+
raise PaymentError(f"Failed to get payment: {str(e)}") from e
|
|
107
|
+
|
|
108
|
+
def get_receipt(self, receipt_id: str) -> ExecutionReceipt:
|
|
109
|
+
"""
|
|
110
|
+
Get execution receipt by ID
|
|
111
|
+
|
|
112
|
+
Args:
|
|
113
|
+
receipt_id: Receipt ID
|
|
114
|
+
|
|
115
|
+
Returns:
|
|
116
|
+
ExecutionReceipt: The execution receipt
|
|
117
|
+
|
|
118
|
+
Raises:
|
|
119
|
+
PaymentError: If receipt not found or request fails
|
|
120
|
+
"""
|
|
121
|
+
try:
|
|
122
|
+
response = requests.get(
|
|
123
|
+
f"{self.base_url}/api/receipts/{receipt_id}",
|
|
124
|
+
headers=self._get_headers()
|
|
125
|
+
)
|
|
126
|
+
response.raise_for_status()
|
|
127
|
+
data = response.json()
|
|
128
|
+
|
|
129
|
+
receipt_data = data.get("receipt")
|
|
130
|
+
if not receipt_data:
|
|
131
|
+
raise PaymentError(f"Receipt {receipt_id} not found")
|
|
132
|
+
|
|
133
|
+
return self._parse_receipt(receipt_data)
|
|
134
|
+
except requests.RequestException as e:
|
|
135
|
+
raise PaymentError(f"Failed to get receipt: {str(e)}") from e
|
|
136
|
+
|
|
137
|
+
def _parse_payment(self, data: dict) -> Payment:
|
|
138
|
+
"""Parse payment data from API response"""
|
|
139
|
+
return Payment(
|
|
140
|
+
id=data["id"],
|
|
141
|
+
service_id=data["serviceId"],
|
|
142
|
+
buyer_wallet_id=data["buyerWalletId"],
|
|
143
|
+
seller_wallet_id=data["sellerWalletId"],
|
|
144
|
+
amount=data["amount"],
|
|
145
|
+
platform_fee=data["platformFee"],
|
|
146
|
+
currency=data["currency"],
|
|
147
|
+
status=data["status"],
|
|
148
|
+
created_at=data["createdAt"],
|
|
149
|
+
dispute_status=data.get("disputeStatus"),
|
|
150
|
+
tx_id=data.get("txId"),
|
|
151
|
+
completed_at=data.get("completedAt")
|
|
152
|
+
)
|
|
153
|
+
|
|
154
|
+
def _parse_receipt(self, data: dict) -> ExecutionReceipt:
|
|
155
|
+
"""Parse receipt data from API response"""
|
|
156
|
+
return ExecutionReceipt(
|
|
157
|
+
id=data["id"],
|
|
158
|
+
payment_id=data["paymentId"],
|
|
159
|
+
service_id=data["serviceId"],
|
|
160
|
+
input_hash=data["inputHash"],
|
|
161
|
+
output_hash=data["outputHash"],
|
|
162
|
+
timestamp=data["timestamp"],
|
|
163
|
+
execution_time_ms=data["executionTimeMs"],
|
|
164
|
+
provider_signature=data["providerSignature"],
|
|
165
|
+
platform_signature=data["platformSignature"],
|
|
166
|
+
receipt_hash=data["receiptHash"],
|
|
167
|
+
blockchain_tx_id=data.get("blockchainTxId"),
|
|
168
|
+
blockchain_anchored_at=data.get("blockchainAnchoredAt")
|
|
169
|
+
)
|