fastcheck/fastcheck_api/app/services/sheriff_v2_native_service.py
2026-04-30 09:00:15 -04:00

145 lines
6.0 KiB
Python

from __future__ import annotations
import asyncio
import time
from typing import Any
import httpx
from fastcheck_api.app.core.config import settings
class SheriffV2NativeService:
def __init__(self) -> None:
self._client = httpx.AsyncClient(timeout=60)
self._token: str | None = None
self._token_expires_at: float = 0.0
async def close(self) -> None:
await self._client.aclose()
def _base_url(self) -> str:
base_url = (settings.sheriff_v2_base_url or "").strip()
if not base_url:
raise RuntimeError("Missing SHERIFF_V2_BASE_URL")
return base_url.rstrip("/")
def _client_identifier(self) -> str:
return (settings.sheriff_v2_client_identifier or "SheriffSecureClient-v1").strip()
def _credentials(self) -> tuple[str, str]:
access_key = (settings.sheriff_v2_access_key or "").strip()
access_secret = (settings.sheriff_v2_access_secret or "").strip()
if not access_key or not access_secret:
raise RuntimeError("Missing shf V2 access credentials")
return access_key, access_secret
async def _get_token(self) -> str:
now = time.time()
if self._token and now < (self._token_expires_at - 30):
return self._token
base_url = self._base_url()
access_key, access_secret = self._credentials()
url = f"{base_url}/api/clients/v2/apiCredentials/getToken"
headers = {
"Content-Type": "application/json",
"accept": "application/json",
"x-client-identifier": self._client_identifier(),
}
resp = await self._client.post(url, headers=headers, json={"accessKey": access_key, "accessSecret": access_secret})
resp.raise_for_status()
payload = resp.json()
token: str | None = None
if isinstance(payload, dict):
raw = payload.get("data") or payload.get("token") or payload.get("access_token") or payload.get("accessToken")
if isinstance(raw, str):
token = raw
elif isinstance(raw, dict):
inner = raw.get("token") or raw.get("access_token") or raw.get("accessToken")
if isinstance(inner, str):
token = inner
token = (token or "").strip()
if not token:
raise RuntimeError("shf token response invalid")
ttl = int(settings.sheriff_v2_token_ttl_seconds or 600)
self._token = token
self._token_expires_at = now + ttl
return token
async def _request(self, method: str, path: str, *, params: dict[str, Any] | None = None, json: dict[str, Any] | None = None) -> dict[str, Any]:
base_url = self._base_url()
url = f"{base_url}{path}"
token = await self._get_token()
headers = {
"accept": "application/json",
"Authorization": f"Bearer {token}",
"x-client-identifier": self._client_identifier(),
}
resp = await self._client.request(method, url, headers=headers, params=params, json=json)
if resp.status_code == 401:
self._token = None
token = await self._get_token()
headers["Authorization"] = f"Bearer {token}"
resp = await self._client.request(method, url, headers=headers, params=params, json=json)
if resp.status_code >= 400:
body = (resp.text or "")[:2000]
raise RuntimeError(f"shf {resp.status_code} for {method} {path}: {body}")
data = resp.json()
if not isinstance(data, dict):
raise RuntimeError("Unexpected shf response")
return data
async def query_rut(self, *, rut: str, is_monitoring: bool = False) -> dict[str, Any]:
rut = (rut or "").strip()
if not rut:
raise ValueError("Missing rut")
cargar = await self._request(
"POST",
"/api/clients/v2/helper/cargarRut",
json={"rut": rut, "isMonitoring": bool(is_monitoring), "includeEquifax": False},
)
await asyncio.sleep(2)
resumen = await self._request(
"GET",
"/api/clients/v2/helper/resumen",
params={"rut": rut, "complete": "True"},
)
result: dict[str, Any] = {"success": True, "cargarRut": cargar, "resumen": resumen}
ok = True
if isinstance(cargar, dict) and ("success" in cargar) and (not bool(cargar.get("success"))):
ok = False
if isinstance(resumen, dict) and ("success" in resumen) and (not bool(resumen.get("success"))):
ok = False
if ok:
judicial_civil, judicial_cobranza, judicial_laboral, multa_laboral, mora_previsional, compliance, malla_societaria, credit_score = await asyncio.gather(
self._request("GET", f"/api/clients/v2/helper/judicial/{rut}/civil"),
self._request("GET", f"/api/clients/v2/helper/judicial/{rut}/cobranza"),
self._request("GET", f"/api/clients/v2/helper/judicial/{rut}/laboral"),
self._request("GET", f"/api/clients/v2/helper/cobranzaLaboral/{rut}/multaLaboral"),
self._request("GET", f"/api/clients/v2/helper/cobranzaLaboral/{rut}/moraPrevisional"),
self._request("GET", f"/api/clients/v2/helper/compliance/{rut}"),
self._request("GET", f"/api/clients/v2/helper/legal/{rut}/mallaSocietaria"),
self._request("GET", f"/api/clients/v2/creditScore/{rut}"),
)
result.update(
{
"judicial": {"civil": judicial_civil, "cobranza": judicial_cobranza, "laboral": judicial_laboral},
"cobranzaLaboral": {"multaLaboral": multa_laboral, "moraPrevisional": mora_previsional},
"compliance": compliance,
"legal": {"mallaSocietaria": malla_societaria},
"creditScore": credit_score,
}
)
return result
sheriff_v2_native_service = SheriffV2NativeService()