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
+7 -1
View File
@@ -34,6 +34,12 @@ class Promocion extends Model
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);
}
}
+38 -17
View File
@@ -28,7 +28,7 @@ class WhatsAppService
$this->token = $token;
$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
@@ -48,11 +48,30 @@ class WhatsAppService
}
if (!empty($parametros)) {
$components[] = [
'type' => 'body',
'parameters' => array_map(fn($p) => ['type' => 'text', 'text' => $p], $parametros),
];
}
$components[] = [
'type' => 'body',
'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, [
'messaging_product' => 'whatsapp',
@@ -66,7 +85,7 @@ class WhatsAppService
]);
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;
}
@@ -111,16 +130,18 @@ public function enviarPromocion(string $telefono, Promocion $promocion, string $
$codigo = 'PROMO-' . strtoupper(substr(md5($promocion->id . $telefono), 0, 6));
$mensajeId = $this->enviarTemplate(
$telefono,
'promocion_laravel',
[
$nombre,
$promocion->fecha_inicio->locale('es')->isoFormat('D [de] MMMM'),
$promocion->fecha_fin->locale('es')->isoFormat('D [de] MMMM'),
$codigo,
],
$promocion->imagenUrl()
);
$telefono,
'promocion_laravel',
[
'nombre' => $nombre ?: 'estudiante',
'fecha_inicio' => $promocion->fecha_inicio->locale('es')->isoFormat('D [de] MMMM'),
'fecha_fin' => $promocion->fecha_fin->locale('es')->isoFormat('D [de] MMMM'),
'codigo' => $codigo,
],
$promocion->imagenUrl()
);
if (!$mensajeId) {
throw new \RuntimeException('No se pudo enviar la plantilla');