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:
oib
2025-12-22 10:33:23 +01:00
parent d98b2c7772
commit c8be9d7414
260 changed files with 59033 additions and 351 deletions

View File

@ -0,0 +1,19 @@
"""
Payment processor connectors for AITBC Enterprise
"""
from .base import PaymentConnector, PaymentMethod, Charge, Refund, Subscription
from .stripe import StripeConnector
from .paypal import PayPalConnector
from .square import SquareConnector
__all__ = [
"PaymentConnector",
"PaymentMethod",
"Charge",
"Refund",
"Subscription",
"StripeConnector",
"PayPalConnector",
"SquareConnector",
]

View File

@ -0,0 +1,256 @@
"""
Base classes for payment processor connectors
"""
from abc import ABC, abstractmethod
from typing import Dict, Any, Optional, List
from datetime import datetime
from dataclasses import dataclass
from enum import Enum
class PaymentStatus(Enum):
"""Payment status enumeration"""
PENDING = "pending"
SUCCEEDED = "succeeded"
FAILED = "failed"
REFUNDED = "refunded"
PARTIALLY_REFUNDED = "partially_refunded"
CANCELED = "canceled"
class RefundStatus(Enum):
"""Refund status enumeration"""
PENDING = "pending"
SUCCEEDED = "succeeded"
FAILED = "failed"
CANCELED = "canceled"
class SubscriptionStatus(Enum):
"""Subscription status enumeration"""
TRIALING = "trialing"
ACTIVE = "active"
PAST_DUE = "past_due"
CANCELED = "canceled"
UNPAID = "unpaid"
@dataclass
class PaymentMethod:
"""Payment method representation"""
id: str
type: str
created_at: datetime
metadata: Dict[str, Any]
# Card-specific fields
brand: Optional[str] = None
last4: Optional[str] = None
exp_month: Optional[int] = None
exp_year: Optional[int] = None
# Bank account fields
bank_name: Optional[str] = None
last4_ach: Optional[str] = None
routing_number: Optional[str] = None
@classmethod
def from_stripe_payment_method(cls, pm_data: Dict[str, Any]) -> 'PaymentMethod':
"""Create from Stripe payment method data"""
card = pm_data.get("card", {})
return cls(
id=pm_data["id"],
type=pm_data["type"],
created_at=datetime.fromtimestamp(pm_data["created"]),
metadata=pm_data.get("metadata", {}),
brand=card.get("brand"),
last4=card.get("last4"),
exp_month=card.get("exp_month"),
exp_year=card.get("exp_year")
)
@dataclass
class Charge:
"""Charge representation"""
id: str
amount: int
currency: str
status: PaymentStatus
created_at: datetime
updated_at: datetime
description: Optional[str]
metadata: Dict[str, Any]
# Refund information
amount_refunded: int = 0
refunds: List[Dict[str, Any]] = None
# Payment method
payment_method_id: Optional[str] = None
payment_method_details: Optional[Dict[str, Any]] = None
def __post_init__(self):
if self.refunds is None:
self.refunds = []
@classmethod
def from_stripe_charge(cls, charge_data: Dict[str, Any]) -> 'Charge':
"""Create from Stripe charge data"""
return cls(
id=charge_data["id"],
amount=charge_data["amount"],
currency=charge_data["currency"],
status=PaymentStatus(charge_data["status"]),
created_at=datetime.fromtimestamp(charge_data["created"]),
updated_at=datetime.fromtimestamp(charge_data.get("updated", charge_data["created"])),
description=charge_data.get("description"),
metadata=charge_data.get("metadata", {}),
amount_refunded=charge_data.get("amount_refunded", 0),
refunds=[r.to_dict() for r in charge_data.get("refunds", {}).get("data", [])],
payment_method_id=charge_data.get("payment_method"),
payment_method_details=charge_data.get("payment_method_details")
)
@dataclass
class Refund:
"""Refund representation"""
id: str
amount: int
currency: str
status: RefundStatus
created_at: datetime
updated_at: datetime
charge_id: str
reason: Optional[str]
metadata: Dict[str, Any]
@classmethod
def from_stripe_refund(cls, refund_data: Dict[str, Any]) -> 'Refund':
"""Create from Stripe refund data"""
return cls(
id=refund_data["id"],
amount=refund_data["amount"],
currency=refund_data["currency"],
status=RefundStatus(refund_data["status"]),
created_at=datetime.fromtimestamp(refund_data["created"]),
updated_at=datetime.fromtimestamp(refund_data.get("updated", refund_data["created"])),
charge_id=refund_data["charge"],
reason=refund_data.get("reason"),
metadata=refund_data.get("metadata", {})
)
@dataclass
class Subscription:
"""Subscription representation"""
id: str
status: SubscriptionStatus
created_at: datetime
updated_at: datetime
current_period_start: datetime
current_period_end: datetime
customer_id: str
metadata: Dict[str, Any]
# Pricing
amount: Optional[int] = None
currency: Optional[str] = None
interval: Optional[str] = None
interval_count: Optional[int] = None
# Trial
trial_start: Optional[datetime] = None
trial_end: Optional[datetime] = None
# Cancellation
canceled_at: Optional[datetime] = None
ended_at: Optional[datetime] = None
@classmethod
def from_stripe_subscription(cls, sub_data: Dict[str, Any]) -> 'Subscription':
"""Create from Stripe subscription data"""
items = sub_data.get("items", {}).get("data", [])
first_item = items[0] if items else {}
price = first_item.get("price", {})
return cls(
id=sub_data["id"],
status=SubscriptionStatus(sub_data["status"]),
created_at=datetime.fromtimestamp(sub_data["created"]),
updated_at=datetime.fromtimestamp(sub_data.get("updated", sub_data["created"])),
current_period_start=datetime.fromtimestamp(sub_data["current_period_start"]),
current_period_end=datetime.fromtimestamp(sub_data["current_period_end"]),
customer_id=sub_data["customer"],
metadata=sub_data.get("metadata", {}),
amount=price.get("unit_amount"),
currency=price.get("currency"),
interval=price.get("recurring", {}).get("interval"),
interval_count=price.get("recurring", {}).get("interval_count"),
trial_start=datetime.fromtimestamp(sub_data["trial_start"]) if sub_data.get("trial_start") else None,
trial_end=datetime.fromtimestamp(sub_data["trial_end"]) if sub_data.get("trial_end") else None,
canceled_at=datetime.fromtimestamp(sub_data["canceled_at"]) if sub_data.get("canceled_at") else None,
ended_at=datetime.fromtimestamp(sub_data["ended_at"]) if sub_data.get("ended_at") else None
)
class PaymentConnector(ABC):
"""Abstract base class for payment connectors"""
def __init__(self, client, config):
self.client = client
self.config = config
@abstractmethod
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"""
pass
@abstractmethod
async def create_refund(
self,
charge_id: str,
amount: Optional[int] = None,
reason: Optional[str] = None
) -> Refund:
"""Create a refund"""
pass
@abstractmethod
async def create_payment_method(
self,
type: str,
card: Dict[str, Any],
metadata: Optional[Dict[str, Any]] = None
) -> PaymentMethod:
"""Create a payment method"""
pass
@abstractmethod
async def create_subscription(
self,
customer: str,
items: List[Dict[str, Any]],
metadata: Optional[Dict[str, Any]] = None
) -> Subscription:
"""Create a subscription"""
pass
@abstractmethod
async def cancel_subscription(
self,
subscription_id: str,
at_period_end: bool = True
) -> Subscription:
"""Cancel a subscription"""
pass

View File

@ -0,0 +1,33 @@
"""
PayPal payment connector for AITBC Enterprise (Placeholder)
"""
from .base import PaymentConnector, PaymentMethod, Charge, Refund, Subscription
class PayPalConnector(PaymentConnector):
"""PayPal payment processor connector"""
def __init__(self, client, config, paypal_client_id, paypal_secret):
# TODO: Implement PayPal connector
raise NotImplementedError("PayPal connector not yet implemented")
async def create_charge(self, amount, currency, source, description=None, metadata=None):
# TODO: Implement PayPal charge creation
raise NotImplementedError
async def create_refund(self, charge_id, amount=None, reason=None):
# TODO: Implement PayPal refund
raise NotImplementedError
async def create_payment_method(self, type, card, metadata=None):
# TODO: Implement PayPal payment method
raise NotImplementedError
async def create_subscription(self, customer, items, metadata=None):
# TODO: Implement PayPal subscription
raise NotImplementedError
async def cancel_subscription(self, subscription_id, at_period_end=True):
# TODO: Implement PayPal subscription cancellation
raise NotImplementedError

View File

@ -0,0 +1,33 @@
"""
Square payment connector for AITBC Enterprise (Placeholder)
"""
from .base import PaymentConnector, PaymentMethod, Charge, Refund, Subscription
class SquareConnector(PaymentConnector):
"""Square payment processor connector"""
def __init__(self, client, config, square_access_token):
# TODO: Implement Square connector
raise NotImplementedError("Square connector not yet implemented")
async def create_charge(self, amount, currency, source, description=None, metadata=None):
# TODO: Implement Square charge creation
raise NotImplementedError
async def create_refund(self, charge_id, amount=None, reason=None):
# TODO: Implement Square refund
raise NotImplementedError
async def create_payment_method(self, type, card, metadata=None):
# TODO: Implement Square payment method
raise NotImplementedError
async def create_subscription(self, customer, items, metadata=None):
# TODO: Implement Square subscription
raise NotImplementedError
async def cancel_subscription(self, subscription_id, at_period_end=True):
# TODO: Implement Square subscription cancellation
raise NotImplementedError

View File

@ -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}")