- 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>
117 lines
3.8 KiB
PHP
117 lines
3.8 KiB
PHP
<?php
|
|
|
|
namespace App\Services;
|
|
|
|
use App\Models\ChatConfig;
|
|
use Illuminate\Support\Facades\Http;
|
|
use Illuminate\Support\Facades\Log;
|
|
|
|
class GeminiVisionService
|
|
{
|
|
private string $apiKey;
|
|
private string $apiUrl = 'https://generativelanguage.googleapis.com/v1beta/models/gemini-1.5-flash:generateContent';
|
|
|
|
public function __construct()
|
|
{
|
|
$this->apiKey = ChatConfig::get('gemini_api_key', '');
|
|
}
|
|
|
|
/**
|
|
* Extrae datos de pago de una imagen de comprobante.
|
|
*
|
|
* @param string $base64 Imagen en base64
|
|
* @param string $mimeType MIME type (image/jpeg, image/png, etc.)
|
|
* @return array|null ['banco', 'valor', 'referencia', 'fecha', 'hora', 'remitente'] o null
|
|
*/
|
|
public function extraerPago(string $base64, string $mimeType): ?array
|
|
{
|
|
if (! $this->apiKey) {
|
|
Log::warning('[Gemini Vision] No hay API key configurada.');
|
|
return null;
|
|
}
|
|
|
|
$prompt = $this->buildPrompt();
|
|
|
|
try {
|
|
$response = Http::withHeaders(['Content-Type' => 'application/json'])
|
|
->timeout(15)
|
|
->post("{$this->apiUrl}?key={$this->apiKey}", [
|
|
'contents' => [[
|
|
'parts' => [
|
|
['text' => $prompt],
|
|
['inline_data' => ['mime_type' => $mimeType, 'data' => $base64]],
|
|
],
|
|
]],
|
|
'generationConfig' => [
|
|
'temperature' => 0.1,
|
|
'maxOutputTokens' => 200,
|
|
],
|
|
]);
|
|
|
|
if (! $response->successful()) {
|
|
Log::warning('[Gemini Vision] API error: ' . $response->body());
|
|
return null;
|
|
}
|
|
|
|
$text = $response->json('candidates.0.content.parts.0.text', '');
|
|
return $this->parseRespuesta($text);
|
|
|
|
} catch (\Throwable $e) {
|
|
Log::error('[Gemini Vision] Excepcion: ' . $e->getMessage());
|
|
return null;
|
|
}
|
|
}
|
|
|
|
private function buildPrompt(): string
|
|
{
|
|
return <<<PROMPT
|
|
Analiza esta imagen de comprobante de pago bancario o transferencia.
|
|
|
|
Extrae los siguientes campos:
|
|
- banco: nombre del banco (Bancolombia, Nequi, Davivienda, etc.)
|
|
- valor: monto en numeros enteros sin simbolos ni puntos (ej: 50000)
|
|
- referencia: numero de referencia o transaccion (si existe)
|
|
- fecha: fecha en formato dd/mm/yyyy
|
|
- hora: hora en formato HH:mm (si existe)
|
|
- remitente: nombre de quien envia el dinero (si aparece)
|
|
|
|
Responde UNICAMENTE con un JSON valido, sin markdown, sin explicacion:
|
|
{"banco": "...", "valor": 50000, "referencia": "...", "fecha": "...", "hora": "...", "remitente": "..."}
|
|
|
|
Si no encuentras un campo, usa null para ese campo.
|
|
Si la imagen NO es un comprobante de pago, responde exactamente: {"error": "no_es_comprobante"}
|
|
PROMPT;
|
|
}
|
|
|
|
private function parseRespuesta(string $text): ?array
|
|
{
|
|
$text = trim($text);
|
|
$text = preg_replace('/```json\s*|\s*```/', '', $text);
|
|
$text = trim($text);
|
|
|
|
$json = json_decode($text, true);
|
|
|
|
if (! is_array($json)) {
|
|
return null;
|
|
}
|
|
|
|
if (isset($json['error'])) {
|
|
return null;
|
|
}
|
|
|
|
// Normalizar valor a entero
|
|
if (isset($json['valor'])) {
|
|
$json['valor'] = (int) preg_replace('/[^0-9]/', '', (string) $json['valor']);
|
|
}
|
|
|
|
return [
|
|
'banco' => $json['banco'] ?? null,
|
|
'valor' => $json['valor'] ?? null,
|
|
'referencia' => $json['referencia'] ?? null,
|
|
'fecha' => $json['fecha'] ?? null,
|
|
'hora' => $json['hora'] ?? null,
|
|
'remitente' => $json['remitente'] ?? null,
|
|
];
|
|
}
|
|
}
|