feat: add marketplace metrics, privacy features, and service registry endpoints
- Add Prometheus metrics for marketplace API throughput and error rates with new dashboard panels - Implement confidential transaction models with encryption support and access control - Add key management system with registration, rotation, and audit logging - Create services and registry routers for service discovery and management - Integrate ZK proof generation for privacy-preserving receipts - Add metrics instru
This commit is contained in:
@ -0,0 +1,489 @@
|
||||
"""
|
||||
Stripe payment connector for AITBC Enterprise
|
||||
"""
|
||||
|
||||
import asyncio
|
||||
import logging
|
||||
from typing import Dict, Any, Optional, List
|
||||
from datetime import datetime, timedelta
|
||||
import stripe
|
||||
|
||||
from ..base import BaseConnector, OperationResult, Transaction
|
||||
from ..core import ConnectorConfig
|
||||
from .base import PaymentConnector, PaymentMethod, Charge, Refund, Subscription
|
||||
from ..exceptions import PaymentError, ValidationError
|
||||
|
||||
|
||||
class StripeConnector(PaymentConnector):
|
||||
"""Stripe payment processor connector"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
client: 'AITBCClient',
|
||||
config: ConnectorConfig,
|
||||
stripe_api_key: str,
|
||||
webhook_secret: Optional[str] = None
|
||||
):
|
||||
super().__init__(client, config)
|
||||
|
||||
# Stripe configuration
|
||||
self.stripe_api_key = stripe_api_key
|
||||
self.webhook_secret = webhook_secret
|
||||
|
||||
# Initialize Stripe client
|
||||
stripe.api_key = stripe_api_key
|
||||
stripe.api_version = "2023-10-16"
|
||||
|
||||
# Stripe-specific configuration
|
||||
self._stripe_config = {
|
||||
"api_key": stripe_api_key,
|
||||
"api_version": stripe.api_version,
|
||||
"connect_timeout": config.timeout,
|
||||
"read_timeout": config.timeout
|
||||
}
|
||||
|
||||
async def _initialize(self) -> None:
|
||||
"""Initialize Stripe connector"""
|
||||
try:
|
||||
# Test Stripe connection
|
||||
await self._test_stripe_connection()
|
||||
|
||||
# Set up webhook handler
|
||||
if self.webhook_secret:
|
||||
await self._setup_webhook_handler()
|
||||
|
||||
self.logger.info("Stripe connector initialized")
|
||||
|
||||
except Exception as e:
|
||||
raise PaymentError(f"Failed to initialize Stripe: {e}")
|
||||
|
||||
async def _cleanup(self) -> None:
|
||||
"""Cleanup Stripe connector"""
|
||||
# No specific cleanup needed for Stripe
|
||||
pass
|
||||
|
||||
async def _execute_operation(
|
||||
self,
|
||||
operation: str,
|
||||
data: Dict[str, Any],
|
||||
**kwargs
|
||||
) -> OperationResult:
|
||||
"""Execute Stripe-specific operations"""
|
||||
try:
|
||||
if operation == "create_charge":
|
||||
return await self._create_charge(data)
|
||||
elif operation == "create_refund":
|
||||
return await self._create_refund(data)
|
||||
elif operation == "create_payment_method":
|
||||
return await self._create_payment_method(data)
|
||||
elif operation == "create_customer":
|
||||
return await self._create_customer(data)
|
||||
elif operation == "create_subscription":
|
||||
return await self._create_subscription(data)
|
||||
elif operation == "cancel_subscription":
|
||||
return await self._cancel_subscription(data)
|
||||
elif operation == "retrieve_balance":
|
||||
return await self._retrieve_balance()
|
||||
else:
|
||||
raise ValidationError(f"Unknown operation: {operation}")
|
||||
|
||||
except stripe.error.StripeError as e:
|
||||
self.logger.error(f"Stripe error: {e}")
|
||||
return OperationResult(
|
||||
success=False,
|
||||
error=str(e),
|
||||
metadata={"stripe_error_code": getattr(e, 'code', None)}
|
||||
)
|
||||
except Exception as e:
|
||||
self.logger.error(f"Operation failed: {e}")
|
||||
return OperationResult(
|
||||
success=False,
|
||||
error=str(e)
|
||||
)
|
||||
|
||||
async def create_charge(
|
||||
self,
|
||||
amount: int,
|
||||
currency: str,
|
||||
source: str,
|
||||
description: Optional[str] = None,
|
||||
metadata: Optional[Dict[str, Any]] = None
|
||||
) -> Charge:
|
||||
"""Create a charge"""
|
||||
result = await self.execute_operation(
|
||||
"create_charge",
|
||||
{
|
||||
"amount": amount,
|
||||
"currency": currency,
|
||||
"source": source,
|
||||
"description": description,
|
||||
"metadata": metadata or {}
|
||||
}
|
||||
)
|
||||
|
||||
if not result.success:
|
||||
raise PaymentError(result.error)
|
||||
|
||||
return Charge.from_stripe_charge(result.data)
|
||||
|
||||
async def create_refund(
|
||||
self,
|
||||
charge_id: str,
|
||||
amount: Optional[int] = None,
|
||||
reason: Optional[str] = None
|
||||
) -> Refund:
|
||||
"""Create a refund"""
|
||||
result = await self.execute_operation(
|
||||
"create_refund",
|
||||
{
|
||||
"charge": charge_id,
|
||||
"amount": amount,
|
||||
"reason": reason
|
||||
}
|
||||
)
|
||||
|
||||
if not result.success:
|
||||
raise PaymentError(result.error)
|
||||
|
||||
return Refund.from_stripe_refund(result.data)
|
||||
|
||||
async def create_payment_method(
|
||||
self,
|
||||
type: str,
|
||||
card: Dict[str, Any],
|
||||
metadata: Optional[Dict[str, Any]] = None
|
||||
) -> PaymentMethod:
|
||||
"""Create a payment method"""
|
||||
result = await self.execute_operation(
|
||||
"create_payment_method",
|
||||
{
|
||||
"type": type,
|
||||
"card": card,
|
||||
"metadata": metadata or {}
|
||||
}
|
||||
)
|
||||
|
||||
if not result.success:
|
||||
raise PaymentError(result.error)
|
||||
|
||||
return PaymentMethod.from_stripe_payment_method(result.data)
|
||||
|
||||
async def create_subscription(
|
||||
self,
|
||||
customer: str,
|
||||
items: List[Dict[str, Any]],
|
||||
metadata: Optional[Dict[str, Any]] = None
|
||||
) -> Subscription:
|
||||
"""Create a subscription"""
|
||||
result = await self.execute_operation(
|
||||
"create_subscription",
|
||||
{
|
||||
"customer": customer,
|
||||
"items": items,
|
||||
"metadata": metadata or {}
|
||||
}
|
||||
)
|
||||
|
||||
if not result.success:
|
||||
raise PaymentError(result.error)
|
||||
|
||||
return Subscription.from_stripe_subscription(result.data)
|
||||
|
||||
async def cancel_subscription(
|
||||
self,
|
||||
subscription_id: str,
|
||||
at_period_end: bool = True
|
||||
) -> Subscription:
|
||||
"""Cancel a subscription"""
|
||||
result = await self.execute_operation(
|
||||
"cancel_subscription",
|
||||
{
|
||||
"subscription": subscription_id,
|
||||
"at_period_end": at_period_end
|
||||
}
|
||||
)
|
||||
|
||||
if not result.success:
|
||||
raise PaymentError(result.error)
|
||||
|
||||
return Subscription.from_stripe_subscription(result.data)
|
||||
|
||||
async def retrieve_balance(self) -> Dict[str, Any]:
|
||||
"""Retrieve account balance"""
|
||||
result = await self.execute_operation("retrieve_balance", {})
|
||||
|
||||
if not result.success:
|
||||
raise PaymentError(result.error)
|
||||
|
||||
return result.data
|
||||
|
||||
async def verify_webhook(self, payload: bytes, signature: str) -> bool:
|
||||
"""Verify Stripe webhook signature"""
|
||||
try:
|
||||
stripe.WebhookSignature.verify_header(
|
||||
payload,
|
||||
signature,
|
||||
self.webhook_secret,
|
||||
300
|
||||
)
|
||||
return True
|
||||
except stripe.error.SignatureVerificationError:
|
||||
return False
|
||||
|
||||
async def handle_webhook(self, payload: bytes) -> Dict[str, Any]:
|
||||
"""Handle Stripe webhook"""
|
||||
try:
|
||||
event = stripe.Webhook.construct_event(
|
||||
payload,
|
||||
None, # Already verified
|
||||
self.webhook_secret,
|
||||
300
|
||||
)
|
||||
|
||||
# Process event based on type
|
||||
result = await self._process_webhook_event(event)
|
||||
|
||||
return {
|
||||
"processed": True,
|
||||
"event_type": event.type,
|
||||
"event_id": event.id,
|
||||
"result": result
|
||||
}
|
||||
|
||||
except Exception as e:
|
||||
self.logger.error(f"Webhook processing failed: {e}")
|
||||
return {
|
||||
"processed": False,
|
||||
"error": str(e)
|
||||
}
|
||||
|
||||
# Private methods
|
||||
|
||||
async def _test_stripe_connection(self):
|
||||
"""Test Stripe API connection"""
|
||||
try:
|
||||
# Use asyncio to run in thread
|
||||
loop = asyncio.get_event_loop()
|
||||
await loop.run_in_executor(None, stripe.Balance.retrieve)
|
||||
except Exception as e:
|
||||
raise PaymentError(f"Stripe connection test failed: {e}")
|
||||
|
||||
async def _setup_webhook_handler(self):
|
||||
"""Setup webhook handler"""
|
||||
# Register webhook verification with base connector
|
||||
self.add_operation_handler("webhook.verified", self._handle_verified_webhook)
|
||||
|
||||
async def _create_charge(self, data: Dict[str, Any]) -> OperationResult:
|
||||
"""Create Stripe charge"""
|
||||
loop = asyncio.get_event_loop()
|
||||
|
||||
try:
|
||||
charge = await loop.run_in_executor(
|
||||
None,
|
||||
lambda: stripe.Charge.create(**data)
|
||||
)
|
||||
|
||||
return OperationResult(
|
||||
success=True,
|
||||
data=charge.to_dict(),
|
||||
metadata={"charge_id": charge.id}
|
||||
)
|
||||
|
||||
except Exception as e:
|
||||
raise PaymentError(f"Failed to create charge: {e}")
|
||||
|
||||
async def _create_refund(self, data: Dict[str, Any]) -> OperationResult:
|
||||
"""Create Stripe refund"""
|
||||
loop = asyncio.get_event_loop()
|
||||
|
||||
try:
|
||||
refund = await loop.run_in_executor(
|
||||
None,
|
||||
lambda: stripe.Refund.create(**data)
|
||||
)
|
||||
|
||||
return OperationResult(
|
||||
success=True,
|
||||
data=refund.to_dict(),
|
||||
metadata={"refund_id": refund.id}
|
||||
)
|
||||
|
||||
except Exception as e:
|
||||
raise PaymentError(f"Failed to create refund: {e}")
|
||||
|
||||
async def _create_payment_method(self, data: Dict[str, Any]) -> OperationResult:
|
||||
"""Create Stripe payment method"""
|
||||
loop = asyncio.get_event_loop()
|
||||
|
||||
try:
|
||||
pm = await loop.run_in_executor(
|
||||
None,
|
||||
lambda: stripe.PaymentMethod.create(**data)
|
||||
)
|
||||
|
||||
return OperationResult(
|
||||
success=True,
|
||||
data=pm.to_dict(),
|
||||
metadata={"payment_method_id": pm.id}
|
||||
)
|
||||
|
||||
except Exception as e:
|
||||
raise PaymentError(f"Failed to create payment method: {e}")
|
||||
|
||||
async def _create_customer(self, data: Dict[str, Any]) -> OperationResult:
|
||||
"""Create Stripe customer"""
|
||||
loop = asyncio.get_event_loop()
|
||||
|
||||
try:
|
||||
customer = await loop.run_in_executor(
|
||||
None,
|
||||
lambda: stripe.Customer.create(**data)
|
||||
)
|
||||
|
||||
return OperationResult(
|
||||
success=True,
|
||||
data=customer.to_dict(),
|
||||
metadata={"customer_id": customer.id}
|
||||
)
|
||||
|
||||
except Exception as e:
|
||||
raise PaymentError(f"Failed to create customer: {e}")
|
||||
|
||||
async def _create_subscription(self, data: Dict[str, Any]) -> OperationResult:
|
||||
"""Create Stripe subscription"""
|
||||
loop = asyncio.get_event_loop()
|
||||
|
||||
try:
|
||||
subscription = await loop.run_in_executor(
|
||||
None,
|
||||
lambda: stripe.Subscription.create(**data)
|
||||
)
|
||||
|
||||
return OperationResult(
|
||||
success=True,
|
||||
data=subscription.to_dict(),
|
||||
metadata={"subscription_id": subscription.id}
|
||||
)
|
||||
|
||||
except Exception as e:
|
||||
raise PaymentError(f"Failed to create subscription: {e}")
|
||||
|
||||
async def _cancel_subscription(self, data: Dict[str, Any]) -> OperationResult:
|
||||
"""Cancel Stripe subscription"""
|
||||
loop = asyncio.get_event_loop()
|
||||
|
||||
try:
|
||||
subscription = await loop.run_in_executor(
|
||||
None,
|
||||
lambda: stripe.Subscription.retrieve(data["subscription"])
|
||||
)
|
||||
|
||||
subscription = await loop.run_in_executor(
|
||||
None,
|
||||
lambda: subscription.cancel(at_period_end=data.get("at_period_end", True))
|
||||
)
|
||||
|
||||
return OperationResult(
|
||||
success=True,
|
||||
data=subscription.to_dict(),
|
||||
metadata={"subscription_id": subscription.id}
|
||||
)
|
||||
|
||||
except Exception as e:
|
||||
raise PaymentError(f"Failed to cancel subscription: {e}")
|
||||
|
||||
async def _retrieve_balance(self) -> OperationResult:
|
||||
"""Retrieve Stripe balance"""
|
||||
loop = asyncio.get_event_loop()
|
||||
|
||||
try:
|
||||
balance = await loop.run_in_executor(None, stripe.Balance.retrieve)
|
||||
|
||||
return OperationResult(
|
||||
success=True,
|
||||
data=balance.to_dict()
|
||||
)
|
||||
|
||||
except Exception as e:
|
||||
raise PaymentError(f"Failed to retrieve balance: {e}")
|
||||
|
||||
async def _process_webhook_event(self, event) -> Dict[str, Any]:
|
||||
"""Process webhook event"""
|
||||
event_type = event.type
|
||||
|
||||
if event_type.startswith("charge."):
|
||||
return await self._handle_charge_event(event)
|
||||
elif event_type.startswith("payment_method."):
|
||||
return await self._handle_payment_method_event(event)
|
||||
elif event_type.startswith("customer."):
|
||||
return await self._handle_customer_event(event)
|
||||
elif event_type.startswith("invoice."):
|
||||
return await self._handle_invoice_event(event)
|
||||
else:
|
||||
self.logger.info(f"Unhandled webhook event type: {event_type}")
|
||||
return {"status": "ignored"}
|
||||
|
||||
async def _handle_charge_event(self, event) -> Dict[str, Any]:
|
||||
"""Handle charge-related webhook events"""
|
||||
charge = event.data.object
|
||||
|
||||
# Emit to AITBC
|
||||
await self.client.post(
|
||||
"/webhooks/stripe/charge",
|
||||
json={
|
||||
"event_id": event.id,
|
||||
"event_type": event.type,
|
||||
"charge": charge.to_dict()
|
||||
}
|
||||
)
|
||||
|
||||
return {"status": "processed", "charge_id": charge.id}
|
||||
|
||||
async def _handle_payment_method_event(self, event) -> Dict[str, Any]:
|
||||
"""Handle payment method webhook events"""
|
||||
pm = event.data.object
|
||||
|
||||
await self.client.post(
|
||||
"/webhooks/stripe/payment_method",
|
||||
json={
|
||||
"event_id": event.id,
|
||||
"event_type": event.type,
|
||||
"payment_method": pm.to_dict()
|
||||
}
|
||||
)
|
||||
|
||||
return {"status": "processed", "payment_method_id": pm.id}
|
||||
|
||||
async def _handle_customer_event(self, event) -> Dict[str, Any]:
|
||||
"""Handle customer webhook events"""
|
||||
customer = event.data.object
|
||||
|
||||
await self.client.post(
|
||||
"/webhooks/stripe/customer",
|
||||
json={
|
||||
"event_id": event.id,
|
||||
"event_type": event.type,
|
||||
"customer": customer.to_dict()
|
||||
}
|
||||
)
|
||||
|
||||
return {"status": "processed", "customer_id": customer.id}
|
||||
|
||||
async def _handle_invoice_event(self, event) -> Dict[str, Any]:
|
||||
"""Handle invoice webhook events"""
|
||||
invoice = event.data.object
|
||||
|
||||
await self.client.post(
|
||||
"/webhooks/stripe/invoice",
|
||||
json={
|
||||
"event_id": event.id,
|
||||
"event_type": event.type,
|
||||
"invoice": invoice.to_dict()
|
||||
}
|
||||
)
|
||||
|
||||
return {"status": "processed", "invoice_id": invoice.id}
|
||||
|
||||
async def _handle_verified_webhook(self, data: Dict[str, Any]):
|
||||
"""Handle verified webhook"""
|
||||
self.logger.info(f"Webhook verified: {data}")
|
||||
Reference in New Issue
Block a user