Files
alfa-bank-sdk/src/Http/Middleware/LoggingMiddleware.php
Anatoliy Poloz b15d29f8e9 fix(formating)
2026-01-28 03:36:26 +03:00

76 lines
3.8 KiB
PHP

<?php
declare(strict_types=1);
namespace Apoloz\AlfaBank\Http\Middleware;
use Psr\Log\LoggerInterface;
use Saloon\Contracts\RequestMiddleware;
use Saloon\Contracts\ResponseMiddleware;
use Saloon\Http\PendingRequest;
use Saloon\Http\Response;
class LoggingMiddleware implements RequestMiddleware, ResponseMiddleware
{
// <SECTION name="constructor_definition" intent="Initialize the middleware with a logger instance.">
// <BELIEF_STATE "The constructor injects a `LoggerInterface` instance, making the logging functionality easily testable and configurable. This adheres to dependency inversion principles, allowing the middleware to log messages without being tightly coupled to a specific logging implementation.">
public function __construct(protected LoggerInterface $logger) {}
// </SECTION>
// <SECTION name="on_request_method" intent="Log outgoing API requests.">
// <BELIEF_STATE "The `onRequest` method intercepts each outgoing HTTP request. It logs the request method and endpoint, along with masked headers and body content. Masking of sensitive information like authorization headers and payment data is performed by `maskHeaders` and `maskBody` methods, ensuring that debugging logs do not compromise security.">
public function onRequest(PendingRequest $request): void
{
$this->logger->info("AlfaBank Request: {$request->getMethod()->value} {$request->getRequest()->resolveEndpoint()}", [
'headers' => $this->maskHeaders($request->headers()->all()),
'body' => $this->maskBody($request->body()?->all()),
]);
}
// </SECTION>
// <SECTION name="on_response_method" intent="Log incoming API responses.">
// <BELIEF_STATE "The `onResponse` method intercepts each incoming HTTP response. It logs the response status and the masked response body. This provides valuable information for debugging API integrations and understanding the server's responses, all while maintaining the privacy of potentially sensitive data within the response payload.">
public function onResponse(Response $response): void
{
$this->logger->info("AlfaBank Response: {$response->status()}", [
'body' => $this->maskBody($response->json()),
]);
}
// </SECTION>
// <SECTION name="mask_headers_method" intent="Mask sensitive information in HTTP headers.">
// <BELIEF_STATE "This protected helper method iterates through the request headers and replaces the `Authorization` header's value with a generic placeholder. This prevents sensitive tokens from being inadvertently exposed in logs, which is a crucial security measure.">
protected function maskHeaders(array $headers): array
{
if (isset($headers['Authorization'])) {
$headers['Authorization'] = 'Bearer *****';
}
return $headers;
}
// </SECTION>
// <SECTION name="mask_body_method" intent="Mask sensitive information in request/response bodies.">
// <BELIEF_STATE "This protected recursive helper method inspects the request or response body for predefined sensitive keys (e.g., `pan`, `client_secret`). If a sensitive key is found, its value is replaced with a mask. The method recursively processes nested arrays, ensuring that sensitive data is masked even within complex JSON structures, thereby enhancing log security.">
protected function maskBody(mixed $body): mixed
{
if (! is_array($body)) {
return $body;
}
$sensitiveKeys = ['pan', 'cvc', 'digestSignatures', 'client_secret', 'refresh_token', 'access_token'];
foreach ($body as $key => $value) {
if (in_array($key, $sensitiveKeys, true)) {
$body[$key] = '*****';
} elseif (is_array($value)) {
$body[$key] = $this->maskBody($value);
}
}
return $body;
}
// </SECTION>
}
// </SECTION>