<?php
 namespace App\Services\Tracking\Providers; use App\Services\Tracking\Contracts\TrackingProvider; use App\Services\Tracking\TrackingEvent; use Illuminate\Support\Facades\Http; use Illuminate\Support\Facades\Log; class TiktokEventsApiProvider implements TrackingProvider { private const ENDPOINT = 'https://business-api.tiktok.com/open_api/v1.3/event/track/'; private const NAME_MAP = [ 'Purchase' => 'CompletePayment', 'AddToCart' => 'AddToCart', 'InitiateCheckout' => 'InitiateCheckout', 'CompleteRegistration' => 'CompleteRegistration', 'ViewContent' => 'ViewContent', 'Lead' => 'SubmitForm', ]; public function isEnabled(): bool { return !empty(setting('tiktok_pixel_id')) && !empty(setting('tiktok_events_api_token')); } public function send(TrackingEvent $event): void { if (!isset(self::NAME_MAP[$event->name])) return; $pixelId = setting('tiktok_pixel_id'); $token = setting('tiktok_events_api_token'); $testCode = setting('tiktok_events_test_event_code'); $payload = [ 'event_source' => 'web', 'event_source_id' => $pixelId, 'data' => [$this->buildEventData($event)], ]; if (!empty($testCode)) { $payload['test_event_code'] = $testCode; } $response = Http::timeout(5) ->withHeaders(['Access-Token' => $token]) ->post(self::ENDPOINT, $payload); if ($response->failed() || ($response->json('code') !== 0 && $response->json('code') !== null)) { Log::channel('tracking')->error('TikTok Events API send failed', [ 'status' => $response->status(), 'body' => $response->body(), 'event' => $event->name, 'event_id' => $event->eventId, ]); throw new \RuntimeException("TikTok Events API failed with status {$response->status()}"); } Log::channel('tracking')->info('TikTok Events API sent', [ 'event' => $event->name, 'event_id' => $event->eventId, 'status' => $response->status(), ]); } private function buildEventData(TrackingEvent $event): array { $data = [ 'event' => self::NAME_MAP[$event->name], 'event_time' => $event->timestamp, 'user' => $this->buildUser($event), 'properties' => $this->buildProperties($event), ]; if ($event->eventId) $data['event_id'] = $event->eventId; if ($event->sourceUrl) { $data['page'] = ['url' => $event->sourceUrl]; } return $data; } private function buildUser(TrackingEvent $event): array { $u = []; $user = $event->user; if (!$user) return $u; if ($user->emailSha256) $u['email'] = $user->emailSha256; if ($user->phoneSha256) $u['phone'] = $user->phoneSha256; if ($user->ipAddress) $u['ip'] = $user->ipAddress; if ($user->userAgent) $u['user_agent'] = $user->userAgent; if ($user->ttclid) $u['ttclid'] = $user->ttclid; return $u; } private function buildProperties(TrackingEvent $event): array { $p = []; if (isset($event->params['value'])) $p['value'] = (float) $event->params['value']; if (isset($event->params['currency'])) $p['currency'] = $event->params['currency']; if (isset($event->params['transaction_id'])) $p['order_id'] = $event->params['transaction_id']; if (isset($event->params['contents']) && is_array($event->params['contents'])) { $p['contents'] = array_map(fn ($c) => [ 'content_id' => (string) ($c['id'] ?? ''), 'quantity' => (int) ($c['quantity'] ?? 1), 'price' => (float) ($c['item_price'] ?? 0), ], $event->params['contents']); } return $p; } } 