Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
80 changes: 72 additions & 8 deletions santander_sdk/api_client/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,11 @@
SantanderClientError,
SantanderRequestError,
)
from .helpers import get_status_code_description, try_parse_response_to_json
from .helpers import try_parse_response_to_json

BEFORE_EXPIRE_TOKEN_SECONDS = timedelta(seconds=60)
TOKEN_ENDPOINT = "/auth/oauth/v2/token"

logger = logging.getLogger(__name__)


class SantanderApiClient:
"""
Expand Down Expand Up @@ -47,7 +45,7 @@ def __init__(self, config: SantanderClientConfiguration):
self.session = BaseURLSession(base_url=config.base_url)
self.session.cert = config.cert
self.session.auth = SantanderAuth.from_config(config)

self.logger = config.logger or logging.getLogger(__name__)
self._set_default_workspace_id()

def _set_default_workspace_id(self):
Expand All @@ -58,7 +56,9 @@ def _set_default_workspace_id(self):
"Conta sem configuração de workspace na configuração e na conta."
)

logger.info(f"Workspace obtido e configurado com sucesso: {workspace_id}")
self.logger.info(
f"Workspace obtido e configurado com sucesso: {workspace_id}"
)
self.config.set_workspace_id(workspace_id)

def get(self, endpoint: str, params: dict | None = None) -> dict:
Expand Down Expand Up @@ -94,17 +94,81 @@ def _request(
params: dict | None = None,
) -> dict:
url = self._prepare_url(endpoint)
response = None
try:
response = self.session.request(
method, url, json=data, params=params, timeout=60
)
response.raise_for_status()
self._log_request_success_if_needed(method, url, params, data, response)

return response.json()
except requests.exceptions.RequestException as e:
status_code = getattr(e.response, "status_code", 0)
error_content = try_parse_response_to_json(e.response)
status_description = get_status_code_description(status_code)
self._log_error_if_needed(method, url, params, data, e)
raise SantanderRequestError(
"Not successful code", status_code, error_content
)

raise SantanderRequestError(status_description, status_code, error_content)
except Exception as e:
raise SantanderRequestError(f"Erro na requisição: {e}", 0, None) from e
self._log_error_if_needed(method, url, params, data, e)
raise SantanderRequestError("Error in request: %s" % str(e), 0, None) from e

def _log_error_if_needed(
self,
method: str,
url: str,
params: dict | None,
data: dict | None,
error: Exception | None,
):
if self.config.log_request_response_level not in ["ALL", "ERROR"]:
self.logger.info("Logging error is disabled in client configuration")

response = getattr(error, "response", None)
extra = self._get_request_summary(
method, url, response, request_data=data, request_params=params, error=error
)
self.logger.error("API request failed", extra=extra)

def _log_request_success_if_needed(
self,
method: str,
url: str,
params: dict | None,
data: dict | None,
response: requests.Response,
):
if not self.config.log_request_response_level == "ALL":
self.logger.info("Request successful", url)
return

extra = self._get_request_summary(
method, url, response, request_data=data, request_params=params
)
self.logger.info("API request successful", extra=extra)

def _get_request_summary(
self,
method: str,
url: str,
response: requests.Response | None,
request_data: dict | None = None,
request_params: dict | None = None,
error: Exception | None = None,
) -> dict:
return {
"method": method,
"url": url,
"request_body": request_data,
"request_params": request_params,
"status_code": response.status_code if response is not None else None,
"response_body": try_parse_response_to_json(response)
if response is not None
else None,
"status": "error" if error else "success",
"error": {"message": str(error), "type": type(error).__name__}
if error
else None,
}
8 changes: 8 additions & 0 deletions santander_sdk/api_client/client_configuration.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
from typing import Literal
import logging


class SantanderClientConfiguration:
def __init__(
self,
Expand All @@ -6,12 +10,16 @@ def __init__(
cert: str,
base_url: str,
workspace_id: str = "",
log_request_response_level: Literal["ERROR", "ALL", "NONE"] = "ERROR",
logger: logging.Logger | None = None,
):
self.client_id = client_id
self.client_secret = client_secret
self.workspace_id = workspace_id
self.cert = cert
self.base_url = base_url
self.log_request_response_level = log_request_response_level
self.logger = logger or logging.getLogger(__name__)

def set_workspace_id(self, workspace_id: str):
self.workspace_id = workspace_id
Expand Down
20 changes: 0 additions & 20 deletions santander_sdk/api_client/helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,26 +61,6 @@ def try_parse_response_to_json(response) -> dict | None:
return error_content


SANTANDER_STATUS_DESCRIPTIONS = {
200: "Sucesso",
201: "Recurso criado",
400: "Erro de informação do cliente",
401: "Não autorizado/Autenticado",
403: "Não Autorizado",
404: "Informação não encontrada",
406: "O recurso de destino não possui uma representação atual que seria aceitável",
422: "Entidade não processa/inadequada",
429: "O usuário enviou muitas solicitações em um determinado período",
500: "Erro de Servidor, Aplicação está fora",
501: "O servidor não oferece suporte à funcionalidade necessária para atender à solicitação",
}


def get_status_code_description(status_code: int | str) -> str:
"""Retorna a descrição do status do Santander"""
return f"{status_code} - {SANTANDER_STATUS_DESCRIPTIONS.get(int(status_code), 'Erro desconhecido')}"


def only_numbers(s):
return re.sub("[^0-9]", "", s) if s else s

Expand Down
11 changes: 5 additions & 6 deletions santander_sdk/payment_receipts.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,6 @@
https://developer.santander.com.br/api/documentacao/comprovantes-visao-geral/
"""

