feat: add Shoppro payment status synchronization service

- Implemented ShopproPaymentStatusSyncService to handle payment status synchronization between Shoppro and Orderpro.
- Added methods for resolving watched status codes, finding candidate orders, and syncing individual order payments.
- Introduced ShopproStatusMappingRepository for managing status mappings between Shoppro and Orderpro.
- Created ShopproStatusSyncService to facilitate synchronization of order statuses from Shoppro to Orderpro.
This commit is contained in:
2026-03-08 20:41:10 +01:00
parent 3ba6202770
commit af052e1ff5
50 changed files with 6110 additions and 2602 deletions

View File

@@ -0,0 +1,155 @@
<?php
declare(strict_types=1);
namespace App\Modules\Settings;
use PDO;
use Throwable;
final class IntegrationsRepository
{
public function __construct(private readonly PDO $pdo)
{
}
/**
* @return array<string, mixed>|null
*/
public function findByTypeAndName(string $type, string $name): ?array
{
try {
$statement = $this->pdo->prepare(
'SELECT * FROM integrations WHERE type = :type AND name = :name LIMIT 1'
);
$statement->execute([
'type' => trim($type),
'name' => trim($name),
]);
$row = $statement->fetch(PDO::FETCH_ASSOC);
} catch (Throwable) {
return null;
}
return is_array($row) ? $row : null;
}
/**
* @return array<string, mixed>|null
*/
public function findFirstByType(string $type): ?array
{
try {
$statement = $this->pdo->prepare(
'SELECT * FROM integrations WHERE type = :type ORDER BY id ASC LIMIT 1'
);
$statement->execute(['type' => trim($type)]);
$row = $statement->fetch(PDO::FETCH_ASSOC);
} catch (Throwable) {
return null;
}
return is_array($row) ? $row : null;
}
/**
* @return array<string, mixed>|null
*/
public function findById(int $id): ?array
{
if ($id <= 0) {
return null;
}
try {
$statement = $this->pdo->prepare('SELECT * FROM integrations WHERE id = :id LIMIT 1');
$statement->execute(['id' => $id]);
$row = $statement->fetch(PDO::FETCH_ASSOC);
} catch (Throwable) {
return null;
}
return is_array($row) ? $row : null;
}
public function ensureIntegration(
string $type,
string $name,
string $baseUrl,
int $timeoutSeconds = 10,
bool $isActive = true
): int {
$typeValue = trim($type);
$nameValue = trim($name);
$baseUrlValue = trim($baseUrl);
$existing = $this->findByTypeAndName($typeValue, $nameValue);
if ($existing !== null) {
return (int) ($existing['id'] ?? 0);
}
$statement = $this->pdo->prepare(
'INSERT INTO integrations (
type, name, base_url, timeout_seconds, is_active, created_at, updated_at
) VALUES (
:type, :name, :base_url, :timeout_seconds, :is_active, NOW(), NOW()
)'
);
$statement->execute([
'type' => $typeValue,
'name' => $nameValue,
'base_url' => $baseUrlValue,
'timeout_seconds' => max(1, $timeoutSeconds),
'is_active' => $isActive ? 1 : 0,
]);
return (int) $this->pdo->lastInsertId();
}
public function updateApiKeyEncrypted(int $integrationId, ?string $encrypted): void
{
if ($integrationId <= 0) {
return;
}
$statement = $this->pdo->prepare(
'UPDATE integrations
SET api_key_encrypted = :api_key_encrypted,
updated_at = NOW()
WHERE id = :id'
);
$statement->execute([
'id' => $integrationId,
'api_key_encrypted' => $this->nullableString((string) $encrypted),
]);
}
public function getApiKeyEncrypted(int $integrationId): ?string
{
if ($integrationId <= 0) {
return null;
}
try {
$statement = $this->pdo->prepare(
'SELECT api_key_encrypted FROM integrations WHERE id = :id LIMIT 1'
);
$statement->execute(['id' => $integrationId]);
$value = $statement->fetchColumn();
} catch (Throwable) {
return null;
}
if (!is_string($value)) {
return null;
}
$trimmed = trim($value);
return $trimmed === '' ? null : $trimmed;
}
private function nullableString(string $value): ?string
{
$trimmed = trim($value);
return $trimmed === '' ? null : $trimmed;
}
}