112 lines
4.1 KiB
PHP
112 lines
4.1 KiB
PHP
<?php
|
|
declare(strict_types=1);
|
|
|
|
namespace Esun\SeventeenTrack\Model;
|
|
|
|
use Magento\Framework\Exception\AuthenticationException;
|
|
use Magento\Framework\Exception\InputException;
|
|
use Magento\Framework\Serialize\Serializer\Json;
|
|
use Psr\Log\LoggerInterface;
|
|
|
|
class WebhookProcessor
|
|
{
|
|
private const MAX_BODY_BYTES = 2097152;
|
|
|
|
public function __construct(
|
|
private readonly Config $config,
|
|
private readonly TrackingRepository $tracking,
|
|
private readonly DeliveryStatusUpdater $deliveryStatusUpdater,
|
|
private readonly Json $json,
|
|
private readonly LoggerInterface $logger
|
|
) {
|
|
}
|
|
|
|
/** @return array{event:string,tracking_number:string,status:string,message:string} */
|
|
public function execute(string $rawBody, string $signature): array
|
|
{
|
|
$apiKey = $this->config->getApiKey();
|
|
if (!$this->config->isEnabled() || $apiKey === '') {
|
|
throw new \RuntimeException('17TRACK is disabled or the API key is missing.');
|
|
}
|
|
if ($rawBody === '' || strlen($rawBody) > self::MAX_BODY_BYTES) {
|
|
throw new InputException(__('The 17TRACK webhook body is empty or too large.'));
|
|
}
|
|
$this->verifySignature($rawBody, $signature, $apiKey);
|
|
|
|
try {
|
|
$payload = $this->json->unserialize($rawBody);
|
|
} catch (\InvalidArgumentException $exception) {
|
|
throw new InputException(__('The 17TRACK webhook body is not valid JSON.'), $exception);
|
|
}
|
|
if (!is_array($payload) || !is_array($payload['data'] ?? null)) {
|
|
throw new InputException(__('The 17TRACK webhook payload is missing data.'));
|
|
}
|
|
|
|
$event = (string)($payload['event'] ?? '');
|
|
$data = $payload['data'];
|
|
$number = trim((string)($data['number'] ?? ''));
|
|
if ($number === '') {
|
|
throw new InputException(__('The 17TRACK webhook payload is missing a tracking number.'));
|
|
}
|
|
|
|
if ($event === 'TRACKING_UPDATED') {
|
|
$orderIds = $this->tracking->saveTrackingInfo(
|
|
$number,
|
|
$data,
|
|
$this->json->serialize($data)
|
|
);
|
|
if ($orderIds === []) {
|
|
$this->logger->warning('Ignored a 17TRACK webhook for an unknown tracking number.', [
|
|
'tracking_number' => $number,
|
|
]);
|
|
return [
|
|
'event' => $event,
|
|
'tracking_number' => $number,
|
|
'status' => 'ignored',
|
|
'message' => 'Unknown tracking number.',
|
|
];
|
|
}
|
|
$this->deliveryStatusUpdater->execute($orderIds);
|
|
return [
|
|
'event' => $event,
|
|
'tracking_number' => $number,
|
|
'status' => 'success',
|
|
'message' => 'Tracking update processed.',
|
|
];
|
|
}
|
|
|
|
if ($event === 'TRACKING_STOPPED') {
|
|
$this->tracking->saveStoppedInfo($number, $this->json->serialize($data));
|
|
return [
|
|
'event' => $event,
|
|
'tracking_number' => $number,
|
|
'status' => 'success',
|
|
'message' => 'Tracking stopped event processed.',
|
|
];
|
|
}
|
|
|
|
$this->logger->info('Ignored an unsupported 17TRACK webhook event.', [
|
|
'event' => $event,
|
|
'tracking_number' => $number,
|
|
]);
|
|
return [
|
|
'event' => $event,
|
|
'tracking_number' => $number,
|
|
'status' => 'ignored',
|
|
'message' => 'Unsupported webhook event.',
|
|
];
|
|
}
|
|
|
|
private function verifySignature(string $rawBody, string $signature, string $apiKey): void
|
|
{
|
|
$signature = strtolower(trim($signature));
|
|
if (!preg_match('/^[a-f0-9]{64}$/', $signature)) {
|
|
throw new AuthenticationException(__('The 17TRACK webhook signature is missing or invalid.'));
|
|
}
|
|
$expected = hash('sha256', $rawBody . '/' . $apiKey);
|
|
if (!hash_equals($expected, $signature)) {
|
|
throw new AuthenticationException(__('The 17TRACK webhook signature is missing or invalid.'));
|
|
}
|
|
}
|
|
}
|