AfEvent.php 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399
  1. <?php
  2. namespace App\Jobs;
  3. use App\Game\GlobalUserInfo;
  4. use App\Game\WebChannelConfig;
  5. use App\Services\ApkService;
  6. use GuzzleHttp\Client;
  7. use Illuminate\Bus\Queueable;
  8. use Illuminate\Queue\SerializesModels;
  9. use Illuminate\Queue\InteractsWithQueue;
  10. use Illuminate\Contracts\Queue\ShouldQueue;
  11. use Illuminate\Foundation\Bus\Dispatchable;
  12. use Illuminate\Support\Facades\DB;
  13. use Illuminate\Support\Facades\Log;
  14. class AfEvent implements ShouldQueue
  15. {
  16. use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
  17. protected $data;
  18. protected $user;
  19. /**
  20. * Create a new job instance.
  21. *
  22. * @return void
  23. */
  24. public function __construct($data = [])
  25. {
  26. $this->data = $data;
  27. }
  28. /**
  29. * Execute the job.
  30. *
  31. * @return void
  32. */
  33. public function handle()
  34. {
  35. Log::channel('adjustEvent')->info('enter:'.json_encode($this->data));
  36. if (empty($this->data)) return;
  37. [$UserID, $payAmt, $AdId, $eventType, $channel, $pfType, $params] = $this->normalizePayload($this->data);
  38. if (!$UserID) {
  39. return;
  40. }
  41. if (empty($eventType)) $eventType = 2;
  42. Log::channel('adjustEvent')->info('接收数据' . \GuzzleHttp\json_encode($this->data));
  43. if ($this->isFacebookPlatform($pfType)) {
  44. $this->reportFacebookEvents($channel, $UserID, $payAmt, $AdId, $params);
  45. return;
  46. }
  47. if ($payAmt && !empty($AdId)) {
  48. // 执行AF事件
  49. $dao = new \App\dao\af\AfEvent();
  50. $dao->pay($AdId, $UserID, $eventType);
  51. $dao->paySum($AdId, $UserID, $payAmt, $eventType);
  52. $dao->payCount($AdId, $UserID, $eventType);
  53. }
  54. // $dao->recordKwaiRecharge($UserID, $payAmt);
  55. // $dao->recordGoogleRecharge($UserID, $payAmt);
  56. }
  57. protected function normalizePayload($data)
  58. {
  59. if (isset($data['UserID'])) {
  60. $user = GlobalUserInfo::getGameUserInfo('UserID', $data['UserID']);
  61. $this->user = $user;
  62. $params = is_array($data['params'] ?? null) ? $data['params'] : [];
  63. $orderSn = $params['order_sn'] ?? '';
  64. $orderInfo = $orderSn === '' ? null : DB::connection('write')->table('agent.dbo.order')
  65. ->where('order_sn', $orderSn)
  66. ->select('AdId', 'eventType','Channel')
  67. ->first();
  68. $channel = $data['channel']
  69. ?? ($orderInfo->Channel ?? null)
  70. ?? ($user->Channel ?? null)
  71. ?? $this->extractChannelFromCookie($data['UserID'])
  72. ?? 100;
  73. $channelConfig = $channel ? WebChannelConfig::getByChannel($channel) : null;
  74. $params['config'] = $channelConfig;
  75. $params['event_name'] = $data['event_name'] ?? ($params['event_name'] ?? '');
  76. $params['custom_data'] = is_array($data['custom_data'] ?? null) ? $data['custom_data'] : ($params['custom_data'] ?? []);
  77. Log::channel('adjustEvent')->info('normalizePayload:' . json_encode([
  78. 'user_id' => $data['UserID'] ?? 0,
  79. 'event_name' => $params['event_name'],
  80. 'order_sn' => $orderSn,
  81. 'order_channel' => $orderInfo->Channel ?? null,
  82. 'global_channel' => $user->Channel ?? null,
  83. 'final_channel' => $channel,
  84. 'platform_name' => $channelConfig->PlatformName ?? null,
  85. ], JSON_UNESCAPED_UNICODE));
  86. return [
  87. $data['UserID'] ?? 0,
  88. $params['golds'] ?? 0,
  89. $orderInfo->AdId ?? '',
  90. $orderInfo->eventType ?? 2,
  91. $channel,
  92. $channelConfig->PlatformName ?? '',
  93. $params,
  94. ];
  95. }
  96. return [
  97. $data[0] ?? 0,
  98. $data[1] ?? 0,
  99. $data[2] ?? '',
  100. $data[3] ?? 2,
  101. $data[4] ?? 0,
  102. $data[5] ?? '',
  103. $data[6] ?? [],
  104. ];
  105. }
  106. protected function isFacebookPlatform($pfType)
  107. {
  108. return is_string($pfType) && stripos($pfType, 'fb') !== false;
  109. }
  110. protected function reportFacebookEvents($channel, $userID, $payAmt, $adId, array $params = [])
  111. {
  112. try {
  113. $config = $params['config'] ?? null;
  114. if (!$config || !$this->isFacebookPlatform($config->PlatformName ?? '')) {
  115. Log::channel('adjustEvent')->info('facebook s2s skipped: invalid config', [
  116. 'channel' => $channel,
  117. 'user_id' => $userID,
  118. 'platform_name' => $config->PlatformName ?? null,
  119. ]);
  120. return;
  121. }
  122. $pixelId = trim((string)($config->PlatformID ?? ''));
  123. $accessToken = trim((string)($config->PlatformToken ?? ''));
  124. if ($pixelId === '' || $accessToken === '') {
  125. Log::channel('adjustEvent')->info('facebook s2s skipped: missing pixel config', [
  126. 'channel' => $channel,
  127. 'user_id' => $userID,
  128. 'pixel_id' => $pixelId,
  129. ]);
  130. return;
  131. }
  132. $events = $this->buildFacebookEvents($userID, $payAmt, $adId, $params);
  133. if (empty($events)) {
  134. return;
  135. }
  136. $url = "https://graph.facebook.com/v21.0/{$pixelId}/events";
  137. $payload = [
  138. 'data' => $events,
  139. 'access_token' => $accessToken,
  140. ];
  141. Log::channel('adjustEvent')->info('facebook s2s request ' . json_encode([
  142. 'channel' => $channel,
  143. 'user_id' => $userID,
  144. 'pixel_id' => $pixelId,
  145. 'event_count' => count($events),
  146. 'payload' => $payload,
  147. ], JSON_UNESCAPED_UNICODE));
  148. $client = new Client([
  149. 'timeout' => 10,
  150. 'http_errors' => false,
  151. ]);
  152. $response = $client->post($url, ['json' => $payload]);
  153. Log::channel('adjustEvent')->info('facebook s2s response ' . json_encode([
  154. 'channel' => $channel,
  155. 'user_id' => $userID,
  156. 'status' => $response->getStatusCode(),
  157. 'body' => (string)$response->getBody(),
  158. ], JSON_UNESCAPED_UNICODE));
  159. } catch (\Throwable $e) {
  160. Log::channel('adjustEvent')->info('facebook s2s failed ' . json_encode([
  161. 'channel' => $channel,
  162. 'user_id' => $userID,
  163. 'message' => $e->getMessage(),
  164. ], JSON_UNESCAPED_UNICODE));
  165. }
  166. }
  167. protected function buildFacebookEvents($userID, $payAmt, $adId, array $params = [])
  168. {
  169. $cookieInfo = $this->getFacebookCookieInfo($userID);
  170. if (!empty($params['event_name'])) {
  171. return $this->buildSingleFacebookEvent($userID, $adId, $params, $cookieInfo);
  172. }
  173. $orderSn = $params['order_sn'] ?? '';
  174. $first = !empty($params['first']);
  175. $isD0 = !empty($params['isd0']);
  176. $currency = env('CONFIG_24680_CURRENCY', 'BRL');
  177. $userData = $this->buildFacebookUserData($userID, $adId, $params, $cookieInfo);
  178. $customData = array_filter([
  179. 'value' => (float)$payAmt,
  180. 'currency' => $currency,
  181. ], function ($value) {
  182. return $value !== '' && $value !== null;
  183. });
  184. $origin = $cookieInfo['Origin'] ?? '';
  185. $events = [];
  186. if ($first) {
  187. if ($isD0) {
  188. $events[] = $this->makeFacebookEvent('firstpayD0', 'd0_' . $orderSn, $userData, $customData, $origin);
  189. $events[] = $this->makeFacebookEvent('AddToWishlist', 'aw_' . $orderSn, $userData, $customData, $origin);
  190. } else {
  191. $events[] = $this->makeFacebookEvent('firstpayD1', 'd1_' . $orderSn, $userData, $customData, $origin);
  192. $events[] = $this->makeFacebookEvent('AddToCart', 'ac_' . $orderSn, $userData, $customData, $origin);
  193. }
  194. } else if (!$isD0) {
  195. $events[] = $this->makeFacebookEvent('payagain', 'pa_' . $orderSn, $userData, $customData, $origin);
  196. $events[] = $this->makeFacebookEvent('InitiateCheckout', 'ic_' . $orderSn, $userData, $customData, $origin);
  197. }
  198. $events[] = $this->makeFacebookEvent('Purchase', 'pay_' . $orderSn, $userData, $customData, $origin);
  199. return $events;
  200. }
  201. protected function buildSingleFacebookEvent($userID, $adId, array $params = [], array $cookieInfo = [])
  202. {
  203. $user = $this->user;
  204. $eventName = trim((string)($params['event_name'] ?? ''));
  205. if ($eventName === '') {
  206. return [];
  207. }
  208. $eventId = $eventName . '_' . ($user->FPID ?? '');
  209. $customData = is_array($params['custom_data'] ?? null) ? $params['custom_data'] : [];
  210. $customData = array_filter($customData, function ($value) {
  211. return $value !== '' && $value !== null;
  212. });
  213. return [
  214. $this->makeFacebookEvent(
  215. $eventName,
  216. $eventId,
  217. $this->buildFacebookUserData($userID, $adId, $params, $cookieInfo),
  218. $customData,
  219. $cookieInfo['Origin'] ?? ''
  220. )
  221. ];
  222. }
  223. protected function buildFacebookUserData($userID, $adId, array $params = [], array $cookieInfo = [])
  224. {
  225. $user = $this->user;
  226. if (empty($cookieInfo)) {
  227. $cookieInfo = $this->getFacebookCookieInfo($userID);
  228. }
  229. $cookieValues = $this->parseCookieValues($cookieInfo['Cookie'] ?? '');
  230. $browserIds = $this->resolveFacebookBrowserIds($userID, $params, $cookieInfo, $cookieValues);
  231. $externalId = $params['udid'] ?? ($user ? md5($user->UserID) : md5($userID));
  232. $userData = [
  233. 'external_id' => (string)$externalId,
  234. 'fbp' => $browserIds['fbp'],
  235. 'fbc' => $browserIds['fbc'],
  236. 'client_ip_address' => $cookieInfo['IP'] ?? '',
  237. 'client_user_agent' => $cookieInfo['ClickUA'] ?? $cookieInfo['GameUA'] ?? '',
  238. ];
  239. return array_filter($userData, function ($value) {
  240. return $value !== '' && $value !== null;
  241. });
  242. }
  243. protected function getFacebookCookieInfo($userID)
  244. {
  245. $user = $this->user;
  246. return ApkService::loadCookie($userID, $user->FPID ?? '', $user->FF ?? '') ?: [];
  247. }
  248. protected function parseCookieValues($cookieString)
  249. {
  250. $cookies = [];
  251. if (!is_string($cookieString) || $cookieString === '') {
  252. return $cookies;
  253. }
  254. foreach (explode(';', $cookieString) as $part) {
  255. $part = trim($part);
  256. if ($part === '' || strpos($part, '=') === false) {
  257. continue;
  258. }
  259. [$name, $value] = explode('=', $part, 2);
  260. $cookies[trim($name)] = trim($value);
  261. }
  262. return $cookies;
  263. }
  264. protected function resolveFacebookBrowserIds($userID, array $params, array $cookieInfo, array $cookieValues)
  265. {
  266. $fbp = trim((string)($cookieValues['_fbp'] ?? ''));
  267. $fbc = trim((string)($cookieValues['_fbc'] ?? ''));
  268. $cookieParamsFbclid = '';
  269. if (!empty($cookieInfo['Params'])) {
  270. $cookieParams = json_decode($cookieInfo['Params'], true);
  271. if (is_array($cookieParams)) {
  272. $cookieParamsFbclid = trim((string)($cookieParams['fbclid'] ?? ''));
  273. }
  274. }
  275. if ($cookieParamsFbclid === '') {
  276. return compact('fbp', 'fbc');
  277. }
  278. $timestamp = $this->resolveFacebookCookieTimestamp($cookieInfo);
  279. $currentFbclid = ApkService::extractFbclid($fbc);
  280. if ($fbc === '' || $currentFbclid === '' || $currentFbclid !== $cookieParamsFbclid) {
  281. $fbc = 'fb.1.' . $timestamp . '.' . $cookieParamsFbclid;
  282. }
  283. if ($fbp === '') {
  284. $seed = implode('|', [
  285. $userID,
  286. $cookieInfo['FPID'] ?? '',
  287. $cookieInfo['FF'] ?? '',
  288. $cookieInfo['IP'] ?? '',
  289. $cookieInfo['CreateTime'] ?? '',
  290. ]);
  291. $fbp = 'fb.1.' . $timestamp . '.' . substr(md5($seed), 0, 16);
  292. }
  293. return compact('fbp', 'fbc');
  294. }
  295. protected function resolveFacebookCookieTimestamp(array $cookieInfo)
  296. {
  297. $rawTime = $cookieInfo['CreateTime'] ?? '';
  298. if (!empty($rawTime)) {
  299. $timestamp = strtotime((string)$rawTime);
  300. if ($timestamp !== false) {
  301. return (string)($timestamp * 1000);
  302. }
  303. }
  304. return (string)round(microtime(true) * 1000);
  305. }
  306. protected function extractChannelFromCookie($userID)
  307. {
  308. $cookieInfo = $this->getFacebookCookieInfo($userID);
  309. if (!$cookieInfo || empty($cookieInfo['Params'])) {
  310. return null;
  311. }
  312. $params = json_decode($cookieInfo['Params'], true);
  313. if (!is_array($params)) {
  314. return null;
  315. }
  316. return $params['c'] ?? null;
  317. }
  318. protected function makeFacebookEvent($eventName, $eventId, array $userData, array $customData, $eventSourceUrl = '')
  319. {
  320. $event = [
  321. 'event_name' => $eventName,
  322. 'event_time' => time(),
  323. 'event_id' => $eventId,
  324. 'action_source' => 'website',
  325. 'user_data' => $userData,
  326. ];
  327. if (!empty($customData)) {
  328. $event['custom_data'] = (object)$customData;
  329. }
  330. if (!empty($eventSourceUrl)) {
  331. $event['event_source_url'] = $eventSourceUrl;
  332. }
  333. return $event;
  334. }
  335. }