""" app/billing/stripe_client.py ----------------------------- Thin wrappers around the Stripe SDK. No Flask imports — usable from routes, webhooks, and background tasks alike. All functions raise RuntimeError with a clear message if STRIPE_SECRET_KEY is absent, rather than producing cryptic Stripe SDK authentication errors. """ import os import logging logger = logging.getLogger(__name__) def _get_stripe(): """Import and initialise Stripe with the secret key. Raises if key is missing.""" import stripe as _stripe key = os.environ.get('STRIPE_SECRET_KEY') if not key: raise RuntimeError( 'STRIPE_SECRET_KEY is not set. ' 'Add it to your environment before enabling BILLING_ENABLED=true.' ) _stripe.api_key = key return _stripe def create_stripe_customer(*, email: str, name: str, tenant_id: int, slug: str) -> dict: """Create a Stripe Customer and return the customer dict. Stores tenant_id and slug in Stripe metadata for webhook reverse-lookup. """ stripe = _get_stripe() customer = stripe.Customer.create( email=email, name=name, metadata={'tenant_id': str(tenant_id), 'slug': slug}, ) logger.info('BILLING | stripe_customer_created | tenant=%s customer=%s', slug, customer['id']) return customer def create_checkout_session( *, customer_id: str, price_id: str, tenant_id: int, success_url: str, cancel_url: str, ) -> dict: """Create a Stripe Checkout Session for a subscription and return the session dict. `success_url` should contain `{CHECKOUT_SESSION_ID}` which Stripe replaces with the actual session ID on redirect (pass as `{{CHECKOUT_SESSION_ID}}` inside an f-string to produce the literal braces). """ stripe = _get_stripe() session = stripe.checkout.Session.create( customer=customer_id, mode='subscription', line_items=[{'price': price_id, 'quantity': 1}], success_url=success_url, cancel_url=cancel_url, metadata={'tenant_id': str(tenant_id)}, allow_promotion_codes=True, ) logger.info('BILLING | checkout_session_created | tenant_id=%s session=%s', tenant_id, session['id']) return session def create_portal_session(*, customer_id: str, return_url: str) -> dict: """Create a Stripe Billing Portal Session and return the session dict.""" stripe = _get_stripe() session = stripe.billing_portal.Session.create( customer=customer_id, return_url=return_url, ) logger.info('BILLING | portal_session_created | customer=%s', customer_id) return session def retrieve_subscription(subscription_id: str) -> dict: """Retrieve a Stripe Subscription object.""" stripe = _get_stripe() return stripe.Subscription.retrieve(subscription_id)