feat: web chat con IA, validación de pagos y OTP por correo

- Chat público en /chat con flujos de compra, recarga y credenciales
- Verificación de identidad por código OTP enviado al correo registrado
- Botones enriquecidos: cards, links, credenciales, validación de comprobante
- Gemini IA para detección de intención en texto libre
- Gemini Vision para extraer datos de foto de comprobante
- Validador de pagos cruzando IA con correos IMAP (Bancolombia)
- MercadoPago como pasarela de pago (reemplaza Wompi en el chat)
- Migraciones: payload en chat_messages, user_id en chat_contacts
- Config admin: API key Gemini, toggles IA y validación de foto

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Lizandro
2026-07-14 00:09:31 +00:00
co-authored by Claude Sonnet 4.6
parent a56a69dccb
commit 536377363c
13 changed files with 1862 additions and 95 deletions
@@ -57,6 +57,63 @@
@error('mensaje_transferencia') <span class="text-red-500 text-xs">{{ $message }}</span> @enderror
</div>
<hr class="border-gray-100">
{{-- Gemini IA --}}
<div>
<h3 class="text-sm font-bold text-gray-700 mb-3">Inteligencia Artificial (Gemini)</h3>
<div class="space-y-4">
<div>
<label class="block text-sm font-semibold text-gray-700 mb-1">API Key de Gemini</label>
<p class="text-xs text-gray-400 mb-2">Obtén tu clave en aistudio.google.com</p>
<input wire:model.defer="gemini_api_key" type="password" placeholder="AIza..."
class="w-full border border-gray-300 rounded-lg px-3 py-2 text-sm focus:ring-2 focus:ring-emerald-500 outline-none font-mono">
</div>
<div class="flex items-center gap-3">
<input wire:model.defer="gemini_habilitado" type="checkbox" value="1"
id="gemini_hab" class="w-4 h-4 accent-emerald-600">
<label for="gemini_hab" class="text-sm text-gray-700">
Habilitar IA para interpretar texto libre del usuario
</label>
</div>
<div>
<label class="block text-sm font-semibold text-gray-700 mb-1">Contexto extra para la IA (opcional)</label>
<textarea wire:model.defer="gemini_prompt_extra" rows="2"
class="w-full border border-gray-300 rounded-lg px-3 py-2 text-sm focus:ring-2 focus:ring-emerald-500 outline-none resize-none"
placeholder="Ej: Solo vendemos streaming. No ofrecemos juegos."></textarea>
</div>
</div>
</div>
<hr class="border-gray-100">
{{-- Validacion de pagos --}}
<div>
<h3 class="text-sm font-bold text-gray-700 mb-1">Validacion de pagos por foto + correo IMAP</h3>
<p class="text-xs text-gray-400 mb-4">La IA lee el comprobante y cruza los datos con los correos del IMAP configurado en la seccion WhatsApp.</p>
<div class="space-y-4">
<div class="flex items-center gap-3">
<input wire:model.defer="validacion_foto_habilitada" type="checkbox" value="1"
id="val_foto" class="w-4 h-4 accent-emerald-600">
<label for="val_foto" class="text-sm text-gray-700">Habilitar validacion de comprobante por foto</label>
</div>
<div>
<label class="block text-sm font-semibold text-gray-700 mb-1">Accion al confirmar pago</label>
<select wire:model.defer="validacion_accion_auto"
class="w-full border border-gray-300 rounded-lg px-3 py-2 text-sm focus:ring-2 focus:ring-emerald-500 outline-none">
<option value="solo_notificar">Solo notificar al asesor (manual)</option>
<option value="recargar_saldo">Acreditar saldo automaticamente</option>
</select>
</div>
<div>
<label class="block text-sm font-semibold text-gray-700 mb-1">Tolerancia de monto ($)</label>
<input wire:model.defer="validacion_monto_tolerancia" type="number" min="0"
class="w-32 border border-gray-300 rounded-lg px-3 py-2 text-sm focus:ring-2 focus:ring-emerald-500 outline-none">
<p class="text-xs text-gray-400 mt-1">Diferencia maxima permitida entre comprobante y correo. Recomendado: 0.</p>
</div>
</div>
</div>
{{-- Guardar --}}
<div class="flex justify-end pt-2">
<button wire:click="guardar"
@@ -64,7 +121,7 @@
<svg class="w-4 h-4" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" d="M9 12.75 11.25 15 15 9.75M21 12a9 9 0 1 1-18 0 9 9 0 0 1 18 0Z" />
</svg>
<span wire:loading.remove wire:target="guardar">Guardar configuración</span>
<span wire:loading.remove wire:target="guardar">Guardar configuracion</span>
<span wire:loading wire:target="guardar">Guardando...</span>
</button>
</div>