170 lines
5.8 KiB
PHP
170 lines
5.8 KiB
PHP
|
|
<?php
|
|||
|
|
|
|||
|
|
declare(strict_types=1);
|
|||
|
|
|
|||
|
|
namespace app\common\service;
|
|||
|
|
|
|||
|
|
use think\Log;
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 分流中转页服务端像素回传(Facebook CAPI / TikTok Events API)
|
|||
|
|
*/
|
|||
|
|
class SplitPixelPostbackService
|
|||
|
|
{
|
|||
|
|
/**
|
|||
|
|
* @param array{facebook: array<int, array<string, mixed>>, tiktok: array<int, array<string, mixed>>} $config
|
|||
|
|
*/
|
|||
|
|
public static function dispatch(array $config, string $clientIp, string $userAgent, string $eventSourceUrl = ''): void
|
|||
|
|
{
|
|||
|
|
foreach (SplitPixelConfigService::getEnabledSorted($config, SplitPixelConfigService::PLATFORM_FACEBOOK) as $row) {
|
|||
|
|
if ((int) ($row['server_postback'] ?? 0) !== 1) {
|
|||
|
|
continue;
|
|||
|
|
}
|
|||
|
|
$token = trim((string) ($row['access_token'] ?? ''));
|
|||
|
|
if ($token === '') {
|
|||
|
|
continue;
|
|||
|
|
}
|
|||
|
|
self::sendFacebook($row, $token, $clientIp, $userAgent, $eventSourceUrl);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
foreach (SplitPixelConfigService::getEnabledSorted($config, SplitPixelConfigService::PLATFORM_TIKTOK) as $row) {
|
|||
|
|
if ((int) ($row['server_postback'] ?? 0) !== 1) {
|
|||
|
|
continue;
|
|||
|
|
}
|
|||
|
|
$token = trim((string) ($row['access_token'] ?? ''));
|
|||
|
|
if ($token === '') {
|
|||
|
|
continue;
|
|||
|
|
}
|
|||
|
|
self::sendTikTok($row, $token, $clientIp, $userAgent, $eventSourceUrl);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* @param array<string, mixed> $row
|
|||
|
|
*/
|
|||
|
|
private static function sendFacebook(array $row, string $accessToken, string $clientIp, string $userAgent, string $eventSourceUrl): void
|
|||
|
|
{
|
|||
|
|
$pixelId = trim((string) ($row['pixel_id'] ?? ''));
|
|||
|
|
if ($pixelId === '') {
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
$eventName = (string) ($row['event'] ?? 'PageView');
|
|||
|
|
$testCode = trim((string) ($row['test_code'] ?? ''));
|
|||
|
|
|
|||
|
|
$payload = [
|
|||
|
|
'data' => [[
|
|||
|
|
'event_name' => $eventName,
|
|||
|
|
'event_time' => time(),
|
|||
|
|
'action_source' => 'website',
|
|||
|
|
'user_data' => array_filter([
|
|||
|
|
'client_ip_address' => $clientIp !== '' ? $clientIp : null,
|
|||
|
|
'client_user_agent' => $userAgent !== '' ? $userAgent : null,
|
|||
|
|
]),
|
|||
|
|
]],
|
|||
|
|
];
|
|||
|
|
if ($eventSourceUrl !== '') {
|
|||
|
|
$payload['data'][0]['event_source_url'] = $eventSourceUrl;
|
|||
|
|
}
|
|||
|
|
if ($testCode !== '') {
|
|||
|
|
$payload['test_event_code'] = $testCode;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
$url = 'https://graph.facebook.com/v19.0/' . rawurlencode($pixelId) . '/events?access_token=' . rawurlencode($accessToken);
|
|||
|
|
self::postJson($url, $payload, [], 'facebook', $pixelId);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* @param array<string, mixed> $row
|
|||
|
|
*/
|
|||
|
|
private static function sendTikTok(array $row, string $accessToken, string $clientIp, string $userAgent, string $eventSourceUrl): void
|
|||
|
|
{
|
|||
|
|
$pixelId = trim((string) ($row['pixel_id'] ?? ''));
|
|||
|
|
if ($pixelId === '') {
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
$event = self::mapTikTokServerEvent((string) ($row['event'] ?? 'PageView'));
|
|||
|
|
$testCode = trim((string) ($row['test_code'] ?? ''));
|
|||
|
|
|
|||
|
|
$payload = [
|
|||
|
|
'pixel_code' => $pixelId,
|
|||
|
|
'event' => $event,
|
|||
|
|
'event_id' => uniqid('split_', true),
|
|||
|
|
'timestamp' => gmdate('c'),
|
|||
|
|
'context' => array_filter([
|
|||
|
|
'ip' => $clientIp !== '' ? $clientIp : null,
|
|||
|
|
'user_agent' => $userAgent !== '' ? $userAgent : null,
|
|||
|
|
'page' => $eventSourceUrl !== '' ? ['url' => $eventSourceUrl] : null,
|
|||
|
|
]),
|
|||
|
|
];
|
|||
|
|
if ($testCode !== '') {
|
|||
|
|
$payload['test_event_code'] = $testCode;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
$url = 'https://business-api.tiktok.com/open_api/v1.3/event/track/';
|
|||
|
|
self::postJson($url, $payload, [
|
|||
|
|
'Access-Token: ' . $accessToken,
|
|||
|
|
'Content-Type: application/json',
|
|||
|
|
], 'tiktok', $pixelId);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private static function mapTikTokServerEvent(string $event): string
|
|||
|
|
{
|
|||
|
|
$map = [
|
|||
|
|
'PageView' => 'Pageview',
|
|||
|
|
'Lead' => 'SubmitForm',
|
|||
|
|
'Contact' => 'Contact',
|
|||
|
|
'AddToCart' => 'AddToCart',
|
|||
|
|
'Purchase' => 'CompletePayment',
|
|||
|
|
'Subscribe' => 'Subscribe',
|
|||
|
|
];
|
|||
|
|
|
|||
|
|
return $map[$event] ?? 'Pageview';
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* @param array<string, mixed> $payload
|
|||
|
|
* @param array<int, string> $headers
|
|||
|
|
*/
|
|||
|
|
private static function postJson(string $url, array $payload, array $headers, string $platform, string $pixelId): void
|
|||
|
|
{
|
|||
|
|
$json = json_encode($payload, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
|
|||
|
|
if ($json === false) {
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
$ch = curl_init($url);
|
|||
|
|
if ($ch === false) {
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
$defaultHeaders = ['Content-Type: application/json'];
|
|||
|
|
curl_setopt_array($ch, [
|
|||
|
|
CURLOPT_POST => true,
|
|||
|
|
CURLOPT_POSTFIELDS => $json,
|
|||
|
|
CURLOPT_HTTPHEADER => array_merge($defaultHeaders, $headers),
|
|||
|
|
CURLOPT_RETURNTRANSFER => true,
|
|||
|
|
CURLOPT_TIMEOUT => 3,
|
|||
|
|
CURLOPT_CONNECTTIMEOUT => 2,
|
|||
|
|
]);
|
|||
|
|
|
|||
|
|
$response = curl_exec($ch);
|
|||
|
|
$errno = curl_errno($ch);
|
|||
|
|
$httpCode = (int) curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
|||
|
|
curl_close($ch);
|
|||
|
|
|
|||
|
|
if ($errno !== 0 || ($httpCode >= 400 && $httpCode !== 0)) {
|
|||
|
|
Log::error(sprintf(
|
|||
|
|
'Split pixel postback failed platform=%s pixel=%s http=%d curl=%d',
|
|||
|
|
$platform,
|
|||
|
|
$pixelId,
|
|||
|
|
$httpCode,
|
|||
|
|
$errno
|
|||
|
|
));
|
|||
|
|
} elseif (is_string($response) && strpos($response, '"error"') !== false) {
|
|||
|
|
Log::error(sprintf('Split pixel postback api error platform=%s pixel=%s', $platform, $pixelId));
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|