import logging
from time import sleep
from typing import Generator, List, cast
from santander_sdk.api_client.client import SantanderApiClient
Expand All @@ -60,8 +59,6 @@

RECEIPTS_ENDPOINT = "/consult_payment_receipts/v1/payment_receipts"

logger = logging.getLogger(__name__)


def payment_list(
client: SantanderApiClient, params: ListPaymentParams
Expand Down Expand Up @@ -161,18 +158,20 @@ def _handle_already_created(
This happens when a receipt was requested a long time ago or the previous
attempt returned an error like EXPUNGED or ERROR.
"""
logger.info("Receipt already requested. Trying to get the receipt request ID.")
client.logger.info(
"Receipt already requested. Trying to get the receipt request ID."
)
receipt_history = receipt_creation_history(client, payment_id)
if not receipt_history["paymentReceiptsFileRequests"]:
logger.error("No previous receipts in history")
client.logger.error("No previous receipts in history")
raise
last_from_history = receipt_history["paymentReceiptsFileRequests"][-1]
request_id = last_from_history["request"]["requestId"]
result = get_receipt(client, payment_id, request_id)
if result["status"] not in [ReceiptStatus.EXPUNGED, ReceiptStatus.ERROR]:
return result

logger.info("The last receipt is in an error state, creating another one.")
client.logger.info("The last receipt is in an error state, creating another one.")
sleep(0.5)
endpoint = f"{RECEIPTS_ENDPOINT}/{payment_id}/file_requests"
response = cast(ReceiptInfoResponse, client.post(endpoint, None))
Expand Down
8 changes: 3 additions & 5 deletions santander_sdk/pix.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
from decimal import Decimal as D
import logging
import uuid
from typing import cast

Expand All @@ -17,7 +16,6 @@
TransferPixResult,
)

logger = logging.getLogger("santanderLogger")
PIX_ENDPOINT = "/management_payments_partners/v1/workspaces/:workspaceid/pix_payments"


Expand All @@ -29,7 +27,7 @@ def transfer_pix(
tags: list[str] = [],
id: uuid.UUID | str | None = None,
) -> TransferPixResult:
transfer_flow = SantanderPaymentFlow(client, PIX_ENDPOINT, logger)
transfer_flow = SantanderPaymentFlow(client, PIX_ENDPOINT)

try:
if value is None or value <= 0:
Expand Down Expand Up @@ -60,7 +58,7 @@ def transfer_pix(
}
except Exception as e:
error_message = str(e)
logger.error(error_message)
client.logger.error(error_message)
return {
"success": False,
"request_id": transfer_flow.request_id,
Expand All @@ -83,7 +81,7 @@ def _generate_create_pix_dict(
value: D,
description: str,
tags: list = [],
id: uuid.UUID | None = None,
id: uuid.UUID | str | None = None,
) -> dict:
data = {
"tags": tags,
Expand Down
15 changes: 7 additions & 8 deletions santander_sdk/transfer_flow.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import logging
from time import sleep
from typing import List, Literal, cast

Expand Down Expand Up @@ -32,10 +31,8 @@ def __init__(
self,
client: SantanderApiClient,
endpoint: str,
logger: logging.Logger | None = None,
):
self.client = client
self.logger = logger or logging.getLogger(__name__)
self.endpoint = endpoint
self.request_id = None

Expand All @@ -45,13 +42,13 @@ def create_payment(self, data: dict) -> SantanderPixResponse:
)
self.request_id = response.get("id")
self._check_for_rejected_error(response)
self.logger.info("Payment created: ", response.get("id"))
self.client.logger.info("Payment created: ", response.get("id"))
return response

def ensure_ready_to_pay(self, confirm_data) -> None:
payment_status = confirm_data.get("status")
if payment_status != CreateOrderStatus.READY_TO_PAY:
self.logger.info("PIX is not ready for payment", payment_status)
self.client.logger.info("PIX is not ready for payment", payment_status)
self._payment_status_polling(
payment_id=confirm_data.get("id"),
until_status=[CreateOrderStatus.READY_TO_PAY],
Expand All @@ -64,7 +61,7 @@ def confirm_payment(
try:
confirm_response = self._request_confirm_payment(confirm_data, payment_id)
except SantanderRequestError as e:
self.logger.error(str(e), payment_id, "checking current status")
self.client.logger.error(str(e), payment_id, "checking current status")
confirm_response = self._request_payment_status(payment_id)

if not confirm_response.get("status") == ConfirmOrderStatus.PAYED:
Expand All @@ -73,7 +70,9 @@ def confirm_payment(
payment_id, confirm_response.get("status", "")
)
except SantanderStatusTimeoutError as e:
self.logger.info("Timeout occurred while updating status:", str(e))
self.client.logger.info(
"Timeout occurred while updating status:", str(e)
)
return confirm_response

@retry_one_time_on_request_exception
Expand Down Expand Up @@ -125,7 +124,7 @@ def _payment_status_polling(

for attempt in range(1, max_update_attemps + 1):
response = self._request_payment_status(payment_id)
self.logger.info(
self.client.logger.info(
f"Checking status by polling: {payment_id} - {response.get('status')}"
)
if response.get("status") in until_status:
Expand Down
Loading