import logging from typing import Any import httpx from gapido_auth.domain.errors import ExternalServiceError from gapido_auth.domain.ports import SmsClient logger = logging.getLogger(__name__) class SmsIrSmsClient(SmsClient): """SMS.ir verify API implementation of the SMS provider strategy.""" _endpoint = "https://api.sms.ir/v1/send/verify" def __init__( self, api_key: str, timeout_seconds: float = 10.0, transport: httpx.AsyncBaseTransport | None = None, ) -> None: """Configure credentials, timeout, and optional test transport.""" self._api_key = api_key self._timeout_seconds = timeout_seconds self._transport = transport async def send_otp(self, mobile: str, code: str, template: str) -> None: """Send an OTP through SMS.ir using the configured verify template id.""" headers = { "Content-Type": "application/json", "Accept": "application/json", "x-api-key": self._api_key, } payload = { "mobile": mobile, "templateId": int(template), "parameters": [{"name": "OTP", "value": code}], } try: async with httpx.AsyncClient( timeout=self._timeout_seconds, transport=self._transport ) as client: response = await client.post(self._endpoint, json=payload, headers=headers) except httpx.HTTPError as exc: logger.warning("SMS.ir network error for mobile=%s", mobile, exc_info=True) raise ExternalServiceError("SMS.ir network error") from exc if response.status_code != 200: logger.warning("SMS.ir HTTP error status=%s mobile=%s", response.status_code, mobile) raise ExternalServiceError("SMS.ir HTTP error") data: dict[str, Any] = response.json() if str(data.get("status", "")) != "1": logger.warning("SMS.ir API error status=%s mobile=%s", data.get("status"), mobile) raise ExternalServiceError("SMS.ir API error") logger.info("OTP SMS sent successfully to mobile=%s via SMS.ir", mobile)