feat: WhatsApp — parámetros nombrados en template, API v25.0 e imagenUrl con soporte de URL externas

- Actualiza graph.facebook.com de v19.0 a v25.0
- Cambia parámetros posicionales a nombrados (parameter_name) en enviarTemplate para compatibilidad con plantillas avanzadas
- enviarPromocion ahora pasa nombre fallback 'estudiante' y claves explícitas
- imagenUrl() soporta imágenes con URL absoluta (http/https) además de rutas relativas de storage
- Agrega Log::info del payload completo antes del envío para facilitar debugging

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-05-28 20:44:13 -06:00
parent f246a709c4
commit a0c3194e88
3 changed files with 53 additions and 19 deletions
+8 -1
View File
@@ -23,7 +23,14 @@
"Bash(Select-String -Pattern \"entrega|Entrega\" -Context 0,0)", "Bash(Select-String -Pattern \"entrega|Entrega\" -Context 0,0)",
"Bash(php -l app/Services/WhatsAppService.php)", "Bash(php -l app/Services/WhatsAppService.php)",
"Bash(php -l app/Http/Controllers/WhatsAppWebhookController.php)", "Bash(php -l app/Http/Controllers/WhatsAppWebhookController.php)",
"Bash(php -l app/Http/Controllers/WhatsAppAnalyticsController.php)" "Bash(php -l app/Http/Controllers/WhatsAppAnalyticsController.php)",
"Bash(Test-Path *)",
"Bash(git -C \"C:/Proyectos/SistemaEducativoLaravel\" status)",
"Bash(git -C \"C:/Proyectos/SistemaEducativoLaravel\" diff)",
"Bash(git *)",
"Bash(dir C:\\\\Proyectos\\\\SistemaEducativoLaravel\\\\public *)",
"Bash(findstr \"APP_URL\" \"C:\\\\Proyectos\\\\SistemaEducativoLaravel\\\\.env.example\")",
"Bash(findstr \"APP_URL\" \"C:\\\\Proyectos\\\\SistemaEducativoLaravel\\\\.env\")"
] ]
} }
} }
+7 -1
View File
@@ -34,6 +34,12 @@ class Promocion extends Model
public function imagenUrl(): ?string public function imagenUrl(): ?string
{ {
return $this->imagen ? asset('storage/' . $this->imagen) : null; if (!$this->imagen) return null;
if (str_starts_with($this->imagen, 'http')) {
return $this->imagen;
}
return asset('storage/' . $this->imagen);
} }
} }
+28 -7
View File
@@ -28,7 +28,7 @@ class WhatsAppService
$this->token = $token; $this->token = $token;
$this->phoneId = $phoneId; $this->phoneId = $phoneId;
$this->apiUrl = "https://graph.facebook.com/v19.0/{$this->phoneId}/messages"; $this->apiUrl = "https://graph.facebook.com/v25.0/{$this->phoneId}/messages";
} }
public function enviarTemplate(string $telefono, string $templateName, array $parametros = [], ?string $imagenUrl = null): ?string public function enviarTemplate(string $telefono, string $templateName, array $parametros = [], ?string $imagenUrl = null): ?string
@@ -50,10 +50,29 @@ class WhatsAppService
if (!empty($parametros)) { if (!empty($parametros)) {
$components[] = [ $components[] = [
'type' => 'body', 'type' => 'body',
'parameters' => array_map(fn($p) => ['type' => 'text', 'text' => $p], $parametros), 'parameters' => array_map(fn($nombre, $valor) => [
'type' => 'text',
'text' => (string) $valor,
'parameter_name' => $nombre,
], array_keys($parametros), array_values($parametros)),
]; ];
} }
Log::info('WhatsApp payload', [
'url' => $this->apiUrl,
'body' => [
'messaging_product' => 'whatsapp',
'to' => $this->normalizarTelefono($telefono),
'type' => 'template',
'template' => [
'name' => $templateName,
'language' => ['code' => 'es_MX'],
'components' => $components,
],
]
]);
$response = Http::withToken($this->token)->post($this->apiUrl, [ $response = Http::withToken($this->token)->post($this->apiUrl, [
'messaging_product' => 'whatsapp', 'messaging_product' => 'whatsapp',
'to' => $this->normalizarTelefono($telefono), 'to' => $this->normalizarTelefono($telefono),
@@ -66,7 +85,7 @@ class WhatsAppService
]); ]);
if ($response->failed()) { if ($response->failed()) {
Log::error('WhatsApp template error', ['telefono' => $telefono, 'response' => $response->json()]); Log::error('WhatsApp template error', ['telefono' => $telefono, 'response' => $response->json(), 'body' => $response->body()]);
return null; return null;
} }
@@ -114,14 +133,16 @@ public function enviarPromocion(string $telefono, Promocion $promocion, string $
$telefono, $telefono,
'promocion_laravel', 'promocion_laravel',
[ [
$nombre, 'nombre' => $nombre ?: 'estudiante',
$promocion->fecha_inicio->locale('es')->isoFormat('D [de] MMMM'), 'fecha_inicio' => $promocion->fecha_inicio->locale('es')->isoFormat('D [de] MMMM'),
$promocion->fecha_fin->locale('es')->isoFormat('D [de] MMMM'), 'fecha_fin' => $promocion->fecha_fin->locale('es')->isoFormat('D [de] MMMM'),
$codigo, 'codigo' => $codigo,
], ],
$promocion->imagenUrl() $promocion->imagenUrl()
); );
if (!$mensajeId) { if (!$mensajeId) {
throw new \RuntimeException('No se pudo enviar la plantilla'); throw new \RuntimeException('No se pudo enviar la plantilla');
} }