agent-mcp 0.1.3__py3-none-any.whl → 0.1.5__py3-none-any.whl

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 (57) hide show
  1. agent_mcp/__init__.py +66 -12
  2. agent_mcp/a2a_protocol.py +316 -0
  3. agent_mcp/agent_lightning_library.py +214 -0
  4. agent_mcp/camel_mcp_adapter.py +521 -0
  5. agent_mcp/claude_mcp_adapter.py +195 -0
  6. agent_mcp/cli.py +47 -0
  7. agent_mcp/google_ai_mcp_adapter.py +183 -0
  8. agent_mcp/heterogeneous_group_chat.py +412 -38
  9. agent_mcp/langchain_mcp_adapter.py +176 -43
  10. agent_mcp/llamaindex_mcp_adapter.py +410 -0
  11. agent_mcp/mcp_agent.py +26 -0
  12. agent_mcp/mcp_transport.py +11 -5
  13. agent_mcp/microsoft_agent_framework.py +591 -0
  14. agent_mcp/missing_frameworks.py +435 -0
  15. agent_mcp/openapi_protocol.py +616 -0
  16. agent_mcp/payments.py +804 -0
  17. agent_mcp/pydantic_ai_mcp_adapter.py +628 -0
  18. agent_mcp/registry.py +768 -0
  19. agent_mcp/security.py +864 -0
  20. {agent_mcp-0.1.3.dist-info → agent_mcp-0.1.5.dist-info}/METADATA +173 -49
  21. agent_mcp-0.1.5.dist-info/RECORD +62 -0
  22. {agent_mcp-0.1.3.dist-info → agent_mcp-0.1.5.dist-info}/WHEEL +1 -1
  23. agent_mcp-0.1.5.dist-info/entry_points.txt +4 -0
  24. agent_mcp-0.1.5.dist-info/top_level.txt +3 -0
  25. demos/__init__.py +1 -0
  26. demos/basic/__init__.py +1 -0
  27. demos/basic/framework_examples.py +108 -0
  28. demos/basic/langchain_camel_demo.py +272 -0
  29. demos/basic/simple_chat.py +355 -0
  30. demos/basic/simple_integration_example.py +51 -0
  31. demos/collaboration/collaborative_task_example.py +437 -0
  32. demos/collaboration/group_chat_example.py +130 -0
  33. demos/collaboration/simplified_crewai_example.py +39 -0
  34. demos/comprehensive_framework_demo.py +202 -0
  35. demos/langgraph/autonomous_langgraph_network.py +808 -0
  36. demos/langgraph/langgraph_agent_network.py +415 -0
  37. demos/langgraph/langgraph_collaborative_task.py +619 -0
  38. demos/langgraph/langgraph_example.py +227 -0
  39. demos/langgraph/run_langgraph_examples.py +213 -0
  40. demos/network/agent_network_example.py +381 -0
  41. demos/network/email_agent.py +130 -0
  42. demos/network/email_agent_demo.py +46 -0
  43. demos/network/heterogeneous_network_example.py +216 -0
  44. demos/network/multi_framework_example.py +199 -0
  45. demos/utils/check_imports.py +49 -0
  46. demos/workflows/autonomous_agent_workflow.py +248 -0
  47. demos/workflows/mcp_features_demo.py +353 -0
  48. demos/workflows/run_agent_collaboration_demo.py +63 -0
  49. demos/workflows/run_agent_collaboration_with_logs.py +396 -0
  50. demos/workflows/show_agent_interactions.py +107 -0
  51. demos/workflows/simplified_autonomous_demo.py +74 -0
  52. functions/main.py +144 -0
  53. functions/mcp_network_server.py +513 -0
  54. functions/utils.py +47 -0
  55. agent_mcp-0.1.3.dist-info/RECORD +0 -18
  56. agent_mcp-0.1.3.dist-info/entry_points.txt +0 -2
  57. agent_mcp-0.1.3.dist-info/top_level.txt +0 -1
