false, 'error' => 'Use POST'], 405); } $body = require_json_body(); $code = trim((string) ($body['code'] ?? '')); $redirectUri = trim((string) ($body['redirectUri'] ?? '')); $mergeGuestKey = trim((string) ($body['mergeGuestKey'] ?? '')); if ($code === '') { json_response(['ok' => false, 'error' => 'missing code'], 400); } $oauth = read_store()['oauth'] ?? []; $clientId = trim((string) ($oauth['googleClientId'] ?? '')); $clientSecret = trim((string) ($oauth['googleClientSecret'] ?? '')); $configuredRedirect = trim((string) ($oauth['googleRedirectUri'] ?? '')); if ($clientId === '' || $clientSecret === '') { json_response(['ok' => false, 'error' => 'ยังไม่ได้ตั้งค่า Google OAuth ใน Admin'], 503); } if ($redirectUri === '') { $redirectUri = $configuredRedirect; } if ($redirectUri === '') { json_response(['ok' => false, 'error' => 'missing redirectUri'], 400); } if ($configuredRedirect !== '' && $redirectUri !== $configuredRedirect) { json_response(['ok' => false, 'error' => 'redirectUri ไม่ตรงกับที่ตั้งใน Admin'], 400); } if (!function_exists('curl_init')) { json_response(['ok' => false, 'error' => 'PHP cURL ไม่พร้อมใช้งาน'], 500); } $tokenPayload = http_build_query([ 'code' => $code, 'client_id' => $clientId, 'client_secret' => $clientSecret, 'redirect_uri' => $redirectUri, 'grant_type' => 'authorization_code', ]); $ch = curl_init('https://oauth2.googleapis.com/token'); curl_setopt_array($ch, [ CURLOPT_POST => true, CURLOPT_POSTFIELDS => $tokenPayload, CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => ['Content-Type: application/x-www-form-urlencoded'], CURLOPT_TIMEOUT => 20, ]); $tokenRaw = curl_exec($ch); $tokenHttp = (int) curl_getinfo($ch, CURLINFO_HTTP_CODE); curl_close($ch); $tokenJson = json_decode((string) $tokenRaw, true); if ($tokenHttp < 200 || $tokenHttp >= 300 || !is_array($tokenJson)) { json_response(['ok' => false, 'error' => 'แลก token จาก Google ไม่สำเร็จ', 'detail' => (string) $tokenRaw], 502); } $accessToken = trim((string) ($tokenJson['access_token'] ?? '')); if ($accessToken === '') { json_response(['ok' => false, 'error' => 'Google ไม่คืน access_token', 'detail' => $tokenJson], 502); } $ch2 = curl_init('https://www.googleapis.com/oauth2/v3/userinfo'); curl_setopt_array($ch2, [ CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => ['Authorization: Bearer ' . $accessToken], CURLOPT_TIMEOUT => 20, ]); $userRaw = curl_exec($ch2); $userHttp = (int) curl_getinfo($ch2, CURLINFO_HTTP_CODE); curl_close($ch2); $user = json_decode((string) $userRaw, true); if ($userHttp < 200 || $userHttp >= 300 || !is_array($user)) { json_response(['ok' => false, 'error' => 'อ่านข้อมูลผู้ใช้จาก Google ไม่สำเร็จ'], 502); } $googleSub = trim((string) ($user['sub'] ?? '')); if ($googleSub === '') { json_response(['ok' => false, 'error' => 'Google ไม่คืน sub (user id)'], 502); } $playerKey = google_player_key($googleSub); if (!valid_player_key($playerKey)) { json_response(['ok' => false, 'error' => 'playerKey จาก Google ไม่ถูกต้อง'], 500); } $email = trim((string) ($user['email'] ?? '')); $name = trim((string) ($user['name'] ?? '')); if ($name === '') { $name = trim((string) ($user['given_name'] ?? '')); } if ($name === '') { $name = $email !== '' ? preg_replace('/@.*$/', '', $email) : 'Google Player'; } if (function_exists('mb_substr')) { $name = mb_substr($name, 0, 32); } else { $name = substr($name, 0, 32); } $picture = trim((string) ($user['picture'] ?? '')); $now = gmdate('c'); $store = read_store(); if (!isset($store['accounts']) || !is_array($store['accounts'])) { $store['accounts'] = []; } $idx = find_account_index_by_player_key($store['accounts'], $playerKey); $isNew = $idx < 0; if ($idx < 0) { $store['accounts'][] = [ 'id' => new_id(), 'email' => $email, 'displayName' => $name, 'loginType' => 'google', 'providerUserId' => $playerKey, 'googleSub' => $googleSub, 'avatarUrl' => $picture, 'notes' => 'oauth: google', 'blocked' => false, 'coins' => 0, 'createdAt' => $now, 'updatedAt' => $now, ]; $idx = count($store['accounts']) - 1; } else { $store['accounts'][$idx]['loginType'] = 'google'; $store['accounts'][$idx]['email'] = $email !== '' ? $email : ($store['accounts'][$idx]['email'] ?? ''); if ($name !== '') { $store['accounts'][$idx]['displayName'] = $name; } $store['accounts'][$idx]['googleSub'] = $googleSub; if ($picture !== '') { $store['accounts'][$idx]['avatarUrl'] = $picture; } $store['accounts'][$idx]['updatedAt'] = $now; } if (!empty($store['accounts'][$idx]['blocked'])) { json_response(['ok' => false, 'error' => 'บัญชีนี้ถูกระงับ'], 403); } if ($isNew && $mergeGuestKey !== '') { merge_guest_account_into($store, $idx, $mergeGuestKey); } if (!write_store($store)) { json_response(['ok' => false, 'error' => 'บันทึกบัญชีไม่สำเร็จ'], 500); } $acc = $store['accounts'][$idx] ?? []; json_response([ 'ok' => true, 'playerKey' => $playerKey, 'loginType' => 'google', 'displayName' => (string) ($acc['displayName'] ?? $name), 'email' => (string) ($acc['email'] ?? $email), 'avatarUrl' => (string) ($acc['avatarUrl'] ?? $picture), 'coins' => max(0, (int) ($acc['coins'] ?? 0)), 'accountId' => (string) ($acc['id'] ?? ''), 'isNew' => $isNew, ]);