# 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 UTC, datetime 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(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, store_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: store_id: Filter by store 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", ]