# Copyright (c) 2025 Patrick Motsch # All rights reserved. """ Stripe Checkout service for billing credit top-ups. Creates Checkout Sessions for redirect-based payment flow. """ import logging from typing import Optional from modules.shared.configuration import APP_CONFIG logger = logging.getLogger(__name__) # Server-side allowed amounts in CHF - never trust client ALLOWED_AMOUNTS_CHF = [10, 25, 50, 100, 250, 500] def create_checkout_session( mandate_id: str, user_id: Optional[str], amount_chf: float ) -> str: """ Create a Stripe Checkout Session for credit top-up. Amount and currency are validated server-side. The client-provided amount must match an allowed preset. Args: mandate_id: Target mandate ID user_id: Target user ID (for PREPAY_USER) or None (for mandate pool) amount_chf: Amount in CHF (must be in ALLOWED_AMOUNTS_CHF) Returns: Stripe Checkout Session URL for redirect Raises: ValueError: If amount is invalid """ import stripe # Validate amount server-side if amount_chf not in ALLOWED_AMOUNTS_CHF: raise ValueError( f"Invalid amount {amount_chf} CHF. Allowed: {ALLOWED_AMOUNTS_CHF}" ) # Pin API version from config (match Stripe Dashboard) api_version = APP_CONFIG.get("STRIPE_API_VERSION") if api_version: stripe.api_version = api_version # Get secrets secret_key = APP_CONFIG.get("STRIPE_SECRET_KEY_SECRET") or APP_CONFIG.get("STRIPE_SECRET_KEY") if not secret_key: raise ValueError("STRIPE_SECRET_KEY_SECRET not configured") stripe.api_key = secret_key frontend_url = APP_CONFIG.get("APP_FRONTEND_URL", "https://nyla-int.poweron-center.net") base_path = "/admin/billing" success_url = f"{frontend_url.rstrip('/')}{base_path}?success=true&session_id={{CHECKOUT_SESSION_ID}}" cancel_url = f"{frontend_url.rstrip('/')}{base_path}?canceled=true" # Amount in cents for Stripe (CHF uses 2 decimal places) amount_cents = int(round(amount_chf * 100)) metadata = { "mandateId": mandate_id, "amountChf": str(amount_chf), } if user_id: metadata["userId"] = user_id session = stripe.checkout.Session.create( mode="payment", line_items=[ { "price_data": { "currency": "chf", "unit_amount": amount_cents, "product_data": { "name": "Guthaben aufladen", "description": "AI Service Guthaben (CHF)", }, }, "quantity": 1, } ], success_url=success_url, cancel_url=cancel_url, metadata=metadata, ) if not session or not session.url: raise ValueError("Stripe Checkout Session creation failed") logger.info( f"Created Stripe Checkout Session {session.id} for mandate {mandate_id}, " f"amount {amount_chf} CHF" ) return session.url