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,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",
|
||||
]
|
||||
@ -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
|
||||
@ -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
|
||||
@ -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
|
||||
@ -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