| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222 |
- <?php
- namespace App\Game\Services;
- use App\Game\GlobalUserInfo;
- use App\Game\WebChannelConfig;
- use App\Http\helper\HttpCurl;
- use App\Jobs\FacebookServerEvent;
- use Illuminate\Http\Request;
- use Illuminate\Support\Facades\Log;
- class FacebookEventService
- {
- /**
- * 通过服务端 Facebook Conversions API 上报事件
- *
- * 去重策略(前后端统一):
- * 仅依赖 Facebook 官方的 event_id 去重:
- * - 前端 fbq 上报时必须使用同一个 event_id
- * - 服务端调用 Conversions API 时也传入同一个 event_id
- * - Facebook 会自动把 Browser + Server 事件按照 event_id 合并
- *
- * @param string $eventName
- * @param string $eventId
- * @param int|string $userId
- * @param float|int|null $value
- * @param string|null $currency
- * @param string $pixelId
- * @param string $accessToken
- * @param array $extraCustomData
- */
- public static function trackEvent(
- string $eventName,
- string $eventId,
- $userId,
- $value = null,
- ?string $currency = null,
- string $pixelId = '',
- string $accessToken = '',
- array $extraCustomData = []
- ): void {
- if (empty($pixelId) || empty($accessToken)) {
- return;
- }
- $eventTime = time();
- $externalId = (string)$userId;
- // Facebook Conversions API 要求 user_data 做哈希,这里只对 external_id 做一次 sha256
- $userData = [
- 'external_id' => hash('sha256', $externalId),
- ];
- $customData = array_merge(
- array_filter([
- 'currency' => $currency,
- 'value' => $value,
- ], static function ($v) {
- return $v !== null && $v !== '';
- }),
- $extraCustomData
- );
- $payload = [
- 'data' => [
- [
- 'event_name' => $eventName,
- 'event_time' => $eventTime,
- 'action_source' => 'website',
- 'event_id' => $eventId,
- 'user_data' => $userData,
- 'custom_data' => $customData,
- ],
- ],
- ];
- $url = sprintf(
- 'https://graph.facebook.com/v25.0/%s/events?access_token=%s',
- $pixelId,
- $accessToken
- );
- try {
- $http = new HttpCurl();
- $response = $http->curlPost($url, $payload, 'json', true);
- Log::info('FacebookEventService trackEvent success', [
- 'event_name' => $eventName,
- 'event_id' => $eventId,
- 'user_id' => $userId,
- 'pixel_id' => $pixelId,
- 'payload' => $payload,
- 'response' => $response,
- ]);
- } catch (\Throwable $e) {
- Log::error('FacebookEventService trackEvent error', [
- 'event_name' => $eventName,
- 'event_id' => $eventId,
- 'user_id' => $userId,
- 'pixel_id' => $pixelId,
- 'message' => $e->getMessage(),
- ]);
- }
- }
- /**
- * 注册完成事件(对应前端 CompleteRegistration)
- */
- public static function trackCompleteRegistration(GlobalUserInfo $user, WebChannelConfig $config): void
- {
- if (empty($config->PlatformID) || empty($config->PlatformToken)) {
- return;
- }
- $pixelId = $config->PlatformID;
- $accessToken = $config->PlatformToken;
- // 建议前端也使用相同的 event_id:reg_{UserID}
- $eventId = 'reg_' . $user->UserID;
- FacebookServerEvent::dispatch(
- 'CompleteRegistration',
- $eventId,
- $user->UserID,
- null,
- null,
- $pixelId,
- $accessToken
- );
- }
- /**
- * 支付事件(对应前端 pay + firstpayD0/firstpayD1/payagain/Purchase)
- *
- * @param int|string $userId
- * @param string $orderSn
- * @param float|int $amount
- * @param string $currency
- * @param bool $isFirst
- * @param bool $isD0
- * @param int $channel
- */
- public static function trackPayEvent(
- $userId,
- string $orderSn,
- $amount,
- string $currency,
- bool $isFirst,
- bool $isD0,
- int $channel
- ): void {
- $config = WebChannelConfig::getByChannel($channel);
- if (!$config || empty($config->PlatformID) || empty($config->PlatformToken)) {
- return;
- }
- $pixelId = $config->PlatformID;
- $accessToken = $config->PlatformToken;
- // 统一使用同一个 event_id(与前端保持一致)
- // 前端所有支付相关事件共用:purchase_{order_sn}
- $eventId = 'purchase_' . $orderSn;
- // 标准 Purchase 事件(所有支付都会上报)
- FacebookServerEvent::dispatch(
- 'Purchase',
- $eventId,
- $userId,
- $amount,
- $currency,
- $pixelId,
- $accessToken
- );
- // 首次支付
- if ($isFirst) {
- FacebookServerEvent::dispatch(
- 'firstpayD1',
- $eventId,
- $userId,
- $amount,
- $currency,
- $pixelId,
- $accessToken
- );
- if ($isD0) {
- // D0 首充自定义事件
- FacebookServerEvent::dispatch(
- 'firstpayD0',
- $eventId,
- $userId,
- $amount,
- $currency,
- $pixelId,
- $accessToken
- );
- // D0 首充标准事件:AddToWishlist(与前端保持一致)
- FacebookServerEvent::dispatch(
- 'AddToWishlist',
- $eventId,
- $userId,
- $amount,
- $currency,
- $pixelId,
- $accessToken
- );
- }
- } elseif (!$isD0) {
- // 复充(非首次且非 D0)
- FacebookServerEvent::dispatch(
- 'payagain',
- $eventId,
- $userId,
- $amount,
- $currency,
- $pixelId,
- $accessToken
- );
- }
- }
- }
|