false, 'error' => 'Método no permitido']); exit; } if (empty($_FILES['photo']) || $_FILES['photo']['error'] !== UPLOAD_ERR_OK) { $code = $_FILES['photo']['error'] ?? -1; echo json_encode(['success' => false, 'error' => "No se recibió archivo válido (código {$code})"]); exit; } $file = $_FILES['photo']; // Validar tipo MIME $allowedMimes = ['image/jpeg', 'image/png']; $finfo = new finfo(FILEINFO_MIME_TYPE); $mime = $finfo->file($file['tmp_name']); if (!in_array($mime, $allowedMimes, true)) { echo json_encode(['success' => false, 'error' => 'Solo se permiten imágenes JPG o PNG']); exit; } // Validar tamaño (máx 5 MB) if ($file['size'] > 5 * 1024 * 1024) { echo json_encode(['success' => false, 'error' => 'La imagen no puede superar 5 MB']); exit; } try { $config = getWhatsAppConfigFromDB(); $token = $config['token'] ?? ''; $phoneId = $config['phone_number_id'] ?? ''; // Extraer versión de la API URL (ej. v22.0) $apiUrl = rtrim($config['api_url'] ?: 'https://graph.facebook.com/v22.0/', '/'); $apiVersion = 'v22.0'; if (preg_match('#/(v\d+\.\d+)#', $apiUrl, $m)) { $apiVersion = $m[1]; } if (empty($token) || empty($phoneId)) { echo json_encode(['success' => false, 'error' => 'Token o Phone Number ID no configurado']); exit; } $fileSize = $file['size']; $fileName = basename($file['name']); // ── Paso 1: Crear sesión de upload ──────────────────────────────────────── // POST https://graph.facebook.com/{version}/app/uploads $sessionUrl = "https://graph.facebook.com/{$apiVersion}/app/uploads?" . http_build_query([ 'file_name' => $fileName, 'file_length' => $fileSize, 'file_type' => $mime, 'access_token'=> $token, ]); $ch = curl_init(); curl_setopt_array($ch, [ CURLOPT_URL => $sessionUrl, CURLOPT_RETURNTRANSFER => true, CURLOPT_POST => true, CURLOPT_POSTFIELDS => '', CURLOPT_TIMEOUT => 20, CURLOPT_IPRESOLVE => CURL_IPRESOLVE_V4, CURLOPT_SSLVERSION => CURL_SSLVERSION_TLSv1_2, ]); $sessionResp = curl_exec($ch); $sessionCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); $sessionErr = curl_error($ch); curl_close($ch); if ($sessionErr) throw new Exception("Error creando sesión de upload: {$sessionErr}"); $sessionData = json_decode($sessionResp, true); if ($sessionCode !== 200 || empty($sessionData['id'])) { $msg = $sessionData['error']['message'] ?? $sessionResp; throw new Exception("Sesión upload error ({$sessionCode}): {$msg}"); } $uploadSessionId = $sessionData['id']; // ── Paso 2: Subir el archivo binario ────────────────────────────────────── // POST https://graph.facebook.com/{version}/{upload_session_id} $fileContent = file_get_contents($file['tmp_name']); if ($fileContent === false) throw new Exception("No se pudo leer el archivo temporal"); $uploadUrl = "https://graph.facebook.com/{$apiVersion}/{$uploadSessionId}"; $ch2 = curl_init(); curl_setopt_array($ch2, [ CURLOPT_URL => $uploadUrl, CURLOPT_RETURNTRANSFER => true, CURLOPT_POST => true, CURLOPT_POSTFIELDS => $fileContent, CURLOPT_HTTPHEADER => [ "Authorization: OAuth {$token}", "Content-Type: {$mime}", "file_offset: 0", ], CURLOPT_TIMEOUT => 60, CURLOPT_IPRESOLVE => CURL_IPRESOLVE_V4, CURLOPT_SSLVERSION => CURL_SSLVERSION_TLSv1_2, ]); $uploadResp = curl_exec($ch2); $uploadCode = curl_getinfo($ch2, CURLINFO_HTTP_CODE); $uploadErr = curl_error($ch2); curl_close($ch2); if ($uploadErr) throw new Exception("Error subiendo archivo: {$uploadErr}"); $uploadData = json_decode($uploadResp, true); if ($uploadCode !== 200 || empty($uploadData['h'])) { $msg = $uploadData['error']['message'] ?? $uploadResp; throw new Exception("Upload file error ({$uploadCode}): {$msg}"); } $fileHandle = $uploadData['h']; // ej. "h:abc123..." // ── Paso 3: Aplicar la foto al perfil ───────────────────────────────────── $profileUrl = "{$apiUrl}/{$phoneId}/whatsapp_business_profile"; $payload = [ 'messaging_product' => 'whatsapp', 'profile_picture_handle' => $fileHandle, ]; $ch3 = curl_init(); curl_setopt_array($ch3, [ CURLOPT_URL => $profileUrl, CURLOPT_RETURNTRANSFER => true, CURLOPT_POST => true, CURLOPT_POSTFIELDS => json_encode($payload), CURLOPT_HTTPHEADER => [ "Authorization: Bearer {$token}", "Content-Type: application/json", ], CURLOPT_TIMEOUT => 20, CURLOPT_IPRESOLVE => CURL_IPRESOLVE_V4, CURLOPT_SSLVERSION => CURL_SSLVERSION_TLSv1_2, ]); $profileResp = curl_exec($ch3); $profileCode = curl_getinfo($ch3, CURLINFO_HTTP_CODE); $profileErr = curl_error($ch3); curl_close($ch3); if ($profileErr) throw new Exception("Error actualizando foto: {$profileErr}"); $profileData = json_decode($profileResp, true); if ($profileCode !== 200) { $msg = $profileData['error']['message'] ?? $profileResp; throw new Exception("Profile photo error ({$profileCode}): {$msg}"); } echo json_encode(['success' => true, 'message' => 'Foto de perfil actualizada correctamente']); } catch (Exception $e) { error_log('[upload_whatsapp_profile_photo] ' . $e->getMessage()); echo json_encode(['success' => false, 'error' => $e->getMessage()]); } require_once '../config/config.php'; header('Content-Type: application/json; charset=utf-8'); requireAuthentication(); if ($_SERVER['REQUEST_METHOD'] !== 'POST') { echo json_encode(['success' => false, 'error' => 'Método no permitido']); exit; } if (empty($_FILES['photo']) || $_FILES['photo']['error'] !== UPLOAD_ERR_OK) { $code = $_FILES['photo']['error'] ?? -1; echo json_encode(['success' => false, 'error' => "No se recibió archivo válido (código {$code})"]); exit; } $file = $_FILES['photo']; // Validar tipo MIME $allowedMimes = ['image/jpeg', 'image/png']; $finfo = new finfo(FILEINFO_MIME_TYPE); $mime = $finfo->file($file['tmp_name']); if (!in_array($mime, $allowedMimes, true)) { echo json_encode(['success' => false, 'error' => 'Solo se permiten imágenes JPG o PNG']); exit; } // Validar tamaño (máx 5 MB) if ($file['size'] > 5 * 1024 * 1024) { echo json_encode(['success' => false, 'error' => 'La imagen no puede superar 5 MB']); exit; } try { $config = getWhatsAppConfigFromDB(); $token = $config['token'] ?? ''; $phoneId = $config['phone_number_id'] ?? ''; $apiUrl = rtrim($config['api_url'] ?: 'https://graph.facebook.com/v22.0/', '/'); if (empty($token) || empty($phoneId)) { echo json_encode(['success' => false, 'error' => 'Token o Phone Number ID no configurado']); exit; } // ── Paso 1: subir la imagen como media ─────────────────────────────────── $uploadUrl = "{$apiUrl}/{$phoneId}/media"; $cfile = new CURLFile($file['tmp_name'], $mime, basename($file['name'])); $ch = curl_init(); curl_setopt_array($ch, [ CURLOPT_URL => $uploadUrl, CURLOPT_RETURNTRANSFER => true, CURLOPT_POST => true, CURLOPT_POSTFIELDS => [ 'messaging_product' => 'whatsapp', 'file' => $cfile, 'type' => $mime, ], CURLOPT_HTTPHEADER => ["Authorization: Bearer {$token}"], CURLOPT_TIMEOUT => 30, CURLOPT_IPRESOLVE => CURL_IPRESOLVE_V4, CURLOPT_SSLVERSION => CURL_SSLVERSION_TLSv1_2, ]); $uploadResponse = curl_exec($ch); $uploadCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); $uploadError = curl_error($ch); curl_close($ch); if ($uploadError) { throw new Exception("Error al subir imagen: {$uploadError}"); } $uploadData = json_decode($uploadResponse, true); if ($uploadCode !== 200 || empty($uploadData['id'])) { $msg = $uploadData['error']['message'] ?? 'Error desconocido al subir'; throw new Exception("Upload error ({$uploadCode}): {$msg}"); } $mediaHandle = $uploadData['id']; // ── Paso 2: actualizar la foto de perfil con el handle ─────────────────── $profileUrl = "{$apiUrl}/{$phoneId}/whatsapp_business_profile"; $payload = [ 'messaging_product' => 'whatsapp', 'profile_picture_handle' => $mediaHandle, ]; $ch2 = curl_init(); curl_setopt_array($ch2, [ CURLOPT_URL => $profileUrl, CURLOPT_RETURNTRANSFER => true, CURLOPT_POST => true, CURLOPT_POSTFIELDS => json_encode($payload), CURLOPT_HTTPHEADER => [ "Authorization: Bearer {$token}", "Content-Type: application/json", ], CURLOPT_TIMEOUT => 20, CURLOPT_IPRESOLVE => CURL_IPRESOLVE_V4, CURLOPT_SSLVERSION => CURL_SSLVERSION_TLSv1_2, ]); $profileResponse = curl_exec($ch2); $profileCode = curl_getinfo($ch2, CURLINFO_HTTP_CODE); $profileError = curl_error($ch2); curl_close($ch2); if ($profileError) { throw new Exception("Error al actualizar foto: {$profileError}"); } $profileData = json_decode($profileResponse, true); if ($profileCode !== 200) { $msg = $profileData['error']['message'] ?? 'Error desconocido al actualizar foto'; throw new Exception("Profile photo error ({$profileCode}): {$msg}"); } echo json_encode(['success' => true, 'message' => 'Foto de perfil actualizada correctamente']); } catch (Exception $e) { error_log('[upload_whatsapp_profile_photo] ' . $e->getMessage()); echo json_encode(['success' => false, 'error' => $e->getMessage()]); }