403Webshell
Server IP : 3.147.158.171  /  Your IP : 216.73.216.216
Web Server : Apache/2.4.67 (Amazon Linux) OpenSSL/3.5.5
System : Linux ip-172-31-2-178.us-east-2.compute.internal 6.1.172-216.329.amzn2023.x86_64 #1 SMP PREEMPT_DYNAMIC Wed May 20 06:31:34 UTC 2026 x86_64
User : ec2-user ( 1000)
PHP Version : 8.4.21
Disable Function : NONE
MySQL : OFF  |  cURL : ON  |  WGET : ON  |  Perl : ON  |  Python : OFF  |  Sudo : ON  |  Pkexec : OFF
Directory :  /tsai/www/html/api.turmansolutions.ai/app/services/

Upload File :
current_dir [ Writeable ] document_root [ Writeable ]

 

Command :


[ Back ]     

Current File : /tsai/www/html/api.turmansolutions.ai/app/services/credits_client.py
"""HTTP client for the store's internal Scike Credits API.

The FastAPI backend meters usage; the Drupal Commerce store owns money and
entitlement. This client is the server-to-server bridge between them, keyed by
site slug and authenticated with a shared secret.

Configuration (in /api/.env):
  - STORE_CREDITS_URL     Base URL of the store, e.g. https://scike.ai
  - SCIKE_CREDITS_SECRET  Shared secret matching the store's
                          $settings['scike_credits_shared_secret'].

Every call is best-effort and fails open (returns None / an "allowed" verdict on
transport error) so a store outage never hard-blocks generation.
"""

import logging
import os
from typing import Any, Dict, Optional

import httpx

_TIMEOUT = httpx.Timeout(6.0, connect=3.0)


def _base_url() -> Optional[str]:
    url = os.getenv('STORE_CREDITS_URL')
    return url.rstrip('/') if url else None


def _headers() -> Dict[str, str]:
    return {
        'X-Scike-Credits-Secret': os.getenv('SCIKE_CREDITS_SECRET', ''),
        'Content-Type': 'application/json',
    }


def is_configured() -> bool:
    return bool(_base_url() and os.getenv('SCIKE_CREDITS_SECRET'))


async def check(slug: str, used_credits: float) -> Optional[Dict[str, Any]]:
    """Enforcement decision. Returns the store verdict, or None on error/unconfigured.

    Verdict shape: {allowed, remaining, recharged, reason, status}.
    """
    base = _base_url()
    if not base or not is_configured():
        return None
    try:
        async with httpx.AsyncClient(timeout=_TIMEOUT) as client:
            resp = await client.post(
                f'{base}/scike-credits/check',
                headers=_headers(),
                json={'slug': slug, 'used_credits': used_credits},
            )
            resp.raise_for_status()
            return resp.json()
    except Exception as e:
        logging.warning(f"credits check failed for {slug}: {e}")
        return None


async def report(slug: str, used_credits: float, period_start: Optional[int] = None) -> None:
    """Idempotent usage reconciliation (level, not delta). Best-effort."""
    base = _base_url()
    if not base or not is_configured():
        return
    try:
        payload: Dict[str, Any] = {'slug': slug, 'used_credits': used_credits}
        if period_start is not None:
            payload['period_start'] = period_start
        async with httpx.AsyncClient(timeout=_TIMEOUT) as client:
            resp = await client.post(
                f'{base}/scike-credits/report',
                headers=_headers(),
                json=payload,
            )
            resp.raise_for_status()
    except Exception as e:
        logging.warning(f"credits report failed for {slug}: {e}")


async def status(slug: str) -> Optional[Dict[str, Any]]:
    """Full account state for the dashboard, or None if unavailable/no account."""
    base = _base_url()
    if not base or not is_configured():
        return None
    try:
        async with httpx.AsyncClient(timeout=_TIMEOUT) as client:
            resp = await client.get(
                f'{base}/scike-credits/status',
                headers=_headers(),
                params={'slug': slug},
            )
            if resp.status_code == 404:
                return None
            resp.raise_for_status()
            return resp.json()
    except Exception as e:
        logging.warning(f"credits status failed for {slug}: {e}")
        return None


async def update_settings(slug: str, settings: Dict[str, Any]) -> Optional[Dict[str, Any]]:
    """Update auto-recharge / increment / cap. Returns the fresh status."""
    base = _base_url()
    if not base or not is_configured():
        return None
    try:
        payload = {'slug': slug, **settings}
        async with httpx.AsyncClient(timeout=_TIMEOUT) as client:
            resp = await client.post(
                f'{base}/scike-credits/settings',
                headers=_headers(),
                json=payload,
            )
            if resp.status_code == 404:
                return None
            resp.raise_for_status()
            return resp.json()
    except Exception as e:
        logging.warning(f"credits settings update failed for {slug}: {e}")
        return None


async def portal_link(slug: str, return_url: Optional[str] = None) -> Optional[str]:
    """Create a Stripe Customer Portal session URL, or None if unavailable."""
    base = _base_url()
    if not base or not is_configured():
        return None
    try:
        params = {'slug': slug}
        if return_url:
            params['return_url'] = return_url
        async with httpx.AsyncClient(timeout=_TIMEOUT) as client:
            resp = await client.get(
                f'{base}/scike-credits/portal-link',
                headers=_headers(),
                params=params,
            )
            if resp.status_code == 404:
                return None
            resp.raise_for_status()
            return resp.json().get('url')
    except Exception as e:
        logging.warning(f"credits portal-link failed for {slug}: {e}")
        return None

Youez - 2016 - github.com/yon3zu
LinuXploit