Files
orderPRO/src/Modules/Settings/InpostIntegrationRepository.php
Jacek Pyziak f8db8c0162 refactor(01-tech-debt): extract AllegroTokenManager and StringHelper
Phase 1 complete (2/2 plans):

- Plan 01-01: Extract AllegroTokenManager — OAuth token logic
  centralized from 4 classes into dedicated manager class

- Plan 01-02: Extract StringHelper — nullableString/normalizeDateTime/
  normalizeColorHex extracted from 15+ classes into App\Core\Support\StringHelper;
  removed 19 duplicate private methods

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-12 23:36:06 +01:00

220 lines
8.3 KiB
PHP

<?php
declare(strict_types=1);
namespace App\Modules\Settings;
use App\Core\Support\StringHelper;
use PDO;
use RuntimeException;
use Throwable;
final class InpostIntegrationRepository
{
private const INTEGRATION_TYPE = 'inpost';
private const INTEGRATION_NAME = 'InPost ShipX';
private const INTEGRATION_BASE_URL = 'https://api-shipx-pl.easypack24.net';
private readonly IntegrationsRepository $integrations;
private readonly IntegrationSecretCipher $cipher;
public function __construct(
private readonly PDO $pdo,
private readonly string $secret
) {
$this->integrations = new IntegrationsRepository($this->pdo);
$this->cipher = new IntegrationSecretCipher($this->secret);
}
/**
* @return array<string, mixed>
*/
public function getSettings(): array
{
$row = $this->fetchRow();
if ($row === null) {
$row = $this->defaultSettings();
}
$encryptedToken = $this->resolveEncryptedToken($row);
return [
'has_api_token' => $encryptedToken !== null && $encryptedToken !== '',
'organization_id' => (string) ($row['organization_id'] ?? ''),
'environment' => (string) ($row['environment'] ?? 'sandbox'),
'default_dispatch_method' => (string) ($row['default_dispatch_method'] ?? 'pop'),
'default_dispatch_point' => (string) ($row['default_dispatch_point'] ?? ''),
'default_insurance' => isset($row['default_insurance']) && $row['default_insurance'] !== null
? (float) $row['default_insurance']
: null,
'default_locker_size' => (string) ($row['default_locker_size'] ?? 'small'),
'default_courier_length' => (int) ($row['default_courier_length'] ?? 20),
'default_courier_width' => (int) ($row['default_courier_width'] ?? 15),
'default_courier_height' => (int) ($row['default_courier_height'] ?? 8),
'label_format' => (string) ($row['label_format'] ?? 'Pdf'),
'weekend_delivery' => (bool) ($row['weekend_delivery'] ?? false),
'auto_insurance_value' => (bool) ($row['auto_insurance_value'] ?? false),
'multi_parcel' => (bool) ($row['multi_parcel'] ?? false),
];
}
/**
* @param array<string, mixed> $payload
*/
public function saveSettings(array $payload): void
{
$this->ensureRow();
$current = $this->fetchRow();
if ($current === null) {
throw new RuntimeException('Brak rekordu konfiguracji InPost.');
}
$integrationId = $this->ensureBaseIntegration();
$currentEncrypted = $this->resolveEncryptedToken($current);
$apiToken = trim((string) ($payload['api_token'] ?? ''));
$nextEncrypted = $currentEncrypted;
if ($apiToken !== '') {
$nextEncrypted = $this->cipher->encrypt($apiToken);
}
$this->integrations->updateApiKeyEncrypted($integrationId, $nextEncrypted);
$statement = $this->pdo->prepare(
'UPDATE inpost_integration_settings
SET api_token_encrypted = :api_token_encrypted,
organization_id = :organization_id,
environment = :environment,
default_dispatch_method = :default_dispatch_method,
default_dispatch_point = :default_dispatch_point,
default_insurance = :default_insurance,
default_locker_size = :default_locker_size,
default_courier_length = :default_courier_length,
default_courier_width = :default_courier_width,
default_courier_height = :default_courier_height,
label_format = :label_format,
weekend_delivery = :weekend_delivery,
auto_insurance_value = :auto_insurance_value,
multi_parcel = :multi_parcel,
updated_at = NOW()
WHERE id = 1'
);
$statement->execute([
'api_token_encrypted' => StringHelper::nullableString((string) $nextEncrypted),
'organization_id' => StringHelper::nullableString(trim((string) ($payload['organization_id'] ?? ''))),
'environment' => in_array($payload['environment'] ?? '', ['sandbox', 'production'], true)
? $payload['environment']
: 'sandbox',
'default_dispatch_method' => in_array($payload['default_dispatch_method'] ?? '', ['pop', 'parcel_locker', 'courier'], true)
? $payload['default_dispatch_method']
: 'pop',
'default_dispatch_point' => StringHelper::nullableString(trim((string) ($payload['default_dispatch_point'] ?? ''))),
'default_insurance' => ($payload['default_insurance'] ?? '') !== ''
? (float) $payload['default_insurance']
: null,
'default_locker_size' => in_array($payload['default_locker_size'] ?? '', ['small', 'medium', 'large'], true)
? $payload['default_locker_size']
: 'small',
'default_courier_length' => max(1, (int) ($payload['default_courier_length'] ?? 20)),
'default_courier_width' => max(1, (int) ($payload['default_courier_width'] ?? 15)),
'default_courier_height' => max(1, (int) ($payload['default_courier_height'] ?? 8)),
'label_format' => in_array($payload['label_format'] ?? '', ['Pdf', 'Zpl', 'Epl'], true)
? $payload['label_format']
: 'Pdf',
'weekend_delivery' => !empty($payload['weekend_delivery']) ? 1 : 0,
'auto_insurance_value' => !empty($payload['auto_insurance_value']) ? 1 : 0,
'multi_parcel' => !empty($payload['multi_parcel']) ? 1 : 0,
]);
}
/**
* @return string|null Decrypted API token or null
*/
public function getDecryptedToken(): ?string
{
$row = $this->fetchRow();
if ($row === null) {
return null;
}
$encrypted = $this->resolveEncryptedToken($row);
if ($encrypted === null || $encrypted === '') {
return null;
}
return $this->cipher->decrypt($encrypted);
}
private function ensureRow(): void
{
$statement = $this->pdo->prepare(
'INSERT INTO inpost_integration_settings (id, created_at, updated_at)
VALUES (1, NOW(), NOW())
ON DUPLICATE KEY UPDATE updated_at = VALUES(updated_at)'
);
$statement->execute();
}
/**
* @return array<string, mixed>|null
*/
private function fetchRow(): ?array
{
try {
$statement = $this->pdo->prepare('SELECT * FROM inpost_integration_settings WHERE id = 1 LIMIT 1');
$statement->execute();
$row = $statement->fetch(PDO::FETCH_ASSOC);
} catch (Throwable) {
return null;
}
return is_array($row) ? $row : null;
}
/**
* @return array<string, mixed>
*/
private function defaultSettings(): array
{
return [
'has_api_token' => false,
'organization_id' => '',
'environment' => 'sandbox',
'default_dispatch_method' => 'pop',
'default_dispatch_point' => '',
'default_insurance' => null,
'default_locker_size' => 'small',
'default_courier_length' => 20,
'default_courier_width' => 15,
'default_courier_height' => 8,
'label_format' => 'Pdf',
'weekend_delivery' => false,
'auto_insurance_value' => false,
'multi_parcel' => false,
];
}
private function resolveEncryptedToken(array $row): ?string
{
$integrationId = $this->ensureBaseIntegration();
$fromBase = $this->integrations->getApiKeyEncrypted($integrationId);
if ($fromBase !== null && $fromBase !== '') {
return $fromBase;
}
$legacy = trim((string) ($row['api_token_encrypted'] ?? ''));
return $legacy === '' ? null : $legacy;
}
private function ensureBaseIntegration(): int
{
return $this->integrations->ensureIntegration(
self::INTEGRATION_TYPE,
self::INTEGRATION_NAME,
self::INTEGRATION_BASE_URL,
20,
true
);
}
}