# /opt/docker/dev/service_finder/backend/app/services/stripe_adapter.py """ Stripe integrációs adapter a Payment Router számára. Kezeli a Stripe Checkout Session létrehozását és a webhook validációt. """ import logging from typing import Dict, Any, Optional, Tuple from datetime import datetime, timedelta from decimal import Decimal from app.core.config import settings from app.models.marketplace.payment import PaymentIntent, PaymentIntentStatus from app.models import WalletType logger = logging.getLogger("stripe-adapter") # Try to import stripe, but handle the case when it's not installed try: import stripe STRIPE_AVAILABLE = True except ImportError: stripe = None STRIPE_AVAILABLE = False logger.warning("Stripe module not installed. Stripe functionality will be disabled.") class StripeAdapter: """Stripe API adapter a fizetési gateway integrációhoz.""" def __init__(self): """Inicializálja a Stripe klienst a konfigurációból.""" # Use getattr with defaults for missing settings self.stripe_api_key = getattr(settings, 'STRIPE_SECRET_KEY', None) self.webhook_secret = getattr(settings, 'STRIPE_WEBHOOK_SECRET', None) self.currency = getattr(settings, 'STRIPE_CURRENCY', "EUR") # Check if stripe module is available if not STRIPE_AVAILABLE: logger.warning("Stripe Python module not installed. Stripe adapter disabled.") self.stripe_available = False elif not self.stripe_api_key: logger.warning("STRIPE_SECRET_KEY nincs beállítva, Stripe adapter nem működik") self.stripe_available = False else: stripe.api_key = self.stripe_api_key self.stripe_available = True logger.info(f"Stripe adapter inicializálva currency={self.currency}") async def create_checkout_session( self, payment_intent: PaymentIntent, success_url: str, cancel_url: str, metadata: Optional[Dict[str, Any]] = None ) -> Dict[str, Any]: """ Stripe Checkout Session létrehozása a PaymentIntent alapján. Args: payment_intent: A PaymentIntent objektum success_url: Sikeres fizetés után átirányítási URL cancel_url: Megszakított fizetés után átirányítási URL metadata: Extra metadata a Stripe számára Returns: Dict: Stripe Checkout Session adatai """ if not self.stripe_available: raise ValueError("Stripe nem elérhető, STRIPE_SECRET_KEY hiányzik") if payment_intent.status != PaymentIntentStatus.PENDING: raise ValueError(f"PaymentIntent nem PENDING státuszú: {payment_intent.status}") # Alap metadata (kötelező: intent_token) base_metadata = { "intent_token": str(payment_intent.intent_token), "payment_intent_id": payment_intent.id, "payer_id": payment_intent.payer_id, "target_wallet_type": payment_intent.target_wallet_type.value, } if payment_intent.beneficiary_id: base_metadata["beneficiary_id"] = payment_intent.beneficiary_id # Egyesített metadata final_metadata = {**base_metadata, **(metadata or {})} try: # Stripe Checkout Session létrehozása session = stripe.checkout.Session.create( payment_method_types=["card"], line_items=[ { "price_data": { "currency": self.currency.lower(), "product_data": { "name": f"Service Finder - {payment_intent.target_wallet_type.value} feltöltés", "description": f"Net: {payment_intent.net_amount} {self.currency}, Fee: {payment_intent.handling_fee} {self.currency}", }, "unit_amount": int(payment_intent.gross_amount * 100), # Stripe centben várja }, "quantity": 1, } ], mode="payment", success_url=success_url, cancel_url=cancel_url, client_reference_id=str(payment_intent.id), metadata=final_metadata, expires_at=int((datetime.utcnow() + timedelta(hours=24)).timestamp()), ) logger.info( f"Stripe Checkout Session létrehozva: {session.id}, " f"amount={payment_intent.gross_amount}{self.currency}, " f"intent_token={payment_intent.intent_token}" ) return { "session_id": session.id, "url": session.url, "payment_intent_id": session.payment_intent, "expires_at": datetime.fromtimestamp(session.expires_at), "metadata": final_metadata, } except stripe.error.StripeError as e: logger.error(f"Stripe hiba Checkout Session létrehozásakor: {e}") raise ValueError(f"Stripe hiba: {e.user_message if hasattr(e, 'user_message') else str(e)}") async def verify_webhook_signature( self, payload: bytes, signature: str ) -> Tuple[bool, Optional[Dict[str, Any]]]: """ Stripe webhook aláírás validálása (Kettős Lakat - 1. lépés). Args: payload: A nyers HTTP request body signature: A Stripe-Signature header értéke Returns: Tuple: (sikeres validáció, event adatok vagy None) """ if not self.webhook_secret: logger.error("STRIPE_WEBHOOK_SECRET nincs beállítva, webhook validáció sikertelen") return False, None try: event = stripe.Webhook.construct_event( payload, signature, self.webhook_secret ) logger.info(f"Stripe webhook validálva: {event.type} (id: {event.id})") return True, event except stripe.error.SignatureVerificationError as e: logger.error(f"Stripe webhook aláírás érvénytelen: {e}") return False, None except Exception as e: logger.error(f"Stripe webhook feldolgozási hiba: {e}") return False, None async def handle_checkout_completed( self, event: Dict[str, Any] ) -> Dict[str, Any]: """ checkout.session.completed esemény feldolgozása. Args: event: Stripe webhook event Returns: Dict: Feldolgozási eredmény """ session = event["data"]["object"] # Metadata kinyerése metadata = session.get("metadata", {}) intent_token = metadata.get("intent_token") if not intent_token: logger.error("Stripe session metadata nem tartalmaz intent_token-t") return {"success": False, "error": "Missing intent_token in metadata"} # Összeg ellenőrzése (cent -> valuta) amount_total = session.get("amount_total", 0) / 100.0 # Centből valuta logger.info( f"Stripe checkout completed: session={session['id']}, " f"amount={amount_total}, intent_token={intent_token}" ) return { "success": True, "session_id": session["id"], "payment_intent_id": session.get("payment_intent"), "amount_total": amount_total, "currency": session.get("currency", "eur").upper(), "metadata": metadata, "intent_token": intent_token, } async def handle_payment_intent_succeeded( self, event: Dict[str, Any] ) -> Dict[str, Any]: """ payment_intent.succeeded esemény feldolgozása. Args: event: Stripe webhook event Returns: Dict: Feldolgozási eredmény """ payment_intent = event["data"]["object"] logger.info( f"Stripe payment intent succeeded: {payment_intent['id']}, " f"amount={payment_intent['amount']/100}" ) return { "success": True, "payment_intent_id": payment_intent["id"], "amount": payment_intent["amount"] / 100.0, "currency": payment_intent.get("currency", "eur").upper(), "status": payment_intent.get("status"), } # Globális példány stripe_adapter = StripeAdapter()