feat: página de mensaje de prueba para simular webhook entrante
- GET /admin/test-message: formulario con empresa, número, nombre y texto - POST /admin/test-message/send: construye payload WhatsApp, firma HMAC y hace POST al webhook propio — pipeline completo sin tocar Meta Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Sonnet 4.6
parent
7b8a4c2056
commit
b083d49769
@@ -3053,4 +3053,168 @@ HTML;
|
||||
echo '</div>';
|
||||
}
|
||||
}
|
||||
|
||||
// ─── GET /admin/test-message ──────────────────────────────────────────────
|
||||
|
||||
public static function testMessage(): void
|
||||
{
|
||||
SessionAuth::require();
|
||||
$user = SessionAuth::user();
|
||||
$companies = CompanyRepository::all('is_active = 1');
|
||||
|
||||
$opts = '';
|
||||
foreach ($companies as $c) {
|
||||
$opts .= '<option value="' . self::h($c['phone_number_id']) . '" data-phone="' . self::h($c['display_phone'] ?? '') . '">'
|
||||
. self::h($c['name']) . '</option>';
|
||||
}
|
||||
|
||||
echo Layout::open('Mensaje de prueba', 'test', $user['username'] ?? 'Admin');
|
||||
echo <<<HTML
|
||||
<div class="wrap" style="max-width:640px">
|
||||
<div style="margin-bottom:20px">
|
||||
<h1 style="font-size:18px;font-weight:700;color:#111827">Mensaje de prueba</h1>
|
||||
<p style="color:#6b7280;font-size:13px;margin-top:4px">Simula un mensaje entrante de WhatsApp al webhook. El bot responde igual que en producción.</p>
|
||||
</div>
|
||||
|
||||
<div id="result" style="display:none;margin-bottom:16px"></div>
|
||||
|
||||
<div class="card">
|
||||
<div class="card-b">
|
||||
<form id="testForm">
|
||||
<div class="form-group">
|
||||
<label>Empresa</label>
|
||||
<select name="phone_number_id" id="cmpSel" required>{$opts}</select>
|
||||
</div>
|
||||
<div class="form-row">
|
||||
<div class="form-group">
|
||||
<label>Número origen (sin +)</label>
|
||||
<input type="text" name="from_number" placeholder="573001234567" required pattern="[0-9]+" value="573001234567">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>Nombre</label>
|
||||
<input type="text" name="from_name" placeholder="Cliente prueba" value="Cliente prueba">
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>Mensaje</label>
|
||||
<textarea name="message" rows="3" placeholder="Hola, quiero información" required style="resize:vertical">Hola</textarea>
|
||||
</div>
|
||||
<button type="submit" class="btn-primary" id="sendBtn" style="width:100%">Enviar mensaje de prueba</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
document.getElementById('testForm').addEventListener('submit', async function(e) {
|
||||
e.preventDefault();
|
||||
const btn = document.getElementById('sendBtn');
|
||||
const res = document.getElementById('result');
|
||||
btn.disabled = true;
|
||||
btn.textContent = 'Enviando…';
|
||||
res.style.display = 'none';
|
||||
|
||||
const fd = new FormData(this);
|
||||
try {
|
||||
const r = await fetch('/admin/test-message/send', { method:'POST', body: fd });
|
||||
const json = await r.json();
|
||||
res.style.display = '';
|
||||
if (json.ok) {
|
||||
res.innerHTML = '<div class="toast toast-success">✓ ' + json.message + '</div>';
|
||||
} else {
|
||||
res.innerHTML = '<div class="toast toast-error">✗ ' + json.error + '</div>';
|
||||
}
|
||||
} catch(err) {
|
||||
res.style.display = '';
|
||||
res.innerHTML = '<div class="toast toast-error">Error de red: ' + err.message + '</div>';
|
||||
}
|
||||
btn.disabled = false;
|
||||
btn.textContent = 'Enviar mensaje de prueba';
|
||||
});
|
||||
</script>
|
||||
HTML;
|
||||
echo Layout::close();
|
||||
}
|
||||
|
||||
// ─── POST /admin/test-message/send ───────────────────────────────────────
|
||||
|
||||
public static function testMessageSend(): void
|
||||
{
|
||||
SessionAuth::require();
|
||||
header('Content-Type: application/json; charset=utf-8');
|
||||
|
||||
$phoneNumberId = trim($_POST['phone_number_id'] ?? '');
|
||||
$fromNumber = preg_replace('/\D/', '', $_POST['from_number'] ?? '');
|
||||
$fromName = trim($_POST['from_name'] ?? 'Test');
|
||||
$message = trim($_POST['message'] ?? '');
|
||||
|
||||
if ($phoneNumberId === '' || $fromNumber === '' || $message === '') {
|
||||
echo json_encode(['ok' => false, 'error' => 'Faltan campos requeridos']);
|
||||
exit;
|
||||
}
|
||||
|
||||
$payload = [
|
||||
'object' => 'whatsapp_business_account',
|
||||
'entry' => [[
|
||||
'id' => 'TEST_WABA',
|
||||
'changes' => [[
|
||||
'field' => 'messages',
|
||||
'value' => [
|
||||
'messaging_product' => 'whatsapp',
|
||||
'metadata' => [
|
||||
'display_phone_number' => '0000000000',
|
||||
'phone_number_id' => $phoneNumberId,
|
||||
],
|
||||
'contacts' => [[
|
||||
'profile' => ['name' => $fromName],
|
||||
'wa_id' => $fromNumber,
|
||||
]],
|
||||
'messages' => [[
|
||||
'id' => 'wamid.TEST_' . time(),
|
||||
'from' => $fromNumber,
|
||||
'timestamp' => (string)time(),
|
||||
'type' => 'text',
|
||||
'text' => ['body' => $message],
|
||||
]],
|
||||
],
|
||||
]],
|
||||
]],
|
||||
];
|
||||
|
||||
$json = json_encode($payload, JSON_UNESCAPED_UNICODE);
|
||||
$appSecret = env('WHATSAPP_APP_SECRET', '');
|
||||
$sig = $appSecret !== '' ? 'sha256=' . hash_hmac('sha256', $json, $appSecret) : 'sha256=test';
|
||||
|
||||
// POST al webhook propio vía cURL
|
||||
$webhookUrl = 'https://admin.palmas360.com/admin/v1/wp-webhook';
|
||||
$ch = curl_init($webhookUrl);
|
||||
curl_setopt_array($ch, [
|
||||
CURLOPT_POST => true,
|
||||
CURLOPT_POSTFIELDS => $json,
|
||||
CURLOPT_HTTPHEADER => [
|
||||
'Content-Type: application/json',
|
||||
'X-Hub-Signature-256: ' . $sig,
|
||||
],
|
||||
CURLOPT_RETURNTRANSFER => true,
|
||||
CURLOPT_TIMEOUT => 15,
|
||||
CURLOPT_SSL_VERIFYPEER => false,
|
||||
]);
|
||||
$resp = curl_exec($ch);
|
||||
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
||||
$curlErr = curl_error($ch);
|
||||
curl_close($ch);
|
||||
|
||||
if ($curlErr) {
|
||||
echo json_encode(['ok' => false, 'error' => 'cURL: ' . $curlErr]);
|
||||
exit;
|
||||
}
|
||||
|
||||
if ($httpCode === 200) {
|
||||
echo json_encode(['ok' => true, 'message' => 'Mensaje enviado al webhook. Revisa Live Feed y logs.']);
|
||||
} else {
|
||||
$body = is_string($resp) ? substr($resp, 0, 200) : '';
|
||||
echo json_encode(['ok' => false, 'error' => "Webhook respondió HTTP $httpCode. $body"]);
|
||||
}
|
||||
exit;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user