false, 'error' => 'Database connection not initialized'], 500); } if ($_SERVER['REQUEST_METHOD'] === 'OPTIONS') { exit(0); } if ($_SERVER['REQUEST_METHOD'] !== 'POST') { og_respond(['success' => false, 'error' => 'Method not allowed'], 405); } $secret = og_env('PINGPONG_1V1_SHARED_SECRET'); if (!$secret) { og_respond(['success' => false, 'error' => 'Server not configured (missing PINGPONG_1V1_SHARED_SECRET)'], 500); } $raw = file_get_contents('php://input'); $check = og_require_node_signature($secret, $raw, 60000); if (empty($check['ok'])) { og_respond(['success' => false, 'error' => 'Invalid signature', 'code' => $check['error']], 401); } $payload = json_decode($raw, true); if (!$payload || !is_array($payload)) { og_respond(['success' => false, 'error' => 'Invalid JSON'], 400); } $matchId = isset($payload['matchId']) ? (int) $payload['matchId'] : 0; $winnerUserId = isset($payload['winnerUserId']) ? (int) $payload['winnerUserId'] : 0; $loserUserId = isset($payload['loserUserId']) ? (int) $payload['loserUserId'] : 0; $score = isset($payload['score']) ? (string) $payload['score'] : null; if ($matchId <= 0 || $winnerUserId <= 0 || $loserUserId <= 0 || !$score) { og_respond(['success' => false, 'error' => 'Missing required fields'], 400); } // Ensure table exists (idempotent) $pdo->exec( "CREATE TABLE IF NOT EXISTS rewards_jobs ( id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY, discipline VARCHAR(50) NOT NULL, mode VARCHAR(50) NOT NULL, match_id BIGINT UNSIGNED NOT NULL, payload_json LONGTEXT NOT NULL, status VARCHAR(20) NOT NULL DEFAULT 'queued', attempts INT NOT NULL DEFAULT 0, result_json LONGTEXT NULL, last_error TEXT NULL, created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, UNIQUE KEY uniq_match (discipline, mode, match_id), INDEX idx_status_created (status, created_at), INDEX idx_match (match_id) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4" ); // idempotent: avoid duplicate jobs if Node retries $stmt = $pdo->prepare( 'INSERT IGNORE INTO rewards_jobs (discipline, mode, match_id, payload_json, status) VALUES (:discipline, :mode, :match_id, :payload_json, :status)' ); $stmt->execute([ ':discipline' => 'ping-pong', ':mode' => '1v1', ':match_id' => $matchId, ':payload_json' => json_encode($payload, JSON_UNESCAPED_UNICODE), ':status' => 'queued', ]); $stmt2 = $pdo->prepare('SELECT id, status FROM rewards_jobs WHERE discipline = :d AND mode = :m AND match_id = :mid'); $stmt2->execute([':d' => 'ping-pong', ':m' => '1v1', ':mid' => $matchId]); $row = $stmt2->fetch(PDO::FETCH_ASSOC); if (!$row) { og_respond(['success' => false, 'error' => 'Failed to enqueue rewards job'], 500); } $jobId = (int) $row['id']; og_respond([ 'success' => true, 'jobId' => $jobId, 'status' => 'queued', // UI can show "processing" animation and poll status endpoint. ]);