agent_mcp/payments.py ADDED
@@ -0,0 +1,804 @@
1
+ """
2
+ Hybrid Payment Gateway for AgentMCP
3
+ Business-friendly payment system supporting both traditional fiat and cryptocurrency
4
+
5
+ This module provides a comprehensive payment solution supporting:
6
+ - Traditional fiat payments via Stripe Connect
7
+ - Cryptocurrency payments via USDC on Base
8
+ - x402 Protocol for agent micropayments
9
+ - Agent Payments Protocol (AP2) integration
10
+ - Escrow services for task completion assurance
11
+ """
12
+
13
+ import asyncio
14
+ import json
15
+ import uuid
16
+ import hashlib
17
+ import time
18
+ from datetime import datetime, timezone, timedelta
19
+ from typing import Dict, Any, List, Optional, Union, Tuple
20
+ from dataclasses import dataclass, asdict
21
+ from enum import Enum
22
+ import logging
23
+ import aiohttp
24
+ from decimal import Decimal, ROUND_HALF_UP
25
+
26
+ logger = logging.getLogger(__name__)
27
+
28
+ # Try to import payment libraries
29
+ try:
30
+ import stripe
31
+ STRIPE_AVAILABLE = True
32
+ except ImportError:
33
+ STRIPE_AVAILABLE = False
34
+ stripe = None
35
+ logger.warning("Stripe not available. Install with: pip install stripe")
36
+
37
+ try:
38
+ from web3 import Web3
39
+ from web3.middleware import geth_poa_middleware
40
+ from eth_account import Account
41
+ WEB3_AVAILABLE = True
42
+ except ImportError:
43
+ WEB3_AVAILABLE = False
44
+ Web3 = None
45
+ Account = None
46
+ logger.warning("Web3 not available. Install with: pip install web3")
47
+
48
+ class PaymentMethod(Enum):
49
+ """Supported payment methods"""
50
+ STRIPE = "stripe"
51
+ USDC = "usdc"
52
+ X402 = "x402"
53
+ AP2 = "ap2"
54
+ ESCROW = "escrow"
55
+
56
+ class PaymentStatus(Enum):
57
+ """Payment status tracking"""
58
+ PENDING = "pending"
59
+ PROCESSING = "processing"
60
+ COMPLETED = "completed"
61
+ FAILED = "failed"
62
+ REFUNDED = "refunded"
63
+ ESCROWED = "escrowed"
64
+
65
+ @dataclass
66
+ class PaymentRequest:
67
+ """Standardized payment request structure"""
68
+ sender_agent_id: str
69
+ receiver_agent_id: str
70
+ amount: float
71
+ currency: str = "USD"
72
+ task_id: str = None
73
+ description: str = ""
74
+ payment_method: PaymentMethod = PaymentMethod.STRIPE
75
+ metadata: Dict[str, Any] = None
76
+
77
+ def __post_init__(self):
78
+ if self.metadata is None:
79
+ self.metadata = {}
80
+ if self.task_id is None:
81
+ self.task_id = str(uuid.uuid4())
82
+
83
+ @dataclass
84
+ class PaymentResponse:
85
+ """Standardized payment response structure"""
86
+ payment_id: str
87
+ status: PaymentStatus
88
+ amount: float
89
+ currency: str
90
+ sender_agent_id: str
91
+ receiver_agent_id: str
92
+ transaction_id: str = None
93
+ block_number: int = None
94
+ created_at: str = None
95
+ completed_at: str = None
96
+ fee: float = 0.0
97
+ error_message: str = None
98
+ metadata: Dict[str, Any] = None
99
+
100
+ def __post_init__(self):
101
+ if self.metadata is None:
102
+ self.metadata = {}
103
+ if self.created_at is None:
104
+ self.created_at = datetime.now(timezone.utc).isoformat()
105
+
106
+ class StripePaymentGateway:
107
+ """Stripe Connect integration for fiat payments"""
108
+
109
+ def __init__(self, api_key: str, webhook_secret: str = None):
110
+ if not STRIPE_AVAILABLE:
111
+ raise ImportError("Stripe is not installed")
112
+
113
+ stripe.api_key = api_key
114
+ self.api_key = api_key
115
+ self.webhook_secret = webhook_secret
116
+ self.agent_accounts = {}
117
+
118
+ async def create_agent_account(
119
+ self,
120
+ agent_id: str,
121
+ email: str,
122
+ business_name: str = None
123
+ ) -> Dict[str, Any]:
124
+ """Create a Stripe Connect account for an agent"""
125
+ try:
126
+ # Check if account already exists
127
+ if agent_id in self.agent_accounts:
128
+ return {
129
+ "status": "success",
130
+ "account_id": self.agent_accounts[agent_id]["account_id"],
131
+ "message": "Existing account retrieved"
132
+ }
133
+
134
+ # Create Express account
135
+ account_data = {
136
+ "type": "express",
137
+ "country": "US",
138
+ "email": email,
139
+ "capabilities": ["card_payments", "transfers"],
140
+ "business_type": "individual",
141
+ "metadata": {
142
+ "agent_id": agent_id,
143
+ "created_by": "AgentMCP"
144
+ }
145
+ }
146
+
147
+ if business_name:
148
+ account_data["business_profile"] = {
149
+ "name": business_name,
150
+ "product_description": "AI Agent Services"
151
+ }
152
+
153
+ account = stripe.Account.create(**account_data)
154
+
155
+ # Store account info
156
+ self.agent_accounts[agent_id] = {
157
+ "account_id": account.id,
158
+ "email": email,
159
+ "business_name": business_name,
160
+ "created_at": datetime.now(timezone.utc).isoformat()
161
+ }
162
+
163
+ return {
164
+ "status": "success",
165
+ "account_id": account.id,
166
+ "account_link": account.get("account_links", [{}])[0].get("url"),
167
+ "message": "Stripe Connect account created successfully"
168
+ }
169
+
170
+ except Exception as e:
171
+ logger.error(f"Error creating Stripe account for agent {agent_id}: {e}")
172
+ return {
173
+ "status": "error",
174
+ "message": str(e)
175
+ }
176
+
177
+ async def process_payment(self, request: PaymentRequest) -> PaymentResponse:
178
+ """Process a fiat payment via Stripe"""
179
+ try:
180
+ # Get receiver's Stripe account
181
+ receiver_account = await self._get_agent_account(request.receiver_agent_id)
182
+ if not receiver_account:
183
+ return PaymentResponse(
184
+ payment_id=str(uuid.uuid4()),
185
+ status=PaymentStatus.FAILED,
186
+ amount=request.amount,
187
+ currency=request.currency,
188
+ sender_agent_id=request.sender_agent_id,
189
+ receiver_agent_id=request.receiver_agent_id,
190
+ error_message="Receiver account not found"
191
+ )
192
+
193
+ # Create payment intent with transfer
194
+ amount_cents = int(request.amount * 100) # Convert to cents
195
+
196
+ payment_intent = stripe.PaymentIntent.create(
197
+ amount=amount_cents,
198
+ currency=request.currency.lower(),
199
+ transfer_data={
200
+ "destination": receiver_account["account_id"],
201
+ "amount": amount_cents - int(self._calculate_fee(request.amount) * 100) # Subtract fee
202
+ },
203
+ metadata={
204
+ "sender_agent_id": request.sender_agent_id,
205
+ "receiver_agent_id": request.receiver_agent_id,
206
+ "task_id": request.task_id,
207
+ "agentmcp_payment": "true"
208
+ },
209
+ description=request.description or f"Payment from {request.sender_agent_id} to {request.receiver_agent_id}"
210
+ )
211
+
212
+ return PaymentResponse(
213
+ payment_id=payment_intent.id,
214
+ status=self._convert_stripe_status(payment_intent.status),
215
+ amount=request.amount,
216
+ currency=request.currency,
217
+ sender_agent_id=request.sender_agent_id,
218
+ receiver_agent_id=request.receiver_agent_id,
219
+ transaction_id=payment_intent.charges.data[0].id if payment_intent.charges.data else None,
220
+ fee=self._calculate_fee(request.amount),
221
+ created_at=datetime.fromtimestamp(payment_intent.created, timezone.utc).isoformat()
222
+ )
223
+
224
+ except Exception as e:
225
+ logger.error(f"Error processing Stripe payment: {e}")
226
+ return PaymentResponse(
227
+ payment_id=str(uuid.uuid4()),
228
+ status=PaymentStatus.FAILED,
229
+ amount=request.amount,
230
+ currency=request.currency,
231
+ sender_agent_id=request.sender_agent_id,
232
+ receiver_agent_id=request.receiver_agent_id,
233
+ error_message=str(e)
234
+ )
235
+
236
+ async def create_escrow_payment(self, request: PaymentRequest) -> PaymentResponse:
237
+ """Create an escrow payment held until task completion"""
238
+ try:
239
+ amount_cents = int(request.amount * 100)
240
+
241
+ # Create payment intent with manual capture (authorization only)
242
+ payment_intent = stripe.PaymentIntent.create(
243
+ amount=amount_cents,
244
+ currency=request.currency.lower(),
245
+ capture_method="manual", # Don't capture immediately
246
+ metadata={
247
+ "sender_agent_id": request.sender_agent_id,
248
+ "receiver_agent_id": request.receiver_agent_id,
249
+ "task_id": request.task_id,
250
+ "escrow": "true",
251
+ "agentmcp_payment": "true"
252
+ },
253
+ description=f"Escrow payment for task {request.task_id}"
254
+ )
255
+
256
+ return PaymentResponse(
257
+ payment_id=payment_intent.id,
258
+ status=PaymentStatus.ESCROWED,
259
+ amount=request.amount,
260
+ currency=request.currency,
261
+ sender_agent_id=request.sender_agent_id,
262
+ receiver_agent_id=request.receiver_agent_id,
263
+ transaction_id=payment_intent.charges.data[0].id if payment_intent.charges.data else None,
264
+ created_at=datetime.fromtimestamp(payment_intent.created, timezone.utc).isoformat(),
265
+ metadata={"escrow_release_required": True}
266
+ )
267
+
268
+ except Exception as e:
269
+ logger.error(f"Error creating Stripe escrow: {e}")
270
+ return PaymentResponse(
271
+ payment_id=str(uuid.uuid4()),
272
+ status=PaymentStatus.FAILED,
273
+ amount=request.amount,
274
+ currency=request.currency,
275
+ sender_agent_id=request.sender_agent_id,
276
+ receiver_agent_id=request.receiver_agent_id,
277
+ error_message=str(e)
278
+ )
279
+
280
+ async def release_escrow(self, payment_id: str) -> PaymentResponse:
281
+ """Release captured escrow payment"""
282
+ try:
283
+ # Retrieve the payment intent
284
+ payment_intent = stripe.PaymentIntent.retrieve(payment_id)
285
+
286
+ if payment_intent.status != "requires_capture":
287
+ return PaymentResponse(
288
+ payment_id=payment_id,
289
+ status=PaymentStatus.FAILED,
290
+ amount=payment_intent.amount / 100,
291
+ currency=payment_intent.currency.upper(),
292
+ sender_agent_id=payment_intent.metadata.get("sender_agent_id"),
293
+ receiver_agent_id=payment_intent.metadata.get("receiver_agent_id"),
294
+ error_message="Payment is not in escrow"
295
+ )
296
+
297
+ # Capture the payment
298
+ captured_payment = stripe.PaymentIntent.capture(payment_id)
299
+
300
+ return PaymentResponse(
301
+ payment_id=payment_id,
302
+ status=self._convert_stripe_status(captured_payment.status),
303
+ amount=captured_payment.amount / 100,
304
+ currency=captured_payment.currency.upper(),
305
+ sender_agent_id=payment_intent.metadata.get("sender_agent_id"),
306
+ receiver_agent_id=payment_intent.metadata.get("receiver_agent_id"),
307
+ transaction_id=captured_payment.charges.data[0].id if captured_payment.charges.data else None,
308
+ completed_at=datetime.fromtimestamp(captured_payment.created, timezone.utc).isoformat()
309
+ )
310
+
311
+ except Exception as e:
312
+ logger.error(f"Error releasing escrow {payment_id}: {e}")
313
+ return PaymentResponse(
314
+ payment_id=payment_id,
315
+ status=PaymentStatus.FAILED,
316
+ amount=0,
317
+ currency="USD",
318
+ sender_agent_id="unknown",
319
+ receiver_agent_id="unknown",
320
+ error_message=str(e)
321
+ )
322
+
323
+ async def _get_agent_account(self, agent_id: str) -> Optional[Dict[str, Any]]:
324
+ """Get Stripe account info for an agent"""
325
+ return self.agent_accounts.get(agent_id)
326
+
327
+ def _calculate_fee(self, amount: float) -> float:
328
+ """Calculate payment fee (2.9% + $0.30)"""
329
+ return round((amount * 0.029) + 0.30, 2)
330
+
331
+ def _convert_stripe_status(self, stripe_status: str) -> PaymentStatus:
332
+ """Convert Stripe status to PaymentStatus"""
333
+ status_mapping = {
334
+ "requires_payment_method": PaymentStatus.PENDING,
335
+ "requires_confirmation": PaymentStatus.PENDING,
336
+ "requires_action": PaymentStatus.PROCESSING,
337
+ "processing": PaymentStatus.PROCESSING,
338
+ "succeeded": PaymentStatus.COMPLETED,
339
+ "canceled": PaymentStatus.FAILED
340
+ }
341
+ return status_mapping.get(stripe_status, PaymentStatus.FAILED)
342
+
343
+ class USDCPaymentGateway:
344
+ """USDC payment gateway on Base blockchain"""
345
+
346
+ def __init__(self, rpc_url: str, private_key: str, usdc_contract: str = None):
347
+ if not WEB3_AVAILABLE:
348
+ raise ImportError("Web3 is not installed")
349
+
350
+ self.w3 = Web3(Web3.HTTPProvider(rpc_url))
351
+ self.private_key = private_key
352
+ self.account = Account.from_key(private_key)
353
+ self.address = self.account.address
354
+
355
+ # USDC contract on Base
356
+ self.usdc_address = usdc_contract or "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913"
357
+
358
+ # USDC ABI (minimal for transfers)
359
+ self.usdc_abi = [
360
+ {
361
+ "constant": False,
362
+ "inputs": [
363
+ {"name": "_to", "type": "address"},
364
+ {"name": "_value", "type": "uint256"}
365
+ ],
366
+ "name": "transfer",
367
+ "outputs": [{"name": "", "type": "bool"}],
368
+ "payable": False,
369
+ "stateMutability": "nonpayable",
370
+ "type": "function"
371
+ }
372
+ ]
373
+
374
+ self.contract = self.w3.eth.contract(
375
+ address=self.usdc_address,
376
+ abi=self.usdc_abi
377
+ )
378
+
379
+ self.agent_wallets = {}
380
+
381
+ async def create_agent_wallet(self, agent_id: str) -> Dict[str, Any]:
382
+ """Create or derive a wallet for an agent"""
383
+ try:
384
+ if agent_id in self.agent_wallets:
385
+ return {
386
+ "status": "success",
387
+ "address": self.agent_wallets[agent_id]["address"],
388
+ "message": "Existing wallet retrieved"
389
+ }
390
+
391
+ # Derive deterministic wallet from agent_id
392
+ # In production, use HD wallet derivation
393
+ wallet_seed = hashlib.sha256(f"agent:{agent_id}:{self.private_key}".encode()).hexdigest()
394
+ agent_wallet = Account.from_key(wallet_seed)
395
+
396
+ self.agent_wallets[agent_id] = {
397
+ "address": agent_wallet.address,
398
+ "private_key": wallet_seed,
399
+ "created_at": datetime.now(timezone.utc).isoformat()
400
+ }
401
+
402
+ return {
403
+ "status": "success",
404
+ "address": agent_wallet.address,
405
+ "message": "Agent wallet created successfully"
406
+ }
407
+
408
+ except Exception as e:
409
+ logger.error(f"Error creating agent wallet: {e}")
410
+ return {
411
+ "status": "error",
412
+ "message": str(e)
413
+ }
414
+
415
+ async def process_payment(self, request: PaymentRequest) -> PaymentResponse:
416
+ """Process USDC payment on Base"""
417
+ try:
418
+ # Get receiver wallet
419
+ receiver_wallet = await self._get_agent_wallet(request.receiver_agent_id)
420
+ if not receiver_wallet:
421
+ return PaymentResponse(
422
+ payment_id=str(uuid.uuid4()),
423
+ status=PaymentStatus.FAILED,
424
+ amount=request.amount,
425
+ currency="USDC",
426
+ sender_agent_id=request.sender_agent_id,
427
+ receiver_agent_id=request.receiver_agent_id,
428
+ error_message="Receiver wallet not found"
429
+ )
430
+
431
+ # Convert USDC amount (6 decimals)
432
+ amount_usdc = int(request.amount * 1e6)
433
+
434
+ # Get current gas price
435
+ gas_price = self.w3.eth.gas_price
436
+ gas_limit = 100000 # Estimate for ERC20 transfer
437
+ gas_cost = self.w3.from_wei(gas_price * gas_limit, 'ether')
438
+
439
+ # Build transaction
440
+ transaction = {
441
+ 'from': self.address,
442
+ 'to': self.usdc_address,
443
+ 'gas': gas_limit,
444
+ 'gasPrice': gas_price,
445
+ 'nonce': self.w3.eth.get_transaction_count(self.address),
446
+ 'data': self.contract.encodeABI(
447
+ fn_name='transfer',
448
+ args=[receiver_wallet["address"], amount_usdc]
449
+ )
450
+ }
451
+
452
+ # Sign and send transaction
453
+ signed_txn = self.w3.eth.account.sign_transaction(transaction, self.private_key)
454
+ tx_hash = self.w3.eth.send_raw_transaction(signed_txn.rawTransaction)
455
+
456
+ # Wait for confirmation
457
+ receipt = self.w3.eth.wait_for_transaction_receipt(tx_hash, timeout=120)
458
+
459
+ if receipt.status == 1:
460
+ return PaymentResponse(
461
+ payment_id=tx_hash.hex(),
462
+ status=PaymentStatus.COMPLETED,
463
+ amount=request.amount,
464
+ currency="USDC",
465
+ sender_agent_id=request.sender_agent_id,
466
+ receiver_agent_id=request.receiver_agent_id,
467
+ transaction_id=tx_hash.hex(),
468
+ block_number=receipt.blockNumber,
469
+ completed_at=datetime.now(timezone.utc).isoformat(),
470
+ fee=float(gas_cost)
471
+ )
472
+ else:
473
+ return PaymentResponse(
474
+ payment_id=tx_hash.hex(),
475
+ status=PaymentStatus.FAILED,
476
+ amount=request.amount,
477
+ currency="USDC",
478
+ sender_agent_id=request.sender_agent_id,
479
+ receiver_agent_id=request.receiver_agent_id,
480
+ transaction_id=tx_hash.hex(),
481
+ error_message="Transaction failed on blockchain"
482
+ )
483
+
484
+ except Exception as e:
485
+ logger.error(f"Error processing USDC payment: {e}")
486
+ return PaymentResponse(
487
+ payment_id=str(uuid.uuid4()),
488
+ status=PaymentStatus.FAILED,
489
+ amount=request.amount,
490
+ currency="USDC",
491
+ sender_agent_id=request.sender_agent_id,
492
+ receiver_agent_id=request.receiver_agent_id,
493
+ error_message=str(e)
494
+ )
495
+
496
+ async def _get_agent_wallet(self, agent_id: str) -> Optional[Dict[str, Any]]:
497
+ """Get wallet info for an agent"""
498
+ return self.agent_wallets.get(agent_id)
499
+
500
+ class X402PaymentGateway:
501
+ """x402 Protocol implementation for HTTP 402 payments"""
502
+
503
+ def __init__(self, gateway_url: str, api_key: str = None):
504
+ self.gateway_url = gateway_url
505
+ self.api_key = api_key
506
+ self.session = None
507
+
508
+ async def __aenter__(self):
509
+ self.session = aiohttp.ClientSession()
510
+ return self
511
+
512
+ async def __aexit__(self, exc_type, exc_val, exc_tb):
513
+ if self.session:
514
+ await self.session.close()
515
+
516
+ async def process_payment(self, request: PaymentRequest) -> PaymentResponse:
517
+ """Process payment via x402 protocol"""
518
+ try:
519
+ if not self.session:
520
+ self.session = aiohttp.ClientSession()
521
+
522
+ # Create x402 payment header
523
+ payment_header = self._create_payment_header(request.amount)
524
+
525
+ # Build x402 request
526
+ x402_request = {
527
+ "receiver": request.receiver_agent_id,
528
+ "amount": str(request.amount),
529
+ "currency": request.currency,
530
+ "task_id": request.task_id,
531
+ "description": request.description,
532
+ "sender": request.sender_agent_id,
533
+ "timestamp": datetime.now(timezone.utc).isoformat()
534
+ }
535
+
536
+ headers = {
537
+ "X-402-Payment": payment_header,
538
+ "Content-Type": "application/json"
539
+ }
540
+
541
+ if self.api_key:
542
+ headers["Authorization"] = f"Bearer {self.api_key}"
543
+
544
+ async with self.session.post(
545
+ f"{self.gateway_url}/pay",
546
+ json=x402_request,
547
+ headers=headers
548
+ ) as response:
549
+ if response.status == 402: # Expected x402 response
550
+ result = await response.json()
551
+ return PaymentResponse(
552
+ payment_id=result.get("payment_id", str(uuid.uuid4())),
553
+ status=self._convert_x402_status(result.get("status", "pending")),
554
+ amount=request.amount,
555
+ currency=request.currency,
556
+ sender_agent_id=request.sender_agent_id,
557
+ receiver_agent_id=request.receiver_agent_id,
558
+ transaction_id=result.get("transaction_id"),
559
+ metadata={"x402_gateway": True}
560
+ )
561
+ else:
562
+ return PaymentResponse(
563
+ payment_id=str(uuid.uuid4()),
564
+ status=PaymentStatus.FAILED,
565
+ amount=request.amount,
566
+ currency=request.currency,
567
+ sender_agent_id=request.sender_agent_id,
568
+ receiver_agent_id=request.receiver_agent_id,
569
+ error_message=f"x402 gateway error: HTTP {response.status}"
570
+ )
571
+
572
+ except Exception as e:
573
+ logger.error(f"Error processing x402 payment: {e}")
574
+ return PaymentResponse(
575
+ payment_id=str(uuid.uuid4()),
576
+ status=PaymentStatus.FAILED,
577
+ amount=request.amount,
578
+ currency=request.currency,
579
+ sender_agent_id=request.sender_agent_id,
580
+ receiver_agent_id=request.receiver_agent_id,
581
+ error_message=str(e)
582
+ )
583
+
584
+ def _create_payment_header(self, amount: float) -> str:
585
+ """Create x402 payment header"""
586
+ # Simplified x402 header - in production use proper cryptographic signing
587
+ timestamp = str(int(time.time()))
588
+ amount_str = str(amount)
589
+ signature = hashlib.sha256(f"{amount_str}:{timestamp}:{self.api_key}".encode()).hexdigest()
590
+
591
+ return f"x402 amount={amount_str}, ts={timestamp}, sig={signature}"
592
+
593
+ def _convert_x402_status(self, x402_status: str) -> PaymentStatus:
594
+ """Convert x402 status to PaymentStatus"""
595
+ status_mapping = {
596
+ "pending": PaymentStatus.PENDING,
597
+ "processing": PaymentStatus.PROCESSING,
598
+ "completed": PaymentStatus.COMPLETED,
599
+ "failed": PaymentStatus.FAILED,
600
+ "refunded": PaymentStatus.REFUNDED
601
+ }
602
+ return status_mapping.get(x402_status, PaymentStatus.FAILED)
603
+
604
+ class HybridPaymentGateway:
605
+ """Unified payment gateway supporting multiple payment methods"""
606
+
607
+ def __init__(
608
+ self,
609
+ stripe_config: Dict[str, str] = None,
610
+ usdc_config: Dict[str, str] = None,
611
+ x402_config: Dict[str, str] = None
612
+ ):
613
+ # Initialize payment gateways
614
+ self.stripe_gateway = None
615
+ if stripe_config and stripe_config.get("api_key"):
616
+ self.stripe_gateway = StripePaymentGateway(
617
+ stripe_config["api_key"],
618
+ stripe_config.get("webhook_secret")
619
+ )
620
+
621
+ self.usdc_gateway = None
622
+ if usdc_config and usdc_config.get("rpc_url") and usdc_config.get("private_key"):
623
+ self.usdc_gateway = USDCPaymentGateway(
624
+ usdc_config["rpc_url"],
625
+ usdc_config["private_key"],
626
+ usdc_config.get("usdc_contract")
627
+ )
628
+
629
+ self.x402_gateway = None
630
+ if x402_config and x402_config.get("gateway_url"):
631
+ self.x402_gateway = X402PaymentGateway(
632
+ x402_config["gateway_url"],
633
+ x402_config.get("api_key")
634
+ )
635
+
636
+ self.payment_history = []
637
+
638
+ async def process_payment(self, request: PaymentRequest) -> PaymentResponse:
639
+ """Process payment using the specified method"""
640
+ try:
641
+ # Log payment attempt
642
+ logger.info(f"Processing payment: {request.sender_agent_id} -> {request.receiver_agent_id}, {request.amount} {request.currency} via {request.payment_method.value}")
643
+
644
+ # Route to appropriate gateway
645
+ if request.payment_method == PaymentMethod.STRIPE and self.stripe_gateway:
646
+ return await self.stripe_gateway.process_payment(request)
647
+ elif request.payment_method == PaymentMethod.USDC and self.usdc_gateway:
648
+ return await self.usdc_gateway.process_payment(request)
649
+ elif request.payment_method == PaymentMethod.X402 and self.x402_gateway:
650
+ async with self.x402_gateway as gateway:
651
+ return await gateway.process_payment(request)
652
+ else:
653
+ return PaymentResponse(
654
+ payment_id=str(uuid.uuid4()),
655
+ status=PaymentStatus.FAILED,
656
+ amount=request.amount,
657
+ currency=request.currency,
658
+ sender_agent_id=request.sender_agent_id,
659
+ receiver_agent_id=request.receiver_agent_id,
660
+ error_message=f"Payment method {request.payment_method.value} not supported or not configured"
661
+ )
662
+ except Exception as e:
663
+ logger.error(f"Error in hybrid payment gateway: {e}")
664
+ return PaymentResponse(
665
+ payment_id=str(uuid.uuid4()),
666
+ status=PaymentStatus.FAILED,
667
+ amount=request.amount,
668
+ currency=request.currency,
669
+ sender_agent_id=request.sender_agent_id,
670
+ receiver_agent_id=request.receiver_agent_id,
671
+ error_message=str(e)
672
+ )
673
+
674
+ async def create_escrow_payment(self, request: PaymentRequest) -> PaymentResponse:
675
+ """Create escrow payment (Stripe only for now)"""
676
+ if self.stripe_gateway:
677
+ return await self.stripe_gateway.create_escrow_payment(request)
678
+ else:
679
+ return PaymentResponse(
680
+ payment_id=str(uuid.uuid4()),
681
+ status=PaymentStatus.FAILED,
682
+ amount=request.amount,
683
+ currency=request.currency,
684
+ sender_agent_id=request.sender_agent_id,
685
+ receiver_agent_id=request.receiver_agent_id,
686
+ error_message="Escrow not supported for this payment method"
687
+ )
688
+
689
+ async def release_escrow(self, payment_id: str) -> PaymentResponse:
690
+ """Release escrow payment"""
691
+ if self.stripe_gateway:
692
+ return await self.stripe_gateway.release_escrow(payment_id)
693
+ else:
694
+ return PaymentResponse(
695
+ payment_id=payment_id,
696
+ status=PaymentStatus.FAILED,
697
+ amount=0,
698
+ currency="USD",
699
+ sender_agent_id="unknown",
700
+ receiver_agent_id="unknown",
701
+ error_message="Escrow release not supported"
702
+ )
703
+
704
+ async def setup_agent_accounts(
705
+ self,
706
+ agent_id: str,
707
+ email: str = None,
708
+ business_name: str = None
709
+ ) -> Dict[str, Any]:
710
+ """Setup payment accounts for an agent across all gateways"""
711
+ results = {}
712
+
713
+ # Setup Stripe account
714
+ if self.stripe_gateway:
715
+ stripe_result = await self.stripe_gateway.create_agent_account(
716
+ agent_id, email or f"{agent_id}@agentmcp.com", business_name
717
+ )
718
+ results["stripe"] = stripe_result
719
+
720
+ # Setup USDC wallet
721
+ if self.usdc_gateway:
722
+ usdc_result = await self.usdc_gateway.create_agent_wallet(agent_id)
723
+ results["usdc"] = usdc_result
724
+
725
+ # Store results
726
+ self.payment_history.append({
727
+ "agent_id": agent_id,
728
+ "action": "account_setup",
729
+ "timestamp": datetime.now(timezone.utc).isoformat(),
730
+ "results": results
731
+ })
732
+
733
+ return {
734
+ "status": "success",
735
+ "agent_id": agent_id,
736
+ "accounts": results
737
+ }
738
+
739
+ def get_supported_methods(self) -> List[Dict[str, Any]]:
740
+ """Get list of supported payment methods"""
741
+ methods = []
742
+
743
+ if self.stripe_gateway:
744
+ methods.append({
745
+ "method": PaymentMethod.STRIPE.value,
746
+ "display_name": "Stripe (Fiat)",
747
+ "currencies": ["USD", "EUR", "GBP"],
748
+ "fees": "2.9% + $0.30",
749
+ "escrow_supported": True,
750
+ "min_amount": 0.50,
751
+ "max_amount": 999999.99
752
+ })
753
+
754
+ if self.usdc_gateway:
755
+ methods.append({
756
+ "method": PaymentMethod.USDC.value,
757
+ "display_name": "USDC (Base Blockchain)",
758
+ "currencies": ["USDC"],
759
+ "fees": "~$0.01 gas",
760
+ "escrow_supported": False,
761
+ "min_amount": 0.01,
762
+ "max_amount": None
763
+ })
764
+
765
+ if self.x402_gateway:
766
+ methods.append({
767
+ "method": PaymentMethod.X402.value,
768
+ "display_name": "x402 Protocol",
769
+ "currencies": ["USD", "USDC", "EUR"],
770
+ "fees": "Varies by provider",
771
+ "escrow_supported": False,
772
+ "min_amount": 0.01,
773
+ "max_amount": None
774
+ })
775
+
776
+ return methods
777
+
778
+ async def get_payment_history(
779
+ self,
780
+ agent_id: str = None,
781
+ limit: int = 50
782
+ ) -> List[Dict[str, Any]]:
783
+ """Get payment history"""
784
+ history = self.payment_history
785
+
786
+ if agent_id:
787
+ history = [
788
+ record for record in history
789
+ if record.get("sender_agent_id") == agent_id or record.get("receiver_agent_id") == agent_id
790
+ ]
791
+
792
+ return sorted(history, key=lambda x: x.get("timestamp", ""), reverse=True)[:limit]
793
+
794
+ # Export classes for easy importing
795
+ __all__ = [
796
+ 'PaymentMethod',
797
+ 'PaymentStatus',
798
+ 'PaymentRequest',
799
+ 'PaymentResponse',
800
+ 'StripePaymentGateway',
801
+ 'USDCPaymentGateway',
802
+ 'X402PaymentGateway',
803
+ 'HybridPaymentGateway'
804
+ ]