fix: verificar solicitud de recarga antes de llamar Gemini Vision e IMAP
Sin solicitud activa el bot mostraba el análisis completo (Gemini + IMAP) antes de rechazar. Ahora se valida primero: si no hay solicitud pendiente, se rechaza de inmediato sin gastar API. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Sonnet 4.6
parent
a7d7c32350
commit
437adc531f
@@ -475,6 +475,16 @@ class PublicChat extends Component
|
|||||||
$this->procesandoImagen = true;
|
$this->procesandoImagen = true;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
// Verificar solicitud activa ANTES de cualquier llamada de API
|
||||||
|
$solicitud = $this->userId ? \App\Models\SolicitudRecarga::pendiente($this->userId) : null;
|
||||||
|
if (! $solicitud) {
|
||||||
|
$this->procesandoImagen = false;
|
||||||
|
$this->fotoComprobante = null;
|
||||||
|
$this->guardarMensajeBot($this->convId, "⚠️ No tienes ninguna solicitud de recarga activa. Primero inicia el proceso de recarga desde el menú.");
|
||||||
|
$this->cargarMensajes();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
$path = $this->fotoComprobante->store('comprobantes', 'public');
|
$path = $this->fotoComprobante->store('comprobantes', 'public');
|
||||||
$url = asset('storage/' . $path);
|
$url = asset('storage/' . $path);
|
||||||
|
|
||||||
@@ -500,21 +510,6 @@ class PublicChat extends Component
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
$resultado = app(\App\Services\PagoValidadorService::class)->validar(
|
|
||||||
$datosPago, $this->userId, $this->nombreUsuario, 'web'
|
|
||||||
);
|
|
||||||
|
|
||||||
$monto = (int) ($datosPago['valor'] ?? 0);
|
|
||||||
$botones = [];
|
|
||||||
|
|
||||||
// Verificar solicitud activa
|
|
||||||
$solicitud = $this->userId ? \App\Models\SolicitudRecarga::pendiente($this->userId) : null;
|
|
||||||
if (! $solicitud) {
|
|
||||||
$this->guardarMensajeBot($this->convId, "No tienes ninguna solicitud de recarga activa. Inicia una recarga desde el menu para poder enviar el comprobante.");
|
|
||||||
$this->cargarMensajes();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Verificar que el monto coincida con la solicitud
|
// Verificar que el monto coincida con la solicitud
|
||||||
$valorIA = (int) ($datosPago['valor'] ?? 0);
|
$valorIA = (int) ($datosPago['valor'] ?? 0);
|
||||||
if ($valorIA !== $solicitud->monto) {
|
if ($valorIA !== $solicitud->monto) {
|
||||||
@@ -523,6 +518,10 @@ class PublicChat extends Component
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$resultado = app(\App\Services\PagoValidadorService::class)->validar(
|
||||||
|
$datosPago, $this->userId, $this->nombreUsuario, 'web'
|
||||||
|
);
|
||||||
|
|
||||||
$motivo = $resultado['motivo'] ?? '';
|
$motivo = $resultado['motivo'] ?? '';
|
||||||
|
|
||||||
if ($resultado['estado'] === 'confirmado') {
|
if ($resultado['estado'] === 'confirmado') {
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ use App\Models\Usuario_promo;
|
|||||||
use App\Models\recarga;
|
use App\Models\recarga;
|
||||||
use App\Models\Role;
|
use App\Models\Role;
|
||||||
use App\Models\Saldo;
|
use App\Models\Saldo;
|
||||||
|
use App\Models\SolicitudRecarga;
|
||||||
use App\Models\Servicio;
|
use App\Models\Servicio;
|
||||||
use App\Models\Tarifas;
|
use App\Models\Tarifas;
|
||||||
use App\Models\User;
|
use App\Models\User;
|
||||||
@@ -148,7 +149,6 @@ class TelegramBotService
|
|||||||
'history' => $this->showHistory($chatId),
|
'history' => $this->showHistory($chatId),
|
||||||
'profile' => $this->showProfile($chatId),
|
'profile' => $this->showProfile($chatId),
|
||||||
'agent' => $this->showMainMenu($chatId),
|
'agent' => $this->showMainMenu($chatId),
|
||||||
'pay_apply' => $this->applyPayment($chatId, (int) ($parts[1] ?? 0), $parts[2] ?? null),
|
|
||||||
'pay_retry' => $this->retryPayment($chatId),
|
'pay_retry' => $this->retryPayment($chatId),
|
||||||
default => $this->showMainMenu($chatId),
|
default => $this->showMainMenu($chatId),
|
||||||
};
|
};
|
||||||
@@ -169,6 +169,17 @@ class TelegramBotService
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$usuarioId = $state['user_id'] ?? null;
|
||||||
|
$nombreUsuario = $usuarioId ? (User::find($usuarioId)?->name ?? '') : '';
|
||||||
|
|
||||||
|
// Verificar solicitud de recarga activa ANTES de cualquier llamada de API
|
||||||
|
$solicitud = $usuarioId ? SolicitudRecarga::pendiente($usuarioId) : null;
|
||||||
|
if (! $solicitud) {
|
||||||
|
$this->send($chatId, "⚠️ No tienes ninguna solicitud de recarga activa. Primero inicia el proceso de recarga desde el menú.");
|
||||||
|
$this->showMainMenu($chatId);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
$this->send($chatId, "⏳ Analizando tu comprobante...");
|
$this->send($chatId, "⏳ Analizando tu comprobante...");
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@@ -180,7 +191,7 @@ class TelegramBotService
|
|||||||
|
|
||||||
$base64 = base64_encode($imageData['content']);
|
$base64 = base64_encode($imageData['content']);
|
||||||
$mime = $imageData['mime'];
|
$mime = $imageData['mime'];
|
||||||
$datosPago = app(GeminiVisionService::class)->extraerPago($base64, $mime, $state['user_id'] ?? null, 'telegram');
|
$datosPago = app(GeminiVisionService::class)->extraerPago($base64, $mime, $usuarioId, 'telegram');
|
||||||
|
|
||||||
if (! $datosPago) {
|
if (! $datosPago) {
|
||||||
$this->send($chatId, "No pude leer el comprobante. Intenta con una foto más clara.");
|
$this->send($chatId, "No pude leer el comprobante. Intenta con una foto más clara.");
|
||||||
@@ -197,67 +208,48 @@ class TelegramBotService
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
$usuarioId = $state['user_id'] ?? null;
|
// Verificar que el monto del comprobante coincida con la solicitud
|
||||||
$nombreUsuario = $usuarioId ? (User::find($usuarioId)?->name ?? '') : '';
|
$valorIA = (int) ($datosPago['valor'] ?? 0);
|
||||||
|
if ($valorIA !== $solicitud->monto) {
|
||||||
|
$this->send($chatId, "⚠️ El monto del comprobante (*\$" . number_format($valorIA) . "*) no coincide con tu solicitud de recarga (*\$" . number_format($solicitud->monto) . "*).");
|
||||||
|
$this->showMainMenu($chatId);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
$resultado = app(PagoValidadorService::class)->validar(
|
$resultado = app(PagoValidadorService::class)->validar(
|
||||||
$datosPago, $usuarioId, $nombreUsuario, 'telegram'
|
$datosPago, $usuarioId, $nombreUsuario, 'telegram'
|
||||||
);
|
);
|
||||||
$monto = number_format($datosPago['valor'] ?? 0);
|
$motivo = $resultado['motivo'] ?? '';
|
||||||
$ref = $datosPago['referencia'] ?? 'N/A';
|
|
||||||
$valor = (int) ($datosPago['valor'] ?? 0);
|
|
||||||
|
|
||||||
$texto = "📊 *Resultado del análisis:*\n\n";
|
|
||||||
$texto .= "Banco: " . ($datosPago['banco'] ?? 'N/A') . "\n";
|
|
||||||
$texto .= "Monto: *\$$monto*\n";
|
|
||||||
$texto .= "Referencia: `{$ref}`\n";
|
|
||||||
if ($datosPago['fecha'] ?? null) {
|
|
||||||
$texto .= "Fecha: " . $datosPago['fecha'];
|
|
||||||
if ($datosPago['hora'] ?? null) {
|
|
||||||
$texto .= " " . $datosPago['hora'];
|
|
||||||
}
|
|
||||||
$texto .= "\n";
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($resultado['estado'] === 'confirmado') {
|
if ($resultado['estado'] === 'confirmado') {
|
||||||
$texto .= "\nEstado: *✅ Confirmado por correo*";
|
$this->autoAplicarRecarga($chatId, $state, $solicitud, $valorIA);
|
||||||
$buttons = [
|
|
||||||
[['text' => "✅ Aplicar recarga de \$$monto", 'callback_data' => 'pay_apply|' . $valor . '|' . $ref]],
|
|
||||||
[['text' => '🔙 Menú principal', 'callback_data' => 'menu']],
|
|
||||||
];
|
|
||||||
} elseif ($resultado['estado'] === 'ya_usado') {
|
|
||||||
$texto .= "\nEstado: *⛔ Comprobante ya utilizado*\nEste pago ya fue registrado anteriormente.";
|
|
||||||
$buttons = [
|
|
||||||
[['text' => '🔙 Menú principal', 'callback_data' => 'menu']],
|
|
||||||
];
|
|
||||||
} else {
|
|
||||||
$motivo = $resultado['motivo'] ?? '';
|
|
||||||
if ($motivo === 'sin_coincidencia' || $motivo === 'sin_correos') {
|
|
||||||
// Guardar datosPago en estado para poder reintentar sin reenviar foto
|
|
||||||
$state['data']['pago_pendiente'] = $datosPago;
|
|
||||||
$this->setState($chatId, $state);
|
|
||||||
|
|
||||||
$texto .= "\nEstado: *⏳ Por ahora no encontramos tu pago de confirmación*\n"
|
} elseif ($resultado['estado'] === 'ya_usado') {
|
||||||
. "_Puede demorar entre 1 y 2 minutos en llegar. Espera un momento y presiona Reintentar._";
|
$this->sendWithKeyboard($chatId, "⛔ *Comprobante ya utilizado.*\nEste pago ya fue registrado anteriormente.", [
|
||||||
$buttons = [
|
[['text' => '🔙 Menú principal', 'callback_data' => 'menu']],
|
||||||
|
]);
|
||||||
|
|
||||||
|
} elseif ($motivo === 'sin_coincidencia' || $motivo === 'sin_correos') {
|
||||||
|
$state['data']['pago_pendiente'] = $datosPago;
|
||||||
|
$this->setState($chatId, $state);
|
||||||
|
$this->sendWithKeyboard($chatId,
|
||||||
|
"⏳ *Por ahora no encontramos tu pago de confirmación.*\n_Puede demorar entre 1 y 2 minutos en llegar. Espera un momento y presiona Reintentar._",
|
||||||
|
[
|
||||||
[['text' => '🔄 Reintentar', 'callback_data' => 'pay_retry']],
|
[['text' => '🔄 Reintentar', 'callback_data' => 'pay_retry']],
|
||||||
[['text' => '🔙 Menú principal', 'callback_data' => 'menu']],
|
[['text' => '🔙 Menú principal', 'callback_data' => 'menu']],
|
||||||
];
|
]
|
||||||
} else {
|
);
|
||||||
$motivoTexto = match ($motivo) {
|
} else {
|
||||||
'correo_deshabilitado' => '📧 Verificación por correo no configurada',
|
$motivoTexto = match ($motivo) {
|
||||||
'error_imap' => '📧 Error al leer correos IMAP',
|
'correo_deshabilitado' => '📧 Verificación por correo no configurada',
|
||||||
'error_sistema' => '⚙️ Error interno, intenta de nuevo en unos minutos',
|
'error_imap' => '📧 Error al leer correos IMAP',
|
||||||
default => '📧 No se pudo verificar automáticamente',
|
'error_sistema' => '⚙️ Error interno, intenta de nuevo en unos minutos',
|
||||||
};
|
default => '📧 No se pudo verificar automáticamente',
|
||||||
$texto .= "\nEstado: *⚠️ No confirmado*\n_{$motivoTexto}_";
|
};
|
||||||
$buttons = [
|
$this->sendWithKeyboard($chatId, "⚠️ *No confirmado*\n_{$motivoTexto}_", [
|
||||||
[['text' => '🔙 Menú principal', 'callback_data' => 'menu']],
|
[['text' => '🔙 Menú principal', 'callback_data' => 'menu']],
|
||||||
];
|
]);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->sendWithKeyboard($chatId, $texto, $buttons);
|
|
||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
Log::warning('[TelegramBot] Error procesando foto: ' . $e->getMessage());
|
Log::warning('[TelegramBot] Error procesando foto: ' . $e->getMessage());
|
||||||
$this->send($chatId, "Error analizando el comprobante. Por favor intenta de nuevo.");
|
$this->send($chatId, "Error analizando el comprobante. Por favor intenta de nuevo.");
|
||||||
@@ -266,34 +258,31 @@ class TelegramBotService
|
|||||||
|
|
||||||
private function retryPayment(string $chatId): void
|
private function retryPayment(string $chatId): void
|
||||||
{
|
{
|
||||||
$state = $this->getState($chatId);
|
$state = $this->getState($chatId);
|
||||||
$this->convId = $state['conv_id'] ?? null;
|
$this->convId = $state['conv_id'] ?? null;
|
||||||
$datosPago = $state['data']['pago_pendiente'] ?? null;
|
$datosPago = $state['data']['pago_pendiente'] ?? null;
|
||||||
|
$usuarioId = $state['user_id'] ?? null;
|
||||||
|
|
||||||
if (! $datosPago) {
|
if (! $datosPago || ! $usuarioId) {
|
||||||
$this->send($chatId, "No hay ningún comprobante pendiente. Envía la foto de tu comprobante.");
|
$this->send($chatId, "No hay ningún comprobante pendiente. Envía la foto de tu comprobante.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$solicitud = SolicitudRecarga::pendiente($usuarioId);
|
||||||
|
if (! $solicitud) {
|
||||||
|
$this->send($chatId, "⚠️ Tu solicitud de recarga expiró. Inicia una nueva recarga desde el menú.");
|
||||||
|
$this->showMainMenu($chatId);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
$this->send($chatId, "⏳ Verificando de nuevo...");
|
$this->send($chatId, "⏳ Verificando de nuevo...");
|
||||||
|
|
||||||
$usuarioId = $state['user_id'] ?? null;
|
$nombreUsuario = User::find($usuarioId)?->name ?? '';
|
||||||
$nombreUsuario = $usuarioId ? (User::find($usuarioId)?->name ?? '') : '';
|
$resultado = app(PagoValidadorService::class)->validar($datosPago, $usuarioId, $nombreUsuario, 'telegram');
|
||||||
|
$motivo = $resultado['motivo'] ?? '';
|
||||||
$resultado = app(PagoValidadorService::class)->validar($datosPago, $usuarioId, $nombreUsuario, 'telegram');
|
|
||||||
$monto = number_format($datosPago['valor'] ?? 0);
|
|
||||||
$ref = $datosPago['referencia'] ?? 'N/A';
|
|
||||||
$valor = (int) ($datosPago['valor'] ?? 0);
|
|
||||||
|
|
||||||
if ($resultado['estado'] === 'confirmado') {
|
if ($resultado['estado'] === 'confirmado') {
|
||||||
$buttons = [
|
$this->autoAplicarRecarga($chatId, $state, $solicitud, $solicitud->monto);
|
||||||
[['text' => "✅ Aplicar recarga de \$$monto", 'callback_data' => 'pay_apply|' . $valor . '|' . $ref]],
|
|
||||||
[['text' => '🔙 Menú principal', 'callback_data' => 'menu']],
|
|
||||||
];
|
|
||||||
// Limpiar pago pendiente del estado
|
|
||||||
unset($state['data']['pago_pendiente']);
|
|
||||||
$this->setState($chatId, $state);
|
|
||||||
$this->sendWithKeyboard($chatId, "✅ *¡Pago confirmado!* Tu recarga de \$$monto está lista.", $buttons);
|
|
||||||
|
|
||||||
} elseif ($resultado['estado'] === 'ya_usado') {
|
} elseif ($resultado['estado'] === 'ya_usado') {
|
||||||
unset($state['data']['pago_pendiente']);
|
unset($state['data']['pago_pendiente']);
|
||||||
@@ -303,7 +292,6 @@ class TelegramBotService
|
|||||||
]);
|
]);
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
// Sigue sin encontrarse — dejar pendiente para otro reintento
|
|
||||||
$this->sendWithKeyboard($chatId,
|
$this->sendWithKeyboard($chatId,
|
||||||
"⏳ *Aún no encontramos tu pago.*\nPuede demorar entre 1 y 2 minutos en llegar. Espera un momento y presiona Reintentar.",
|
"⏳ *Aún no encontramos tu pago.*\nPuede demorar entre 1 y 2 minutos en llegar. Espera un momento y presiona Reintentar.",
|
||||||
[
|
[
|
||||||
@@ -314,6 +302,26 @@ class TelegramBotService
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private function autoAplicarRecarga(string $chatId, array $state, SolicitudRecarga $solicitud, int $monto): void
|
||||||
|
{
|
||||||
|
$usuarioId = $state['user_id'];
|
||||||
|
$user = User::with('saldo')->find($usuarioId);
|
||||||
|
$saldo = $user->saldo ?? Saldo::create(['usuario_id' => $usuarioId, 'valor' => 0]);
|
||||||
|
$nuevoSaldo = $saldo->valor + $monto;
|
||||||
|
$saldo->update(['valor' => $nuevoSaldo]);
|
||||||
|
|
||||||
|
$solicitud->confirmar();
|
||||||
|
|
||||||
|
unset($state['data']['pago_pendiente'], $state['data']['solicitud_recarga_id']);
|
||||||
|
$this->setState($chatId, $state);
|
||||||
|
|
||||||
|
$this->send($chatId,
|
||||||
|
"✅ *¡Tu recarga de \$" . number_format($monto) . " fue aplicada exitosamente!*\n"
|
||||||
|
. "Tu nuevo saldo es *\$" . number_format($nuevoSaldo) . "*"
|
||||||
|
);
|
||||||
|
$this->showMainMenu($chatId);
|
||||||
|
}
|
||||||
|
|
||||||
public function handleVoice(string $chatId, string $fileId, string $nombre = '', int $duracionSegundos = 0): void
|
public function handleVoice(string $chatId, string $fileId, string $nombre = '', int $duracionSegundos = 0): void
|
||||||
{
|
{
|
||||||
$state = $this->getState($chatId);
|
$state = $this->getState($chatId);
|
||||||
@@ -1216,6 +1224,14 @@ class TelegramBotService
|
|||||||
$text .= "\n⚠️ Después de transferir, envía la *foto del comprobante* aquí y el sistema lo validará automáticamente."
|
$text .= "\n⚠️ Después de transferir, envía la *foto del comprobante* aquí y el sistema lo validará automáticamente."
|
||||||
. "\n_Tienes hasta 15 minutos después de la transferencia para enviar el soporte._";
|
. "\n_Tienes hasta 15 minutos después de la transferencia para enviar el soporte._";
|
||||||
|
|
||||||
|
// Registrar solicitud de recarga pendiente
|
||||||
|
if ($userId = ($this->getState($chatId)['user_id'] ?? null)) {
|
||||||
|
$solicitud = SolicitudRecarga::crear($userId, $monto, 'telegram');
|
||||||
|
$state = $this->getState($chatId);
|
||||||
|
$state['data']['solicitud_recarga_id'] = $solicitud->id;
|
||||||
|
$this->setState($chatId, $state);
|
||||||
|
}
|
||||||
|
|
||||||
$this->sendWithKeyboard($chatId, $text, [
|
$this->sendWithKeyboard($chatId, $text, [
|
||||||
[['text' => '🔙 Menú principal', 'callback_data' => 'menu']],
|
[['text' => '🔙 Menú principal', 'callback_data' => 'menu']],
|
||||||
]);
|
]);
|
||||||
|
|||||||
Reference in New Issue
Block a user