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.
Files changed (126) hide show
  1. package/.env.example +40 -0
  2. package/README.md +35 -4
  3. package/VERIFICATION_SUMMARY.txt +151 -0
  4. package/dist/api/server.d.ts.map +1 -1
  5. package/dist/api/server.js +530 -74
  6. package/dist/api/server.js.map +1 -1
  7. package/dist/bsv/crypto.d.ts +65 -0
  8. package/dist/bsv/crypto.d.ts.map +1 -0
  9. package/dist/bsv/crypto.js +158 -0
  10. package/dist/bsv/crypto.js.map +1 -0
  11. package/dist/bsv/mnee.d.ts +88 -0
  12. package/dist/bsv/mnee.d.ts.map +1 -0
  13. package/dist/bsv/mnee.js +173 -0
  14. package/dist/bsv/mnee.js.map +1 -0
  15. package/dist/bsv/opreturn.d.ts +22 -0
  16. package/dist/bsv/opreturn.d.ts.map +1 -0
  17. package/dist/bsv/opreturn.js +117 -0
  18. package/dist/bsv/opreturn.js.map +1 -0
  19. package/dist/bsv/whatsonchain.d.ts +46 -0
  20. package/dist/bsv/whatsonchain.d.ts.map +1 -0
  21. package/dist/bsv/whatsonchain.js +98 -0
  22. package/dist/bsv/whatsonchain.js.map +1 -0
  23. package/dist/config.d.ts +38 -0
  24. package/dist/config.d.ts.map +1 -0
  25. package/dist/config.js +85 -0
  26. package/dist/config.js.map +1 -0
  27. package/dist/currency/currency.d.ts +70 -0
  28. package/dist/currency/currency.d.ts.map +1 -0
  29. package/dist/currency/currency.js +137 -0
  30. package/dist/currency/currency.js.map +1 -0
  31. package/dist/disputes/dispute.d.ts +50 -0
  32. package/dist/disputes/dispute.d.ts.map +1 -0
  33. package/dist/disputes/dispute.js +162 -0
  34. package/dist/disputes/dispute.js.map +1 -0
  35. package/dist/docs/openapi.yaml +1079 -0
  36. package/dist/docs/swagger.d.ts +12 -0
  37. package/dist/docs/swagger.d.ts.map +1 -0
  38. package/dist/docs/swagger.js +104 -0
  39. package/dist/docs/swagger.js.map +1 -0
  40. package/dist/middleware/auth.d.ts +20 -0
  41. package/dist/middleware/auth.d.ts.map +1 -0
  42. package/dist/middleware/auth.js +32 -0
  43. package/dist/middleware/auth.js.map +1 -0
  44. package/dist/middleware/rateLimit.d.ts +25 -0
  45. package/dist/middleware/rateLimit.d.ts.map +1 -0
  46. package/dist/middleware/rateLimit.js +61 -0
  47. package/dist/middleware/rateLimit.js.map +1 -0
  48. package/dist/payment/payment.d.ts +79 -9
  49. package/dist/payment/payment.d.ts.map +1 -1
  50. package/dist/payment/payment.js +387 -47
  51. package/dist/payment/payment.js.map +1 -1
  52. package/dist/registry/db.d.ts.map +1 -1
  53. package/dist/registry/db.js +110 -3
  54. package/dist/registry/db.js.map +1 -1
  55. package/dist/registry/registry.d.ts +1 -1
  56. package/dist/registry/registry.d.ts.map +1 -1
  57. package/dist/registry/registry.js +12 -4
  58. package/dist/registry/registry.js.map +1 -1
  59. package/dist/types/index.d.ts +34 -0
  60. package/dist/types/index.d.ts.map +1 -1
  61. package/dist/types/index.js.map +1 -1
  62. package/dist/utils/validation.d.ts +27 -0
  63. package/dist/utils/validation.d.ts.map +1 -0
  64. package/dist/utils/validation.js +164 -0
  65. package/dist/utils/validation.js.map +1 -0
  66. package/dist/verification/receipt.d.ts +42 -0
  67. package/dist/verification/receipt.d.ts.map +1 -0
  68. package/dist/verification/receipt.js +10 -0
  69. package/dist/verification/receipt.js.map +1 -0
  70. package/dist/verification/verification.d.ts +41 -0
  71. package/dist/verification/verification.d.ts.map +1 -0
  72. package/dist/verification/verification.js +217 -0
  73. package/dist/verification/verification.js.map +1 -0
  74. package/dist/wallet/providerManager.d.ts +32 -0
  75. package/dist/wallet/providerManager.d.ts.map +1 -0
  76. package/dist/wallet/providerManager.js +118 -0
  77. package/dist/wallet/providerManager.js.map +1 -0
  78. package/dist/wallet/providers/handcash.d.ts +22 -0
  79. package/dist/wallet/providers/handcash.d.ts.map +1 -0
  80. package/dist/wallet/providers/handcash.js +214 -0
  81. package/dist/wallet/providers/handcash.js.map +1 -0
  82. package/dist/wallet/providers/internal.d.ts +15 -0
  83. package/dist/wallet/providers/internal.d.ts.map +1 -0
  84. package/dist/wallet/providers/internal.js +208 -0
  85. package/dist/wallet/providers/internal.js.map +1 -0
  86. package/dist/wallet/providers/types.d.ts +50 -0
  87. package/dist/wallet/providers/types.d.ts.map +1 -0
  88. package/dist/wallet/providers/types.js +6 -0
  89. package/dist/wallet/providers/types.js.map +1 -0
  90. package/dist/wallet/providers/yours.d.ts +18 -0
  91. package/dist/wallet/providers/yours.d.ts.map +1 -0
  92. package/dist/wallet/providers/yours.js +122 -0
  93. package/dist/wallet/providers/yours.js.map +1 -0
  94. package/dist/wallet/wallet.d.ts +52 -5
  95. package/dist/wallet/wallet.d.ts.map +1 -1
  96. package/dist/wallet/wallet.js +223 -34
  97. package/dist/wallet/wallet.js.map +1 -1
  98. package/dist/webhooks/delivery.d.ts +37 -0
  99. package/dist/webhooks/delivery.d.ts.map +1 -0
  100. package/dist/webhooks/delivery.js +182 -0
  101. package/dist/webhooks/delivery.js.map +1 -0
  102. package/dist/webhooks/webhook.d.ts +85 -0
  103. package/dist/webhooks/webhook.d.ts.map +1 -0
  104. package/dist/webhooks/webhook.js +271 -0
  105. package/dist/webhooks/webhook.js.map +1 -0
  106. package/package.json +74 -54
  107. package/sdk-python/LICENSE +21 -0
  108. package/sdk-python/MANIFEST.in +9 -0
  109. package/sdk-python/README.md +372 -0
  110. package/sdk-python/agentspay/__init__.py +97 -0
  111. package/sdk-python/agentspay/client.py +256 -0
  112. package/sdk-python/agentspay/disputes.py +174 -0
  113. package/sdk-python/agentspay/exceptions.py +53 -0
  114. package/sdk-python/agentspay/payments.py +169 -0
  115. package/sdk-python/agentspay/services.py +198 -0
  116. package/sdk-python/agentspay/types.py +154 -0
  117. package/sdk-python/agentspay/wallet.py +113 -0
  118. package/sdk-python/agentspay/webhooks.py +195 -0
  119. package/sdk-python/examples/consumer.py +147 -0
  120. package/sdk-python/examples/provider.py +116 -0
  121. package/sdk-python/pyproject.toml +61 -0
  122. package/sdk-python/setup.py +53 -0
  123. package/sdk-python/tests/test_client.py +221 -0
  124. package/test-addr.js +29 -0
  125. package/test-mnee-simple.js +51 -0
  126. 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
+ )