- 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>
84 lines
3.1 KiB
PHP
84 lines
3.1 KiB
PHP
<?php
|
|
|
|
namespace App\Services;
|
|
|
|
use App\Models\ChatConfig;
|
|
use App\Models\WhatsappSystemConfig;
|
|
use Illuminate\Support\Facades\Log;
|
|
|
|
class PagoValidadorService
|
|
{
|
|
/**
|
|
* Cruza los datos extraídos por IA con los correos IMAP recientes.
|
|
*
|
|
* @param array $datosPago ['banco', 'valor', 'referencia', 'fecha', 'hora', 'remitente']
|
|
* @return array ['estado' => confirmado|no_encontrado|monto_incorrecto, 'correo' => array|null]
|
|
*/
|
|
public function validar(array $datosPago): array
|
|
{
|
|
if (WhatsappSystemConfig::get('correo_imap_enabled', '0') !== '1') {
|
|
return ['estado' => 'no_encontrado', 'correo' => null, 'motivo' => 'correo_deshabilitado'];
|
|
}
|
|
|
|
try {
|
|
$correos = $this->fetchCorreos();
|
|
} catch (\Throwable $e) {
|
|
Log::warning('[PagoValidador] Error leyendo correos: ' . $e->getMessage());
|
|
return ['estado' => 'no_encontrado', 'correo' => null, 'motivo' => 'error_imap'];
|
|
}
|
|
|
|
if (empty($correos)) {
|
|
return ['estado' => 'no_encontrado', 'correo' => null, 'motivo' => 'sin_correos'];
|
|
}
|
|
|
|
$valorIA = (int) ($datosPago['valor'] ?? 0);
|
|
|
|
foreach ($correos as $correo) {
|
|
$datosCorreo = BancolombiaParser::parse($correo['body'] ?? '');
|
|
|
|
if (! $datosCorreo) {
|
|
continue;
|
|
}
|
|
|
|
$valorCorreo = (int) ($datosCorreo['valor'] ?? 0);
|
|
|
|
// Verificar referencia si ambas están disponibles
|
|
if (! empty($datosPago['referencia']) && ! empty($datosCorreo['referencia'])) {
|
|
if ($datosPago['referencia'] === $datosCorreo['referencia']) {
|
|
return ['estado' => 'confirmado', 'correo' => $datosCorreo];
|
|
}
|
|
}
|
|
|
|
// Verificar valor
|
|
if ($valorIA > 0 && $valorCorreo > 0) {
|
|
$tolerancia = (int) ChatConfig::get('validacion_monto_tolerancia', '0');
|
|
|
|
if (abs($valorIA - $valorCorreo) <= $tolerancia) {
|
|
return ['estado' => 'confirmado', 'correo' => $datosCorreo];
|
|
} else {
|
|
return ['estado' => 'monto_incorrecto', 'correo' => $datosCorreo,
|
|
'esperado' => $valorCorreo, 'recibido' => $valorIA];
|
|
}
|
|
}
|
|
}
|
|
|
|
return ['estado' => 'no_encontrado', 'correo' => null, 'motivo' => 'sin_coincidencia'];
|
|
}
|
|
|
|
private function fetchCorreos(): array
|
|
{
|
|
$minutos = (int) WhatsappSystemConfig::get('correo_imap_minutos', '5');
|
|
|
|
$service = new CorreoImapService(
|
|
host: WhatsappSystemConfig::get('correo_imap_host', ''),
|
|
port: (int) WhatsappSystemConfig::get('correo_imap_port', '993'),
|
|
useSsl: WhatsappSystemConfig::get('correo_imap_ssl', '1') === '1',
|
|
user: WhatsappSystemConfig::get('correo_imap_user', ''),
|
|
password: WhatsappSystemConfig::get('correo_imap_password', ''),
|
|
folder: WhatsappSystemConfig::get('correo_imap_folder', 'INBOX'),
|
|
);
|
|
|
|
return $service->fetchLatest(limit: 10, minutesBack: $minutos);
|
|
}
|
|
}
|