Module Classification: - Core (4): core, tenancy, cms, customers - always enabled - Optional (7): payments, billing, inventory, orders, marketplace, analytics, messaging - Internal (2): dev-tools, monitoring - admin-only Key Changes: - Rename platform-admin module to tenancy - Promote CMS and Customers to core modules - Create new payments module (gateway abstractions) - Add billing→payments and orders→payments dependencies - Mark dev-tools and monitoring as internal modules New Infrastructure: - app/modules/events.py: Module event bus (ENABLED, DISABLED, STARTUP, SHUTDOWN) - app/modules/migrations.py: Module-specific migration discovery - app/core/observability.py: Health checks, Prometheus metrics, Sentry integration Enhanced ModuleDefinition: - version, is_internal, permissions - config_schema, default_config - migrations_path - Lifecycle hooks: on_enable, on_disable, on_startup, health_check New Registry Functions: - get_optional_module_codes(), get_internal_module_codes() - is_core_module(), is_internal_module() - get_modules_by_tier(), get_module_tier() Migrations: - zc*: Rename platform-admin to tenancy - zd*: Ensure CMS and Customers enabled for all platforms Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
233 lines
6.3 KiB
Python
233 lines
6.3 KiB
Python
# app/modules/payments/services/payment_service.py
|
|
"""
|
|
Payment service for processing payments through configured gateways.
|
|
|
|
This service provides a unified interface for payment operations
|
|
regardless of the underlying gateway (Stripe, PayPal, etc.).
|
|
|
|
Usage:
|
|
from app.modules.payments.services import PaymentService
|
|
|
|
payment_service = PaymentService()
|
|
|
|
# Process a payment
|
|
result = await payment_service.process_payment(
|
|
amount=1000, # Amount in cents
|
|
currency="EUR",
|
|
payment_method_id="pm_xxx",
|
|
description="Order #123",
|
|
)
|
|
|
|
# Issue a refund
|
|
refund = await payment_service.refund(
|
|
transaction_id="txn_xxx",
|
|
amount=500, # Partial refund
|
|
)
|
|
"""
|
|
|
|
import logging
|
|
from dataclasses import dataclass
|
|
from datetime import datetime, timezone
|
|
from decimal import Decimal
|
|
from enum import Enum
|
|
from typing import Any
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
class PaymentStatus(str, Enum):
|
|
"""Payment transaction status."""
|
|
|
|
PENDING = "pending"
|
|
PROCESSING = "processing"
|
|
SUCCEEDED = "succeeded"
|
|
FAILED = "failed"
|
|
CANCELLED = "cancelled"
|
|
REFUNDED = "refunded"
|
|
PARTIALLY_REFUNDED = "partially_refunded"
|
|
|
|
|
|
class PaymentGateway(str, Enum):
|
|
"""Supported payment gateways."""
|
|
|
|
STRIPE = "stripe"
|
|
PAYPAL = "paypal"
|
|
BANK_TRANSFER = "bank_transfer"
|
|
|
|
|
|
@dataclass
|
|
class PaymentResult:
|
|
"""Result of a payment operation."""
|
|
|
|
success: bool
|
|
transaction_id: str | None = None
|
|
gateway: PaymentGateway | None = None
|
|
status: PaymentStatus = PaymentStatus.PENDING
|
|
amount: int = 0 # Amount in cents
|
|
currency: str = "EUR"
|
|
error_message: str | None = None
|
|
gateway_response: dict[str, Any] | None = None
|
|
created_at: datetime | None = None
|
|
|
|
def to_dict(self) -> dict[str, Any]:
|
|
return {
|
|
"success": self.success,
|
|
"transaction_id": self.transaction_id,
|
|
"gateway": self.gateway.value if self.gateway else None,
|
|
"status": self.status.value,
|
|
"amount": self.amount,
|
|
"currency": self.currency,
|
|
"error_message": self.error_message,
|
|
"created_at": self.created_at.isoformat() if self.created_at else None,
|
|
}
|
|
|
|
|
|
@dataclass
|
|
class RefundResult:
|
|
"""Result of a refund operation."""
|
|
|
|
success: bool
|
|
refund_id: str | None = None
|
|
transaction_id: str | None = None
|
|
amount: int = 0
|
|
status: PaymentStatus = PaymentStatus.PENDING
|
|
error_message: str | None = None
|
|
|
|
|
|
class PaymentService:
|
|
"""
|
|
Service for processing payments through configured gateways.
|
|
|
|
This service provides a unified interface for:
|
|
- Processing payments
|
|
- Issuing refunds
|
|
- Managing payment methods
|
|
- Retrieving transaction history
|
|
"""
|
|
|
|
def __init__(self) -> None:
|
|
self._default_gateway = PaymentGateway.STRIPE
|
|
|
|
async def process_payment(
|
|
self,
|
|
amount: int,
|
|
currency: str = "EUR",
|
|
payment_method_id: str | None = None,
|
|
gateway: PaymentGateway | None = None,
|
|
description: str | None = None,
|
|
metadata: dict[str, Any] | None = None,
|
|
) -> PaymentResult:
|
|
"""
|
|
Process a payment through the specified gateway.
|
|
|
|
Args:
|
|
amount: Amount in cents
|
|
currency: Currency code (EUR, USD, etc.)
|
|
payment_method_id: Stored payment method ID
|
|
gateway: Payment gateway to use (default: stripe)
|
|
description: Payment description
|
|
metadata: Additional metadata for the transaction
|
|
|
|
Returns:
|
|
PaymentResult with transaction details
|
|
"""
|
|
gateway = gateway or self._default_gateway
|
|
|
|
logger.info(
|
|
f"Processing payment: {amount} {currency} via {gateway.value}",
|
|
extra={"amount": amount, "currency": currency, "gateway": gateway.value},
|
|
)
|
|
|
|
# TODO: Implement actual gateway processing
|
|
# For now, return a mock successful result
|
|
return PaymentResult(
|
|
success=True,
|
|
transaction_id=f"txn_{gateway.value}_mock",
|
|
gateway=gateway,
|
|
status=PaymentStatus.SUCCEEDED,
|
|
amount=amount,
|
|
currency=currency,
|
|
created_at=datetime.now(timezone.utc),
|
|
)
|
|
|
|
async def refund(
|
|
self,
|
|
transaction_id: str,
|
|
amount: int | None = None,
|
|
reason: str | None = None,
|
|
) -> RefundResult:
|
|
"""
|
|
Issue a refund for a transaction.
|
|
|
|
Args:
|
|
transaction_id: Original transaction ID
|
|
amount: Refund amount in cents (None for full refund)
|
|
reason: Reason for refund
|
|
|
|
Returns:
|
|
RefundResult with refund details
|
|
"""
|
|
logger.info(
|
|
f"Issuing refund for transaction {transaction_id}",
|
|
extra={"transaction_id": transaction_id, "amount": amount, "reason": reason},
|
|
)
|
|
|
|
# TODO: Implement actual refund processing
|
|
return RefundResult(
|
|
success=True,
|
|
refund_id=f"rf_{transaction_id}",
|
|
transaction_id=transaction_id,
|
|
amount=amount or 0,
|
|
status=PaymentStatus.REFUNDED,
|
|
)
|
|
|
|
async def get_transaction(self, transaction_id: str) -> dict[str, Any] | None:
|
|
"""
|
|
Get transaction details by ID.
|
|
|
|
Args:
|
|
transaction_id: Transaction ID
|
|
|
|
Returns:
|
|
Transaction details or None if not found
|
|
"""
|
|
# TODO: Implement transaction lookup
|
|
return None
|
|
|
|
async def list_transactions(
|
|
self,
|
|
vendor_id: int | None = None,
|
|
platform_id: int | None = None,
|
|
status: PaymentStatus | None = None,
|
|
limit: int = 50,
|
|
offset: int = 0,
|
|
) -> list[dict[str, Any]]:
|
|
"""
|
|
List transactions with optional filters.
|
|
|
|
Args:
|
|
vendor_id: Filter by vendor
|
|
platform_id: Filter by platform
|
|
status: Filter by status
|
|
limit: Maximum results
|
|
offset: Pagination offset
|
|
|
|
Returns:
|
|
List of transaction records
|
|
"""
|
|
# TODO: Implement transaction listing
|
|
return []
|
|
|
|
|
|
# Singleton instance
|
|
payment_service = PaymentService()
|
|
|
|
__all__ = [
|
|
"PaymentService",
|
|
"PaymentStatus",
|
|
"PaymentGateway",
|
|
"PaymentResult",
|
|
"RefundResult",
|
|
"payment_service",
|
|
]
|